Skip to content

dispatch-gates needs a third verdict unreachable — 5 of 110 gate families declare a population that matches nothing #9883

Closed

Description

@claude

Follow-on from #9747 / PR #9875, filed by the domain:devx PM seat under ruling Q3 → A. PR #9875 deliberately did not build this: ruling 2 bounded that card to a three-gate pilot, and this sweep covers all 110 gate families. The pilot existed to produce the measurement that decides whether to generalize. It now has.

The proposal

One hintCovers sweep over git ls-files, giving scripts/pm/dispatch-gates.mjs a third verdict — unreachable — for a gate family whose declared population matches nothing in the tree.

From #9700 / PR #9799's census: it catches 5 of 110 families today, including check:release-body's 'application/json' hint.

Why this generalizes when delta-ratchets do not

This is the distinction PR #9875's data forced, and it is the reason to file this card rather than a blanket one:

  • Delta-ratchets do NOT generalize. The nuisance rate is a property of the population, not the proposal — the same gate family holds one rule measuring 0 net decreases/month and another measuring 10. Each needs its own measurement. (Carded separately.)
  • This sweep DOES generalize. It needs no per-gate knowledge. It asks one question — does this family's declared population match any file? — and that question is answerable identically for all 110.

⇒ The pilot's measurement argues for this sweep and against blanket ratchets. Those are different generalizations, and only one survived contact with the data.

Why an unreachable verdict is worth having

A gate family whose population matches nothing reports the same thing as one that scanned a real population and found no problems: green, silently. That is #4690 restated — "zero is a broken scan, not a clean repo" — and PR #9810's header already raises it to a hard error in its own scope.

Five families are in that state right now. Nobody knows, because nothing says so.

Scope

  • Add the third verdict to scripts/pm/dispatch-gates.mjs.
  • ⛔ Visibility only — no new merge-blocking context. Same constraint as the Seven gates report "clean" when they mean "I saw nothing I understood" — make unrecognised a verdict distinct from pass #9747 pilot; an unreachable verdict is information for whoever dispatches, not a CI failure.
  • Report the 5 current instances so they can be triaged individually. Some may be legitimately dormant; check:release-body looks like a real miss. Do not fix them in this card — the sweep and the instances are separate work.
  • ⚠️ scripts/pm/dispatch-gates.mjs is not a governed surface, so this is an ordinary dev card.

Falsification worth testing

If the sweep reports far more than 5, its recognizer is probably wrong rather than the repo being broken — a hint that matches nothing is usually a hint spelled for a path convention that changed. Report the number before assuming it is a defect count. Filing this card is not a claim that 5 is right; it is a claim that the number should exist.

Refs: #9747 (family) · PR #9875 (the pilot measurement, and ruling 2's boundary) · #9700 / PR #9799 (the census, and where the 5 comes from) · #4690 / PR #9810 (empty-population failure raised to a hard error).


Generated by Claude Code

