Skip to content

feat(gate-115): record a stale name whose target does not exist - #738

Merged
rubenvdlinde merged 1 commit into
mainfrom
feat/gate-115-record-an-answered-finding
Sep 10, 2026
Merged

feat(gate-115): record a stale name whose target does not exist#738
rubenvdlinde merged 1 commit into
mainfrom
feat/gate-115-record-an-answered-finding

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

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=1 turns it on per repo. The moment a repo turns it on, those ten findings offer their owner two moves, and both are bad:

  1. delete a deliberate near-term binding, or
  2. repoint it at a name that misses identically.

The second is the one to worry about. OCA\Integriq\Service\PaymentService does not exist. Neither does OCA\Integriq\Db\SourceMapper. Integriq publishes no /api/sources/{source}/call, no /api/lti/deployments/{id}/launch and no plain GET /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 the generateFromTemplate() and pdok.parcel cases.

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 shared exclusion_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:

  • It attaches to the binding. The statement's own docblock, or the comment lines directly above the line, 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 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.
  • A marker that reaches no finding is reported. A scope rule that quietly does not apply is indistinguishable from a broken one.
  • An excluded finding is counted and printed on every run. Otherwise "0 findings" means either nothing stale or somebody decided the stale thing was fine, and no reader can separate those. This checker has already made the silent version of that mistake once, in the slug ordering it now warns about in a comment.
  • 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. Nothing anybody writes in a comment makes an old slug return rows on a migrated instance, so offering a marker there would only offer a way to silence a live defect.

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

  • 33 unit arms pass, 5 of them new.
  • Mutation checked, all five watched to fail. Exclusion never applies, file-wide scope, bare markers accepted, misplaced reporting removed, slugs made markable. Each reddens the matching arm and nothing else, then restores byte-identical.
  • The gate-acceptance bundle gains a marked stale binding in 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 planted still reports exactly one differing file.
  • Re-run over clean development clones 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.

repo PR
pipelinq 2 markers
learniq 4 markers
openregister 1 marker
buildiq 1 marker
launchpad 2 markers

hermiq's single finding is a register slug and is out of scope here.

🤖 Generated with Claude Code

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant