fix(gates 12-22): four gates could not detect the defect they exist for, and gate-20 had never fired at all - #277
Open
rubenvdlinde wants to merge 2 commits into
Open
Conversation
…or, and gate-20 had never fired at all Every gate in the 12-22 band was given one textbook true positive in a real fleet repo and asked to catch it. Measured against package sha cdfbd7a, and re-measured on top of fef032b (#272), across openregister (207 .vue, 794 spec scenarios), pipelinq (202 .vue, type:"dashboard" pages) and shillinq (an ADR-040 AppHost adopter that keeps its own SettingsController). Seven fired, named the plant, and returned to their exact prior verdict when it was removed: 12, 13, 14 (both invariants), 15, 16, 18, 19, 21, 22. Four did not. 1. GATE-20 HAD NEVER FIRED. NOT RARELY - NEVER. Its search was `grep -nE "->${_pat//(/\\(}" "${_file}"`, and the expanded pattern `->findObjects\(` begins with `-`, so grep parses it as OPTIONS: `grep: invalid option -- '>'`, exit 2, no output. `2>/dev/null || true` discarded the message and the status, every file came back with zero hits, and the gate printed PASS. Planting `$this->objectService->findObjects(...)` in openregister's ActionsController: PASS. Repairing the grep ALONE is not the fix. The receiver test was "the FILE mentions ObjectService somewhere", and with the grep working that yields 14 findings on openregister and 5 on shillinq, ALL FALSE - `createFromArray()` is a real method on OpenRegister's *Mappers*. The receiver is now part of the pattern. Fleet measurement across the three repos afterwards: ONE finding, and it is real - shillinq's BookingNotificationController resolves OCA\OpenRegister\Service\ObjectService from the container inside its non-admin authorisation guard and calls findObject(), which does not exist on it (real surface: find/findAll/saveObject/createObject/updateObject/ deleteObject). That is a BadMethodCallException in an auth guard, shipped. 2. GATE-17 WAS WRONG ABOUT THE SAME API, IN THE OTHER DIRECTION. Four of the six names in OBJECT_SERVICE_CRUD - findObjects, createFromArray, updateFromArray, deleteFromId - do not exist on ObjectService; they are precisely what gate-20 flags as fabricated. findAll/createObject/ updateObject/deleteObject were absent. And `^\s*return\s+new\s+JSONResponse` sits in WRAPPER_NOISE_PATTERNS, which was tested BEFORE the ObjectService check - so the commonest pass-through spelling of all, `return new JSONResponse($this->objectService->findAll([]));`, was discarded as "response wrapping" with the call still inside it. It did not merely miss the shape: RESCUE_PATTERNS' `\$this->\w+Service->\w+\(` then matched it and returned False. The gate rescued its own subject. Blast radius of both changes across openregister/pipelinq/shillinq: 0 new findings. The CRUD-name filter and the `@spec exclude` escape hatch are untouched. 3. GATE-14 NEVER JUDGED THE TEN ROUTES AppHost SUPPLIES (#265, closed). #223 taught invariant 1 about `Routes::standard()`. Invariant 2 asks the opposite question and still read route names as literals out of the leaf's own appinfo/routes.php, where those ten names never appear. Deleting `SettingsController::update()` from shillinq - which keeps its own controller, as aliasControllerUnlessLeafDefinesIt explicitly allows - leaves PUT /api/settings resolving to nothing. That is not a 404: the router matches, ControllerMethodReflector reflects, the request dies 500. shillinq's own docblock on update() spells the hazard out. Gate-14's findings log came back EMPTY and the gate said PASS. 4. THREE GATES REPORTED PASS OVER A CRASHED CHECKER. With python3 replaced by a stub that always exits 1 - so not one file was inspected by any python-backed gate: gate-12 SKIPPED (wiring) gate-15 PASS gate-17 SKIPPED (wiring) gate-16 PASS gate-18 PASS gate-19 FAIL - "an unreported number of scenario(s)" 15/16/18 wrote `2>/dev/null || true` and counted lines in an empty log. 19 read exit 1 as EXIT_FAIL from a helper that never printed its own summary, producing a blocking verdict with a count nobody measured. The three helpers now print a terminal `# count=` marker and the runner requires it; gate-18's helper contract is "always exit 0", so any non-zero exit is a wiring skip; gate-19 treats "EXIT_FAIL with no FAIL - N summary line" as wiring. check_spec_coverage.py and check_dashboard_antipattern.py also stop returning their finding COUNT as an exit status (#209). 5. GATES 12 AND 13 PASSED OVER A src/ WITH ZERO .vue (#274). `[ -d src ]` is not `src/ contains a Vue component`. nldesign's src/ holds one manifest.json, and at fef032b both gates print PASS there - the shape that let twelve gates certify nldesign in #225, still current, not one `rm` away. Now `na`, with the judgement #274 asked for stated in the reason: NcSelect / NcModal / NcDialog are Vue SFC components, a PHP template cannot instantiate one, so these two stay .vue-only and gate-40 keeps the language-agnostic input-label rule for templates/. 6. GATE-22's VERDICT DEPENDED ON WHERE THE GATES WERE CHECKED OUT. `require('ajv/dist/2020')` resolves relative to check_manifest.js, never to the repo under test. Run from openregister's own root with node_modules/ajv PRESENT in that root, the validator printed "Ajv is not resolvable from this process (no node_modules, no NODE_PATH)" - false - and gate-22 went FAIL; exporting NODE_PATH to that same directory flipped it to PASS. Same tree, same package, two verdicts. Resolution is now anchored on the manifest's own repo root, then cwd, then this package, and the degradation names every directory it searched. 7. GATE-15's SLOT MATCHER HAD AN ATTRIBUTE-ORDER BLIND SPOT. `<template #widget-x class="wide">` and `<template #widget-x='{i}'>` did not match - the pattern assumed the slot binding was the last thing before `>`. Same class as gate-44's first-attribute stop in #272. TESTS test_gate_or_objectservice_surface.sh NEW - gate-20 and gate-17, each with the silent sibling that must stay silent (a mapper's real createFromArray; a domain-named method with an identical body), plus a direct assertion that a grep pattern starting with `-` is parsed as options test_gate_crashed_checker_is_not_a_finding.sh all six python-backed gates under a broken interpreter, generically, so a future gate inherits the assertion test_gate_empty_scope_never_passes.sh gates 12/13 on an nldesign-shaped repo, with the one-.vue control test_gate_route_registration.sh new fixture routes-standard-missing- update/, differing from its sibling by exactly the deleted method test_check_manifest.sh ajv resolved from the subject's node_modules with cwd and NODE_PATH pointing elsewhere test_check_dashboard_antipattern.py four slot-tag spellings that were silent misses Every one mutation-checked against the pre-fix tree: the crashed-checker arm is 7 failures red at cdfbd7a, the 12/13 arm 3 failures red at fef032b, the slot-tag arm 4 subtests red, and the ajv anchor red - all green after. 50 helper suites pass, 59 bin-level assertions pass.
…13 ran test-hydra-gates-bin.sh looped 12 and 13 into 'with src/ present, every src-guarded gate really runs'. That fixture's src/ holds one .js file and no .vue, so the assertion encoded the belief this PR removes: that `[ -d src ]` is the same as 'the gate has a subject'. Both gates printed PASS there — an empty glob under an existing src/, nldesign's exact shape. They are now asserted separately and more strictly: not 'did not say na', but 'said na and NAMED the empty .vue glob'. PASS is called out by name as the regression it would be. 61 bin assertions pass, 50 helper suites pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was measured
Every gate in the 12–22 band was given one textbook true positive of exactly what it exists to catch, in a real fleet repo whose shape suits it, and asked to catch it. Then the plant was removed and the gate had to return to its exact prior verdict, and a clean repo had to stay clean.
Baseline package
cdfbd7ab([hydra-gates] gate package: cdfbd7ab1e28d8619fafa6cf772b0013ca4f9595); re-measured on top offef032b(#272) at6f9490f9.Subjects: openregister (207
.vue, 794 spec scenarios,lib/Settings/*register*.json), pipelinq (202.vue, sixtype:"dashboard"pages), shillinq (an ADR-040 AppHost adopter that keeps its ownSettingsController), and an nldesign-shaped fixture whosesrc/holds a singlemanifest.json.<NcSelect :reduce="(option) => option.value" />, noinput-labelnainstead of PASS on asrc/with zero.vue<NcModal>insrc/views/JSONResponsemethod (b) route → missing method (c)settings#updatedeleted from an AppHost adopter<CnDashboardPage>inside a#widget-<id>slot@specfetchAll() { return new JSONResponse($this->objectService->findAll([])); }#### Scenario:with no@e2e$this->objectService->findObjects(...)<<<<<<</=======/>>>>>>>inlib/*.php.mdthat documents themtype+ additional propertyat /pages/0…The four that could not detect their own subject
1. gate-20 had never fired. Not rarely — never.
The pattern begins with
-, so grep parses it as options.2>/dev/null || truediscarded both the message and the status, so every file came back with zero hits and the gate printed PASS everywhere in the fleet.Repairing the grep alone is not the fix. The receiver test was "the file mentions ObjectService somewhere", and with the grep working that produces 14 findings on openregister and 5 on shillinq, all false —
createFromArray()is a real method on OpenRegister's Mappers. The receiver is now part of the pattern. Fleet measurement across the three repos afterwards: one finding, and it is real — shillinq'sBookingNotificationControllerresolvesOCA\OpenRegister\Service\ObjectServicefrom the container inside its non-admin authorisation guard and callsfindObject(), which does not exist on it. ABadMethodCallExceptionin an auth guard, shipped.2. gate-17 was wrong about the same API, in the other direction.
Four of the six names in
OBJECT_SERVICE_CRUD—findObjects,createFromArray,updateFromArray,deleteFromId— do not exist on ObjectService; they are precisely what gate-20 flags as fabricated.findAll/createObject/updateObject/deleteObjectwere absent. And^\s*return\s+new\s+JSONResponsesits inWRAPPER_NOISE_PATTERNS, tested before the ObjectService check — so the commonest pass-through spelling of all was discarded as "response wrapping" with the call still inside it, then matched byRESCUE_PATTERNS'\$this->\w+Service->\w+\(and returnedFalse. The gate rescued its own subject.3. gate-14 never judged the ten routes AppHost supplies (#265).
#223 taught invariant 1 about
Routes::standard(); invariant 2 asks the opposite question and still read route names as literals out of the leaf's ownappinfo/routes.php, where those ten names never appear. DeletingSettingsController::update()from shillinq — which keeps its own controller, asaliasControllerUnlessLeafDefinesItexplicitly allows — leavesPUT /api/settingsresolving to nothing. Not a 404: the router matches,ControllerMethodReflectorreflects, the request dies 500. shillinq's own docblock onupdate()spells the hazard out. Gate-14's findings log came back empty.4. Three gates reported PASS over a crashed checker.
With
python3replaced by a stub that always exits 1 — so not one file was inspected by any python-backed gate:15/16/18 wrote
2>/dev/null || trueand counted lines in an empty log. 19 read exit 1 asEXIT_FAILfrom a helper that never printed its own summary — a blocking verdict carrying a count nobody measured.Two more, found on the way
gates 12 and 13 passed over a
src/with zero.vue(#274).[ -d src ]is not "src/contains a Vue component". Atfef032bboth print PASS on an nldesign-shaped repo — the shape that let twelve gates certify nldesign in #225. Not onermaway: current. Nowna, with the judgement #274 asked for written down —NcSelect/NcModal/NcDialogare Vue SFC components, a PHP template cannot instantiate one, so these two stay.vue-only and gate-40 keeps the language-agnostic rule fortemplates/.gate-22's verdict depended on where the gates were checked out.
require('ajv/dist/2020')resolves relative tocheck_manifest.js, never to the repo under test. Run from openregister's own root withnode_modules/ajvpresent in that root, the validator printed "Ajv is not resolvable from this process (no node_modules, no NODE_PATH)" — false — and gate-22 went FAIL; exportingNODE_PATHto that same directory flipped it to PASS. Resolution is now anchored on the manifest's own repo root, then cwd, then this package, and the degradation names every directory it searched.gate-15's slot matcher had gate-44's blind spot.
<template #widget-x class="wide">and<template #widget-x='{i}'>did not match — the pattern assumed the slot binding was the last thing before>.Anti-widening
Every repair ships with the sibling that must stay silent:
createFromArray()in a file that mentions ObjectService → not flagged. Blast radius across openregister/pipelinq/shillinq: 1 finding, genuine.routes-standard/still yields exactly one finding..vuecarrying the defect and both go straight back to FAIL.python3on the same fixture still FAILs the planted legacy dialect..mdthat documents conflict markers is not flagged.Tests — all mutation-checked
test_gate_or_objectservice_surface.sh(new)--leading grep pattern is parsed as optionstest_gate_crashed_checker_is_not_a_finding.shcdfbd7a— all six python-backed gates under a broken interpreter, asserted generically so a future gate inherits ittest_gate_empty_scope_never_passes.shfef032b— gates 12/13 on the nldesign shape, with the one-.vuecontroltest_gate_route_registration.shroutes-standard-missing-update/, differing from its sibling by exactly the deleted methodtest_check_manifest.shtest_check_dashboard_antipattern.py50helper suites pass (2 quarantined, unchanged),59bin-level assertions pass.Verdicts will move
gate-20 and gate-17 can now produce findings in repos that were green — starting with the shillinq auth-guard call above. Gates 12/13/15/16/18/19 can now report
SKIPPED (wiring)orNOT APPLICABLE, neither of which is a pass.Closes #265. Closes the gate-12 half of #274.