Commit 1d2fcc0
authored
test(workflows): guard against workflow-level schema fields being silently dropped at parse (#2459)
* test(workflows): guard against workflow-level schema fields being silently dropped at parse
parseWorkflow hand-assembles its result field by field, so a field added to
workflowDefinitionSchema but not to that object literal is silently discarded:
the YAML parses, the workflow loads, and the feature is inert.
That already happened. `requires:` landed in workflowBaseSchema in ab81248
(2026-06-01) without touching the loader, and the assembly block only arrived in
2d7bf58 (2026-07-16) — six weeks in which the GitHub capability gate could never
fire for a discovered workflow, fixed incidentally inside an unrelated PR.
This is the third instance of one pattern: parallel enumerations that must agree
with nothing enforcing agreement. The ref-surface enumerations carry a KEEP IN
SYNC comment and were found broken anyway (#2450); the nested key sets are
derived from each schema's .shape and cannot drift (#2455). This applies the
derived form to the second case.
The field list comes from workflowDefinitionSchema.shape, so a new schema field
fails the test until it is given a fixture. Deliberately not solved by deriving
the assembly itself — the hand assembly exists because of warn-and-drop, and
schema.parse() would reject a bad field instead of logging and dropping it.
The per-field assertion clears the mock logger first so it can tell the two
failure causes apart: a warning means the fixture value is invalid (warn-and-drop
working as designed), silence means a valid field was dropped (the actual bug).
Verified by breaking it both ways: removing `requires` from the object literal
reproduces the historical bug and fails with the right diagnosis, and adding a
new schema key fails the ratchet until a fixture exists.
* test(workflows): tighten the parity guard after review
Addresses I1, I2, I3 and S1, S3, S4 from the review on #2459. No change to what
the guard catches; all six make a precision tool more precise.
I1 — the docblock claimed warn-and-drop universally. Re-verified the field audit
against loader.ts rather than taking it on faith: 4 of the 20 hard-reject
(name, description, nodes, evidence_policy at :619-629), 13 warn-and-drop, and 3
coerce silently with no log at all (provider :423, model :425, persist_sessions
:473 — there is no invalid_provider/invalid_model/invalid_persist_sessions warn
event anywhere in the file). Rewritten to say most rather than all, and to point
at loader.ts as the authority instead of restating a per-field table that would
rot the moment a field changes category.
I3 — the two-branch failure message was backwards for exactly those 3 silent
fields: a bad `provider: 123` fixture is discarded with no warning, so the
message confidently blamed the loader and sent the reader into parseWorkflow when
the fixture was at fault. That is the same failure the message exists to prevent,
and the one I hit during development with a bad `thinking: true` fixture. Fixed
by ranking rather than verdict: a warning is still strong evidence the fixture is
wrong, but silence now names both causes and points at the fixture first. Chosen
over listing the three exceptions in a comment, which would duplicate loader.ts
and rot. This subsumes S2's unstated-invariant concern.
I2 — effort, thinking and sandbox used presence checks where the other 17
fixtures check values, and their schemas transform deterministically, so exact
checks are available. Verified by mutation: returning effort:'low' and
thinking:{type:'disabled'} from the loader now fails both round-trips, where
before it left them green.
S1 — the hand-assembly literal predates 2d7bf58; only the requires entry landed
there. Reworded so it cannot be skimmed as "the mechanism didn't exist until then".
S3 — the two diagnostic strings moved out of the assertion into a named message.
S4 — nodes?.length, so a dropped nodes yields a clean false instead of a TypeError.
Verified: full validate green (132 batches, 0 fail); the I3 message re-checked by
running a deliberately invalid provider fixture; I2 re-checked by mutation.1 parent 8704a65 commit 1d2fcc0
1 file changed
Lines changed: 149 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
| |||
4074 | 4077 | | |
4075 | 4078 | | |
4076 | 4079 | | |
| 4080 | + | |
| 4081 | + | |
| 4082 | + | |
| 4083 | + | |
| 4084 | + | |
| 4085 | + | |
| 4086 | + | |
| 4087 | + | |
| 4088 | + | |
| 4089 | + | |
| 4090 | + | |
| 4091 | + | |
| 4092 | + | |
| 4093 | + | |
| 4094 | + | |
| 4095 | + | |
| 4096 | + | |
| 4097 | + | |
| 4098 | + | |
| 4099 | + | |
| 4100 | + | |
| 4101 | + | |
| 4102 | + | |
| 4103 | + | |
| 4104 | + | |
| 4105 | + | |
| 4106 | + | |
| 4107 | + | |
| 4108 | + | |
| 4109 | + | |
| 4110 | + | |
| 4111 | + | |
| 4112 | + | |
| 4113 | + | |
| 4114 | + | |
| 4115 | + | |
| 4116 | + | |
| 4117 | + | |
| 4118 | + | |
| 4119 | + | |
| 4120 | + | |
| 4121 | + | |
| 4122 | + | |
| 4123 | + | |
| 4124 | + | |
| 4125 | + | |
| 4126 | + | |
| 4127 | + | |
| 4128 | + | |
| 4129 | + | |
| 4130 | + | |
| 4131 | + | |
| 4132 | + | |
| 4133 | + | |
| 4134 | + | |
| 4135 | + | |
| 4136 | + | |
| 4137 | + | |
| 4138 | + | |
| 4139 | + | |
| 4140 | + | |
| 4141 | + | |
| 4142 | + | |
| 4143 | + | |
| 4144 | + | |
| 4145 | + | |
| 4146 | + | |
| 4147 | + | |
| 4148 | + | |
| 4149 | + | |
| 4150 | + | |
| 4151 | + | |
| 4152 | + | |
| 4153 | + | |
| 4154 | + | |
| 4155 | + | |
| 4156 | + | |
| 4157 | + | |
| 4158 | + | |
| 4159 | + | |
| 4160 | + | |
| 4161 | + | |
| 4162 | + | |
| 4163 | + | |
| 4164 | + | |
| 4165 | + | |
| 4166 | + | |
| 4167 | + | |
| 4168 | + | |
| 4169 | + | |
| 4170 | + | |
| 4171 | + | |
| 4172 | + | |
| 4173 | + | |
| 4174 | + | |
| 4175 | + | |
| 4176 | + | |
| 4177 | + | |
| 4178 | + | |
| 4179 | + | |
| 4180 | + | |
| 4181 | + | |
| 4182 | + | |
| 4183 | + | |
| 4184 | + | |
| 4185 | + | |
| 4186 | + | |
| 4187 | + | |
| 4188 | + | |
| 4189 | + | |
| 4190 | + | |
| 4191 | + | |
| 4192 | + | |
| 4193 | + | |
| 4194 | + | |
| 4195 | + | |
| 4196 | + | |
| 4197 | + | |
| 4198 | + | |
| 4199 | + | |
| 4200 | + | |
| 4201 | + | |
| 4202 | + | |
| 4203 | + | |
| 4204 | + | |
| 4205 | + | |
| 4206 | + | |
| 4207 | + | |
| 4208 | + | |
| 4209 | + | |
| 4210 | + | |
| 4211 | + | |
| 4212 | + | |
| 4213 | + | |
| 4214 | + | |
| 4215 | + | |
| 4216 | + | |
| 4217 | + | |
| 4218 | + | |
| 4219 | + | |
| 4220 | + | |
| 4221 | + | |
| 4222 | + | |
| 4223 | + | |
| 4224 | + | |
| 4225 | + | |
0 commit comments