Skip to content

fix(pm): read the documented blockquote claim shape, and print runnable gate invocations - #7551

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-7488-7440-pm-tooling
Aug 11, 2026
Merged

fix(pm): read the documented blockquote claim shape, and print runnable gate invocations#7551
huangyiirene merged 1 commit into
mainfrom
claude/issue-7488-7440-pm-tooling

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7488
Fixes #7440

Two scoped scripts/pm/ tooling fixes, packed on one branch. File surface is scripts/pm/check-half-states.mjs + scripts/pm/dispatch-gates.mjs and nothing else. ⛔ No .claude/skills/** edit: the PM ruled direction 1 on #7488 (relax the predicate, the skill's documented blockquote shape stays authoritative), and SKILL.md is owned by #7498's dev this wave.

Both premises were re-verified on origin/main @ 1530870 before implementing, and both held at the dispatched line numbers: check-half-states.mjs:87 carried /^\s*Claim(?:ed)?\s*[::]/mi, dispatch-gates.mjs:66 already captured the --filter package, and :178 printed the bare check name.

Per-card checklist

Each line is one hunk; git diff --stat is 2 files / +48 / −4 and maps 1:1 to the seven items below.

#7488 — the H2 predicate cannot read the documented claim shape

# 落点 (landing) before after
1 check-half-states.mjs:87 — the h2AssigneeNoClaimComment regex /^\s*Claim(?:ed)?\s*[::]/mi\s does not match >, so a line beginning > Claim: never matches /^\s*>?\s*Claim(?:ed)?\s*[::]/mi — an optional blockquote marker, nothing else widened
2 check-half-states.mjs:38 — the H2 invariant docblock describes the marker as a "Claim:" line, silent on the blockquote records that the marker is read with an optional leading > because step 4's own template is a blockquote, and that the strictness either side of it is deliberate
3 check-half-states.mjs:222 — self-test 3 H2 cases, none covering the documented shape +4 cases: blockquote claim clean, indented blockquote clean, and two strictness pins (bare prose containing the word, and blockquoted prose containing the word, both still findings)

#7440 — package-scoped gates printed as bare check:*

# 落点 (landing) before after
4 dispatch-gates.mjs:107 — new exported runnableInvocation({ check, filter, direct }) did not exist; the parsed filter was dropped at the print site returns pnpm --filter PKG run check:x when a filter was parsed, pnpm check:x when not, node scripts/check-x.mjs for a direct-node invocation
5 dispatch-gates.mjs:199 — the matched-gate print site - ${check} [lint.yml] matched via … - ${runnableInvocation(entry)} [lint.yml] matched via … — formatting only, no new discovery logic
6 dispatch-gates.mjs:35 — the "what the output means" docblock says a matched check is high-signal, paste it into the dispatch prompt adds that it is printed as the runnable invocation, and why the bare name reads as nonexistent
7 dispatch-gates.mjs:237 — self-test 4 extraction cases over the sample workflow, nothing asserting the printed form +3 cases driving runnableInvocation from those same three fixtures (the --filter @objectstack/spec sample line at the old :204, the root-scoped one, and the direct-node one), so the sample workflow and the print site cannot drift apart

Item 4's direct-node arm is the one shape the card did not name. It is at the same print site and the same defect class: scripts/check-nul-bytes.mjs is a path, not a pnpm script, so pnpm scripts/check-nul-bytes.mjs would be a new wrong answer. It prints node scripts/check-nul-bytes.mjs.

#7488 (a) — the fleet measurement, done before finalizing

The card asks for a count of already-posted claim comments in each form before the direction is settled. Sample: all 18 open pm:dispatched cards (the exact population the H2 sweep reads) plus the 4 recently-closed cards the card's own table names — 22 cards, every comment thread read through the GitHub API on 2026-08-11.

claim-comment form on the thread cards which
blockquote > Claim: (the SKILL.md step-4 template) 2 #6752, #7378
bare Claim: 11 #7498, #7488, #7440, #7420, #7411, #7276, #7245, #6850, #6814, #5376, #5346
both forms on one thread 0
a claim comment exists, in neither of those two spellings 9 #7485, #7450, #7359, #7363, #7467 (bold **Claim** / **CLAIM** / **DEV CLAIM**, em-dash, no colon); #6599, #6350, #4953, #4001 (Chinese 认领)
no claim comment at all, in any spelling 0

The measurement supports direction 1 and does not argue for direction 2, so there is no fork to report: 2 already-posted claims are in the blockquote form and would be orphaned by dropping the > from the template, while relaxing the predicate orphans nothing. #7378's blockquote claim was posted at 03:26Z on 2026-08-11 — after #7488 was filed at 01:22Z — which is the card's "it will grow" prediction arriving within four hours of the filing.

⚠️ One of the card's statements did not survive the measurement, and it is worth the PM's eye. The card says of the other seven H2 hits: "the other seven are true findings — they carry no claim comment in any form." Measured, all seven do carry claim comments; they are simply written in a third and fourth spelling the predicate also cannot read (bold-with-em-dash, and Chinese 认领). The card's own table is literally correct — it measured exactly two spellings, and neither is present — it is the sentence generalizing from it that is wrong. This does not touch this PR's direction or scope: none of the seven uses a blockquote, so all seven are still reported after the fix, exactly as the acceptance criterion requires. It does mean H2's real false-positive rate today is higher than 1-in-8, and widening to those spellings is explicitly ⛔ out of scope here (the card bars it; "current strictness is a feature"). Filed as #7553.

#7488 (c) — before/after against live data

The live sweep cannot run from this container: the agent proxy answers HTTP 403 "GitHub access is not enabled for this session" on every api.github.com/repos/** path, with and without the container's GITHUB_TOKEN. That is the same limitation #7276's PM seat measured, the same one PR #7379 declared for this script, and the subject of neighbour card #7412; GitHub is reachable here only through MCP tools, which the script does not speak. So the sweep's fetch half was replaced by fixtures carrying the real comment lines read from the API, and its predicate half — the thing this PR changes — was imported from the module and run unmodified, before and after.

H2 replay (BEFORE — origin/main regex) over 22 live cards
  H2 #4001 assignee set but no claim comment on the thread
  H2 #4953 assignee set but no claim comment on the thread
  H2 #6350 assignee set but no claim comment on the thread
  H2 #6599 assignee set but no claim comment on the thread
  H2 #6752 assignee set but no claim comment on the thread
  H2 #7359 assignee set but no claim comment on the thread
  H2 #7363 assignee set but no claim comment on the thread
  H2 #7378 assignee set but no claim comment on the thread
  H2 #7450 assignee set but no claim comment on the thread
  H2 #7467 assignee set but no claim comment on the thread
  H2 #7485 assignee set but no claim comment on the thread
  → 11 reported: #4001, #4953, #6350, #6599, #6752, #7359, #7363, #7378, #7450, #7467, #7485

H2 replay (AFTER — this branch) over 22 live cards
  H2 #4001 assignee set but no claim comment on the thread
  H2 #4953 assignee set but no claim comment on the thread
  H2 #6350 assignee set but no claim comment on the thread
  H2 #6599 assignee set but no claim comment on the thread
  H2 #7359 assignee set but no claim comment on the thread
  H2 #7363 assignee set but no claim comment on the thread
  H2 #7450 assignee set but no claim comment on the thread
  H2 #7467 assignee set but no claim comment on the thread
  H2 #7485 assignee set but no claim comment on the thread
  → 9 reported: #4001, #4953, #6350, #6599, #7359, #7363, #7450, #7467, #7485

Reading the two required assertions off that output:

#4001 carries 43 comments, too large to transcribe; it was measured by grepping the full API payload instead — 0 comments match the before regex, 0 match > Claim:, 0 match the after regex.

#7440 — before/after against live data

Run on a content/docs/ path, which is the exact line the card quotes:

$ node scripts/pm/dispatch-gates.mjs content/docs/ui/forms.mdx      # BEFORE (origin/main)
Local gates for this card (paste into the dispatch prompt):
  - check:doc-formula-expressions   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - check:docs-audit-scope   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - check:quick-reference-counts   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - check:role-word   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'

$ node scripts/pm/dispatch-gates.mjs content/docs/ui/forms.mdx      # AFTER
dispatch-gates: 76 check famil(ies) discovered across 24 workflow file(s) — derived at runtime, nothing listed in this script.

Local gates for this card (paste into the dispatch prompt):
  - pnpm --filter @objectstack/lint run check:doc-formula-expressions   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - pnpm check:docs-audit-scope   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - pnpm check:quick-reference-counts   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'
  - pnpm check:role-word   [lint.yml]   matched via content/docs/ui/forms.mdx ⇢ 'content/docs'

Repo-wide / undetermined (no path literals discoverable — not known irrelevant): 25 famil(ies).

The one package-scoped gate gained its --filter prefix; the three root-scoped ones print pnpm check:* unchanged, as the card specifies. Confirmed against package.json: check:doc-formula-expressions is absent from the root manifest, the other three are present — which is exactly why the bare name read as nonexistent.

Both halves then run verbatim, which is the point of the change:

$ pnpm --filter @objectstack/lint run check:doc-formula-expressions      # the printed line
✓ check:doc-formula-expressions self-test: 24 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 390 files / 1406 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 837 packages/spec/src files — rls-predicate=8, hook-record-condition=1, record-formula=0; 0 exempt.
exit=0

$ pnpm check:doc-formula-expressions                                     # what a dev tried before
 ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command "check:doc-formula-expressions" not found

Did you mean "pnpm check:doc-authoring"?

That second output is #7440's incident reproduced end to end — and pnpm's own suggestion is check:doc-authoring, which is precisely the gate #7245's dev substituted (PR #7417's report: "check:doc-formula-expressions: DOES NOT EXIST in package.json — nearest existing doc-corpus gate check:doc-authoring was run instead"). The tool was sending two independent readers down a path pnpm itself pointed at.

Gates

No path-named gate family covers scripts/pm/**. Derived at work time with the fixed tool itself, on this PR's own file surface:

$ node scripts/pm/dispatch-gates.mjs scripts/pm/check-half-states.mjs scripts/pm/dispatch-gates.mjs
dispatch-gates: 76 check famil(ies) discovered across 24 workflow file(s) — derived at runtime, nothing listed in this script.

No check family names the given paths in its own source.
gate result
node scripts/check-nul-bytes.mjs OK (scanned 7000 text file(s) -- 7000 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes)
control-char self-scan on both edited files (grep -naP, beyond the gate's surface) clean, no hits
npx eslint --max-warnings=0 scripts/pm/ exit 0; --debug confirms both files are linted, not ignored
node scripts/pm/check-half-states.mjs --self-test ✓ 20 cases pass (was 16)
node scripts/pm/dispatch-gates.mjs --self-test ✓ 17 cases pass (was 14)
build closure pnpm --filter '@objectstack/lint^...' build green — needed only so the printed invocation could be run for real

These are report-only tools with no test file of their own; their runs are their tests, which is why the two live before/after sweeps above are the primary verification rather than a supplement.

Changeset

None, deliberately. Both files are internal PM/dev tooling under scripts/pm/, not shipped in any published package and not reachable by any consumer — nothing releases. skip-changeset is the right label; it is applied on this PR.

Out of scope, filed not fixed

#7553 — the same measurement shows H2 is blind to two further claim spellings in live use: bold **Claim** / **CLAIM** / **DEV CLAIM** with an em-dash and no colon (5 cards), and Chinese 认领 (4 cards). Nine of the 22 measured cards remain false positives after this PR. ⛔ Not touched here: #7488 explicitly bars widening the regex beyond the optional blockquote marker, and unlike the blockquote form neither spelling is what SKILL.md tells seats to write — so the real question is whether the protocol converges the fleet on one shape or the predicate learns all four, which is a PM ruling and not a dev-level tooling call. Both directions are written out on #7553.


Generated by Claude Code

…le gate invocations

check-half-states.mjs (#7488): the H2 predicate tested /^\s*Claim(?:ed)?\s*[:...]/mi,
and \s does not match ">", so a claim written in the shape SKILL.md step 4 tells
every seat to write ("> Claim: ...") never matched and its card was reported as a
half-state. The predicate now accepts an optional leading blockquote marker. The
strictness either side of it is unchanged and pinned: the line must still BEGIN
with the word, so ordinary prose containing "claim" is not a claim comment.

dispatch-gates.mjs (#7440): the print site emitted the bare script name, dropping
the --filter package the parser already captured. A dev searching the root
package.json for a package-scoped gate finds nothing and concludes the gate does
not exist — two independent devs did exactly that within one hour (PR #7416,
#7417) with check:doc-formula-expressions, which lives in @objectstack/lint. The
matched-gate lines now print the runnable invocation; root-scoped checks print
"pnpm check:x" unchanged.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 11, 2026 5:10am

Request Review

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

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant