This is a point-in-time record of the week of 2026-08-24 to 2026-08-30. It was written from the repository's history for that week and is not revised afterwards, so later pages may supersede what it says.

Week of 2026-08-24 to 2026-08-30

Window: 2026-08-24 00:00:00 to 2026-08-30 23:59:59, local author time Recapped: 2026-08-31 Commits in window: 276 (216 non-merge, 60 merges)

This is the largest week in the repository's history by a wide margin — 276 commits against 155 the week before, and roughly 98k tokens to read. Four plans were archived, one was created and archived inside the same week, and eleven new plan documents entered the index.

Two things ran end to end. Plan 145 went from a discarded design to 1,172 deleted legacy Parquet objects in five days, taking April from 24.48 GiB to 4.34 GiB. And Plan 142's coordination contract was built, deployed, and immediately taught three defects by its own first production run — one of which, a week later, deadlocked a deploy for nineteen minutes and became a plan of its own.

A note on the count. Seven non-merge commits appear twice under different shas: the same patch replayed onto a second branch, with one copy on master's lineage and one on an abandoned side branch. git log --all counts both. Verified with git patch-id --stable; the pairs are named under Merges. The distinct-change count is therefore 209, not 216, and every sha is still cited below.

What shipped

Linear as the weekly execution layer

Plan 149 is a bounded experiment: the repository stays authoritative, and Linear is tried as the daily work surface for one cycle at a time. The week opened by writing the plan (86b5197), recording Stage 0's bootstrap evidence (468b4ff) and moving the row to closeout, since nothing but observation is owed (3ecda99).

The correction that followed is the useful part. Linear had generated its first cycle from the Monday after the workspace was bootstrapped, so the week the board was actually in use fell outside every cycle; cycles were shifted onto the Monday grid and issue estimation switched from notUsed to fibonacci, which had been storing points that counted toward nothing (263e702, c13ad4a). Three Linear-writing skills — ticket-now, fill-cycle and close-out — landed in the same commit, each structured as the mirror of the plans skill: they write Linear and never repository state.

The GitHub automation table then turned out to describe neither the real triggers nor the real targets, and merge was set to Done rather than Soaking — the auto-transition the plan's own deviation note forbids (59c6080, b4699ef). A deliberate empty commit probed whether branch-name linkage alone fires the trigger (121e48b, cdc435e); branch linkage works, the status automations did not, and three explanations were eliminated rather than confirmed (d12372e, 3504b74).

That reading was itself corrected on the 29th (9c258b6). The merge automation does work — CAR-21 moved to Soaking two seconds after PR #265 merged — and the difference is that its PR title carried (CAR-21). CAR-22 is the negative control: two PRs, zero attachments, four days in Ready. The working rule is now that every PR title names its issue. The same read found the eight-issue seeding cap measured in the wrong unit (Cycle 1 seeded 8 and ran to 21) and the estimate scale's day-mapping off by roughly 3x.

Service health, and the absent-service gap

Plan 140 closed. Stage 4a had to land before 4b, because demoting the health sensors alone would have created silence: ct-container-unhealthy does not fire for a stopped container, since the status filter admits only running, restarting and paused, so a service that is gone leaves the metric rather than reading 0 (bd07963). The expected-service set is consumed from Plan 142's maintenance-running-set.txt rather than restated — an earlier draft inferred it from restart policy, which reproduces the same 28 names today and silently drops the restart-gap class. Stage 4b then set soft_fail=True on http_health_sensor only, so a timeout skips rather than fails. The DAG integrity contract was inverted to assert the new wiring rather than merely lose the old (15c5553).

The fire test is the first time the absent-service path could be observed at all: flaresolverr stopped at 02:32:36Z, the metric read 0 with the series count holding at 28, and the alert fired at 02:38:00Z. Before Stage 4a the exporter would have published 27 series and said nothing (c21a877). Two stale "deploy pending" markers inside the already-archived document were corrected later in the week (749fc80) — found while running the close-out skill, and the same class of error as trusting a status marker over deployed reality.

The structured log contract

Plan 141 existed because Plan 135's log bounds were live but the contract was not: the JSON formatter emitted four fields and dropped anything a caller passed via extra=, and alert rules matched on unanchored text. Stages 0–3 landed the ingestion policy, the Promtail pipeline, the dashboards and the CI contract check (8688fd9, e5f1969), and a logging health check split four out-of-scope gaps across their real owners rather than widening one plan — Plan 141 keeps the formatter fix, Plan 142 gains durable transition history, and Plans 154 and 155 are new (0681ea2, 4cca77b).

