Skip to content

feat(pm): flag decision-box cards with open downstream dependents - #18015

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-17968-decision-box-dependents-section
Sep 13, 2026
Merged

os-project-manager merged 1 commit into
mainfrom
claude/issue-17968-decision-box-dependents-section

Conversation

@claude

@claude claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #17968

Standing triage instruction ④ (2026-08-11 maintainer ruling) asks every round-closing brief to flag the decision cards somebody is waiting on. Verbatim, and quoted verbatim in the code:

④(决策箱依赖旗标,2026-08-11 维护者裁决):每轮收尾简报的决策箱段须标注带有 open 下游依赖的决策卡(判据:任一 open 卡的 Blocked-by: 行指向它;读时派生,⛔ 不打优先级标签)。

The card measured that unrunnable on the triage seat's own tool surface: MCP search_issues is semantic matching, so a literal Blocked-by: needle returns 0 against a board where an open card's first body line is exactly that string. It offers routes A / B / C and picks none.

This is route A over a reader the repo already has — ⛔ no new channel, no charter text, no gate on the line, no objectui edit. scripts/pm/check-half-states.mjs already builds the Blocked-by: reverse index for H14 across BOTH channels (every open card's body; comments for pm:blocked / pm:blocking cards whose body is clean), so ④ is one Map.get per decision card and costs the patrol no request.

What lands

  • A rendered inventory SECTION beside the H17 trigger-file index — never a finding, never a half-state row, never counted in the half-state total, never a label — with one line per open needs-user-decision card that an open card's Blocked-by: line names, listing its waiters (capped by the existing BLOCKING_DEPENDENT_LIST_CAP, overflow counted).
  • Three lines that render on every run, findings or none: the count PAIR (N of M, so a quiet inbox reads differently from an empty one), a declared NOT-MEASURED population equal to H4's own count of open pm:blocked cards with no machine-readable line in either channel (the loss objectui#6653 measured at 17 of 24) — counted off the H4 rows the sweep emitted, one computation with two readers — and the section's own population scope, so a brief can cite both numbers or write NOT MEASURED honestly.
  • The card's reading three — 「list_issues 只能按 label / state / since 过滤,不返回正文检索」 — is true of that MCP tool and FALSE of this script's REST reader. Stated once in the docblock so the next reader does not re-file the card.
  • The H17 docblock's claim to be "the only thing in this file that can never produce a finding" is corrected to "one of the TWO" in the same edit: this section is the other one.

Acceptance

  • Heading phrase Decision-box dependency flags in check-half-states.mjs: git grep -c 0 → 1. Lit control On-hold trigger-file index (H17) still hits, 4 → 4.
  • H4 / H14 machinery byte-unchanged — md5 of each function body, base c45ea8c3 vs HEAD, identical on both sides: h4BlockedNoBlockedBy 1d13e91922b8, h14BlockingCacheIncoherent 7fbcf7210da9, buildBlockingIndex 9a9e68b027de, blockedByTargets eb04b7cd2dc6, needsBlockedByComments e0aad3851d31, renderTriggerIndex 6848a4809bc0.
  • Self-test cases 3765 → 3806 (41 new: the five batteries the card asked for, plus posture, scope, placement and budget). node scripts/pm/check-half-states.mjs --self-test exits 0.
  • Ablation, one-off and restored: deleting the NOT-MEASURED line from the renderer turns exactly the three ④ loss: cases red, exit 1 — the predicted direction. Restored from HEAD; blob back to f48453c67d6c, git diff HEAD empty, self-test green again at 3806.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 41 commands at 8ff5d429; all 41 exit 0, among them check:nul-bytes, check:refd-timer-probe, check:whole-set-label-write, check:pm-half-states, check:self-test-wired, check:commit-card-trailers, check:pm-dispatch-gates. Reconciled: --ran with a record carrying every exit code returns 「41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN」, exit 0. The 47 artifact-roster, 11 wide-population, 10 pending-changeset and 1 path-scheduled families dispatch-gates names as outside that total are left to CI.
  • Still read-only in this mode: no GitHub write is added anywhere, and check:whole-set-label-write is green.

scripts/pm/** publishes nothing ⇒ skip-changeset label, no changeset file.

Acceptance notes

  • noted, not filed: pnpm check:pm-dispatch-gates needs roughly 25 minutes of wall clock in this shared container — past a 300s and a 540s foreground cap, though it exits 0 with 1682 cases passing. The box was running parallel agents, so that is a cost reading under contention rather than a reproducible defect; every dev agent whose diff touches scripts/** derives this family.
  • noted, not filed: the dispatch premise scoped the H14 reverse index to the pm:blockedpm:blocking population. Measured on the tree, the BODY channel is wider — the unscoped open-issue listing, i.e. every open card — and only the COMMENT channel is label-gated. The section's scope line states the real shape rather than the narrower premise.

Generated by Claude Code


Generated by Claude Code

Standing triage instruction ④ (2026-08-11 maintainer ruling) asks every
round-closing brief to flag the `needs-user-decision` cards some open card's
`Blocked-by:` line points at. That was measured unrunnable on the triage seat's
own tool surface: MCP `search_issues` is semantic matching, so a literal
`Blocked-by:` needle returns 0 against a board where an open card's first body
line is exactly that string.

The half-state patrol already builds that reverse index for H14, over both
channels (body, plus comments for `pm:blocked` / `pm:blocking` cards whose body
is clean). So ④ is one lookup, not a new channel: this adds a rendered
inventory section beside the H17 trigger-file index — never a finding, never a
label — carrying the flagged cards and their waiters, a count pair, the H4
count as a declared NOT-MEASURED population, and the section's own population
scope, so a brief can cite both numbers or write NOT MEASURED honestly.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 13, 2026
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author
  • Served-tier: 782/782 claude-fable-5-1 — harness model stamp counted over this seat's own transcript (non-sidechain assistant messages a model served; <synthetic> harness notices excluded) at 2026-09-13T12:08Z; get_session external_metadata.last_served_model read claude-fable-5-1 at 2026-09-13T12:07Z.

Contract review

Head: 8ff5d429 (PR #18015, card #17968) — read at 2026-09-13T12:08Z by the skills seat at the contract-review tier. NOT GOVERNED, measured: the diff touches scripts/pm/check-half-states.mjs only (+372 −5) ⇒ in-seat review, then ready + auto-merge by this seat.

① derived judgments — the patrol gains one report-only inventory section; nothing it reads or writes changes:

  1. The reading (decisionDependentIndex): one Map.get per open needs-user-decision card over the Blocked-by: reverse index buildBlockingIndex already builds for H14 — no request of its own, no second parser, no second listing. Population as the code actually has it, which the dev measured and the dispatch had stated narrower: dependent BODIES from the unscoped open listing (every open card, bounded by OPEN_LISTING_PAGE_CEILING), dependent COMMENTS only from open pm:blocked / pm:blocking cards whose body carries no line. The section declares that scope on its own rendered line instead of widening the read. Correct, and the right direction for a premise falsified in the widening direction.
  2. The render (renderDecisionDependents): three lines always — the count PAIR (N of M, so an empty inbox and a quiet one read differently), the NOT-MEASURED line carrying H4's own row count (open pm:blocked with no readable line in either channel — counted off the emitted findings, not re-derived), and the scope line; rows link the card and name the waiters capped by BLOCKING_DEPENDENT_LIST_CAP with the overflow counted; [] when no inventory is supplied, so every existing two-argument render stays byte-identical. Wired into both renderPlain and renderMarkdown inside the reserved block directly below H17, which is where a brief reads both inventories. Never a finding, never a label, never a remedy: seat-verified no write verb is added anywhere in the diff.
  3. The ruling is stored verbatim in one constant (INSTRUCTION_FOUR_RULING, 116 chars): seat-verified byte-equal to the card's own quote of the 2026-08-11 ruling; it is both the docblock quote and the sentence the section renders. The over-long line is the constant's own stated reason (re-wrapping a ruling is one byte from rewriting it).
  4. Nothing else moves: the dev's md5s of h4BlockedNoBlockedBy, h14BlockingCacheIncoherent, buildBlockingIndex, blockedByTargets, commentBlockedByTargets, needsBlockedByComments, hasBlockedByLine, h17IndexRows, renderTriggerIndex are identical base vs head; the one prose touch outside the new code corrects the H17 docblock's 「the only thing here that can never produce a finding」 to 「one of the TWO」, which the new section made false — in-surface, accepted. Options B (charter text) and C (a gate on the line) untouched, as the claim required.

Seat measurements on the head tree (git archive 8ff5d429): check-half-states.mjs --self-test → 3806 cases pass, exit 0 (base 3765; the new battery pins the two-dependent row, the quiet-inbox pair, the comment channel with its unread control, the NOT-MEASURED line equal to H4's fixture count, and the cap with overflow). --pair 18015 → exit 0 at 2026-09-13T12:07Z. Checks on 8ff5d429 at 2026-09-13T12:07Z: 37 runs, 0 red, 1 running (Lint & Repo Gates) — the seat flips ready and arms auto-merge when it completes green. The dev's ablation (NOT-MEASURED line deleted → exactly the three 「④ loss」 cases red, restored by blob hash) shows the battery is non-vacuous. check:pm-dispatch-gates printed its pass verdict (1682 cases) on a detached run whose exit code was not captured — checker-health of a file this diff does not touch; CI's Lint & Repo Gates is the reading that lands it.

② semver: scripts/pm/** publishes nothing; skip-changeset is right.

③ boundary flags: open_questions empty. Two 「noted, not filed」 items stand as notes (the 25-minute check:pm-dispatch-gates wall clock in a shared container; the premise scope corrected on the page). Landing consequence: scripts/pm/check-half-states.mjs frees for #16904 D2 and #17009 D2; the objectui patrol copy owes the same section — filed by this seat at landing as a bare card with Blocked-by: on this PR.

Implemented-by: claude/issue-17968-decision-box-dependents-section
Reviewed-by: session_01DAcomhvR9kKizeYgg89Vo8

Verdict: PASS — route A as claimed (5652746378): instruction ④ becomes one lookup over the index the patrol already has, both of its numbers rendered, its scope declared; no channel, no charter text, no gate added.


Generated by Claude Code

@os-project-manager
os-project-manager marked this pull request as ready for review September 13, 2026 12:16
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Landing step (NOT GOVERNED, measured: scripts/pm/check-half-states.mjs only) — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T12:16Z. Head 8ff5d429 unchanged since the review of record 5653169126 (PASS) and ACCEPT 5653169259 on #17968. Lint & Repo Gates completed success at 2026-09-13T12:15Z; 37 checks, 0 red. Flipped ready and armed auto-merge at 2026-09-13T12:16Z; the queue's own method lands it as one squash commit. Fixes #17968: the card closes on landing; the landing record, the residue strip and the objectui patrol-copy card follow when origin/main carries (#18015).


Generated by Claude Code

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants