Repository navigation
wiki: serve wp-update the whole drifted set (cap 1000) instead of attempt stamps - #63
Conversation
A review that fails returns before the bot save, and a stage-0 article
whose anchors fail is never written, so neither stamps last_reviewed_at and
the same articles head both /api/work queues every night (100-row window,
457 drifted articles).
- Migration 0014 adds moderation_state.last_review_attempt_at and
last_update_attempt_at (ms, NULL = never attempted).
- POST /api/runs accepts optional attempted: string[] (single-segment
slugs; malformed -> 400). Review runs stamp the review column, wp-update
runs the update column, all runs both, new runs neither. Stamps and the
run row are written in one D1 batch, each stamp guarded on the run_id not
existing yet, so a duplicate report never re-stamps and a failed write
loses neither. Stamps go in chunks of 50 (D1's 100-parameter cap);
unknown slugs are ignored; the response reports {stamped: n}. Over 300
slugs, the run is still recorded and nothing is stamped
(attempted_error). articles.version and last_reviewed_at are never
touched.
- /api/work's last tier orders by MAX(last_reviewed_at, the mode's attempt
stamp), never-touched first, slug as tiebreak; jobs carry
last_attempted_at (the mode's stamp). Separate columns keep a failed
re-pin from pushing a needs-work article back in the review queue.
…s never cost the run - 'all' runs report wp-update, review and new phases in one flat list, so stamping either column would sink articles the other phase never tried; they stamp nothing until the report carries per-phase lists. - A malformed attempted list now records the run like an oversized one (200, attempted_error, nothing stamped): the runner never retries a 400, so a single bad slug used to lose the whole run report and its notes. - Dedup before the 300 cap; transient/rate-limited jobs must not be reported (comment); the deploy-hazard note says POST /api/runs only fails with attempted. - Tests record bind arity (<=100 per statement) and assert stamped for every run kind.
|
Ready to merge after three small fixes (re-reviewed at Posted by the WikiLean PR swarm (lead); updated 2026-10-17. |
Status review (2026-10-14): ❓ Needs an owner decisionNo correctness bugs were found in the code. It was verified against real D1 semantics in Miniflare, it typechecks, and the tests pass, including in a combined build with #46, #50 and #54. It is also inert for queue order: apart from the new slug tiebreak, nothing changes until the runner sends Decision: who owns queue rotation? Fix / consider
To ship (only in the next Brain release window)
🤖 Automated status pass by Claude Code over all 18 open PRs (#46–#63), posted for the repo owner. An independent verifier re-checked each finding. All 18 heads were also merged and tested together in a scratch tree; the only test failure that comes from combining them is #47 × #55 (three other pairs had mechanical text conflicts). Reviewed head: |
…tempt stamps Drop the per-queue attempt stamps (migration 0014, the two moderation_state columns, POST /api/runs `attempted` and the /api/work ordering on them). Review rotation already lives in the runner since #52, which defers only article-specific failures; a second, looser server-side rotation would fight it. The columns were also declared on the drizzle table, so any later Worker deploy without 0014 applied first would fail every moderation_state write and read while the canary still passed. Nothing sends `attempted` yet, so removing it changes no behavior. The wp-update problem the stamps were for is simpler to fix by size. A failed stage-0 re-pin writes nothing, so a 100-row page let the same failures hold its head every night. The corpus is under 1000 articles, so /api/work now serves wp-update up to 1000 rows (review stays at 100) and every drifted article gets a zero-token attempt each night. The runner clamps per mode to match, the nightly asks for 1000, and run_wp_update now paces read-only articles as update_from_upstream's own loop does, since the sweep is several times longer. nightly_health warns once a single wp-update run reaches 800 articles, the point to revisit rotation. The articles.slug tiebreak stays, so ties on last_reviewed_at (every never-reviewed article) come back in a stable order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Recommendation: merge (at
The maintainer's call, not blocking: a stage-0 re-pin still stamps Posted by the WikiLean PR swarm (lead); updated 2026-10-17. |
nh.main() takes its 30-day drift window from time.time(), while the fixture logs are stamped relative to the fixed NOW (2026-10-14 17:46:40Z). test_report_withholds_the_trigger_on_a_small_sample would fail from 2026-11-08 and test_report_warns_when_a_wp_update_run_nears_the_cap from 2026-11-13, turning the required python job red on every PR. Both now run with time.time() patched to NOW; with the clock set to 2026-12-01 they pass, and the unpinned versions fail.
Nothing runs manage/nightly_health.py on a schedule, so its 800-row warning would go unseen. The nightly run itself now says when /api/work filled the page and drifted articles past it went unattempted, which also catches a host override or --limit that cuts the sweep short. The sweep test covers both a short and a full page; it fails with the warning removed.
…ut with this change OPERATIONS.md said every drifted article gets a nightly attempt from release B, but the nightly host's runner still asks for 100 until its checkout contains this change, and promoting a release doesn't move that checkout. It now says both halves must be live and mentions the moderate-log warning. token_budget.md's wp-update limit is 1000.
nightly-moderate.sh now defaults WIKILEAN_WPUPDATE_LIMIT to 1000, and run-now.sh launches it, so a manual near-reset burn would first repeat the full drift sweep the nightly already ran: minutes on a normal night, longer on a first sweep, while the review window runs down. run-now.sh defaults the limit to 100, as before this PR; an explicit WIKILEAN_WPUPDATE_LIMIT still wins.
|
Updated verdict at
Still open, and both are judgment calls rather than blockers: the re-pin-counts-as-review question, and log rotation. When merging, squash, or note in the message that the early commits include the dropped 0014 migration. Earlier verdict at a024762Verdict: merge after two small fixes. I reviewed
This PR is release-affecting ( |
…weep after 10 errors in a row Between moving the nightly host's checkout and promoting the new release, the runner asks for 1000 rows and an older Worker returns 100, and the full-page warning stayed silent. It now also fires when exactly 100 rows come back below the requested limit. run_wp_update had no consecutive-error stop. Each failing article costs about 19.5 s of retries and pacing, so a dead Worker, network or Wikipedia could hold a 1000-row sweep for hours, past nightly-moderate.sh's 4-hour stale-lock recovery. The sweep now stops after 10 errors in a row and returns 3 (aborted), as review does. Tests cover both, and each fails with its fix removed.
The test seeded 150 drifted rows, so any cap of 150 or more passed, including none. It now seeds 1001 and asks for 5000, expecting the first 1000 in slug order.
wiki: serve wp-update the whole drifted set (cap 1000) instead of attempt stamps
…be like the nightly Step 1 masks TOKEN/KEY/SECRET/PASSWORD values from launchctl's environment block. Step 5's token check runs in a subshell, and the launchd probe runs like the nightly's plist (Background, a /bin/bash parent kept alive) and reads runs and the last exit code before bootout. Step 7 says a run registers either way and names the real confirmation. The outcome table splits extract_failed/fetch_failed into host-wide (every article) and article-specific rows, and gains a Full Disk Access row. site/moderate.py line citations, shifted by #52 and #63, become function names. The fresh-checkout note lists what a clone must recreate. site/ops/README.md links the runbook.
Problem
/api/workorders its last tier bylast_reviewed_at, and only a bot save writes that stamp. A stage-0 re-pin whose anchors fail writes nothing (site/update_from_upstream.py,needs-work). So, with a 100-row page, the same failing articles head the wp-update queue every night:Why the attempt stamps were dropped
last_review_attempt_atwould be a second rotation with a looser rule.moderation_statetable. Any later Worker deploy without 0014 applied first would have failed article create, bot saves, the flag POST, the drift cron and/api/work. The release canary does not touchmoderation_state, and the release runbook has no D1 migration step.site/sendsattempted. The runner half was never written.Migration number 0014 is free again; #64 uses
0014_native_companions.sql.Change
wiki/src/index.tsGET /api/work:limitcaps at 1000 formode=wp-update, 100 for review.articles.slugis the lastORDER BYkey, so ties onlast_reviewed_at(every never-reviewed article) come back in a stable order.site/moderate.pyfetch_workclamps per mode (WORK_LIMIT_CAP: wp-update 1000, review 100). #52's review over-fetch keeps its 100 cap.run_wp_updatenow paces every article (2.1 s after a re-pin, 0.5 s otherwise), asupdate_from_upstream.py's own loop has done since the 503-storm fix. Before, it paced only after a re-pin, and the sweep is now several times longer.--limithelp text updated. It also warns when/api/workreturns a full wp-update page, including the 100 rows an older Worker serves whatever the limit, and stops the sweep after 10 errors in a row (returning 3, aborted, as review does).site/ops/nightly-moderate.sh,site/ops/nightly.envWIKILEAN_WPUPDATE_LIMITdefault 300 → 1000. Innightly.envthis is only the commented example.site/ops/run-now.sh,site/ops/README.mdWIKILEAN_WPUPDATE_LIMIT), as before this PR, so it doesn't spend the review window repeating the nightly's full sweep.manage/nightly_health.pymanage/test_nightly_health.pynh.main()tests run with the clock pinned to the fixtures' fixedNOW, so they don't turn the required python job red on 2026-11-08 and 2026-11-13.docs/OPERATIONS.mddocs/token_budget.mdNo migration, no new D1 state, no
/api/runscontract change.Scale
This works while the drifted set stays under 1000 rows and there is a single runner. Past that, the same failures hold the head again.
nightly_health.pywarns at 800, and the nightly run warns when/api/workreturns a full page. At that point, revisit attempt stamps for wp-update only:moderationStatedefinition, so a missed migration breaks only/api/work;docs/BRAIN-RELEASE-RUNBOOK.md.How tested
npx tsc --noEmitpasses.npx vitest run: 931/931 across 39 files.test/work-queue.test.ts: 150 drifted articles. wp-update returns all 150, in slug order (inserted in reverse), and review returns 100.site/test_moderate.py: 145 passed. New tests:run_wp_updateasking for 1000 and pacing every article.manage/test_nightly_health.py: 16 passed, including the new warning.test_wp_update_sweeps_the_whole_limit_and_paces_every_articlenow also runs a full page and checks the warning; it fails with the warning removed.wp_update_page_full(including an older Worker's 100-row page) and the 10-error stop each have a test that fails with the fix removed;site/test_moderate.pypasses 147/147.test/work-queue.test.tsnow seeds 1001 drifted rows and expectslimit=5000to return the first 1000, so it pins the cap itself. With the clock set to 2026-12-01, both pinnednightly_healthtests pass and their unpinned versions fail.scripts/ci-python.shpasses, except the activation-bundle integration check. That check needs Node 22, and the local Node is newer.Risks
.wp_update_report.jsonland.decisions.jsonl, and more cached HTML./stats"Review fresh": re-pins are bot saves that stamplast_reviewed_at, so the first-night sweep will raise that figure while AI review is down. wiki: /stats AI-review freshness, recent run counts, public GET /api/runs (counts only) #50 relabels it and adds an AI-reviewed row. Ship wiki: /stats AI-review freshness, recent run counts, public GET /api/runs (counts only) #50 with or before this PR in release B. If it slips, the jump is expected.Release
wiki/src/index.ts,site/moderate.pyandsite/ops/nightly.envare release-affecting. The Worker change ships only through the release B build and promotion; nothing deploys on merge.main. Either order is safe:What Jack must do
0014_native_companions.sqlapplied to D1 first; the promoter refuses otherwise.mainthat contains this PR (docs/OPERATIONS.md, "Updating an old checkout"). Until then the host's runner still asks for 100.site/ops/nightly.local.envdoesn't setWIKILEAN_WPUPDATE_LIMIT.wp=1000, andwp-update: N drifted article(s)should show N above 100 and close to the drifted count on/stats. A full-page warning means the drifted set reached the limit.0014_moderation_attempt_stamps.sql, from this PR's dropped stamp design, was never meant to be applied. If it was applied by hand anywhere, its two extra nullable columns are harmless: this Worker never names them.🤖 Generated with Claude Code