Five review findings were closed before Stage 4 deployed (0299fbe), the worst of which would have NaN'd every cartracker_* analytics gauge at first restart: validate_snapshot demanded exact set equality with METRIC_NAMES, so adding five metric names invalidated the file already on disk. The deployment queue was sequenced so every restarting step preceded every soak that starts, because Plan 136 Stage 3b was reading trawl's memory curve under scrape load (0371526).

Stage 4 deployed 2026-08-25 19:52 UTC, and the soak clock was recorded from the deploy rather than the merge — CAR-10 was marked "Soaking" when PR #247 merged while production still ran PR #241 (edd036d, e8bcdc0). The same commit recorded a defect in one of the stage's own acceptance instruments. Stage 4 was accepted after an 80,658-second window (1d96980, c60b1fb) and the plan archived (1e735f2).

Scoped coordination and host maintenance

Plan 142 is the largest build of the week at fifty commits. It exists because the repository had a deploy procedure and a storage runbook but no durable procedure for pausing production, updating Ubuntu, rebooting the VM, proving host and stack healthy, and resuming. Reframed on the 25th, Stage 1 replaces deploy intent with one scoped coordination contract rather than adding a second state machine (75d47fb, fdf7a62).

Stage 1 built the scoped requester and V043 (3b0d6dd), gated Airflow admission by scope across eighteen DAGs (4ec8d35), made every service report truthful drain evidence (35c524d, f799184), enforced mutation drain contracts (40568e2), aggregated scoped drain evidence (96c76fa), classified service execution lifecycles ahead of Plan 152 (d285265, 90221e3), guarded authorization (5ddbc62), hardened compatibility (0d690d0, 6f1e4bb) and published health signals with their alert rules (8f953c7).

Then it met a real database. Stage 1 deployed 19:52 on the 25th, and its first real invocation hung — 950 passing tests had all run against mocks (abcec56). Three defects came out of that hour:

After the fixes it worked: twelve sources known, the full draining → active → validating → released lifecycle, and redeploy.sh driving it cleanly twice more unprompted.

Phase B's one-hour maintenance-pool hold ran 20:14:57–21:14:57 and released clean, draining 44 pooled tasks in 74.5s with zero failures (e3b578f). Its finding removes work: peak concurrency was exactly 16, the pool's slot count, so max_active_runs=1 on orphan_checker — which the plan had named as the fix if the herd misbehaved — is not required. The hold's signature in the trawl memory curve turned out not to be the anticipated trough but the absence of the 15-minute detail batch spikes, cleanly separable from listing-scrape drift (4e4b0e2).

Stage 2 built the host-maintenance client end to end (0116d3e, fc6a7d0, fe8294e, 8df9e8e, 8d74a56, 4cb2476, 6123a62, 7aa0b42, 9a44efc, 3ab92e5, 38dd5cd, 5bf84bc, 23268c7, bc78a31, 9c5f2e6), and Stage 3 the resume gate — split by what each side can actually observe, because ops runs in a container and cannot see the running kernel or systemctl --failed (9c592a1, bfa5616, 37f077d, 4d6ed4a, 2e38bf6, d1309a3, 7778861, 14d9ef0, d629ffc, 34828a7, f8bb219).

Five unit tests that failed on Windows while passing on Linux CI were corrected at the boundary where the host assumption entered rather than by adding a Windows CI leg (b80f387, 0bee614, f49aeff). Among them, CoordinationCollector had no describe(), so prometheus_client ran a real PostgreSQL query at import.

Stage 4's window was scoped against the plan, the machinery and the live host before being scheduled — and is not runnable yet (0ab43e4). Five things block it and four are defects in stages this plan already calls built: Stage 3 is built but not deployed, validate-host cannot pass a deferred-reboot window at all because this host's kernels arrive from unattended-upgrades, a live docker compose run one-off makes stop refuse after production is gated, and oauth2-proxy's documented healthcheck exemption fails the release gate because no runtime code reads healthcheck-exemptions.txt. Three of the four share a shape: a slice proven against the case it was written for, meeting a neighbouring case in production.

The solver image, pinned and soaked

Plan 136 had a memory baseline to take before it could cut over. Stage 3a's read on the 25th answered it: a monotonic +40.5 MiB/h climb — ~971 MiB/day — from 1395 to 3256 MiB over 46 hours, no plateau and no involuntary recycle to bound it (437b0c7). Two things in that read were worth more than the slope. The runbook carried ~590 MiB/day, so every operator reading that page had the wrong clock by 65%. And at 5-minute resolution the series topped out at 80.0% of cap while raw 15s peaks were already 84–86.5%, understating it by ~280 MiB.

Stage 3b pinned TRAWL_IMAGE to the v1.4.2 multi-arch index digest — not the arm64 manifest, which would break x86 CI — and put the pin in docker-compose.yml rather than .env, since an .env override is exactly what had hidden six weeks of drift behind :latest (0a3bdb7). The 48-hour soak was accepted on the 27th with the verdict slowed, not yet bounded: restarts 0, zero oom-kills, a 99.87% solve rate against the old build's 95.7–96.1%, real daily reclaim the old build never did — but both peak and floor still drift up over the two cycles observed. The plan moved to closeout with a re-measure gate on 2026-09-17 rather than a verdict now (a7137d0).

Four runbook instructions that actively misled during the 25th's window were corrected from having been run, not from having been read (9705595) — including a grep -ioE 'solved|403' that reported 12 403s over a window where the counter reported zero.

Closing Plan 123's verification gates

Plan 123 had been sitting on production verification that was open and unrecorded from 2026-07-10 — the reason the closeout table grew a Lands column at all. The missing instrument arrived as a side effect of Plan 136 Stage 3a (819386a). The hourly DAG runtime drop is confirmed (135.1s → 82.1s, −39%, sustained across six weeks) and the 8GB DuckDB budget confirmed sufficient at 3.53 GiB peak RSS. The third item was converted to a residual risk rather than quietly counted as verified: the OOM short-circuit asks for proof against a real SIGKILL, and the guardrail prevents the event that would prove the guardrail.

Test-suite findings, and one that grew into its own plan

Plan 139 gained three stages, each from a defect that shipped. Stage F records that CI's database does not model production schemas — Flyway creates an empty airflow schema while Airflow owns the tables and CI points at SQLite, so two of the three search_path queries could not have run in any layer (aa6b7fb, 2805b28). Stage H records a census that drifted in two files counting different units: 13 DAG files wiring a health sensor against 14 sensor tasks in the DagBag, so Plan 134's deletion updated one and missed the other, and the marker split hid it — there was no local command that would have caught it (dfd506f).

Stage G tracked the Promtail contract checker's false failures (2155e23, b71f89e), then outgrew the stage. A fourth occurrence produced two runs of the same branch disagreeing with each other, and a verdict that is not a function of its inputs is not a stage. It became Plan 160 (72b6be6), and Stage G's evidence was removed from Plan 139 before rebasing rather than left to read as a duplicate refuting itself (354f535).

Plan 160's defect is one line of design: _run() keys results by line text and scores anything missing as "corpus says retained, Promtail dropped it", so "dropped by policy" and "not observed" produce an identical signal. Reality has three states and the checker encoded two. The fix moves from subprocess.run(input=...) to Popeninput= closed stdin the instant the lines were written and -stdin treats EOF as shut down now, so the race was created at maximum pressure — and splits the verdict so a varying absence is inconclusive rather than a contract violation (cf549a7).

Stage 0 then contradicted the plan's own premise, which is recorded as a marked correction rather than smoothed: the race was said to need a loaded machine and would not reproduce idle. It reproduced on the first unloaded run and in 12 of 20, and eight CPU burners lowered the rate to 3 in 10 (99a68b7). Stage G's 10/10 clean local runs were not measuring what they appeared to. Against the fixed checker the same loop went 12 failures to 0. The first green CI run is recorded as weaker evidence than the twenty local runs, and the row moved to closeout with a two-week observation gate (e88d176).

April recovery: seventy-six commits, and 20.14 GiB reclaimed

Plan 145 is the week's centre of mass. Its goal and success criteria never changed; its method changed three times, each because a measurement contradicted an identity key the previous method relied on.