Activity

  1. claude commented on Aug 19, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 13
    Session: session_01XqDQYVU5smx29ts9pAErja
    Branch: claude/issue-9883-dispatch-gates-unreachable
    Worktree: objectstack-issue-9883
    Domain: domain:devx
    File surface: scripts/pm/dispatch-gates.mjs (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (node scripts/pm/dispatch-gates.mjs --tier scripts/pm/dispatch-gates.mjs run this round: "no path-derived mandate … This line is a FLOOR, never a clearance"; content limb judged no — see below)
    Clause-②: no
    Serial constraints cleared: swept all open PRs for any touching dispatch-gates — none. The predecessor that produced this card's census, PR #9799 (#9700), is MERGED, as is PR #9875 (#9747) whose ruling gated this generalization.

    Why no: this card's own body scopes it ⛔ visibility only — no new merge-blocking context, the same constraint the #9747 pilot was ruled under. An unreachable verdict is information for whoever dispatches, not a CI failure, so nothing's accept/reject behaviour changes. That scoping is a ruling in the dispatch prompt, not a hope — if the work turns out to require a verdict change, the dev must stop and report, and the declaration flips.

    Swapped in for #9902 this round: that card is Clause-②: yes and needs contract-review tier, which is unavailable (Fable 5 quota exhausted mid-dispatch). #9902 is released back to pm:queue, unstarted.


    Generated by Claude Code

  2. os-steve commented on Aug 19, 2026

    @os-steve
    Collaborator
    {
      "issue": 9883,
      "status": "done",
      "branch": "claude/issue-9883-dispatch-gates-unreachable",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9939",
      "premise_still_valid": true,
      "summary": "dispatch-gates.mjs gained a third verdict, `unreachable`: one hintCovers sweep over `git ls-files` names every gate family whose WHOLE declared population matches nothing in the tree, with the reason each could not reach. It is cross-cutting, not a fourth bucket — like the unfiltered-workflow count it sits outside the residue accounting throw, classifyEntry is untouched, and no accept/reject behaviour anywhere changes (Clause-② stays `no`). The count prints in the residue summary on every run; the named listing with reasons prints under --residue. H1 re-derived: 6 of 114 families today (card said 5 of 110), including check:release-body's 'application/json'. Per ruling 2 none of the six is fixed and none was carded, so the triage stays on this card. Measured clean and NOT filed: the same defect class one level over — CI's own `paths:` triggers — has 0 of 26 positive patterns matching no tracked file.",
      "tests": "All at final commit 7525a9250a, worktree clean (`git status --porcelain` empty). GATES, quoting each gate's own verdict line: `pnpm check:pm-dispatch-gates` -> '✓ dispatch-gates self-test: 310 cases pass.' (289 on origin/main; +21 new cases); `pnpm check:nul-bytes` -> 'check-nul-bytes: OK (scanned 6309 text file(s) ... no raw ASCII control bytes).'; `pnpm check:cross-package-test-inputs` -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.'; `pnpm exec eslint scripts/pm/dispatch-gates.mjs --no-inline-config` -> exit 0, no output. Gate union re-derived AT that commit with `node scripts/pm/dispatch-gates.mjs` (no paths, off the merge base) — it names exactly those three families and nothing else. H4 ABLATION 1 (the verdict fires): in scripts/check-service-providers.mjs the single live hint was rewritten to a file that cannot exist; landing proven independently of the editor's exit code — removed-text `grep -c` 0, injected-text `grep -c` 2 (of 2 before), `git diff --stat` 2 insertions/2 deletions; the sweep went 6 -> 7 and named the family with the 'tree stops at packages/spec/src/system; the layout moved under it' reason. RESTORE LEG run and verified: `git checkout HEAD --` the file, removed-text back to 2, injected-text 0, porcelain empty, sweep back to 6. NO REBUILD LEG EXISTS and none is claimed: the subject under ablation is executed straight from source by `node scripts/pm/dispatch-gates.mjs` — no dist/ or other build artifact is interposed between the edit and the measurement, so the dist-preflight discipline has nothing to check here; the equivalent proof is the grep-count pair above. H4 ABLATION 2 (nothing else moved), measured against a second worktree detached at origin/main, same corpus: `--tier` over 134 sampled paths + 3 multi-path invocations (591 lines) is BYTE-IDENTICAL, md5 22abac3008b88721972305483c9a16c2 both trees; the full derivation over 7 path sets x (plain, --residue) diffs to 0 lines removed or changed and 70 added, all of them the new section/count; and this tool's OWN watch-hint set is unchanged (the same 6 hints), so no other card's matched column moves because this file grew. H3 (the sweep's own emptiness) demonstrated end to end, not only in fixtures: a real `git init` repo with zero tracked files -> REFUSES ('the tracked-file listing came back EMPTY ... zero is a broken scan, not a clean repo (#4690)'); a non-git cwd -> REFUSES, naming --tier as the half that needs no tree; hintCovers ablated to always return false -> the tool exits 2 with 'every one of the 79 famil(ies) that declare a population reached nothing ... suspect the recognizer ... before reading it as a defect count' (that ablation was restored: injected-text count 0, porcelain empty, self-test 310 green again). residueLines additionally refuses an omitted unreachable count, an omitted corpus size, and a zero-file corpus, so '0 unreachable' can never print without the number of files swept beside it.",
      "h1_families": [
        "check:driver-memory-census — '@objectstack/driver-memory' — never was a repo path (package specifier); its real population is a git ls-files census it computes",
        "check:examples-live-imports — 'examples' — THE TREE HAS IT; a single-segment literal carries no separator so hintCovers refuses it as too generic (the only one of the six where the derivation, not the gate, is the thing that cannot express the population)",
        "check:objectui-pin-fresh — 'objectstack-ai/objectui' — never was a repo path (GitHub repo slug); its real population is the top-level file .objectui-sha",
        "node scripts/check-objectui-pin-fresh.mjs — 'objectstack-ai/objectui' — the same script discovered a second time through its direct invocation",
        "check:release-body — 'application/json' — never was a repo path (MIME type); its real population is the packages' CHANGELOGs, named only inside its self-test and therefore masked",
        "node scripts/check-skill-frame-freshness.mjs — 'refs/remotes/origin/main' — never was a repo path (a git ref)"
      ],
      "h2_answer": "Dormant-by-design is NOT expressible from the tree and the verdict does not pretend otherwise: intent is not in the tree and no signal in this repo carries it. What IS mechanical is WHY it could not reach, so every dead hint prints one of three causes — 'never was a repo path' (no tracked path under its first segment; 5 of the 6), 'the tree stops at X; the layout moved under it' (the usual real miss; no live instance today, which is why ablation 1 manufactures one), and 'the tree HAS it; the covering rule refuses the literal as too generic' (the examples case — without naming it a reader goes hunting a directory sitting in front of them). Granularity was decided on measurement, not taste: per-HINT would report 263 dead of 510 unique hints and 38 of the 86 declaring scripts, which is the noise generator H2 predicts; per-FAMILY (every hint dead) reports 6. If dormancy ever needs DECLARING rather than inferring, the shape is the '# dispatch-gates:' marker convention this file already reads for a workflow with no families — deliberately not built with zero instances asking for it.",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT FILED, by ruling 2: the 6 instances above are reported here rather than carded, so the PM's triage stays in one place. check:examples-live-imports is the one with a known one-line remedy on the gate side (spell the hint as a subtree the way #9626 established for content/**); the rest read populations they compute or top-level files the derivation deliberately keeps out of reach.",
        "NOT FILED, measured clean: the same defect class one level over — a CI `paths:` trigger glob that matches no tracked file — has 0 of 26 positive patterns dead today, so there is nothing to card."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions