Filed unassigned, no pm:queue, for triage to route. Found while driving PR #8626 (#8577) to green; not a defect in that PR, which fixed the underlying problem at the doubles and is green.
What happened
PR #8626 went red on CI under the job named ESLint, on the step check:engine-double-contract:
x PINNED [delete]: …/suggested-audience-bindings-install-path.test.ts declares 2 engine
double(s) whose delete() does not route through assertEngineDeleteDispatch (lines 143, 337)
x PINNED [update]: … same two, assertEngineUpdateDispatch
The dev seat had derived its gate union with scripts/pm/dispatch-gates.mjs against the actual changed paths — twice, including a re-derivation after adding a vitest config — and check:engine-double-contract was in neither derivation.
Why it was not in the derivation — measured, not inferred
CHANGE_KIND_GATES declares exactly two change kinds:
- adds or edits a test file →
check:query-options-erasure + check:type-check-coverage + check:type-check-debt - edits a file in a package that owns an
i18n-extract.config.ts → check:i18n
check:engine-double-contract appears in the script only in its closing prose, which explicitly hands it to human judgement:
the rest stay the PM judgment call — new fake engine => check:engine-double-contract, new error code => check:error-code-casing, any edit => check:nul-bytes
So the derivation did not lose an entry it has — it has none, by design. This is worth stating plainly because the first reading (including mine) was "the tool missed it", which would have pointed at the wrong fix.
The other half of the miss was judgement: two delegating pass-through seams wrapping a real engine were not classified as "fake engines", which is exactly the call the prose delegates. That reading is defensible — they delegate to a real ObjectQL — and it is still the reading the gate disagrees with.
The question this raises
A mechanical trigger for this gate does appear to exist: a test file declaring an object literal with delete/update members is the shape check-engine-double-contract.mjs itself scans for. If that is a reliable enough signal, the gate could move from the prose (judgement) into CHANGE_KIND_GATES (derived), and this class of miss stops depending on whether the author recognises their own seam as a double.
Arguments the other way, which is why this is filed rather than fixed:
Whoever takes this should decide whether the gate belongs in the derived set at all, and if so, whether the prose's other two go with it.
Related
Filed unassigned, no
pm:queue, for triage to route. Found while driving PR #8626 (#8577) to green; not a defect in that PR, which fixed the underlying problem at the doubles and is green.What happened
PR #8626 went red on CI under the job named ESLint, on the step
check:engine-double-contract:The dev seat had derived its gate union with
scripts/pm/dispatch-gates.mjsagainst the actual changed paths — twice, including a re-derivation after adding a vitest config — andcheck:engine-double-contractwas in neither derivation.Why it was not in the derivation — measured, not inferred
CHANGE_KIND_GATESdeclares exactly two change kinds:check:query-options-erasure+check:type-check-coverage+check:type-check-debti18n-extract.config.ts→check:i18ncheck:engine-double-contractappears in the script only in its closing prose, which explicitly hands it to human judgement:So the derivation did not lose an entry it has — it has none, by design. This is worth stating plainly because the first reading (including mine) was "the tool missed it", which would have pointed at the wrong fix.
The other half of the miss was judgement: two delegating pass-through seams wrapping a real engine were not classified as "fake engines", which is exactly the call the prose delegates. That reading is defensible — they delegate to a real
ObjectQL— and it is still the reading the gate disagrees with.The question this raises
A mechanical trigger for this gate does appear to exist: a test file declaring an object literal with
delete/updatemembers is the shapecheck-engine-double-contract.mjsitself scans for. If that is a reliable enough signal, the gate could move from the prose (judgement) intoCHANGE_KIND_GATES(derived), and this class of miss stops depending on whether the author recognises their own seam as a double.Arguments the other way, which is why this is filed rather than fixed:
check:error-code-casing,check:nul-bytes) leaves the prose describing a set it no longer matches;check:engine-double-contractcounts declaration sites, so a behaviourally-distinct engine double built byObject.assignover an existing one is not counted #8553 already records that the detector counts declaration sites, so anObject.assign-built double is invisible to it — a convention entry keyed on the same shape inherits that blind spot.Whoever takes this should decide whether the gate belongs in the derived set at all, and if so, whether the prose's other two go with it.
Related
dispatch-gates.mjsnamescheck:type-check-coveragefor a rationale onlycheck:type-check-debtsatisfies — the ratchet half never runs locally #8545 —dispatch-gates.mjsnamed the wrong ratchet invocation (fixed,375376456446)check:engine-double-contractcounts declaration sites, so a behaviourally-distinct engine double built byObject.assignover an existing one is not counted #8553 — the detector counts declaration sites, so anObject.assign-built double is invisible