The week began with a design that reconciled the legacy Parquet to production through metadata joins (c4685e5, b21fd48, 625f211, 45f13a5, 32dc609), which was then reverted wholesale (f946f88, 1601379) and rewritten (7504f88). The disproof: the pack sidecar's listing_id is wrong for 194,639 of 371,095 content matches — right fetched_at, wrong listing — while silver corroborates the legacy listing_id in 194,734 of 194,734 sampled disagreements (5c1162c). A second revision refused all metadata and re-derived everything from bytes at 24.8 core-hours; correct but overbuilt, carrying a two-store union through every stage to avoid a question unpacking answers for free. The third revision flattens the population first (57baacd).

From there it ran:

Four findings from the week are worth carrying forward.

The compression question was settled, unsettled, re-settled and finally answered by the run itself. The sidecar trust boundary was first corrected and tracked as CAR-28 (24eb9d6) — only listing_id is unusable, since fetched_at measures 100.00% exact against artifacts_queue_events for June's 1,124,122 members. An in-memory bench then said the scrambled ordering wins by 19.4% (71db6c6, 27749ce); that claim was withdrawn once both bench tests turned out to be biased in opposite directions (f0f8f3d, e2a8a65), bounded to a subset rather than a second full pass (07c7a38, 74ac0a1), and then run for real. pack-trial --apply split: drawn in current order true ordering is 27.50% larger; drawn in true order it is 4.76% smaller. A 32-point swing from sample selection alone, larger than the effect under test (d7d4e7a). The decision rule was fixed before the run, so the incumbent carries — and a single sample, which the plan originally specified, would have produced a confident wrong answer.

The full apply committed 341,903 artifacts and 701,375 silver rows across 70 receipts on 2026-08-29, in ten minutes (a920001). The V040 live-state proof ran twice, the first window passing and being rolled back deliberately. The arithmetic proves the canary exclusion in both directions: the batches wrote exactly 234 artifacts and 505 rows short of the assign census, and the two sum to precisely that census.

The corrected Stage 6 gate was confirmed cell by cell (11856de). Figures derived from the assign and apply censuses and marked "derived, not yet measured" came back exact: 139,604 members with no queue event, 133,548 materialized, 6,056 old pack members, 325,414 with no listing, 657,629 with one. Three independent measurements of the same population agree.

End state (7af6eda): the prune deleted and verified 983,043 objects with 0 refused; the deletion removed all 1,172 legacy Parquet by exact key in under a minute, 0 refused, 0 absent, 0 errors. April went from 24.48 GiB to 4.34 GiB — 20.14 GiB reclaimed, more than the 13.66 the plan set out to delete, because the flattening created a loose population the prune then removed. The plan was archived the next morning, with Plan 156 moved into the build order in the same transition since its only trigger had been Plan 145 Stage 5 (066d3e2).

A post-mortem was filed under docs/evidence/ and brought up to date twice, once because its own "where it stands" section was overtaken within hours of being written, and once to fold in what this machine's session transcripts hold that the plan documents do not (dd076b7, dfba51d).

Separating fetch from enrichment

Plan 147 was written after Plan 142 Stage 0 found that pausing results_processing for a maintenance window would put the detail scraper into a re-scrape loop. last_detail_scraped_at is the guard that stops a re-fetch, but it is written by the processing service two hops downstream and behind an async queue; when that chain breaks, the claim rows are deleted, nothing sets the timestamp, and the same ~100 listings are re-claimed every fifteen minutes.

A pre-implementation accuracy pass corrected five statements and caught one latent design defect (8e51d0e): Stage 1 as written reopened the very loop it closes, because it rebuilt the staleness view to read a column that only Stage 2 teaches anything to write.

V048 expanded the one column into two — last_detail_fetched_at, scraper-owned; last_detail_enriched_at, processor-owned — as an expand, not a rename, because Flyway here is forward-only with no staging environment (2b03853). Stage 2 moved the guard next to the fetch, setting the fetch timestamp on the same cursor that deletes the claim rows (4fdc0e3), with one deliberate deviation from the plan's literal wording: the writers pass a single parameter bound to both columns, so the two are physically unable to disagree rather than asserted to agree.

The production evidence is what makes this plan finished rather than shipped. V048 applied at 05:35:06 UTC and the load-bearing check is that nothing moved — V040's rule and V048's COALESCE rule evaluated side by side across all 51,081 live rows found zero rows classified differently (f099361). Two rows did diverge, and they turned out to be post-migration writes by the Stage-1-era image, which corrected two claims the plan made. Stage 2's deploy showed the guard operating for the first time: 100 listings claimed, fetched and released, and none of them back in the queue (86ec5f6). Stage 3 answered its own question from data already in the lake rather than building the gauge it specified — the population escalating backoff would target is 13 listings and 278 requests a month, so the follow-up is declined on evidence rather than deferred again (4284a2d) — and verified the plan's whole point in production: results_processing paused 81 minutes with the scraper running, five batches, 2,000 listings, zero repeats (eb938ce). That retired the only reason scrape_detail_pages.claim_batch sat in Plan 142's maintenance pool.

V049 contracted the pair (564db85), and the backfill inside it is the whole reason the migration is more than a DROP COLUMN: 41 legacy-only rows were held only by the COALESCE it removes. The plan closed the same day (ab2aabf, 567147f), with two things recorded because they were nearly missed — the deploy order inverted from Stages 1–3 and the timestamps show it was followed, and a post-deploy check framed as an invariant returned 272 because the framing was wrong, not the database.

The safety mechanism that stopped a deploy

Plan 158 was created and archived inside this week. It was found on the 30th while deploying Plan 147 Stage 2: the deploy hung for nineteen minutes with every production DAG parked, would have hung indefinitely, and was aborted by hand (4ecee69).

_DeployIntentSensor.poke() returned early on intent != "none", and redeploy.sh sets intent to pending before it drains — so during every deploy the INSERT INTO coordination_gate_observations beneath that return was unreachable. The drain then waits on exactly that table. The confirming evidence is that the table was empty for generation 17 and for every generation that has ever existed: the INSERT had never once executed in production. It had not bitten before because the count is zero whenever no affected run is live at the instant intent is declared, which is a race whose losing branch is unbounded.

The archaeology is instructive: commit 4ec8d35 — Plan 142 Stage 1, six days earlier in this same week — extended a sensor whose original body was a bare SELECT intent FROM deploy_intent, adding the coordination query beneath that pre-existing return. Each half was correct and tested; the seam was neither.

Stage 0 wrote recognition and recovery into the runbook first, because the deadlock was live until Stage 1 shipped (220395a). Stage 1 moved the write above both returns with admission behaviour unchanged, asserted over the full cross product of intent × phase × scope (748165e). Stage 2 bounded the one wait in the deploy that had no timeout — _prepare_coordination polled in a while : with no deadline, so the operator was the timeout (ac3d140). Stage 3's method was rewritten before it ran, because the incident's real cause was a stray one-shot container holding the drain, not the timing of the declaration, and reproducing the failure means reproducing the hold (08bc8b5).

It was then proved in production (398e943): four observation rows written at generation 23 across the 15:30 fire while a decoy still held the drain open, authorization landing within 9s of the last blocker clearing. Two corrections came from running the checked-in protocol — dbt/Dockerfile sets ENTRYPOINT ["dbt"], so the documented decoy ran dbt sleep 900 and exited, and the protocol's claim of zero fleet change was wrong because both service Dockerfiles COPY . .. The plan archived, re-filing what it found: evidence to Plan 152 for its trigger, and a new Plan 159 for the observability gap it declined to claim — that same container's healthcheck failed 1,727 consecutive times across fourteen hours with nothing surfacing it (2128755).

The archiver endpoints that never signalled failure

Plan 134 was a two-line sketch that predated four months of change. Its Stage 0 survey corrected three claims and dissolved one of its five endpoints entirely (0056740). /compact/silver/run's predicate is not error: compact_silver catches per-partition exceptions into a failed counter and returns "error": None, so a run where every partition failed is a 200. And /cleanup/parquet/run needs no predicate at all — its query has been the stub SELECT 0, 0 WHERE FALSE since V036 dropped raw_artifacts, so 133 green daily runs did nothing, while cleanup_artifacts has never produced a single dag_run row. That whole dead chain was deleted together, along with the three registries that knew about it (056cde7, 33b275e).

The retrospective answered something sharper than "how often does this fail" (79c837e): twice in three weeks, and Airflow reported 128 consecutive successes across both. During 2026-08-08 to 08-13, MinIO returned XMinioStorageFull on the silver flush for five days across 112 green runs, with dbt building on stale data throughout. The survey also caught its own method error — Plan 141's labels only exist from 2026-08-25, so a query pinned to source="application_file" sees five days rather than ninety.

Stage 1 added warning-only failure predicates on flush and compact, shaped as pure functions on the summary dict, each documenting what it deliberately does not fail on — no count is ever the predicate, because a quiet hour legitimately flushes zero rows (585c56f). And it repaired the pager, which turned out to be the larger half (0306629, a05168b): hourly_analytics_refresh's notify task had 12 failures and zero successes because it read ti.dag_run and ti.execution_date, neither of which exists on Airflow 3's RuntimeTaskInstance. Both lines arrived in one commit on 2026-05-08 and were then copied twice — dbt_build has failed 268 times since, and pack_bronze_html's has never fired and was latent. One shared notifications.py replaces all three, because three copies is how one commit silenced the fleet. Stage 1 deployed on the 30th with a seven-day observation window (1d78528, b28aebb).

Public surface, and the planning index

Plan 138 committed two overview documents that had sat untracked across several sessions, since Stage 0 cannot cite what is uncommitted (2eb7c7e), then recorded them as named Stage 0 sources with a boundary and a gate (e4083a9). The boundary matters: the architectural overview documents the anti-detection path and both print production object prefixes, which the non-goals already bar from public surfaces. Gate 0b requires one section reconciled first — it says FlareSolverr performs the browser bootstrap, while docker-compose.yml marks that container vestigial and names trawl as the live path.

Plan 146 kept the index honest through a week that moved faster than it. It archived Plan 123 and deleted the temporary deployment queue, which had become actively wrong (33b24dd); reconciled rows an hour after writing them when Phase B closed (49fff9b, e6362a8); reconciled active states and build order mid-week (0082959, fb3704e); trued the index up against the plan documents when five rows had drifted (c593cae); and ran a final truth pass on the 30th (6eb8ead). That last one found something worth keeping: Plan 142's blocker said the Stage 3 resume gate was undeployed and both images stale, but ops had been rebuilt incidentally on Plan 147's deploy and had been serving all three coordination endpoints for eleven hours while container-health still 404'd. The fleet had drifted in the helpful direction, and nothing reported the split. Weekly recaps were backfilled to 2026-06-14 and then to the repository's start (1465fa6, cb4de08, 58e68a3).

A commit-plan-attribution skill was added mid-week so future recaps can resolve a plan from the commit message alone (148af5e) — it declares no plan of its own, which is itself an attribution.

New plans, and four retired

Eleven plan documents were created this week. Plan 150, the analytics product and BI serving roadmap (3692ecd). Plan 151, distributed tracing and runtime topology (4375760). Plan 152, scheduled worker lifecycle (0177953), which gained its missing trigger evidence a week later from Plan 158's stray container. Plan 154 and Plan 155, split out of Plan 141's logging health check: 16 of 26 expected-running services have no Loki path, and three panels serve 26 services with no per-service dimension. Plan 156, block-page detection (0be4ef3) — the detail parser keys only on Cloudflare's "Just a moment…" title, so an Akamai "Access Denied" at 430 bytes becomes a silver observation for a live listing with every vehicle field NULL. It stayed in the backlog until Plan 145 Stage 5 finished, because that comparison is only meaningful while the parser is unmodified. Plan 157, April reprocessing enrichment (464c91c), blocked on Plan 125 — Plan 145 stays append-only by design, so it leaves April with three field-coverage shapes.

The last three came out of a planning conversation that stalled. Plan 161 names why: docs/ARCHITECTURE.md:179 carries a Testing Strategy section that is a description of what was true in April, not a contract anything is held to, and it is already false in four checkable ways — so a census against an unenforceable document produces a list of differences and no decisions (e9a3053). Plan 162 is deliberately a stub for the same reason (4673902), carrying only preliminary readings. Plan 163 is a register rather than a project — somewhere to put fixes too small to justify keeping a stale plan alive, which is what had happened for four months (ea48278).

Plans 103 and 107 are superseded by 161 and 162; Plans 73 and 106 are absorbed into 163. All four go to Superseded rather than the archive: they were replaced, not delivered, and the archive would credit them for work done under other plans. One of 106's items is recorded as an obsolete landmine rather than a to-do — C1 asked that /ready return 200 across services, and the premise has since inverted.

What moved between states

Thirty-nine revisions touched docs/PLANS.md in the window — more than five a day. Comparing the last index revision before the window with the last one inside it:

Plan From To
73 backlog superseded
103 backlog superseded
106 backlog superseded
107 backlog superseded
123 closeout archived
136 build closeout
140 build archived
141 build archived
145 build archived
147 build archived
149 (absent) closeout
150, 151, 152, 154, 155, 156 (absent) build
157, 159 (absent) backlog
160 (absent) closeout
161, 162, 163 (absent) build

Plan 158 does not appear because it entered and left inside the window: created in the build order at position 1 on 2026-08-30 and archived the same day. The archive gained six rows — 123, 140, 141, 145, 147 and 158 — taking its stated count from 110 to 116.

One caveat on this table, and it is a defect rather than a footnote. scripts/audit_plan_state_history.py's STATE_BY_HEADING map has no entry for ## Current closeout, which is what the section has been called since b76fb44 on 2026-08-17. Run as written, the script reports every closeout plan as (absent) — Plan 136 reads as leaving the index entirely, and Plans 149 and 160 as never arriving. The table above was produced with that heading added to the map. The gap predates this window and belongs to Plan 146, whose Stage 1 owns the script.

What is still owed

Every plan document created this week appears in a state change, so the "document in no table" defect that Plan 146 exists to prevent did not occur.

Unattributed commits

Three non-merge commits carry no plan reference in their subject or body. All three are recapped above under the work they did; what is missing is the link to a stated why, not the work.

Commit Subject Note
cb4de08 docs: add weekly recaps through August 23 No body. The --stat shows seven files under docs/recaps/; the recap-backfill work belongs to Plan 146 Stage 6, which 1465fa6 and 58e68a3 say explicitly and this one does not.
86b5197 docs: plan Linear execution layer No body. The subject names the plan by title rather than number, and the --stat creates docs/plans/plan_149_linear_execution_layer.md. I read it as Plan 149 on the title, not on the diff.
15c5553 Update DAG integrity contract for the Stage 4 demotion Body names a stage but no plan. It sits between two Plan 140 commits and updates the contract bd07963 changed; I read it as Plan 140 on that reading, which is a neighbouring-commit inference, not the commit's own text.

Two further commits state that they belong to no plan, which is an answer rather than a gap: 148af5e ("No plan — repository commit hygiene requested during Plan 142 implementation"), 32dc609 ("No plan -- pure ruff formatting cleanup") and 58e68a3 ("No plan — historical weekly recap backfill requested").

Merges

Sixty merges. Fifty-eight are pull-request merges (#237–#297, with #244, #275 and #277 not appearing); two are branch-integration merges rather than PRs.

Commit PR Branch
3f0c09c #237 recap/2026-08-23
e4422e3 #238 docs/plan-149-linear-execution-layer
d0d3d84 #239 docs/plan-150-analytics-product-bi
8bd2e54 #240 docs/plan-149-stage-0-evidence
73f9d4d #241 plan-140-stage-4-retire-dag-sensors
30b180d #242 docs/plan-140-stage-4-evidence
964f7f3 #243 feature/plan-142-stage-1
8d50ffc #245 car-14-plan-142-stage-2-platform-neutral-tests
f32d21b #246 plan-149-linear-execution-corrections
80485a0 #247 car-10-plan-141-structured-log-ingestion-contract
ea0ab91 #248 plan-141-deploy-queue-sequencing
bb06054 #249 fix/plan-142-coordination-stale-alert
d078cee #250 docs/2026-08-25-deploy-evidence
458d2c5 #251 feature/plan-142-stage-2-host-procedure
31466a5 #252 feature/plan-142-stage-3-resume-gate
9a6b3c3 #253 plan_141_closeout
c9e7532 #254 plan_141_archive
3f10278 #255 plan-145-car13
6e03905 #256 plan-145-car13
f354eab #257 plan-145-rewrite
26da70e #258 plan-145-revision-2
8c1cd77 #259 plan-136-stage-3b-closeout
cd57a25 #260 plan-145-revision-2
243c5ce #261 plan-145-stage-4
a6f76fb #262 plan-145-stage-4-parse
9f59256 #263 plan-145-stage-4-census-correction
a5f8ed1 #264 plan-145-stage-5-design
48987c1 #265 plan-145-stage-5-compare
d4df9cb #266 plan-145-stage-5-writer
302050c #267 plan-145-docs-slice3-stage5b
a22bda5 #268 plan-145-slice2-probe-mode
05d3749 #269 plan-145-slice2-probe-review2
d10a7ee #270 plan-145-probe-evidence
0fe254d #271 plan-145-slice3-phase-a
c5c5ee2 #272 plan-145-stage5-block-page-filter
3777e2e #273 docs/plan-146-weekly-recap-backfill
a5c05df #274 plan-145-stage5-evidence
9ed0f45 #276 fix/plan-145-apply-canary-exclusion
64631de #278 plan-145-stage-6
85ce87c #279 plan-145-stage-6-dryrun-evidence
1c32504 #280 docs/plan-138-overview-sources
9819075 #281 docs/plan-142-stage-4-findings
c651603 #282 docs/plan-147-preflight-corrections
39d6f7d #283 plan-145-stage-6-complete
a0c0f7f origin/master → feature/plan-147-stage-1
8b10160 #284 feature/plan-147-stage-1
4b2426b #285 feature/plan-147-stage-2
a391e66 #286 plan-158-coordination-gate-deadlock
b7b90ac #287 docs/plan-147-evidence-and-plan-158-stage-3
73e5249 #288 feature/plan-158-stage-2
ee60f5e #289 docs/plan-158-stage-3-evidence
a1ace17 #290 feature/plan-147-stage-3
de18913 #291 feature/plan-147-stage-4
1d8b088 #292 docs/plan-147-closeout
108e910 #294 feature/plan-160-promtail-checker-reliability
d1daa9d origin/master → plan-134, after Plan 160 landed
0010cb8 #293 feature/plan-134-survey-and-dead-path-removal
bfc791a #296 docs/recap-backfill-to-repo-start
610f77e #295 feature/plan-134-stage-1-warning-predicates
2f24be1 #297 docs/plans-cycle-maintenance

Two merges in the window were themselves reverted inside it: 1601379 reverts 3f10278 (#255) and f946f88 reverts 6e03905 (#256), both on the discarded first Plan 145 design.

Seven non-merge commits appear twice under different shas. The pairs, with the copy on origin/master's lineage second in each row:

Off master On master Subject
0681ea2 4cca77b docs: split logging health check into Plans 141, 142, 154 and 155
263e702 c13ad4a feat(plan-149): correct cycle alignment and add three execution skills
59c6080 b4699ef docs(plan-149): correct the GitHub automation table to observed configuration
121e48b cdc435e chore: probe Linear PR/commit automation
d12372e 3504b74 docs(plan-149): record the GitHub automation probe results
8688fd9 e5f1969 feat: implement structured observability contract (Plan 141)
b80f387 0bee614 fix(tests): assert owned contracts, not host platform behavior

Deferred to the next recap

Zero commits sit after this window's end. The recap was opened on the morning of 2026-08-31, before any commit entered the new week.

git rev-list --count --all --since='2026-08-30 23:59:59'   # 0

Correction, 2026-08-31

Two corrections to this recap, both to the note about scripts/audit_plan_state_history.py. The original paragraphs stand as written.

The sha and date are wrong. What moved between states and What is still owed both say the closeout section has been called ## Current closeout since b76fb44 on 2026-08-17. It has not. b76fb44 carries ## Current closeout — finish before opening another large build, which the script's STATE_BY_HEADING does map. The rename to the bare ## Current closeout is 0c08382 on 2026-08-21, Plan 146 Stage 2's collapse to five tables, so the parser was blind for nine days rather than thirteen.

The error came from the search, not the reading: git log -S '## Current closeout' counts occurrences of that string, and the long heading contains it as a substring, so the oldest match was the commit that introduced the long form. A substring search cannot tell a rename from an extension. Reading the headings out of both revisions is what settles it.

The defect is fixed. bf989fc, the same day, maps both closeout forms and adds TestTheStateParserClassifiesEveryLiveHeading to tests/test_planning_docs.py, which asserts every ## heading in the live index is either mapped to a state or explicitly ignored. Measured across all 189 revisions of the index, current closeout was the only unmapped heading, present in 55 of them. The same commit fixed a second defect in _git, which decoded with the locale codec and so could not walk the history at all on a cp1252 host.

The state table above needed no revision — it was produced with that heading added to the map by hand, which is what the fix now does in the script.