Repository navigation
board-snapshot: once the open set completes, every run spends its whole budget on the closed history and never re-reads the live board — cards created or updated since 2026-09-10 are in no snapshot #18045
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2
on Sep 13, 2026 Claim: PM loop round 1
Session:session_01DAcomhvR9kKizeYgg89Vo8(GitHubos-project-manager, skills seat), claimed at 2026-09-13T14:59Z
Branch:claude/issue-18045-board-snapshot-delta-first
Worktree:objectstack-issue-18045
Domain:domain:skills(non-gatescripts/pm/**; NOT a governed path ⇒ in-seat contract-tier review, then ready + auto-merge by this seat). Channel: maintainer direct dispatch — pm@objectstack.ai in this seat's chat session at 2026-09-13T14:58Z: 「现在就派发处理 备份缺口」; the pair (pm:dispatched+ assignee) was written at creation and read back.
File surface:scripts/pm/board-snapshot.mjsONLY (self-test in the same file): a bounded delta walk (state=all,since= the previous manifest'sgenerated_atminus skew) runs FIRST every run, the history backfill takes the remaining budget, the manifest carries both cursors and reads the board's open count. ⛔ no second token, no retry loop, budget stays 800; ⛔ no.claude/**; the workflow file only if a premise forces it (declared).
Container & model:M,mode:subagent,model: opus(default tier — no path mandate); seat review at the contract-review tier
Clause-②: no
Thread-read: none — the card is new; its body is the whole thread at this claim
Serial constraints cleared at 2026-09-13T14:58Z: no branch or open PR names the archiver (git ls-remotefor*snapshot*/*archiv*empty; the dev re-reads open PR file lists before editing); last patrol run on the anchor 2026-09-13T13:46Z named no hold touching this file; verify lock free; rate 14786/15000;origin/mainat08a363aa. Devs in flight after this one: 1 (the seat now caps concurrency at 2).
Dedupe: #17434 (closed — the first-walk open-first fix, predecessor) is the only neighbour; no open card.
Generated by Claude Code
os-dev-report
{ "issue": 18045, "status": "done", "branch": "claude/issue-18045-board-snapshot-delta-first", "pr": "https://github.com/objectstack-ai/objectstack/pull/18054", "premise_still_valid": true, "summary": "A bounded DELTA walk now runs first on every run of scripts/pm/board-snapshot.mjs once the open set is complete: state=all&sort=updated&direction=asc&since=... , archiving every row it sees (open or closed, issues and pull requests), capped at DELTA_REQUEST_SLICE = 300 of the 800-request budget. The history backfill then continues from its own cursor with the remaining 500 and its resume semantics are untouched; a spent slice is not a run stop. The manifest gains walk.delta = { complete, cursor, since, slice } beside walk.history, next_since becomes the delta's high-water mark instead of the backfill's August cursor, resume.phase now names the WALK that stopped, and the board's own open count is bought right after the delta so count_check stops being permanently `pending` in the history phase. One file changed, +588/-34. The workflow, the cron, the 800 budget, the single GITHUB_TOKEN and the no-retry rule are all unchanged. assignee on the card was os-project-manager at pickup (the PM's dispatch pair) and this run never wrote it.", "tests": "All measurements at final head c2a54670 (origin/main 226970bb merged in; the changed file's blob f3723530 is identical before and after that merge). (1) node scripts/pm/board-snapshot.mjs --self-test :: exit 0 -- 89 cases across 8 batteries BEFORE, 125 across 10 AFTER ('OK board-snapshot self-test: 125 cases pass across 10 batteries'). Two new batteries: 'the delta plan: which runs re-read the live board, and from where' (16) and 'the delta walk, driven: the archive catches up with the live board' (18); the census/count-check battery floor moves 15 to 17 and the roster floor 8 to 10. (2) NEW OFFLINE HARNESS: the card's premise that --self-test already drives the walk with an injected fetch is FALSE -- the old cases only exercised pure functions and snapshot() had no instrument at all. driveSnapshot()/fakeBoard() now run snapshot() end to end against an injected globalThis.fetch (no network, no token, restored in a finally, the module-global request counter zeroed and restored). Driven scenarios: the measured b7c5f578 manifest + a 900-row closed backlog (regression, order, count check), a delta larger than its slice (budget split), a history walk interrupted at max-requests=6 (resume unchanged), and three runs over a still board (idempotence). (3) ABLATION: deleted the 25-line delta-first step from snapshot(). On-disk proof -- markers 'THE DELTA, and it goes first' and 'budget.slice(deltaPlan.slice)' went 1 -> 0, git diff --stat showed 25 deletions, blob f3723530 -> e0cdbd7b. Result: 10 of 125 cases RED, THE REGRESSION among them, self-test exit 1. Restored with git checkout HEAD -- path under an EXIT/INT/TERM trap; restored blob f3723530 == HEAD blob, git diff HEAD empty, git status clean, self-test back to 125 green. FIRST ABLATION ATTEMPT WAS A PHANTOM: over a four-row board the backfill reaches 'today' inside one page, so THE REGRESSION case stayed GREEN on the ablated tree. The 900-row backlog was added for exactly that (its own commit c8c7d068's parent 44a90e10) and the case then went red. (4) GATES: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 33 commands at the merged head -- byte-identical family set to the pre-merge derivation. All 33 run in the foreground, each exit code captured before any pipe: 33 green, 0 red. dispatch-gates --ran reconciliation :: exit 0 -- '33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN ... a DERIVED zero -- all 33 recorded an exit code and none of them is 3', and no STALE TREE warning. (5) LINT: pnpm lint (eslint . --no-inline-config, the whole repo, not a narrowed run) :: exit 0 at c2a54670. (6) Control-byte self-scan on the changed file: grep -naP over the C0 set plus DEL -- no match. (7) NOT OWED and not run, with reasons: no package is touched (scripts/pm is not a workspace package), so there is no dependency-closure build and no package test/typecheck; git grep over *.test.ts / *.test.mjs / *.spec.ts finds no test naming board-snapshot, so the tool-script clause is discharged by check:pm-board-snapshot, which IS the file's own --self-test and is in the 33. The live archiver was never run against the real board from this worktree.", "files_changed": [ "scripts/pm/board-snapshot.mjs" ], "premise_checks": { "P1": "CONFIRMED. selectWalkPlan reaches `incremental` only after `history` completes, so while the backfill runs no walk carries a recent since; in the history phase the cursor used is walk.history.cursor and resume.since equals it. board-archive tip b7c5f578 board/manifest.json reads walk_phase: history, walk.open_set.complete: true (completed_at 2026-09-10T15:39:49.484Z), walk.history.cursor: 2026-08-03T11:51:21Z, next_since: null, resume.stopped_by: budget, requests: 800, board.read_at: null, count_check.verdict: pending. Also confirmed the mechanism for board.read_at: readBoardArithmetic only ran when the open phase completed in that run or when the run did NOT stop -- a history run always stops on budget, so it never ran.", "P2": "CONFIRMED. git ls-tree -r over board/issues/ on a shallow fetch of board-archive into a private ref (refs/os18045/board-archive, the shared checkout never switched): 5232 files, highest number 17460. 18010, 18020 and 18025 each matched zero files; 17460 matched one.", "P3": "CONFIRMED. 16 open pull requests (one short page, so the listing is complete); 207 changed-file rows read once across them; zero rows name scripts/pm/board-snapshot.mjs. Neighbour worth knowing: #18033 touches scripts/pm/check-half-states.mjs, which this file imports EXIT_PREREQUISITE_NOT_MET, PROXY_FLAG, labelNames, proxyRearmPlan and resolveSweepRepo from -- it landed as 226970bb and is merged into this branch, self-test still 125 green.", "P4": "CONFIRMED. .github/workflows/board-snapshot.yml is untouched: same cron 7 2,8,14,20, same SNAPSHOT_ARGS, same single GITHUB_TOKEN, same 800 default (the workflow never passes --max-requests, so DEFAULT_MAX_REQUESTS is the budget). No premise forced a workflow edit." }, "deviations": [ "THE DELTA ANCHOR. The card spells the delta's since as 'the previous manifest's generated_at, minus a small skew'. Implemented as an ordered chain instead: the delta's own stored cursor; else walk.open_set.completed_at minus the skew; else next_since; else generated_at minus the skew. Reason: on the measured manifest generated_at is 2026-09-13T14:22Z while the board was last enumerated in full at 2026-09-10T15:39Z, so the card's literal anchor would have declared a three-day window this tool never walked -- the very gap the card was filed about -- and skipped it with the walk added to stop skipping it. Once a delta has run, its own cursor is the anchor and the two readings coincide. DELTA_SKEW_MS is 30 minutes, two runs' worth of the workflow's 15-minute job timeout.", "THE HARNESS. The card said to reuse the existing offline harness ('--self-test drives the walk with injected fetch'). No such harness existed: --self-test only exercised pure functions and never called snapshot(). Built one rather than pinning the walk order by reading the file's own source text, which would go green the moment two statements are reordered.", "ACCEPTANCE NOTES. The two out_of_scope_findings below are carried in this report only, not in a '## Acceptance notes' section of the PR body. Reason, measured and recorded in pm-dispatch references/platform-readings.md: a raw REST PATCH of a PR body appends a bare footer while keeping the existing one (two footers), and the prescription for that cell is to send a body with NO footer -- which would downgrade this PR's session-URL footer to the platform's bare form and lose the durable session attribution the PR-body form exists for. Neither finding is a defect, so a correct body was not rewritten to carry them." ], "open_questions": [], "out_of_scope_findings": [ "noted, not filed: .github/workflows/board-snapshot.yml line 95 calls the archiver's '70-case --self-test'. That count was ALREADY stale on origin/main before this change (89 cases there, 125 here), so this diff does not falsify it -- it was false already. A comment, read by no gate; not a reproducible defect, not a contract violation, not an authoring trap. Carrier: the next PR that edits that workflow file, which is the only thing that reopens it.", "noted, not filed: requestCount is a module-global counter that makeBudget() closes over, so two snapshot() runs in one process share one budget and the second would see it already spent. Harmless for every shipping caller (the CLI runs one snapshot per process) and the new offline harness zeroes and restores it around each drive, which is why that zeroing exists. An observation about a shape, not a defect anything reaches today. Carrier: whoever next needs two runs in one process." ], "mcp_calls": "0 -- every GitHub read on this card went through git or the repo-scoped REST proxy (issue and comments, 16 open PRs and their file lists, the PR create, the label write, this comment), and the seat's 15:52Z instruction routes every write through REST so it is authored as claude[bot]. No MCP GitHub tool was called." }
Generated by Claude Code
ACCEPT — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T16:12Z. Theos-dev-report(5654430206) is read in full; PR #18054 headc2a54670reviewed in-seat at the contract-review tier:## Contract reviewPASS on the PR (the anchor deviation and the harness the dev built are accepted). NOT GOVERNED (scripts/pm/**) ⇒ this seat flips ready and arms auto-merge;Fixes #18045closes this card on landing. The maintainer's direct order 「现在就派发处理 备份缺口」 is delivered on landing; the R2 copy still waits on the four secrets.
Generated by Claude Code
- added a commit that references this issue
on Sep 13, 2026 Landed — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T16:46Z. PR #18054 merged by the queue ase248c4d6(single parentb06b2db5); two readings at 2026-09-13T16:46Z: the queue refgh-readonly-queue/main/pr-18054-*is gone, andgit log origin/maincarries(#18054)with(#18033)as the lit control. NOT GOVERNED landing: review of record 5654442313 (PASS onc2a54670), ACCEPT 5654442689,--pair 18054exit 0, ready + auto-merge at 2026-09-13T16:12Z. The maintainer's direct order 「现在就派发处理 备份缺口」 is delivered: from the 20:07Z scheduled run the archiver walks a bounded delta of the live board first (300 of 800 requests), so every card created or updated since 09-10 entersboard-archive— and R2 (live since the maintainer's 15:27Z run) — within a few runs. Residue (pm:dispatched, assignee) stripped in this pass and read back.
Generated by Claude Code
- added 4 commits that reference this issue
on Sep 17, 2026
Maintainer direct dispatch — the maintainer (pm@objectstack.ai) in the skills seat's chat session at 2026-09-13T14:58Z: 「现在就派发处理 备份缺口」. Filed and claimed by the skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8) under the direct-dispatch channel; the claim comment below carries the pair.Symptom (manifest of the 2026-09-13T14:22Z scheduled run,
board-archivetipb7c5f578)walk_phase: history;walk.open_set.complete: true(completed 2026-09-10T15:39Z);walk.history.cursor: 2026-08-03T11:51:21Z;resume.stopped_by: budget(800 requests spent);next_since: null;board.read_at: null;count_check.verdict: pending.board/issues/: 17460. Nothing created after 2026-09-10 is archived — measured for the 12 cards a suspended account authored on 2026-09-13 (all now 404 on the API) and for this seat's owngen-sdui-manifest-cleanup.test.tscounted one caller lock fd (LOCKFDS1, expected 0) on an unrelated PR'sTest Core (1/6)— main green the same day #18010 pm-dispatch: the merge-queue guard still demands an approval for areferences/-only governed PR — #17950's tier is declared, not enforced #18020 platform-readings: a failed CI job re-runs through the MCP GitHub channel (rerun_failed_jobs201) while the seat's REST token reads 403 — record the row so no seat asks the maintainer for a click again #18025: MISSING in the 14:22Z, 08:28Z and 02:33Z snapshots alike.Contract violated
The workflow's own purpose line: 「the archive answers exactly one question, after a loss: what did the record say?」 On 2026-09-13 a loss happened (an account suspension hid 12 cards and ~30 comments) and the record said nothing, because the walk plan (
selectWalkPlan, phasesopen → history → incremental) only reachesincrementalafterhistorycompletes, andresume.sincewins over everything while it is incomplete. Predecessor #17434 made the FIRST walk open-first; it did not cover the steady state after the open set completes.Fix shape (the dev falsifies each premise first)
state=all&sort=updated&direction=asc&since=<previous manifest's generated_at, minus a small skew>and archive every row (open or closed) — bounded to a declared slice of the 800 budget (e.g. up to 300 requests, or until the delta is exhausted), so a busy board cannot starve the backfill entirely and a quiet one costs a page.delta.since/delta.completeand the history cursor);next_sincebecomes the delta's own high-water mark;board.read_atand the count check read the board's open count each run socount_checkstops beingpending.historyphase plus a card updated aftergenerated_at⇒ that card is archived on the very next run; the budget split; delta-then-history ordering; resume semantics unchanged for an interrupted history walk.GITHUB_TOKENonly; ⛔ no.claude/**; the cron and the workflow file stay as they are unless a premise forces a change (declare it if so).File surface:
scripts/pm/board-snapshot.mjsonly (its self-test lives in the same file). Acceptance:node scripts/pm/board-snapshot.mjs --self-testgreen with the regression case; the derived gate families (dispatch-gates --commands) green; the PR body states the budget split and the new manifest keys.Dedupe keywords: board-snapshot, open set, incremental, since, history backfill, budget, #17434.