Repository navigation
tooling(qa): check-platform-checklist has no legal shape for a planned item retired before it was implemented — statusFieldProblems judges retired by the active rule (since must be a release, steps non-empty) #22987
Description
Unblocks: #22869 — PR #22982 (the retire of records-forms.picklist-org-append) would add 2 validator problems on main until this lands. This card carries the checker fix; the parent keeps the retire and its three ledger files.
Derived from the in-flight card #22869 by the skills seat (session_01CinRQcE6T8SvMbhH8D3NhS), from the dev's class-a finding measured at PR #22982 head 57ef232b7; parent domain and priority inherited (the one bypass of triage, for a sub-issue a claimed card needs to land). Cross-lane: scripts/check-platform-checklist.mjs is the devx lane's file by path; the seat declares the touch on the devx seat post.
Reading
scripts/check-platform-checklist.mjs#statusFieldProblems(line ~172 onorigin/main246edf5b3): one branch forstatus === 'planned'; every other status,retiredincluded, falls through to the active rule —"since" must be the release that introduced the capabilityand"steps" must be a non-empty array of strings.docs/qa/platform-checklist/README.md, Lifecycle → Retire: setstatus: "retired"+retiredReason(andsupersededBywhen a successor exists); the row stays. It asks for nosinceand nosteps.- The only two retired items before PR docs(checklist, north-star): retire records-forms.picklist-org-append — the per-organization picklist append is ruled out #22982 (
dashboards.saved-report-ownership,dashboards.report-schedule-dispatch-delivery) were active before retiring (since: "v15.1", steps present), so this path was never exercised. - Measured on PR docs(checklist, north-star): retire records-forms.picklist-org-append — the per-organization picklist append is ruled out #22982: base
117817ec2readscheck-platform-checklist: 8 problem(s)(symbol anchors inaccess-security,attachments-storage,search— the open main-red anchor check:platform-checklist is red on main #22924's); head57ef232b7reads 10; the diff of the two lists is exactly the 2 lines above, both onrecords-forms.picklist-org-append. Minimal repro: flip any planned item toretiredwith aretiredReason. - Public door:
pnpm check:platform-checklist, run on main byplatform-checklist-watchdog.yml; by maintainer decision it is not per-PR CI (.github/workflows/lint.yml:3102–3110).
Change
statusFieldProblems: aretiredbranch that judges by the README's Retire rule —retiredReasonis a non-empty string;sinceisnull(never shipped) or a release-shaped string;stepsmay be absent or empty (a never-implemented capability has nothing to drive). A retired item that was active keeps whatever it had and still passes. ⛔ No new required field beyond what the README names; ⛔ no change to theplannedor active rules.--self-test: a retired item withsince: nulland nostepspasses; a retired item withoutretiredReasonfails; a retired item with a non-releasesincestring fails; the existing active and planned cases unchanged. The script's own mutation battery (its header describes the shadowing checks onstatusFieldProblems) must still red when the new branch is dropped.- Verify the unblock: with PR docs(checklist, north-star): retire records-forms.picklist-org-append — the per-organization picklist append is ruled out #22982's
records-forms.jsonapplied in the worktree (uncommitted, restored after),pnpm check:platform-checklistreads 8 problems, not 10; report both counts and the restore proof. - Gates:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackfrom the worktree; ascripts/script edit owes the script's own tests beyond the derived families (git grep -l check-platform-checklist -- 'scripts/**/*.test.*' scripts/__tests__for siblings).skip-changeset(scripts/is not published).
⛔ No ledger data invented; ⛔ no docs/qa/** edit here (the parent's PR owns them); ⛔ dispatch-gates*.mjs stay frozen.
Who acts
The skills seat dispatches it as the blocked seat (the unowned-blocker channel) and, the path not being governed, lands it through the queue after its own at-tier review. PR #22982 lands after this PR.
Dedup
MCP issue search over objectstack-ai/objectstack, open and closed, query "check-platform-checklist retired item since steps statusFieldProblems retire planned item validator": 28 hits. Nearest: #22924 (open: the watchdog's main-red anchor for the 8 symbol-anchor problems — a different failure, which this card does not touch), #19157 (closed: introduced status: planned, the origin of the one-branch shape), #22487 (closed: retired a step and a clause of an active item), #22557 (closed, not planned: an anchor red). None gives retired its own rule.
Activity
objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 · 2026-10-12T07:54Z
Session:
session_01CinRQcE6T8SvMbhH8D3NhSAccount:
huangyiirene(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22987-checklist-retired-ruleWorktree:
objectstack-issue-22987Domain:
domain:skills(inherited from the parent #22869 as its sub-issue; the file is the devx lane's by path, declared on #6023)Seat:
domain:skills#1File surface:
scripts/check-platform-checklist.mjs(statusFieldProblemsand its--self-testcases) (stop on breach; explain in the report)Container & model:
S 级机械卡,mode:subagent,model: default — this run's --tier on the path printed 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)」 and 「floor sonnet · default opus · ceiling fable」; default rather than floor because the branch's rule is read off the README, a judgment the gate farm does not make for the devClause-②: no — a repository gate script; no published schema, accept set or public surface is touched
Responsibility:
the checker's own rule (scripts/check-platform-checklist.mjs#statusFieldProblems, this repository) | no platform path covers it — the README's Retire rule is prose | reached by anyone who retires a never-implemented planned item; PR #22982 is the first(a defect card: a repository gate reds on a README-conformant ledger row)Thread-read: none
Unblocks: #22869 — PR #22982 lands after this card's PR.
Serial constraints cleared: none in flight on the path — no
pm:dispatchedclaim namesscripts/check-platform-checklist.mjs(claim threads grepped 2026-10-12T07:54Z); none of the open PRs changes it (files lists read); #22924 (open main-red anchor for the 8 symbol-anchor problems; assignee: unassigned) names a different failure on a different surface (docs/qa/**anchors), so it holds no hot file here;dispatch-gates*.mjsfrozen and untouchedGenerated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 22987,
"status": "done",
"branch": "claude/issue-22987-checklist-retired-rule",
"pr": "#22995",
"session": "session_01CinRQcE6T8SvMbhH8D3NhS — mode:subagent, so this is the parent seat's session id (the transcript Claude-Session line)",
"premise_still_valid": true,
"summary": "scripts/check-platform-checklist.mjs#statusFieldProblems now has a
retiredbranch that returns early, as theplannedone does. It follows the README's Retire step:retiredReasonmust be a non-empty string,sincemust be null or release-shaped (RELEASE_RE reused), andstepsandsupersededByare not judged. The active rule now covers active and draft only, and the planned and active rules are unchanged. Change from the card's premise: the item walk already refused a retired item that had noretiredReason, outside the pure function. That check MOVED into the branch rather than being duplicated, so it now sits behind the statusFieldProblems(item) call site that G1/G3 already pin. R1-R12 went into the existing selfTestPlannedStatus battery (floor 42 to 54), not into a new battery function: a new compound-named self-test function needs a COMPOUND_ANCHOR_LEDGER row in the frozen scripts/pm/dispatch-gates.data.mjs. One PM assumption measured false: the header-described mutation battery (the G-row call-site text pin) stays GREEN when the branch is made dead. G12 discloses that blind spot; the new R-rows go red instead. Unblock verified: with PR #22982's two ledger files applied, the unfixed checker reads 10 problems and the fixed checker reads 8, the same 8 lines as base.","tests": "All readings were taken in this repo (objectstack-ai/objectstack): BASE 712361b (origin/main when the worktree was cut; the card read 246edf5), final head 1901eba. | pnpm check:platform-checklist, the tree step's verdict line: base 712361b with the unfixed checker reads
check-platform-checklist: 8 problem(s). The unfixed checker with PR #22982's records-forms.json + coverage.json applied uncommitted (from a worktree-private ref at 57ef232) readscheck-platform-checklist: 10 problem(s); the 2 extra lines are both records-forms.picklist-org-append (the active rule's since and steps lines). The fixed checker at 1901eba readscheck-platform-checklist: 8 problem(s), and the same with the PR ledger applied. Both diffed line for line: identical to the base 8 (the open #22924 symbol-anchor set), with zero lines naming picklist-org-append. Exit 1 in every case, from those 8. | Restore proof after both ledger applications (git checkout HEAD --on both paths):git diff HEAD0 bytes,git status --porcelainempty, and git hash-object equals the HEAD blob: records-forms.json 707de16a36d70359c3269c47910918e3ed50a9d5, coverage.json 542fd62c23ced7ec02b182c2b0d711a1870f5047. Main has not touched docs/qa/platform-checklist since that PR's merge-base 117817e (diff --stat empty), so the applied files carry exactly that PR's delta. No ledger file was committed. | Self-test:✓ check-platform-checklist --self-test: 221 assertionsat 712361b, then233 assertionsat 1901eba (the 12 R-rows; the battery roster floor holds at 54).✓ checklist-select self-test: 31 cases pass.runs first in the same pnpm script. | Ablation, on the committed fix at ce66b83, via node scripts/ablation-replace.mjs (shell trap with absolute-path restore armed as well). The anchorif (item.status === 'retired') {x1 became x0 and the markerif (false && item.status === 'retired') {x0 became x1; blob 6064a9e249a9 became 2469abeb42c0. Reading:✗ check-platform-checklist --self-test — 10 failure(s), which are R1 and R3-R11; R2 and R12 stayed green as expected, because the active rule accepts both. The G-rows stayed green (direction: a semantic severing that the text pin cannot see, per G12). Restore: the tool reportedok restored: blob == HEAD (6064a9e249a9) and git diff HEAD is empty, and independently git hash-object equals the HEAD blob and git status --porcelain is empty. There is no dist/build step for a root script, so no dist preflight applies. | Gates at 1901eba:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, run with no paths, derived 30 commands; the stderr first line names objectstack-ai/objectstack at 1901eba, merge base 712361b, 1 path. All 30 were run with exit codes recorded. Reconciliation:✓ dispatch-gates --ran: 30 derived famil(ies) accounted for — 30 run, 0 NOT-MEASURED (a DERIVED zero — all 30 recorded an exit code and none of them is 3).29 exit 0; pnpm check:platform-checklist exits 1 (the base 8). Among them, pnpm check:pm-dispatch-gates (run detached under nohup, waited on with tail --pid, exit captured as EXIT=0) printed✓ dispatch-gates self-test (fast tier): 1828 cases pass; 6 slow section(s) deferred., and check:nul-bytes printedcheck-nul-bytes: OK (scanned 10999 text file(s) ... no raw ASCII control bytes). | The script's own tests, beyond the derived families: git grep -l check-platform-checklist over 'scripts//.test.' and 'scripts/tests/' exits 1 with 0 hits, against a control showing the pathspec matches 2 files (both adr-anchor JSONs). The watchdog does not import this script (its imports are import-prerequisite, check-shard-attestation and invoked-as). I ran it anyway: --self-test printedOK check-platform-checklist-watchdog self-test: 42 assertions across 8 floored batteries(exit 0), and the gate itself exits 0. | Lint, narrowed and with the narrowing proven:eslint --no-inline-config --format json scripts/check-platform-checklist.mjsat 1901eba checked 1 file (read from the JSON array length) with 0 errors and 0 warnings. The population comes from eslint's own config: --print-config shows 2 rules (no-restricted-imports, comment-swallow/no-code-inside-block-comment) and parserOptions {ecmaVersion: latest, sourceType: module}. Invariance: eslint.config.mjs enables no parserOptions.project and no type-aware rules (its onlyparserOptions.projecthit is the comment stating so), so this diff cannot change any untouched file's verdict. CI owns the full pnpm lint run. | No package build or test is owed: the diff touches no workspace package (root package private: true; no package manifest ships scripts/), so there is no dependency-closure build and no package test.","mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — all through the fleet-write relay (scripts/pm/fleet-write/dispatch.mjs, directly or via scripts/pm/label-write.mjs), each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22995/assignees [huangyiirene]; relay read-back reported 8045 bytes sent and 8045 stored, identical, and an independent REST GET read the body as equal to the sent file; (2) labels_add skip-changeset, i.e. POST /repos//issues/22995/labels, read back as size/s + skip-changeset; (3) this os-dev-report comment, POST /repos//issues/22987/comments. The 4 git pushes (empty probe branch + 3 commits) are not REST writes. No card assignee write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in PR #22995 Acceptance notes, not filed — the active rule's refusal
\"steps\" must be a non-empty array of stringsnames a string check that does not exist (only Array.isArray and length are checked), which is the advertising the statusFieldProblems docblock forbids. Read-only inference with no producer of non-string steps measured, and the card keeps the active rule frozen. Dedupe words: statusFieldProblems steps array of strings, refusal advertises check, check-platform-checklist active rule"]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
ACCEPT — PR #22995 (head
1901eba70, 1 file +75/−18, draft, not governed:scripts/check-platform-checklist.mjs) · reviewed 2026-10-12T08:40Z bysession_01CinRQcE6T8SvMbhH8D3NhS(skills seat 1).Checklist, verified on GitHub against the diff, not against the report:
main, first lineFixes #22987,Clause-②: noat the head of a body line, PR assignee = the card's; the only closing keyword in the body is that first line (the platform-checklist + NORTH-STAR: retirerecords-forms.picklist-org-appendand the "customers append values per organization" promise — the per-organization picklist append is ruled out #22869 relation is written as "remains open");skip-changesetapplied (scripts/ships in no package); no HTML comment; session-URL footer.check-governed-merges --pr 22995: NOT governed, 93 changed lines ≤ 3000 — ordinary queue landing.retiredbranch that returns early likeplanned—retiredReasona non-empty string,sincenull or release-shaped (RELEASE_REreused),stepsunjudged; the active rule now coversactiveanddraftonly; theplannedand active rules are byte-unchanged (F1–F16 untouched). The README's Retire step, and no field beyond it.retiredReasonrefusal moved INTO the branch (one message, behind thestatusFieldProblems(item)call site that G1/G3 pin) instead of being duplicated — the right shape; the card's premise is corrected on the record.selfTestPlannedStatus, floor 42 → 54 in the script's ownSELF_TEST_BATTERIESmap; no new battery function, so the frozenCOMPOUND_ANCHOR_LEDGERindispatch-gates.data.mjsis untouched — the frozen boundary held. Self-test 221 → 233 assertions.ce66b832dthroughablation-replace.mjs; on-disk proof x1→x0 / x0→x1; blob restored equal to HEAD,git diff HEADempty): the dead branch reds R1 and R3–R11 (10 rows); R2 and R12 stay green by design; the G-row text pin stays green — the blind spot G12 already discloses, so the fixture rows hold the branch. Direction reported, not assumed.git diff HEAD; no ledger file committed.1901eba70,--ran30/30; 29 exit 0;check:platform-checklistexit 1 = the base 8; narrowed eslint on the one file with its population and invariance stated; the watchdog's self-test run anyway (42 assertions). Check-runs on the head at review: 19 success / 11 skipped / 3 in progress, 0 red.retiredbranch (it is a text pin, not a semantic one) — the new R-rows do.Acceptance note carried, not filed: the active rule's refusal
"steps" must be a non-empty array of stringsadvertises an element-type check the code does not perform; read-only, no producer measured; carrier none.Landing: not governed → the seat lands it. When every check-run on the head is success or an expected skip (
check-expected-skips --pr 22995), relaypr_ready+automerge_enable; MERGED confirmed onorigin/main. Then PR #22982 (#22869) may land after its authorized approval.Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Landed: PR #22995 →
794177d9b(squash) · skills seat 1 (session_01CinRQcE6T8SvMbhH8D3NhS) · 2026-10-12T09:53ZThe
retiredbranch ofstatusFieldProblemsis onmain: a planned item retired before implementation (since: null, no steps) is legal when it carriesretiredReason; the watchdog's reading returns to the base symbol-anchor set (#22924's), with no line namingrecords-forms.picklist-org-append— the two problems PR #22982 added are closed.Content read on
origin/main:scripts/check-platform-checklist.mjscarriesitem.status === 'retired'. Landed by the seat through the merge queue (relaypr_ready+automerge_enable, queue merge). The card closed byFixes;pm:dispatchedstripped in this act.Generated by Claude Code