Skip to content

Commit 63fc947

Browse files
fix(pm): board-snapshot reconciles a disagreeing census and settles its cursor, so the patrol is green again (#27769)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D2xUqzuBY2k9Vz3LKcMkDdW)_ Fixes #27726 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:看板快照脚本在计数对不上时,先逐号核对再下结论;并把下次读取的起点往回留 15 分钟。 - 为什么改:巡检从 10-26 起每次都红。实测是 1 张转仓卡和 2 张「GitHub 列表返回旧数据」漏掉的关单,存档永远以为它们还开着。 - 风险与回滚:只动 `scripts/pm/board-snapshot.mjs`,不发布任何包。被销毁(404)的卡照样报红,没有放宽。回滚就是 revert 这一个提交。 - 先看:`scripts/pm/board-snapshot.mjs` 里的 `reconcileOpenSet`(核对逻辑),然后是 `settledCursor` 和 `numberReadVerdict`。 - 两分钟验证:`node scripts/pm/board-snapshot.mjs --self-test`,预期最后一行是 `OK … 191 cases pass across 14 batteries`。 - 需要你做的:无,席位落地。 ## What changes `scripts/pm/board-snapshot.mjs` only. - **The cursor settles.** A live walk's stored cursor (the delta's, or the incremental walk's) is now no later than `LISTING_SETTLE_MS` (15 min) before the instant its last listing page was read (`settledCursor`). The next run re-reads that trailing window. `since` is inclusive, so this is an over-read that writes identical bytes unless a row really changed. On a quiet board the cursor is already older and does not move. - **A disagreeing census is reconciled before it is reported** (`reconcileOpenSet`). When the first count check reads SHORTFALL or SURPLUS, the open listing (`openSetPath`, creation order) is compared with the archive's open records (`reconcileCandidates`; gone-ledger numbers excluded, capped at `RECONCILE_MAX_NUMBERS`). Each number the two disagree on is read on its own, with redirects not followed (`numberReadVerdict`): - 200: archived again. - A 301 to an issue in another repository: written onto the archived record as `transferred_to`. The record stays, and the census counts it apart (`counts.issues_transferred`). - 404 or 410: left exactly as archived. It is still a SURPLUS, still exit 2, and the summary names it with its `--restore`. The board's arithmetic is then read again and the census retaken. The reported verdict is that second reading. The first one is kept in `run.reconcile` and printed on the `reconcile` line. - The read helper gains a `reply` mode for the per-number read, and stays the file's only `fetch`. The header gains § "A listing that lags, and a card that moved", and the SURPLUS wording now says that a transfer is recorded rather than ledgered. ## Why — measured Every scheduled snapshot since 2026-10-26T15:11Z reported SURPLUS 1 to 3, read from the 22 manifests on `board-archive` (ok through 10-26T08:38Z, then 1, 2, 3). I diffed the archive tip (run 40947805554, 20:21Z: archived 323, gone 62, board 258) against the API. Exactly three open issues were archived that the board does not hold: | number | what the API says | why the archive kept it open | |---|---|---| | #25896 | 301 → objectstack-ai/hotcrm#2307 | the listing never returns a transferred number | | #26084 | closed 2026-10-27T02:32:52Z | the 02:33Z run's listing served its pre-close row (updated 01:52) while serving rows updated 02:33:14 and 02:33:18 fresh. The cursor became 02:33:18, so the close was outside every later window. | | #26638 | closed 2026-10-28T02:25:16Z, touched 02:26:13 | same, in the 02:26Z run, whose cursor was 02:26:16 | So it was not a count race. Both candidates on the card are partly true. It is a persistent drift with two classes: a transferred number, and a row the listing served stale inside the read window, after which the newest-row cursor skipped it for good. The same lag had left four closed PRs archived open (#20052, #20072, #20074, #22635). The issue-only count check could not see those. ## Tests New battery *the lagging listing and the moved card* (22 cases, floor 13 → 14 batteries) in `--self-test`. The fake board gains `lagging`/`lagListings` (serves a row's pre-update version), `moved` (301) and `destroyed` (404): - **Today's red, reproduced:** a stale close plus a transfer on a level archive. The first census reads SURPLUS 2, and after reconcile the check reads ok. The transferred record is kept whole with `transferred_to`. The board is read twice, and the second read comes after the repair. The next run reconciles nothing and never reads the moved number again. - **The settle window alone:** a lagged relabel moves no count, so no reconcile runs. It is caught by the next run only because the stored cursor settled. - **⛔ Not a tolerance:** a 404 number stays archived open, the check stays SURPLUS and the run exits 2, naming `--restore=301`. A gone-ledger number is never read. A blank reconcile listing compares nothing and reads no number. - Pure helpers: `settledCursor`, `reconcileCandidates`, `numberReadVerdict` (cross-repo 301, same-repo 301, 404/410, 5xx) and `openSetPath`. **Ablation:** with the reconcile disabled, 9 cases go red. With the settle disabled, 2 cases go red. Restored, all 191 pass. ## Acceptance notes - The five archived-open PRs that answer 404 (#19374, #19471, #19478, #19538, #19540) are destroyed records outside the issue count check. A reconcile reads them each time it runs (5 requests) and names them as destroyed. Nothing quiets them except the gone ledger, which is unchanged. - The earlier transfers recorded by hand in `gone.json` stay there and are excluded from the reconcile, so nothing is counted twice. - Acceptance proper is the next scheduled `Fleet Patrols` snapshot run going green. The live reading below predicts it. ## Verification <details><summary>Live run of this head against a scratch copy of board-archive (read-only against GitHub, wrote only the copy)</summary> ``` archive 25639 record(s): 321 open / 12026 closed issue(s) / 1 transferred, 22 open / 13269 closed pull request(s) reconcile the first census disagreed (SURPLUS of 3: archived 324, board 259), so it was reconciled before the verdict below: the open listing carried 276 number(s); 13 disagreed with the archive and were read on their own. archived again: #20052 closed, #20072 closed, #20074 closed, #22635 closed, #26084 closed, #26638 closed, #27751 open transferred: #25896 → objectstack-ai/hotcrm#2307 destroyed: #19374 (404/410 — --restore=19374), #19471 …, #19478 …, #19538 …, #19540 … The board's arithmetic was then read again; the count check below is that second reading. count check ok — 321 open issue(s) archived, board says 259 (open_issues_count 276 minus 17 open pull request(s)). freshness fresh — this run confirmed the archive against the live board. EXIT=0 ``` A second run straight after: no reconcile, `count check ok`, 34 numbers in 47 requests (the settle window's re-read), EXIT=0. </details> <details><summary>dispatch-gates</summary> `node scripts/pm/dispatch-gates.mjs --commands` derives 38 families. All 38 were run with exit codes captured before any pipe. Nine first exited 3 (`yaml`/`@changesets/cli` missing in a fresh worktree) and were re-run after `pnpm install --frozen-lockfile`, exit 0. `--ran` reconcile: `38 derived famil(ies) accounted for — 38 run, 0 NOT-MEASURED`. Changed lines 501 (+471 / −30), under the 3000 threshold. </details> ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes (eslint on the changed file, exit 0; the full run is CI's). - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset — `scripts/pm/**` publishes nothing; `skip-changeset`. - [x] 6. `packages/spec` touched: n/a. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016WV4cyJuqtXc5LcmkT6E4D --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 684f2f6 commit 63fc947

1 file changed

Lines changed: 665 additions & 33 deletions

File tree

0 commit comments

Comments
 (0)