Skip to content

[finding] board-snapshot: the archive holds nothing above #17726 — the delta walk sits two days behind the live board while the per-run budget goes to history, so the 2026-09-14 suspension's cards were never captured #18137

Description

@claude

Filed by the skills seat (session session_01DAcomhvR9kKizeYgg89Vo8) as a bare finding at 2026-09-14T05:5xZ: ⛔ not routed, not graded — triage grades (#17942 ruling ③; the filer attaches keywords). Class (a): a measured gap between what the backup is for and what it holds.

Dedupe keywords: board-snapshot, board-archive, delta cursor, walk_phase, stopped_by: budget, count_check, hidden cards, #17374 F3, #18045.

The reading (board-archive at 2621e5a45, manifest generated_at 2026-09-14T02:37:36Z; measured 2026-09-14T05:54Z from the branch's recursive tree)

Why it matters

The workflow's own header states the purpose: a suspension erases every issue and comment the account authored, and the archive exists to answer 「what did the record say」 after a loss. On 2026-09-14 the loss happened (os-steve, between 03:43Z and 04:0xZ) and the archive did not hold the account's newest cards, because the delta walk was two days behind and the per-run budget was being spent on the history phase. The backup protected the old board and not the live one — the inverse of what a suspension takes first.

Shapes (⛔ not asserted; the owning seat and the maintainer decide)

  1. Delta-first until caught up: while walk.delta.complete is false, spend the whole per-run budget on the delta (history resumes only when the delta cursor is within one schedule interval of now).
  2. More runs: the repository GITHUB_TOKEN budget is 1,000 requests/hour per repository; hourly runs with the existing 800 cap stay under it if nothing else in the repo spends that token in the same hour — the header's 「who else spends it」 section is the place to check.
  3. A red row: count_check.verdict = "pending" for more than N runs, or a delta cursor older than 24 h, prints a warning the patrol (H64 already reads user-authored seat artefacts) can surface, instead of a green run that quietly archives stale state.
  4. objectui has no board-snapshot workflow and no board-archive branch (git ls-tree origin/main .github/workflows/ and ls-remote --heads origin board-archive at 05:55Z) — its hidden card objectui#9459 has no backup at all; a port is a separate card if the maintainer wants one.

Generated by Claude Code

Activity

  1. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    Triage (stand-in routing for the VACANT triage seat #6015, noted on #7623; grade by the lane's self-triage): lands in domain:skills (scripts/pm/board-snapshot.mjs + .github/workflows/board-snapshot.yml, the PM board archive — SUBJECT decides the lane); rationale: class (a) — the archive's purpose is stated in its own header (answer 「what did the record say」 after a suspension) and it measurably fails it on the newest cards: nothing above #17726 at 2621e5a45, the delta walk two days behind while the per-run budget goes to the history phase (resume.stopped_by = budget, count_check.verdict = pending). finding dropped; pm:queue · Bug · priority:p2 — the loss it exists for happened on 2026-09-14 and the newest cards were the missing ones. Direction (seat reading, veto window): delta-first — while walk.delta.complete is false the whole per-run budget goes to the delta and history resumes only once the delta is caught up; the manifest's count_check becomes the pin; ⛔ no cadence change and ⛔ no budget raise without the maintainer's word (the archive-cadence question on the maintainer's list stays theirs). Dedupe: open finding cards grepped for board-snapshot / archive → only this card (control platform-readings lit). Serial: both files free (#18049 → PR #18136 landed). Landing: workflow + scripts/pm/**, non-governed ⇒ in-seat review → ready → queue; default tier. Skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T02:22Z.


    Generated by Claude Code

  2. added theissue type on Sep 15, 2026
  3. changed the issue type fromtoon Sep 15, 2026
  4. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    Correction to this seat's grading 5673704446 — the premise was re-checked on the archive itself before dispatch, and it no longer holds; re-graded p2 Bug → p3 Task, scope narrowed. Skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T02:57Z. Read at 2026-09-15T02:56Z: origin/board-archive tip 2731e47a (snapshot 2026-09-15T02:34Z), board/manifest.json: walk.delta.complete = true, delta cursor 2026-09-15T02:30Z, since 2026-09-14T04:46Z, slice 300; highest archived number 18239; count_check.verdict = surplus (expected 519, archived 536, shortfall 0); resume.stopped_by = budget now stops the HISTORY phase only (cursor 2026-08-09). The delta-first design this card asks for in shape 1 is the one #18045 → PR #18054 landed (DELTA_REQUEST_SLICE 300 of 800, delta before backfill, per the script's own header): the measurement this card was filed on (archive at 2621e5a45, delta cursor 2026-09-12) was taken during that fix's catch-up window, and the bounded slice has since caught up. The seat graded on the card's numbers instead of re-reading the archive first — recorded as this seat's slip. What survives: shape 3 — a visible red row when the delta cursor is older than the schedule interval by more than 24 h or count_check.verdict stays pending across runs, so a stale archive is never a green run; shape 2 (more runs) and any budget raise stay the maintainer's; shape 4 (an objectui port) is a separate card if the maintainer wants one. Grade now: pm:queue · Task · priority:p3, scope = shape 3 only, measured against the live manifest at dispatch time. Not dispatched this wave.


    Generated by Claude Code

  5. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01HZfg2AwVX191qCizp88gQr (skills seat; claimed at 2026-09-15T04:33Z)
    Branch: claude/issue-18137-board-snapshot-stale-archive-red-row
    Worktree: objectstack-issue-18137
    Domain: domain:skills
    File surface: scripts/pm/board-snapshot.mjs + .github/workflows/board-snapshot.yml (the stale-archive verdict and the step that surfaces it; the self-test) (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate for scripts/pm/** or .github/workflows/**; the verdict's threshold is a design judgment after a measurement
    Clause-②: no
    Thread-read: 5673977209
    Serial constraints cleared: scope = shape 3 only per the correction 5673977209 (a stale archive is never a green run; ⛔ no cadence change, ⛔ no budget raise — both stay the maintainer's); both files free — every open PR's file list scanned at 2026-09-15T04:31Z, none touches board-snapshot; PR #18136 (#18049) landed before this claim; verify lock free; H17 index ∩ this face: none; batch 3, devs in flight 2 before this claim (#18201, #18237; #17497's dev reported — PR #18256 awaits approval)


    Generated by Claude Code

  6. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 18137,
      "status": "done",
      "branch": "claude/issue-18137-board-snapshot-stale-archive-red-row",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18266",
      "premise_still_valid": true,
      "head_sha": "d53749c2",
      "files_changed": [
        "scripts/pm/board-snapshot.mjs",
        ".github/workflows/board-snapshot.yml"
      ],
      "summary": "Shape 3 only, per correction 5673977209. board-snapshot now carries a `freshness` block (verdicts building / fresh / stale) and a new EXIT_STALE = 5, so a run whose count check has reached no verdict for longer than 24 h exits non-zero and the workflow's exit-code step fires; the workflow names code 5 in its error line and its step is renamed. The archive still lands: the commit and the R2 mirror run before that step, measured and kept. No cron, cap or slice change. P1 CONFIRMED on the live archive: 19 consecutive `pending` manifests 2026-09-10T20:23Z..2026-09-14T20:24Z, every scheduled run `success`, including 2026-09-14T02:37Z whose delta cursor was two days behind. SEPARATE READING the seat should see: scheduled run 46 (2026-09-15T02:31Z) is ALREADY red at exit 2 with `count_check.verdict = surplus`, expected 519 / archived 536 — the correction cited that verdict as the healthy caught-up state, but surplus is EXIT_COUNT_MISMATCH and it is this tool's destruction signature, consistent with the 2026-09-14 suspension destroying cards the archive still holds.",
      "verdict_design": {
        "anchor": "count_check pending streak start stamp (`freshness.pending_since`), carried byte-identical while the streak lasts",
        "N_chosen": "24 h from the FIRST unconfirmed run, which is itself one schedule interval after the last confirmed one, so the red lands interval + 24 h = 30 h after the archive was last known level on the four-a-day cron — the card's arithmetic, five consecutive runs at the current cadence",
        "reading_behind_N": "all 21 manifests board-archive has ever carried, read at 2026-09-15T04:5xZ: 1 run `ok` (2026-09-10T15:40Z, the run that finished the open set), 19 `pending`, 1 `surplus`. Legitimate `pending` by the manifest's own reason: 13 runs 'the board's own count was not read this run' (pre-#18054), 5 runs 'this run's delta did not catch up' (the post-#18054 catch-up, 2026-09-13T20:23Z to 2026-09-15T02:34Z = 30 h 04 m end to end, a bounded slice closing a three-day gap), 1 run 'the open set is still being walked'. So the window never reds a single stalled run (6 h), never reds a day-long gap, reds the tail of that worst-case catch-up, and reds every run of the four-day streak the card was filed on.",
        "b_leg_reachable": "yes — snapshot() already reads the previous manifest (`previous = readJsonFile(join(outDir, MANIFEST_NAME))`), so the streak state is available; it is stored as a stamp rather than a run counter (see deviations)"
      },
      "measured_before_after_on_the_card_manifest": {
        "before": "exit 0 — last two rows: 'count check  PENDING — archived 485 open issue(s); the board reports 512.' / 'paused  the per-run budget of 800 requests is spent — this is a planned stop, not a failure.'",
        "after": "exit 5 — every line above unchanged, plus 'freshness    STALE — no run has reached a count-check verdict since 2026-09-10T20:23:11.768Z (78 h ago); the window is 24 h.' and three further lines naming that the records were committed first and that it clears on the first run whose delta catches up",
        "how": "renderRun driven on the same buildManifest input, the BEFORE leg importing the file as of cb648cb1 (temporary copy inside scripts/pm so its relative imports resolve, deleted immediately; tree verified clean afterwards)"
      },
      "self_test": {
        "before": "125 cases / 10 batteries",
        "after": "145 cases / 11 batteries",
        "all_green": true,
        "new_battery": "the freshness row: a stale archive is never a green run (18 cases) plus 2 cases added to the driven battery"
      },
      "tests": "node scripts/pm/board-snapshot.mjs --self-test :: exit 0 — 'OK board-snapshot self-test: 145 cases pass across 11 batteries' (was 125 across 10 on cb648cb1; every pre-existing case keeps its outcome). GATE UNION on the final head d53749c2: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack with NO paths gives 51 families; all 51 run in the foreground, each exit captured by redirect (cmd redirected to a log, then EXIT=$?), all 51 exit 0. check:pm-dispatch-gates run detached per its own header (nohup, log tailed, `tail --pid` to block): 'VERDICT command-exit=0' with 'dispatch-gates self-test: 1723 cases pass'. Reconciled: `dispatch-gates --ran` = '51 derived families accounted for - 51 run, 0 NOT-MEASURED (a DERIVED zero - all 51 recorded an exit code and none of them is 3)', 0 UNRUN. ABLATION (fix committed first; each leg proved on disk by block-occurrence counts, restored with `git checkout HEAD -- path` and verified by blob hash 1ead53c1 equal to HEAD, `git diff HEAD` empty): (1) delete the EXIT_STALE leg from renderRun's exit selection: removed-block 1 to 0, marker 1, self-test RED, 2 of 145 fail (the card replay and the driven stale run). (2) age the delta cursor instead of the streak, i.e. the literal reading of condition (a): removed-block 1 to 0, marker 1, self-test RED, 2 of 145 fail including the quiet-board case — the false alarm is real and the case naming it is an instrument. Direction predicted and observed both times: turns red. Tree re-runs 145/145 green after restore. NOT MEASURED: the workflow file cannot be executed here (no runner, no scoped token) — validated by the repo's own workflow gates (check:ci-filter-parity, check:workflow-status-functions, check:workflow-step-name-quoting, check:self-test-workflow-commands, check:step-collectors, all exit 0) and by reading the step order.",
      "gates": {
        "derived": 51,
        "ran": 51,
        "not_measured": 0,
        "unrun": 0,
        "ran_line": "dispatch-gates --ran: 51 derived famil(ies) accounted for — 51 run, 0 NOT-MEASURED (a DERIVED zero — all 51 recorded an exit code and none of them is 3).",
        "head_sha": "d53749c2",
        "outside_the_runnable_total": "52 artifact-roster families, 11 declared wide-population families, 14 pending-changeset families (this PR writes no changeset), 7 workflow-valued invocations and 1 path-scheduled CI job — each named by the derivation and each CI's"
      },
      "deviations": [
        "P2(a) FALSIFIED as literally spelled: 'the delta cursor is older than the schedule interval by more than 24 h' is a false-alarm generator. walk.delta.cursor is the `updated_at` of the newest row the delta archived, not the instant the board was read: an empty page is a short page and nextWalkStep ends the walk returning the cursor it was handed, so on a quiet board the delta completes every run, the archive is exactly level, and that cursor sits as far in the past as the last card anyone touched. Implemented instead on the count check's own pending streak, which is exactly 'this run could not confirm the archive against the board'. Driven in the self-test (five-day-quiet board: delta complete, count_check ok, cursor five days old, verdict fresh, exit 0) and ablated (leg 2 above).",
        "P2(b) spelled as a STAMP, not `count_check.pending_runs`: a counter of consecutive runs moves on every run, and materialManifest exists precisely because a value that moves every run commits a manifest-only diff on every scheduled run and buries the real ones. `freshness.pending_since` is written once when the streak opens, carried byte-identical while it lasts, cleared when a verdict returns — two manifest writes per incident, and the pinned idempotence case survives (a case was added to pin that).",
        "The script carries NO cadence constant. The window is 24 h from the first unconfirmed run rather than 'interval + 24 h' computed in code, which yields the same 30 h on the current cron without encoding a cadence that is the maintainer's to change.",
        "A third freshness verdict, `building`, was added for an archive whose open set has never been enumerated in full. Without it every `pull_request` run of the workflow (temp dir, no manifest, --limit) would go red on a tool that is working, and so would the first days of any sibling adoption.",
        "Merged origin/main (fd124714) into the branch before the union, because the derivation printed a STALE TREE warning naming a gate source that had changed (scripts/pm/check-clause2-carriers.mjs). Roster identical before and after the merge; the union was re-run whole on the merged head.",
        "Process hygiene, reported rather than buried: (i) the first detached check:pm-dispatch-gates run was killed and relaunched because the origin/main merge changed files under it mid-run — its PIDs were read before the kill and were the only such processes on the box at that moment; (ii) a later `pgrep -f` pattern matched my own shell and SIGTERMed it (exit 144), the asking-shell hazard AGENTS.md names; no other agent's process was touched, and a sibling run of the same checker (worktree objectstack-issue-18141) was identified by its child's absolute path and left alone.",
        "A first ablation attempt counted a multi-line block with `grep -cF`, which counts matching LINES; the leg's own guard refused the reading as a no-op and it was redone with block-occurrence counts. Recorded because the first reading was void, not merely noisy."
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called, read or write",
      "api_writes": "3 REST proxy writes: POST /repos/objectstack-ai/objectstack/pulls (draft #18266), POST /repos/objectstack-ai/objectstack/issues/18266/labels (skip-changeset; read back as ['size/m','skip-changeset'], union holds, nothing stripped), POST /repos/objectstack-ai/objectstack/issues/18137/comments (this report). Plus 3 `git push` on the feature branch (the empty-branch write-route probe, the implementation commit, the merge commit). Reads were REST GETs only.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: .github/workflows/board-snapshot.yml's header calls the script's self-test '70-case'; it was 125 before this PR and is 145 after. A stale number in a comment is a doc nit, not one of the three filing classes, and it is outside this card's defect class. Carrier: the next PR that edits that paragraph.",
        "noted, not filed: scheduled run 46 (2026-09-15T02:31Z) is already RED at exit 2 — count_check surplus of 17, board 519 / archive 536, the first non-pending verdict since 2026-09-10. That is the archive holding records the board no longer has, the signature this tool was built for, consistent with the 2026-09-14 suspension; `--restore=N` prints them back and gone.json is the only thing that quiets it. Board state for the seat and the maintainer, not a code defect and not this PR's."
      ]
    }

    Generated by Claude Code

  7. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    ACCEPT — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T05:45Z. Report 5675341806 read in full; PR #18266 (head d53749c, the branch's merge of origin/main) reviewed in-seat at the contract-review tier: ## Contract review PASS, record 5675368811 on the PR. Checklist: draft, base main, first line Fixes #18137, no other closing keyword beside a card number (body scanned); changed files = the claim's file surface exactly (scripts/pm/board-snapshot.mjs +350/−5, .github/workflows/board-snapshot.yml +23/−2); skip-changeset on the PR, read back with ci/cd, size/m; mcp_calls 0 and three REST-proxy writes plus three pushes, inside the Writes: budget; gates 51 derived / 51 run / 0 NOT-MEASURED / 0 UNRUN with exits captured by redirect, the dispatch-gates battery detached per its header. Seat-measured on the head in a detached worktree: --self-test → 145 cases / 11 batteries pass, exit 0 (main 125 / 10); eslint exit 0; the two workflow gates green; the diff read whole — the exit-code step stays last after the commit and the mirror. The seat's P2(a) (age the delta cursor) was falsified by the dev's measurement and is recorded as the seat's overturned assumption; the anchor is the count check's pending streak. Deviations (7) accepted on the record. Correction the seat owns publicly: the correction 5673977209 above read the archive's count_check.verdict = surplus (expected 519 / archived 536) as the healthy caught-up state — it is EXIT_COUNT_MISMATCH, and scheduled run 46 (2026-09-15T02:31Z) is already RED on it: the archive holds 17 records the live board no longer has, the tool's destruction signature, consistent with the 2026-09-14 suspension. Board state, not a code defect: the maintainer decides between --restore=N (print them back) and a gone.json ledger entry (quiet the red); carried to the round report and the seat post's maintainer items. Landing: non-governed ⇒ the seat lands — ready through the CCR route and auto-merge SQUASH now that Lint & Repo Gates and TypeScript Type Check conclude success and every check on the head is green (36 runs: 25 success, 11 skipped, 0 failed at 2026-09-15T05:34Z); flip in the same act. After landing: the next scheduled snapshot run carries the freshness row; the header's 「70-case」 figure is a doc nit for the next editor.


    Generated by Claude Code

  8. claude commented on Sep 15, 2026

    @claude
    ContributorAuthor

    Landed — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T06:05Z. PR #18266 (head d53749c2) merged by the queue as e9091487f5adeb74b580b927921ee83e32550a84 (single-parent squash per git rev-list --parents: 1 parent); two readings at 2026-09-15T06:05Z: the queue ref gh-readonly-queue/main/pr-18266-* is gone from origin, and git log origin/main carries (#18266). In-seat landing (path face not governed): review of record 5675368811, ACCEPT 5675369063, provenance 5675380153; ready + auto-merge SQUASH through the CCR routes at 2026-09-15T05:45Z, enqueued 2026-09-15T05:45Z. Now on origin/main: board-snapshot.mjs carries the freshness row (building / fresh / stale, anchored on the count check's pending streak, 24 h window) and EXIT_STALE 5; the workflow's last step reads code 5 after the commit and the mirror; self-test 145. The next scheduled snapshot run is the first under the row — and it stays RED on the count check's own surplus (17 records the board no longer has) until the maintainer decides restore vs the gone ledger. Residue (pm:dispatched, assignee) stripped through label-write.mjs and read back.


    Generated by Claude Code

  9. added a commit that references this issue on Sep 17, 2026
    e909148
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions