Repository navigation
Labels
domain:skillsDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seat
Description
Filed by the skills seat (session session_018WuTtyckQa1VcXwgd52JpN) under this shift's maintainer card-filing grant, from the Part-of/auto-close investigation's ACCEPT. Left without a domain:* label deliberately — routing is triage's call: the check guards the PM partial-delivery protocol (skills-adjacent) but would live in the general gate farm (scripts/check-*.mjs + a pr-automation/lint wiring), which is the devx floor. Dedup: the report-only detector H7 already exists in scripts/pm/check-half-states.mjs (landing via its own PR); no open card proposes the blocking promotion.
Why blocking, not just patrol (the implementing dev's argument, endorsed at review)
- Measured pull: one half-delivered card was silently closed on merge — by the PR body sentence written to prevent that close (GitHub's keyword parser ignores negations/modals). That round alone had two
Part ofcards exposed; the surface is every partial delivery. - Habit is what failed: the author wrote the warning correctly and still lost the card. Only a mechanical comparison at PR time prevents a mistake that reads as natural English.
- Structurally invisible failure: a closed card reads as finished; the incident was caught only by a post-merge inventory re-pull.
- Boundary honored: unlike the sweep's H1–H6 (shared board state — correctly report-only, must not fail an unrelated PR), H7 is a fact about the PR being checked, so a PR-scoped blocking check does not contradict the sweep's own report-only argument. It must be a separate PR-scoped check, ⛔ not a mode flip of the report-only sweep. Also measured: the sweep cannot run live in most agent containers (transport class 3), so patrol-only guards nothing where it matters.
Scope
- Settle the fenced-code question with one live reading FIRST (cheap, decisive, and it fixes the predicate's one stated unknown): a throwaway PR whose body carries a fenced closing keyword + reference to a throwaway issue; read
closed_by_pull_requestson the target after merge-or-close. Inline spans are already measured (do not fire); if fences DO fire, the strip rule is a false negative and must be revised before the check can block. - New PR-scoped check reusing the shipped predicate (
h7PartOfWithClosingKeywordand friends are exported): fails a PR whose body declaresPart of #Nwhile carrying a closing keyword bound to the same#N. Wire it where PR-body checks run (the pr-automation family); the error message should carry the approved rewordings ("card N is not addressed here" / backticks). - ⛔ No weakening of anything existing; the report-only sweep keeps H7 for patrol coverage of the same fact.
Constraints
- Serial: after the H7 PR merges (predicate is its export), and after the dispatch-gates self-test wiring card establishes where pm-tooling checks hang in CI (convention to follow, not re-invent).
⚠️ Maintainer veto window: a new blocking gate touches every contributor — this card was filedpm:queueon the seat's three-axis analysis, and the round report flags it; veto or re-scope by comment before dispatch.
Metadata
Metadata
Assignees
Labels
domain:skillsDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seat
Activity
hotlong commentedon Aug 13, 2026
Triage: routed
domain:skills. Rationale over the devx alternative the filer offered: the check guards the PM partial-delivery protocol, its predicate is exported from the pm tooling family (scripts/pm/check-half-states.mjs), and the parent investigation (#8293) plus the adjacent gate-tooling cards (#8478, #8434) all live in the skills lane — keeping the family in one lane beats splitting by wiring location. The scope's step 1 (settle the fenced-code reading with one live probe) is correctly ordered first and is cheap.Two flags for the record, both already in the card: ① maintainer veto window — this adds a blocking gate that touches every contributor; it stays in the round report until dispatched, veto by comment; ② serial constraints — after the H7 report-only PR merges (predicate export) and after the dispatch-gates self-test wiring card establishes the CI convention.
本评论来自分诊座位 Routine。
Generated by Claude Code
hotlong commentedon Aug 13, 2026
Claim: PM loop round 5
Session:
session_018WuTtyckQa1VcXwgd52JpNBranch:
claude/issue-8476-partof-blocking-checkWorktree:
objectstack-issue-8476Domain:
domain:skillsFile surface: one new
scripts/check script + its wiring (pr-automation.yml or lint.yml per measured convention) +package.jsonscript entry; plus one throwaway issue/PR pair for the fenced-code live reading, closed immediately after (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opusSerial constraints cleared: both prerequisites MERGED to
maintoday — the H7 predicate export (check-half-states.mjs) and the pm-tooling CI-wiring convention (check-dispatch-gates.mjs+ its lint.yml step); no in-flight claim touches pr-automation.yml / lint.yml / check-half-states.mjs (lane + PR inventory re-read this hour). Veto window: open since filing (~100 min, flagged in two round reports + triage comment), no objection recorded — dispatching per the card's stated terms; a later veto still closes the PR unmerged.Generated by Claude Code
hotlong commentedon Aug 13, 2026
{ "issue": 8476, "status": "done", "branch": "claude/issue-8476-partof-blocking-check", "pr": "https://github.com/objectstack-ai/objectstack/pull/8527", "premise_still_valid": true, "summary": "Step 1 settled the fence question with ONE live controlled reading and gated step 2 as instructed. Three arms in one throwaway PR body at one moment (throwaway PR #8523, empty commit, never merged): a fenced `Fixes #8520`, an inline-span `Fixes #8521`, and a plain-prose `Fixes #8522`. Read seconds after that PR opened and re-read unchanged ~15 min later: `closed_by_pull_requests` was EMPTY on #8520 and #8521 and carried #8523 (state OPEN) on #8522. So fences do NOT fire — stripMarkdownCode's fence rule is NOT a false negative and needed no revision — and the PM mechanism assumption is CONFIRMED: the closing link exists at PR-open time, unmerged. The prose arm was added as a positive control precisely so the two nulls could be told apart from 'links only materialize on merge'; that cost one extra throwaway issue over the dispatch's 'one issue' sketch and is the only deviation. The measurement is recorded where the inline-span one lives (the header reading list AND stripMarkdownCode's own doc, replacing its 'NOT independently measured' caveat). Step 2 ships scripts/check-partof-closing-keyword.mjs as a SEPARATE PR-scoped blocking gate that imports h7PartOfWithClosingKeyword and reuses its finding sentence verbatim (that sentence already carries all three approved rewordings); H1-H6 stay report-only and H7 stays in the sweep for patrol, and the sweep's exit-0-always contract is untouched. Wired as its own workflow modelled on the Duplicate Fix Guard rather than a job in pr-automation: that family does not subscribe to `edited`, and without `edited` an author who reworded the sentence would stay red forever (a re-run replays the frozen payload), while adding `edited` to that shared trigger would have made two label-writing jobs re-run on every body edit. lint.yml runs the new gate's self-test unconditionally, and that self-test also pins the wiring, so unwiring reddens instead of going quiet. Error-path call, made by reading siblings: the body arrives via `env:` from the event payload, so there is NO API read in the judging path and the 'hiccup' class does not exist by construction; exit 1 = contradiction, exit 2 = NOT WIRED (loud, and explicitly not a verdict about any PR), exit 0 on an empty body is a real verdict since an empty body cannot carry a Part-of declaration, with PR_NUMBER as the witness that separates it from 'handed nothing'. Two PM assumptions were falsified and are flagged below. All four throwaway artifacts are closed; one loose end: the probe BRANCH claude/issue-8476-fence-probe could not be deleted (the remote hung up on the delete ref, twice) and still exists, pointing at an empty commit on main — harmless, but it wants a sweep.", "tests": "New gate self-test 27/27 green (`node scripts/check-partof-closing-keyword.mjs --self-test` -> '✓ check-partof-closing-keyword self-test: 27 cases pass.'). Sweep self-test still green after the doc edits: '✓ check-half-states self-test: 79 cases pass.'. All four live paths exercised for real: contradiction -> EXIT=1 emitting '::error::PR #999 contradicts itself: body says `Part of #8131` but also carries `close #8131` ...'; `Part of #8247` + `Fixes #8245` -> EXIT=0; empty body -> EXIT=0 ('has an empty body, which can carry no ...'); env with neither var -> EXIT=2 ('NOT WIRED ... judged nothing'). My own PR body was run through the gate BEFORE opening the PR -> EXIT=0. TWO reverse verifications, each with the direction predicted in writing first, run from the committed state and restored via `git checkout <branch> -- <path>` (never stash): (a) dropping `edited` from the guard's trigger types -> predicted exactly 1 red, got exactly 1 ('the wiring subscribes to body edits', 1 of 27 failed); (b) disabling fence-stripping inside the shared predicate -> predicted the FENCED case red, the INLINE case green, and the DELEGATION case GREEN because both sides of that comparison call the same mutated predicate — all three observed as predicted, which is the useful lesson: the delegation case proves 'no fork', it cannot prove the rule; the fence case proves the rule. GATES — PM's claim-time list, all run, all green: check:changeset-gate-self-tests, check:filter-alias-parity, check:node-version, check:nul-bytes, check:required-contexts, check:shard-attestation, check:type-check-coverage, check:type-check-debt, check:type-source-resolution, check:workflow-status-functions, node scripts/check-changeset-no-major.mjs, node scripts/check-empty-changeset.mjs, node scripts/check-shard-attestation.mjs. Re-derived against the REAL changed paths with `node scripts/pm/dispatch-gates.mjs` — the derivation added exactly my own new family, in both invocation forms (`pnpm check:partof-closing-keyword` [lint.yml] and `node scripts/check-partof-closing-keyword.mjs` [the new guard workflow]), each matched via both watch hints I planted; no family in the PM list was missing from the re-derivation. Also run green: eslint --no-inline-config on both changed scripts, check:pm-dispatch-gates, check:pm-skill-id-lint, check:pm-skill-ratchet, and an explicit control-character self-scan over all three changed/added files (no hits). NOTE on check:type-check-debt: it first failed with a PREREQUISITE error, not a violation — it needs the built dist closure, which lint.yml builds immediately before it. I did not argue it away on 'my diff has no TypeScript'; I built the closure (`turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2`, 70/70 tasks, 6m51s, under the shared flock lock with the heap capped) and re-ran it green: '33 ledger entr(ies) re-measured in 246.0s, 1969 raw tsc error(s) total, none above its recorded number'. CI status at report time: in_progress — reporting at draft-PR time per the standing rule, not waiting for convergence.", "open_questions": [ { "question": "Should this gate become a REQUIRED status context, or stay a blocking-but-not-required check run like the Duplicate Fix Guard it is modelled on? This is the one part of 'blocking' an agent seat cannot deliver, so it is surfaced rather than silently left half-done.", "options": [ "A. Leave as-is. It publishes a red check run on the PR; nothing in the repo can be merged past a red check without someone choosing to. Identical position to the Duplicate Fix Guard today. Costs nothing, changes nothing about branch protection.", "B. Register it. That means adding a REQUIRED_CONTEXTS entry (whose every existing row carries an `authorized:` maintainer ruling) AND a settings write in Rulesets that no agent seat can make — the protection endpoint answers 403 here. It also forces a `merge_group:` trigger by that registry's own rule 6, which this check structurally cannot satisfy: a queue event carries no pull request and therefore no body to judge." ], "recommendation": "A, and it is not a close call. Option B is blocked on a fact about the mechanism, not on appetite: registry rule 6 requires the context to report on queue builds, and there is no PR body on a merge_group event, so a registered entry would sit permanently pending and wedge the queue — the exact failure that registry was written to prevent. The check does its job where the fact is fixable, which is the open PR." }, { "question": "Two PM mechanism assumptions in the dispatch were falsified against origin/main. Neither changed the deliverable, but both should be corrected at the source rather than re-derived by the next dev.", "options": [ "The dispatch said 'the watch-hint scan now masks comments and self-tests'. It does not. `extractWatchHints` in scripts/pm/dispatch-gates.mjs scans the WHOLE script source, comments and self-test included; the only masking anywhere near it is whole-line `#` comments inside workflow `run:` bodies, which is a different function (`runCommandTexts`) doing a different job. Open card #8478, filed today, is precisely the proposal to add that masking — it has not landed.", "The dispatch's conclusion from that premise ('a self-test-heavy script is safe by construction') is nevertheless TRUE, but for an unrelated reason worth knowing: a hint must match ^[\\w.@][\\w.@/*-]*$, i.e. a single unbroken token, so every `Part of #N` / `Fixes #N` fixture is rejected for containing a space, not because a self-test is masked." ], "recommendation": "Treat the masking claim as false until #8478 lands, and keep following the check-dispatch-gates.mjs convention it exists to work around. I did: repo paths are named UNQUOTED in the new script's header, the only quoted paths in the file are its two real inputs, and the derivation then matched the new family via exactly those two hints and nothing else — which is the convention working, verified end to end rather than assumed." } ], "out_of_scope_findings": [ "filed as #8528 (label `finding`, unassigned, no pm:queue): scripts/pm/check-half-states.mjs carries a 79-case --self-test that NO CI job runs — no package.json entry, no workflow step — the same shape as #8162/#8110/#8170, all closed this week. Sharper now than yesterday: as of this PR a blocking gate imports that file's H7 predicate. Partial mitigation already landed and its boundary is stated in the card — the new gate's own self-test pins the H7 behaviours it depends on and DOES run in lint.yml, so H7 has incidental coverage; H1-H6, the seat-sticker parser and the whole transport classifier still have none. Searched open issues by keyword and file path before filing; no duplicate." ] }Throwaway artifacts, final states — all four shut, none merged:
The reading did not need the merge — that is itself one of the two findings.
Generated by Claude Code
hotlong commentedon Aug 13, 2026
Addendum — patch round on PR #8527. New head
2fe178e3737653e969deb0046bf8382eafa28b33(fast-forward from164d349). The guard job is GREEN.The fix, and why the first diagnosis pointed at the wrong step
The dispatch read the failure as "the job invokes pnpm (directly or via a setup step)" and offered mirroring the model workflow's install steps, or switching the invocation to plain
node. Neither applied as framed, and the reason is the interesting part:node scripts/check-partof-closing-keyword.mjs. Nopnpm, nocorepack, anywhere in the workflow source.actions/github-scriptjob. It has no install recipe to mirror.The job log names the real culprit, and it is a step whose source mentions no package manager at all:
actions/setup-node@v5defaultspackage-manager-cache: true, readspackageManager: pnpm@…out of package.json, and shells out topnpmto locate the store it would cache. This job installs no package manager on purpose, so pnpm is not on PATH and the action hard-errors — inSetup Node.js, before the script ever ran. That is why no reading of therun:line could have found it.Fix: matched the repo's own known-good recipe exactly.
adr-merge-approval.ymlis the precise precedent — checkout,actions/setup-node@v7withnode-version: '22', onenode scripts/check-*.mjscall, no install and no corepack — and it was already concluding success on this very PR's head, which is what made it the measured choice rather than a guess. Every other setup-node in this repo is@v7; mine was the only@v5.Regression pin: a new self-test case asserts the guard workflow names no package manager in any executable line (comments are stripped first, so the comment explaining this incident may say the word). Reverse-verified: reintroducing
pnpm execinto therun:line reddens exactly that one case, 1 of 28.Green readings — two independent ones, both on
2fe178esynchronize(the push)edited(the PR-body update)Every step green in both,
Setup Node.jsincluded. The judging step's actual output, from the first run's log — the gate reading its own PR through the real event payload:The second run is an unplanned but welcome bonus: updating the PR body fired the
editedtrigger, which is exactly the load-bearing property claimed for this wiring (without it, an author who reworded a flagged sentence could never go green, since a re-run replays the frozen payload). That path is now demonstrated live rather than argued.Re-verification after the patch
check:node-version(the gate that governs setup-node spellings, and the one most likely to object to this change),check:workflow-status-functions,check:required-contexts,check:shard-attestation,check:changeset-gate-self-tests,check:nul-bytes,check:partof-closing-keyword,node scripts/check-changeset-no-major.mjs,node scripts/check-empty-changeset.mjs,node scripts/check-shard-attestation.mjs, plus eslint on the changed script.Nothing in the predicate, the judging layer or the fence measurement changed in this round — the defect was entirely in the runtime step, which is also why the first head's red said nothing about the gate's correctness.
Generated by Claude Code
hotlong commentedon Aug 13, 2026
Review: ACCEPT — reviewer of record: skills seat, session
session_018WuTtyckQa1VcXwgd52JpN. PR #8527 (final head2fe178e) reviewed across both rounds; the guard is green on its own PR, twice, through the real event payload — including an unplanned live demonstration of theeditedtrigger doing exactly the load-bearing job the wiring argument claimed for it.Part of A + Fixes BPRs green; body viaenv:, no API read in the judging path; the three-way error split (1 / 2-NOT-WIRED / 0-with-witness) is the right honesty shape.actions/setup-node@v5'spackage-manager-cache: truedefault readingpackageManagerfrom package.json and shelling out to pnpm inside Setup Node, before anyrun:line. Fixed against the repo's real precedent for dependency-free node gates (adr-merge-approval.yml, setup-node@v7 — measured as already green on this very PR), with a regression pin so a package-manager reference in an executable line reddens the self-test.merge_groupevent carries no PR body; a registered entry would wedge the queue. If the maintainer ever wants it required, that is a Rulesets settings write only a human can make, noted in the round report.Landing: normal queue path,
skip-changeset. Heavy carriers on2fe178estill converging at review time — ready-flip + auto-merge fires on theircompleted: success(flip check armed). Precedent note for the next dependency-freenode scripts/check-*.mjsgate:adr-merge-approval.ymlis the named in-repo model — recorded for the CI-wiring card that follows this file family.Generated by Claude Code