Skip to content

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

Closed

Description

@objectstack-fleet

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 on origin/main 246edf5b3): one branch for status === 'planned'; every other status, retired included, falls through to the active rule — "since" must be the release that introduced the capability and "steps" must be a non-empty array of strings.
  • docs/qa/platform-checklist/README.md, Lifecycle → Retire: set status: "retired" + retiredReason (and supersededBy when a successor exists); the row stays. It asks for no since and no steps.
  • 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 117817ec2 reads check-platform-checklist: 8 problem(s) (symbol anchors in access-security, attachments-storage, search — the open main-red anchor check:platform-checklist is red on main #22924's); head 57ef232b7 reads 10; the diff of the two lists is exactly the 2 lines above, both on records-forms.picklist-org-append. Minimal repro: flip any planned item to retired with a retiredReason.
  • Public door: pnpm check:platform-checklist, run on main by platform-checklist-watchdog.yml; by maintainer decision it is not per-PR CI (.github/workflows/lint.yml:3102–3110).

Change

  1. statusFieldProblems: a retired branch that judges by the README's Retire rule — retiredReason is a non-empty string; since is null (never shipped) or a release-shaped string; steps may 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 the planned or active rules.
  2. --self-test: a retired item with since: null and no steps passes; a retired item without retiredReason fails; a retired item with a non-release since string fails; the existing active and planned cases unchanged. The script's own mutation battery (its header describes the shadowing checks on statusFieldProblems) must still red when the new branch is dropped.
  3. 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.json applied in the worktree (uncommitted, restored after), pnpm check:platform-checklist reads 8 problems, not 10; report both counts and the restore proof.
  4. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from the worktree; a scripts/ 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.

Refs: #22869 · PR #22982 · #22924.

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-12T07:54Z
    Session: session_01CinRQcE6T8SvMbhH8D3NhS
    Account: huangyiirene (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-22987-checklist-retired-rule
    Worktree: objectstack-issue-22987
    Domain: 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#1
    File surface: scripts/check-platform-checklist.mjs (statusFieldProblems and its --self-test cases) (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 dev
    Clause-②: 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:dispatched claim names scripts/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*.mjs frozen and untouched


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    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 retired branch that returns early, as the planned one does. It follows the README's Retire step: retiredReason must be a non-empty string, since must be null or release-shaped (RELEASE_RE reused), and steps and supersededBy are 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 no retiredReason, 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) reads check-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 reads check-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 HEAD 0 bytes, git status --porcelain empty, 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 assertions at 712361b, then 233 assertions at 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 anchor if (item.status === 'retired') { x1 became x0 and the marker if (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 reported ok 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 printed check-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 printed OK 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.mjs at 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 only parserOptions.project hit 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 strings names 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

  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #22995 (head 1901eba70, 1 file +75/−18, draft, not governed: scripts/check-platform-checklist.mjs) · reviewed 2026-10-12T08:40Z by session_01CinRQcE6T8SvMbhH8D3NhS (skills seat 1).

    Checklist, verified on GitHub against the diff, not against the report:

    • PR form: draft, base main, first line Fixes #22987, Clause-②: no at 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: retire records-forms.picklist-org-append and the "customers append values per organization" promise — the per-organization picklist append is ruled out #22869 relation is written as "remains open"); skip-changeset applied (scripts/ ships in no package); no HTML comment; session-URL footer.
    • Scope: exactly the one file the claim declared. check-governed-merges --pr 22995: NOT governed, 93 changed lines ≤ 3000 — ordinary queue landing.
    • The rule, read in the diff: a retired branch that returns early like planned — retiredReason a non-empty string, since null or release-shaped (RELEASE_RE reused), steps unjudged; the active rule now covers active and draft only; the planned and active rules are byte-unchanged (F1–F16 untouched). The README's Retire step, and no field beyond it.
    • Deviation accepted: the item walk's older standalone retiredReason refusal moved INTO the branch (one message, behind the statusFieldProblems(item) call site that G1/G3 pin) instead of being duplicated — the right shape; the card's premise is corrected on the record.
    • Battery: R1–R12 inside selfTestPlannedStatus, floor 42 → 54 in the script's own SELF_TEST_BATTERIES map; no new battery function, so the frozen COMPOUND_ANCHOR_LEDGER in dispatch-gates.data.mjs is untouched — the frozen boundary held. Self-test 221 → 233 assertions.
    • Ablation (on the committed fix ce66b832d through ablation-replace.mjs; on-disk proof x1→x0 / x0→x1; blob restored equal to HEAD, git diff HEAD empty): 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.
    • The unblock, measured: the unfixed checker with PR docs(checklist, north-star): retire records-forms.picklist-org-append — the per-organization picklist append is ruled out #22982's two ledger files applied → 10 problems; the fixed checker with the same files → 8, line-identical to base (check:platform-checklist is red on main #22924's symbol-anchor set); restore proven by blob hash and an empty git diff HEAD; no ledger file committed.
    • Gates: 30 derived families run on 1901eba70, --ran 30/30; 29 exit 0; check:platform-checklist exit 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.
    • PM mechanism assumption corrected on the record: the header-described mutation battery does NOT red on a dead retired branch (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 strings advertises 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), relay pr_ready + automerge_enable; MERGED confirmed on origin/main. Then PR #22982 (#22869) may land after its authorized approval.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22995 → 794177d9b (squash) · skills seat 1 (session_01CinRQcE6T8SvMbhH8D3NhS) · 2026-10-12T09:53Z

    The retired branch of statusFieldProblems is on main: a planned item retired before implementation (since: null, no steps) is legal when it carries retiredReason; the watchdog's reading returns to the base symbol-anchor set (#22924's), with no line naming records-forms.picklist-org-append — the two problems PR #22982 added are closed.

    Content read on origin/main: scripts/check-platform-checklist.mjs carries item.status === 'retired'. Landed by the seat through the merge queue (relay pr_ready + automerge_enable, queue merge). The card closed by Fixes; pm:dispatched stripped in this act.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:skillspriority:p2Medium: important, M3tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions