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:
- Three drain queries named the wrong schema.
detail_scrape_claimsis inops, andtask_instance/dag_runare inairflow, which is not on the ops role's search_path._database_countturns the error intounknown, unknown fails closed, and the operator saw "still draining" rather than "your SQL is wrong" (5260d3d). A fail-closed gate that cannot distinguish busy from broken presents a defect as patience. No test layer could have caught it: the unit test patches_database_count, and CI's Postgres has no Airflow tables at all (2c96cf8). - The contract could not gate the deploy that installed it — seven drain sources read endpoints shipping in the same release. Recorded as a migration artifact rather than a property of the contract, with the narrower recurring rule written down.
ct-coordination-stalefired on the healthy steady state within minutes, becausenoDataState: Alertingturned the absence of a window into a page, carrying the released window's stale labels (6258184).
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 Popen — input= 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:
- Stage 1, the frozen census (
3f6e6d4): 1,172 objects, 14,670,223,837 bytes, 951,821 rows reproduced exactly, zero hash mismatches over 807,797 non-empty bodies. It also pinned down that the Plan 72 writer archivedb""when the page file was already gone while still copying the database sha256 — so for 43,014 rows the stored hash is real and the bytes are not. - Stage 2, materializing the bodies as ordinary
.html.zstobjects behind a newapril-processorcontainer (47f5a35,0b2437e), with object keys derived from content hash so a crashed run resumes rather than duplicates. - Stage 3, dedupe and unpack (
6bc5d87,958dca3,e17c0a7): 371,095 distinct objects deleted against a rate gate, then every pack member verified against its sidecar hash and written back under its original key to preserve theminio_path → artifact_idjoin. - Stage 4, the parse (
0d3b481,6ac8242,051f7d0), with identity resolved in three tiers because the unpack manifest's sidecarlisting_idis wrong for 313,701 of 457,084 named members. - Stage 5, compare / assign / apply / canary, which took nine review rounds
(
0a7aad4,1eb5ba9,4799a67,6daa70b,76e7409,7c7290e,746409a,cd0b463,561f82e,58a1e27,72b15ec,6c1ef5a,bfb280b,0d80645,89a83ca,2470850,8ba3f82,56f7e91,e434b8f,8b0fcc1,5a5cce7,4fafbe6,67cbc82,0c77b2a,7410016,5802cb5,7db6262,34541a7,f75d37e,713db1c,86ebe41,953fa25,b69f572,9e0d952,e4bbefd,0158863,f51102b,a920001). - Stage 5b (
dd6aa26,5ccb5ad), a packer defect found before Stage 6 could write anything: theobsCTE reduced silver withany_value(listing_id)and nosourcefilter, so one detail artifact's ~6.7 candidate listings collapsed to an arbitrary one. Correct for 31.4% of April members, 8.4% of July. Nothing on the read path consults it, so no artifact was ever served wrongly — but sidecar identity was unusable for joins, which is what cost this plan three revisions. Identity and placement were split rather than merged, so not one member moved. - Stage 6, the repack, retirement, prune and deletion (
b579a81,104019c,44b505f,0eade3c,d7d4e7a,11856de,c30fb1c,7af6eda).
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
- Plan 142's maintenance window is not runnable. Five blockers, four of them
defects in stages the plan already calls built: Stage 3 undeployed on
container-health,validate-hostunable to pass a deferred-reboot window at all, a live one-off makingstoprefuse, andoauth2-proxy's exemption failing the release gate because nothing at runtime readshealthcheck-exemptions.txt. - Plan 145 leaves a verification gap, stated rather than smoothed.
canary-flush-verifyproves the lake round trip by key for 234 artifacts; the other 341,669 rest on receipts plus the flushers' delete-on-success contract. Also,recovery/plan145/parsed/(1,204 objects, 329 MB) must not be pruned — it is Plan 157's sole input, and regenerating it costs a ~17-hour reparse. - Plan 134's Stages 2 and 3 are owed, gated on the seven-day observation window closing 2026-09-06. The row stays in the build order rather than moving to closeout precisely because code is still owed.
- Plan 136's verdict is deferred, not reached: slowed but not bounded, with a re-measure on 2026-09-17 deciding whether 3c/3d are built at all.
- Plan 160 is observing to 2026-09-13. One green CI run is explicitly recorded as weaker evidence than the twenty local runs.
- Three commits from the 29th are on branches that have not merged:
c593caeand9c258b6ondocs/plan-149-cycle-1-findings, anddfba51dondocs/plan-145-post-mortem-transcript-evidence. Their work is recapped above because it is in the window; none of it is onorigin/masteras of the recap date. - Plan 149's Cycle 1 measures table is deliberately empty. The read waits for the cycle close on 2026-08-31, one day after this window.
- The state-history script's heading map is stale, as described above. Nothing currently fails when it is, which is the part worth fixing.
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.