feat(gate-115): record a stale name whose target does not exist - #738
Merged
Conversation
Ten of gate-115's twenty findings are answered questions. Someone read the other app's route table or class surface, found that the target does not exist under any name, and left the stale name deliberately. The gate raises all ten on every run, and it had no way to be told. That matters before any repo turns it blocking, because the only two moves available then are to delete a deliberate near-term binding or to repoint it at a name that misses identically. The second is the dangerous one: a repoint that lands on a route nobody publishes reads to the next person as a fix already applied, and it is quieter than the stale name it replaced, which at least still said out loud that something was unfinished. So this adds the package's one exclusion convention to gate-115: `@stale-fleet-app-id exclude <reason>`, with the reason graded by the shared `exclusion_reason.is_reason_bearing()`, exactly as gate-6, 16, 19, 25, 26, 52 and 57 already read their own tags. Nothing about the format is new. Four properties, each with an arm that reddens if it goes: * It attaches to the BINDING, the statement's own docblock or the comment lines directly above it, reusing the statement unit the dual-spelling accept already defines. `statement_ranges()` is split out of `statement_blocks()` so there is one definition of a statement rather than two that can drift. The enclosing method's docblock is out of scope on purpose: a marker there would cover bindings added later that nobody read the other repo about. * A marker that reaches no finding is REPORTED as misplaced. A scope rule that quietly does not apply is indistinguishable from a broken one, and "why is my exclusion not working" is an afternoon without the answer printed next to it. * An excluded finding is COUNTED AND PRINTED on every run. Without that, "0 findings" means either nothing stale or somebody decided the stale thing was fine, and no reader can tell those apart. This checker has already made the silent version of that mistake once, in the slug ordering. * A bare marker excludes nothing, and register slugs take no marker at all. A slug is answered by probing OpenRegister for the name the register responds to, and nothing written in a comment makes an old slug return rows on a migrated instance. The marker test runs LAST in the loop, after the finding is established, so a marker can only ever silence something that would have been reported. Verified: the 33 unit arms pass; five mutations (exclusion never applies, file-wide scope, bare markers accepted, misplaced reporting removed, slugs made markable) each redden the matching arm and nothing else. The gate-acceptance bundle gains a marked binding in the file BOTH arms share, so removing marker support turns the CLEAN arm red rather than leaving the planted one green; confirmed by mutation, clean exits 1 mutated and 0 restored. Re-run over clean `development` clones of the six repos carrying findings, the counts are unchanged from before this commit.
This was referenced Sep 10, 2026
Merged
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 this is
Gate-115 shipped today reporting 20 findings across 6 repos. Ten of them are stale register slugs, which need a probe and are not touched here. The other ten are stale names, and every one is a question somebody already answered: an agent read the other app's route table or class surface, found the target does not exist under any name, and left the binding deliberately. Nine of the ten already carry the evidence in a comment right next to the line.
The gate raises all ten on every run and had no way to be told.
Why it matters now rather than later
Gate-115 ships advisory, and
HYDRA_GATE_STALE_FLEET_APP_ID_BLOCKING=1turns it on per repo. The moment a repo turns it on, those ten findings offer their owner two moves, and both are bad:The second is the one to worry about.
OCA\Integriq\Service\PaymentServicedoes not exist. Neither doesOCA\Integriq\Db\SourceMapper. Integriq publishes no/api/sources/{source}/call, no/api/lti/deployments/{id}/launchand no plainGET /api/endpoints. A repoint onto any of those produces a lookup that misses exactly as it missed before, on a diff that reads to the next person as a fix already applied. That is quieter than the stale name, which at least still says out loud that the integration is unfinished. This is the same shape the gate's own header warns about in thegenerateFromTemplate()andpdok.parcelcases.So the answer to these ten is to record what was read.
What changed
@stale-fleet-app-id exclude <reason>, on the package's one exclusion convention, with the reason graded by the sharedexclusion_reason.is_reason_bearing(). Gates 6, 16, 19, 25, 26, 52 and 57 already read their own tags this way. Nothing about the format is new.Four properties, each pinned by an arm:
statement_ranges()is split out ofstatement_blocks()so there is one definition of a statement rather than two that can drift apart. The enclosing method's docblock is deliberately out of scope, because a marker there would cover bindings added later that nobody read the other repo about.The marker test runs last in the loop, after the finding is established, so a marker can only ever silence something that would have been reported. Without that ordering, a marker beside a name that is already correct would count as a recorded answer and the recorded count would become evidence that somebody answered a question nobody asked.
Verification
Fallbacks.php, the file both arms share, so removing marker support turns the CLEAN arm red rather than leaving the planted one green. Confirmed: clean exits 1 mutated, 0 restored.diff -rq clean plantedstill reports exactly one differing file.developmentclones of all six repos carrying findings, the counts are unchanged from before this commit: pipelinq 2+5, learniq 4+0, openregister 1+2, buildiq 1+2, launchpad 2+0, hermiq 0+1.Merge order
This lands first. CI resolves hydra-gates at
@main, so the ten markers in the app repos do nothing until it does. Gate-115 is advisory in all six, so no app CI changes state either way.hermiq's single finding is a register slug and is out of scope here.
🤖 Generated with Claude Code