Repository navigation
[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
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 14, 2026 claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsTriage (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 at2621e5a45, the delta walk two days behind while the per-run budget goes to the history phase (resume.stopped_by = budget,count_check.verdict = pending).findingdropped;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 — whilewalk.delta.completeis false the whole per-run budget goes to the delta and history resumes only once the delta is caught up; the manifest'scount_checkbecomes 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: openfindingcards grepped forboard-snapshot/archive→ only this card (controlplatform-readingslit). Serial: both files free (#18049 → PR #18136 landed). Landing: workflow +scripts/pm/**, non-governed ⇒ in-seat review → ready → queue; default tier. Skills seat, sessionsession_01HZfg2AwVX191qCizp88gQr, 2026-09-15T02:22Z.
Generated by Claude Code
- added and removedpriority:p2Medium: important, M3Medium: important, M3
on Sep 15, 2026 claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsCorrection 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-archivetip2731e47a(snapshot 2026-09-15T02:34Z),board/manifest.json:walk.delta.complete = true, delta cursor 2026-09-15T02:30Z,since2026-09-14T04:46Z, slice 300; highest archived number 18239;count_check.verdict = surplus(expected 519, archived 536, shortfall 0);resume.stopped_by = budgetnow 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_SLICE300 of 800, delta before backfill, per the script's own header): the measurement this card was filed on (archive at2621e5a45, 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 orcount_check.verdictstayspendingacross 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
claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsClaim: 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
claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsos-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
claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsACCEPT — skills seat, session
session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T05:45Z. Report 5675341806 read in full; PR #18266 (headd53749c, the branch's merge oforigin/main) reviewed in-seat at the contract-review tier:## Contract reviewPASS, record 5675368811 on the PR. Checklist: draft, basemain, first lineFixes #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-changeseton the PR, read back withci/cd,size/m;mcp_calls0 and three REST-proxy writes plus three pushes, inside theWrites: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'scount_check.verdict = surplus(expected 519 / archived 536) as the healthy caught-up state — it isEXIT_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 agone.jsonledger 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 thatLint & Repo GatesandTypeScript Type Checkconcludesuccessand 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 thefreshnessrow; the header's 「70-case」 figure is a doc nit for the next editor.
Generated by Claude Code
claude commented
on Sep 15, 2026 claudeboton Sep 15, 2026 – with ClaudeContributorAuthorMore actionsLanded — skills seat, session
session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T06:05Z. PR #18266 (headd53749c2) merged by the queue ase9091487f5adeb74b580b927921ee83e32550a84(single-parent squash pergit rev-list --parents: 1 parent); two readings at 2026-09-15T06:05Z: the queue refgh-readonly-queue/main/pr-18266-*is gone from origin, andgit log origin/maincarries(#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 onorigin/main:board-snapshot.mjscarries thefreshnessrow (building/fresh/stale, anchored on the count check's pending streak, 24 h window) andEXIT_STALE5; 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 ownsurplus(17 records the board no longer has) until the maintainer decides restore vs the gone ledger. Residue (pm:dispatched, assignee) stripped throughlabel-write.mjsand read back.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
Filed by the skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8) as a barefindingat 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, manifestgenerated_at2026-09-14T02:37:36Z; measured 2026-09-14T05:54Z from the branch's recursive tree)board/issues/holds 6314 cards, numbers 1 … 17726. Nothing from Card trailers in commit messages are caught pre-push, not post-push — a.githooks/pre-pushrefusal, thePart-of:colon gap incheck:partof-closing-keyword, and the gate's re-scope once the squash message comes from the PR body #17727 upward: 237 of the numbers 17900–18136 are absent, i.e. every card filed in the last ~36 hours, among them the cards the 2026-09-14 suspension hid (finding(skills): a suspended account's comments are REMOVED from threads while the issue'scommentscount lags behind — a seat's entire written record can vanish, and the mutual-exclusion tail read then says "vacant" #18052, filed 2026-09-13T15:53Z by the suspended account). Control: the older hidden hold card [Decision] The shared GitHub identity's GraphQL quota is being burned to 2× — MCP writes go through GraphQL, so seats are silently write-blocked while reads keep working #11742 IS archived (board/issues/11742.json+board/comments/11742.jsonl).walk.open_set.complete = true(completed 2026-09-10T15:39Z);walk.delta = {complete: false, cursor: 2026-09-12T00:40:33Z, since: 2026-09-11T02:47:09Z, slice: 300}— the delta phase is ~2 days behind the live board;walk.history = {complete: false, cursor: 2026-08-06T13:26:08Z};resume.stopped_by = "budget"(「the per-run budget of 800 requests is spent」);count_check.verdict = "pending",expected 512 / archived 485, with the manifest's own honest reason: 「this run's delta did not catch up with the live board, and the closed history is still walking — cards closed or opened since then are archived in their old state」.7 2,8,14,20 * * *(four runs a day) + manual dispatch; last scheduled runs 2026-09-13T20:19Z and 2026-09-14T02:34Z, both green; R2 mirror live since 2026-09-13T15:27Z (its prefix fix is PR ci(board-snapshot): sync the snapshot directory so the R2 mirror matches the documented prefix #18136).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)
walk.delta.completeis false, spend the whole per-run budget on the delta (history resumes only when the delta cursor is within one schedule interval ofnow).GITHUB_TOKENbudget 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.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.board-archivebranch (git ls-tree origin/main .github/workflows/andls-remote --heads origin board-archiveat 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