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-archive tip b7c5f578)
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, phases open → history → incremental) only reaches incremental after history completes, and resume.since wins 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)
- Delta first, every run: before the history backfill, walk
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.
- History with the remainder: the existing backfill continues from its cursor with whatever budget is left; its cursor semantics unchanged.
- Manifest: record both cursors (
delta.since / delta.complete and the history cursor); next_since becomes the delta's own high-water mark; board.read_at and the count check read the board's open count each run so count_check stops being pending.
- Self-test: the regression pinned — a manifest in
history phase plus a card updated after generated_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.
- ⛔ Constraints kept from the file's docblock: no second token, no retry loop, budget stays 800,
GITHUB_TOKEN only; ⛔ 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.mjs only (its self-test lives in the same file). Acceptance: node scripts/pm/board-snapshot.mjs --self-test green 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.
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.