Description
Filed by the domain:skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve) as the follow-up the seat named on PR #15848's verdict. Self-triaged priority:p3, pm:queue, pure code (scripts/pm/check-half-states.mjs).
What landed (PR #15848, merged 2026-09-05T11:52Z)
.claude/skills/pm-dispatch/SKILL.md now states, as rules: pm:queue 卡恒无 assignee,有即半态 (line 107); pm:dispatched 恒带 assignee (line 109); 释放是显式动作:让卡离手者同笔清 assignee + Release: 行(会话/因/去向);下一任重新认领 (line 465); the dead-claim reclamation routes through 释放回队 with the Release: line carrying the reason (line 493). references/state-machine.md carries the releaser line beside the applier and remover.
What is stale in the sweep
- H1 (
h1DispatchedNoAssignee) — the header inventory (line 88) still explains the row as "dispatch marks a claim; a claim is …" rather than as the definitional reading the rule now gives it (pm:dispatchedalways carries an assignee). - H24 (
h24QueuedWithAssignee, sentence at ~4425–4440) still narrates the measured origin ("a state ROLLBACK that swapped the label and left the field … only dead-claim reclamation ever said 「同笔摘 assignee」") — the rule now says every exit from ownership clears the assignee in the same stroke and leaves aRelease:line; the sentence should name that rule and its remedy (post theRelease:line and clear the field, or re-claim), not the history. - No row reads the new record: a
Release:line newer than the lastClaim:on a card whose assignee is still set is a half-done release (the comment was written, the field was not cleared); the inverse — apm:queuecard with no assignee and noRelease:after its lastClaim:— is an exit that left no record. Both are report-only patrol input in the H1/H24 family.
What this card does
- H1 and H24 prose aligned to the landed rule (quote the SKILL.md lines, never paraphrase them into new Chinese); predicates unchanged.
- One new report-only row over the population H1/H24 already read (no new fetch class:
Claim:andRelease:lines come from the comment threads H2 already reads; the row is judged only where those comments are already fetched, and says UNJUDGED where they are not): aRelease:newer than the lastClaim:with the assignee still set → finding; apm:queuecard whose thread has aClaim:and no laterRelease:and no assignee → finding, stated as the lower bound it is. Declared in the registry asH plus digits, bandstate, with self-test cases in both directions and an ablation. - Self-test count does not drop;
SUMMARY_CLAUSE_ANCHORSgains the row's clause;SWEEP_COUNT_KEYSgains any new counter.
Serial
scripts/pm/check-half-states.mjs is in no flight and no open PR (PR #15795 and #15755 landed). Pure code ⇒ in-seat review, then ready + auto-merge by the seat.
Dedup
REST enumeration 2026-09-05T10:3xZ (1,200 items, 797 issues, positive controls hit) found no card on a release act or the assignee's exit; #15846 (closed by PR #15848) is the rule half, and its body names this sweep half as the follow-up.
Generated by Claude Code
Activity
claude commentedon Sep 5, 2026
Claim: PM loop round 7 — filed and taken at first touch by the seat as the sweep half of the release rule PR #15848 landed (p3, pure code): H1/H24 prose aligned to the landed readings, one new report-only row over the population those rows already read
Session:
session_019RfFHiRCSs3JXLK4cwcfoxBranch:
claude/issue-15884-half-states-read-the-release-ruleWorktree:
objectstack-issue-15884Domain:
domain:skillsFile surface:
scripts/pm/check-half-states.mjsonly — the H1 header-inventory sentence and the H24 finding sentence quote the landedSKILL.mdlines (107, 109, 465, 493) instead of narrating the measured origin; one new registry row (idHplus digits, bandstate) over the comment threads H2 already fetches: aRelease:line newer than the lastClaim:while the assignee is still set, and apm:queuecard with aClaim:, no laterRelease:and no assignee — both report-only, UNJUDGED where the thread was not fetched; self-test cases both directions, ablation,SUMMARY_CLAUSE_ANCHORSandSWEEP_COUNT_KEYSupdated; no new fetch class; predicates of H1/H24 unchanged.Container & model: S,
mode:subagent,model: opus(dispatch-gates.mjs --tieron the path: no path-derived mandate; clause ② not engaged — a report-only patrol row, no accept/reject verdict moves)Clause-②: no
Serial constraints cleared: the file is in no flight and no open PR (PR #15755 and #15795 landed; scan 12:2xZ);
SKILL.mdis untouched by this card. Pure code (check-governed-merges --testexit 0 forscripts/pm/**) ⇒ in-seat review, then ready + auto-merge by the seat;skip-changeset. The PR carriesFixes #15884.Decision re-read (12:2xZ): the rule this row reads is on
mainsince 11:52Z (PR #15848, the maintainer's R3); the row adds no rule and reads only records the protocol already prescribes — no ruling owed.Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 15884, "status": "done", "branch": "claude/issue-15884-half-states-read-the-release-rule", "pr": "https://github.com/objectstack-ai/objectstack/pull/15901", "premise_still_valid": true, "summary": "One file, scripts/pm/check-half-states.mjs, at f845bc5d16. H1's header-inventory sentence now reads the state model's definition of the label (SKILL.md line 109) and the release act that is the only lawful way to empty the field (line 465), instead of narrating 'dispatch marks a claim'. H24's finding sentence opens on 「pm:queue 卡恒无 assignee,有即半态」, keeps both contradicting readers and the human/agent asymmetry, drops the state-ROLLBACK history and the 'only dead-claim reclamation ever said' clause, and states the remedy as the release act itself, quoting line 465 unbroken and naming both lawful exits (finish the release, or re-claim). New report-only row H47, band state, over the comment threads the sweep already holds: leg (a) a Release: line newer than the last Claim: while the assignee is still set; leg (b) an open pm:queue card with no assignee whose Claim: no later Release: answers, stated as the lower bound it is. RELEASE_COMMENT_MARKER mirrors CLAIM_COMMENT_MARKER's strictness and is no wider; CLAIM_COMMENT_MARKER is untouched and unmoved. The predicates of h1DispatchedNoAssignee and h24QueuedWithAssignee are unchanged, and the diff adds no REST path: the row calls commentCache.get only, so a card whose thread no other row bought is UNJUDGED rather than clean, and the releaseCandidates/releaseJudged pair plus the summary clause disclose the gap. The push sits at the foot of the per-card loop rather than at its head beside H1/H24, because at the head the cache is empty for that card on every iteration and the row would report UNJUDGED forever while looking healthy. Ordering is created_at with a thread-order fallback (governingClaim's and latestSeatMarker's existing resolution) rather than comment id, since ids are monotonic in practice but documented as an ordering nowhere, and equal stamps are exactly what a hurried release produces. PR body carries a duplicated attribution footer: the platform appended its own after mine, nothing was eaten, and it was left rather than PATCHed so the draft bit is never re-sent.", "tests": "SELF-TEST: 2341 cases pass (node scripts/pm/check-half-states.mjs --self-test, verdict line '✓ check-half-states self-test: 2341 cases pass.'), up from 2267 on main. No case dropped: comm over the two runs' case-name lists differs by exactly one entry, 'H24: …and names the paired write it is owed', whose assertion is preserved and re-pointed at the landed spelling 同笔清 assignee, with three companions added beside it. ABLATION (this script has no build step and no dist — it is a plain .mjs run directly, so there is no rebuild leg; the mutation was confirmed ON DISK instead): a throwaway copy placed in scripts/pm/ so every sibling resolves, proved byte-identical to HEAD:scripts/pm/check-half-states.mjs by blob hash before each leg, control green at 2341/2341. Leg A, h47ReleaseRecordDesync always declines (injected-text grep count 1, blob moved to 9e01cc2adf) -> 19 of 19 firing-direction H47 cases red, clean-direction cases stay green. Leg B, the findings.push removed (deleted-text grep count 0, injected 1) -> 2 red: the H47 registry-coverage case and the file's own standing invariant 'no band names a family the sweep never emits'. Leg C, the summary clause removed (deleted render line 0, injected 1, anchor row still 1) -> 9 red: 4 generic summaryClause invariants plus 5 H47 summary cases. Every restore proved by blob hash back to e98159bf32; the tree file's blob was e98159bf32 before and after, and git diff HEAD over it is empty. LIVE SWEEPS, read-only, before = origin/main's blob (extracted and hash-checked against origin/main:scripts/pm/check-half-states.mjs) and after = HEAD: objectstack 307 findings before / 305 after, objectui 163 / 167 — and H47 emitted ZERO rows on both boards, which is the expected reading: no card carries a Release: line yet (the rule landed 11:52Z), so leg (a) has no carrier by construction and leg (b) fires only where a thread was already fetched. Coverage disclosed as 85 of 272 candidate cards on objectstack and 28 of 235 on objectui. The before/after totals are eight minutes apart on live boards worked by parallel seats, so the deltas are drift, not this change: per-family counts show H24 identical in both pairs (1 objectstack, 4 objectui) and H47 contributing nothing to either side. GATES: derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the final one-path file list; all 32 commands run and --ran reconciles, '✓ dispatch-gates --ran: 32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED.' Seven of them first answered PREREQUISITE NOT MET exit 3 because the fresh worktree had no node_modules; after pnpm install all 32 are exit 0. The eight artifact-roster gates whose roster sits under scripts/ were run rather than read as silent: green except check:dts-closure and check:published-readme-exports, which answer PREREQUISITE NOT MET without a closure build and are NOT MEASURED here rather than passed. Whole-repo pnpm lint (eslint . --no-inline-config) exit 0 through bash scripts/pm/os-verify-lock.sh on slot issue-15884, VERDICT line 'command-exit 0 · held the lock 79s'. check-governed-merges --test on the final file list: 'NOT governed — ordinary queue landing applies'. Every exit code captured by redirecting first, never through a pipe. CI ON THE HEAD f845bc5d16: all 35 check runs completed — 24 success, 11 skipped, 0 failure — including Lint & Repo Gates, Live half-state sweep, Test Core 1..6, all four Type Check jobs, Check Changeset, Governed Surface Queue Guard.", "mcp_calls": "0 — no MCP GitHub call was made in this run: the card and its thread came from the public-repo single-issue payload channel, and every write (issue create, PR create, label add, read-backs, check-run polling) went through repo-scoped REST with curl after a 200 probe.", "open_questions": [], "out_of_scope_findings": [ "filed as #15899: H8's remedy sentence (~line 1724), H19's release text (~3435) and the H24 header-inventory paragraph (~488) present 「同笔摘 assignee」 in quote brackets — the register this file uses for a verbatim SKILL.md quotation — but that phrasing is in no SKILL.md and never has been (git grep outside this script: zero hits; git log -S over SKILL.md on a non-shallow clone: empty). It prescribes only the first half of the landed release act, which is the exact shape the new H47 row exists to report, and four self-test cases pin it. Not addressed in this PR: the card's ruling scoped the diff to the H1 header sentence, the H24 finding sentence and the new row. No label and no assignee, left for triage." ] }Generated by Claude Code