fix: expire the docs cache and skip an unpublished area - #10
Conversation
`Docs` cached every fetched page to disk for ever, so `source_commit()` returned whichever build the cache first saw. A worker held an index five days old; every window `plan` could close ended there, and a roadmap whose first pull request merged after that commit bootstrapped to a cursor outside the documented history. `window_prs` refuses on such a cursor, and the exception aborted the whole plan, so one new roadmap stopped all progress reporting until the directory was deleted by hand. Give the disk cache a one-hour TTL (`TAUCETI_DOCS_TTL`), and skip an area whose cursor is newer than the documented build rather than failing the plan. The refusal itself is unchanged for a cursor that is in no reachable history: that is the rewritten-branch case and stays loud. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LgBAGJrhF7Frp6TbBZaY8
…wide aborts Addressing a review of the first commit. Per-URL expiry does not make a run coherent: the probe page can cross its TTL and fix `source_commit()` at a new build while another page is still served, in TTL, from the old one. `facts.collect` then reads line spans from one build and blames them at the other's commit, which shifts a declaration's span onto whatever now occupies those lines. `declarations` now checks every page against the build the reader already committed to, re-fetches once, and refuses if the site is still moving. Also: write cache entries through a temp file and `os.replace`, so a concurrent worker cannot read a truncated page (a half-written module page parses as zero declarations, which reads as an honest empty result); require the cached age to be non-negative, so a clock correction cannot make an entry immortal again; fall back to the transport when a fresh entry cannot be read. Two more single-area conditions that aborted the whole plan: say which of the two cases a cursor ahead of the documented build is, rather than calling a documentation rollback "not published yet"; and check `has_commit` before asking `merge-base` about a merge commit GitHub named, since a pull request merged since the last fetch is unknown to the checkout and `merge-base` fails rather than answering "no". The second was hit for real while verifying this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LgBAGJrhF7Frp6TbBZaY8
|
Second opinion from Codex, and what changed as a result. Its blocker was correct and is now fixed. Blocker — per-entry TTL did not give one run one build. Confirmed the mechanism: Concurrent writes were not atomic. Freshness edge cases. Age must now be non-negative as well as small, so a corrected clock or a skewed shared filesystem cannot make an entry immortal again; A recorded cursor ahead of the docs is no longer described as "not published yet." A bootstrapped cursor ahead of the build is ordinary. A recorded one means the documentation went backwards, and the two now get different messages, because they want different reactions from whoever reads them. One more plan-wide abort, hit for real while verifying the branch. Left as follow-ups, both pre-existing and neither a regression here:
Tests: 16 in |
Move the merge gate and the announcer to the same TauCetiProgress build the worker now runs, per the must-match rule both pins document. Picks up TauCetiProject/TauCetiProgress#10, which fixes the never-expiring documentation cache behind the five-day progress reporting outage, and stops three single-area conditions from aborting the whole plan. Neither the STATUS.md / PROGRESS.md formats nor the gate's validators changed, so this is a version move rather than a behaviour change on this side. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LgBAGJrhF7Frp6TbBZaY8
Picks up the documentation-cache expiry and the three plan-wide aborts fixed in TauCetiProject/TauCetiProgress#10. Until this lands the worker keeps running f735789, whose documentation cache never expires — so the outage it fixes returns as soon as /tmp/tauceti-docs-cache repopulates. The pins in TauCetiRoadmap/.github/workflows/progress-{merge,announce}.yml move to the same SHA. Claude-Session: https://claude.ai/code/session_011LgBAGJrhF7Frp6TbBZaY8 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move the merge gate and the announcer to the same TauCetiProgress build the worker now runs, per the must-match rule both pins document. Picks up TauCetiProject/TauCetiProgress#10, which fixes the never-expiring documentation cache behind the five-day progress reporting outage, and stops three single-area conditions from aborting the whole plan. Neither the STATUS.md / PROGRESS.md formats nor the gate's validators changed, so this is a version move rather than a behaviour change on this side. Claude-Session: https://claude.ai/code/session_011LgBAGJrhF7Frp6TbBZaY8 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This PR gives the published-documentation disk cache a one-hour expiry and makes
planskip a roadmap area whose cursor is newer than the documented build, instead of aborting the whole plan on it.Docs._getcached every fetched page to/tmp/tauceti-docs-cachewith no expiry, sosource_commit()kept returning whichever build the cache happened to see first. A worker was found holding an index five days old. Every windowplancould close therefore ended at that old commit, and a roadmap area whose first pull request merged after it bootstrapped to a cursor outside the documented history.window_prsrefuses on such a cursor,build_planonly catchesfiles.FormatError, and so a single new roadmap stopped progress reporting for the whole fleet until someone deleted the cache directory by hand. Four areas were in that state (EllipticCurves first, on 2026-08-07), and the worker's error breaker had latched after three consecutive failures.The TTL is
TAUCETI_DOCS_TTL, defaulting to 3600 seconds: far shorter than the deploy cadence and far longer than one run, so the cache still does its real job of serving the hundreds of module pages a bootstrap reads. Within a run the in-process memo remains the authority, so an expiry landing mid-run cannot mix two builds into one report.The skip is deliberately narrow. A cursor that is in neither the documented history nor
refis the rewritten-branch case, and that still falls through toarea_windowand raises exactly as before.🤖 Prepared with Claude Code