diff --git a/docs/retrospective.md b/docs/retrospective.md
index d5d71512f7..e599c3cce0 100644
--- a/docs/retrospective.md
+++ b/docs/retrospective.md
@@ -152,10 +152,12 @@ hand-offs to be replayed; and keep the game's bytes out of the repository from t
binaries, the 363,214 functions, the 4,000 commits — follows from those, at a cost that is mostly the diagnosis of
one's own instruments.
-## 7. After 100 % — what the readability generation has taught so far (Gen3, Phases 35–37; advanced at the end of every session)
+## 7. After 100 % — what the readability generation has taught so far (Gen3, Phases 35–38; advanced by every phase's closing work, R121)
-*From `PhaseEnd_Phase35.md` and `PhaseEnd_Phase36.md` (their "what we believed, what failed, what we would do sooner" sections) and
-decision log "P35", "P36 S99/S101/S104/S105", "P37 S106". Same four questions as §1–§4, for the work that began when the bytes were done.*
+*From `PhaseEnd_Phase35.md`, `PhaseEnd_Phase36.md` and `PhaseEnd_Phase37.md` (their "what we believed, what failed, what we would do
+sooner" sections), decision log "P35", "P36 S99/S101/S104/S105", "P37 S106"; for the migration and Phase 38, the Phase 38 plan's change
+lines, task summaries and logs, and git. Same four questions as §1–§4, for the work that began when the bytes were done. Where the
+record is thin it says so; inferences are marked.*
**What we believed, and what was true.** (P35) That sotn writes duplicate functions explicitly, so ~12,000 duplicate copies were the normal
end state — read from its tree, sotn shares stage code once by an include; the belief rested on one remembered parenthetical (→ R106).
@@ -170,6 +172,13 @@ same tree said the opposite, and a 165-body probe measured it: 91 % neutral, the
could carry the mapped parameter types "in one pass" — a parameter cannot be typed while its body still adds offsets to it (pointer
arithmetic scales), so widths and arity come first and the typed signature is a per-callee unit after the sites are members. That a new base
symbol wanted a linker fragment — the binary's splat symbol file is the one curated source (R15) and already feeds the link.
+(P37 close) That the lying-declaration count measured the work — it went 13,832 → 8,830 → 2,367 → 2,171 → 1,796 through instrument
+repairs as much as rewrites. (Migration) That moving to a new working method was a harness change — the product's own tools read four of
+the documents it retired, and 36 links pointed at them. (P38) That the milestone's counts were reachable as written — the census defined
+a zero duplicate count as one name per layout (293 opaque eight-byte structs would have become one), a zero outside-canon count as moving
+1,774 function-local stack frames, and a zero dead count as deleting 114 of 134 names that live types use as members. That equal layout
+hashes meant one type — attributes, member-type twins and same-name members at other offsets all break it. That classes with matching
+names would fold without respelling members — true only where the names were meaningful, not placeholders.
**What failed, and why it looked right.** A session died at 91 % context with 35 minutes of state only in its transcript, after refreshing
its checkpoint five times — two bank commits inside a task had no log line (→ R101). A tool's restore path was `git checkout`, which wiped
@@ -184,12 +193,33 @@ in `build/`, and the snapshot refresh copied it in; the fix is a refresh that re
forked from inside a worker thread deadlocked a batch at 0 % CPU with no message. A selftest's inflight probe used the live path and deleted
a real batch's restore snapshot. And the first audit of the legacy type header read 1,013 violations, 987 of them the audit's own — it paired
a trailing comment with the next field.
+(P37 close) A judge stopped with `kill` and restored eight seconds later consumed its own snapshot and left six files with no restore —
+it looked stopped; it was finishing its in-flight units. (Migration) The new launcher's PhaseEnd sort crashed on the id `3_5`, filed with
+an underscore beside `33.5` and `37.5`: the migration copied the ids without normalising them, and the sort met a number and a string
+only when a plan was approved. A bare `.run/` line in the new ignore file overrode the older re-includes, so per-phase evidence under
+`.run/` could from then on be committed only by force. And the duty to advance story §10 and this section at every session end lived in
+the old method's memory and checkpoint routine, which the migration did not carry; neither document moved between 09-12 and 09-30, and
+R121 restored the duty on 09-30. (P38) The phase gate asked for counts its own census defined as impossible, found only at T5, after
+the work was planned; it looked right because T2's known-true case passed — the gate failed on the starting tree with exactly the
+baseline counts — but that case proved the instrument reads, not that the target is reachable. The census counted a struct's tag and its
+typedef as two names, and T5 split nine pairs to satisfy the instrument before T5.1 fixed the instrument. The layout hash ignored
+`packed`/`aligned`, so two definitions looked like one type; the byte gate proved packed is not natural twice, 142 and 148 of 218
+binaries red, before the hash learned the attributes. Eighteen "conflicting types" rows outlived their fix because the engine writes no
+row for a unit with nothing left to do, so the census over-counted from a stale ledger. The reasoning behind the gate-reading decision
+was spoken, not written: the plan holds one change line and the discussion record opened that day is an empty template.
**What it cost.** P35: three sessions (one a recovery) and ~2.5 h of gates; no model tokens beyond the coordinator and three gate-1 agents.
P36: nine sessions, 575 commits, ~200 agent draws, roughly 15–20 M agent tokens — the mechanical rungs took 37 % of the sites for ~20
CPU-hours and no tokens, the search another 6 % for ~5 CPU-hours, the agents the rest, at 1 close per agent rising to 4–7 as the TU batch and
the method file matured. P37 so far: two sessions; four reconnaissance agents (~1 M tokens), a fleet census that runs in 3.5 minutes, and an
engine of ~2,300 new lines built in one session with no agents — three clean fleet runs and two byte-gated batches on the way.
+P37 in all: three sessions and 115 commits; the kill/restore race was its one real incident. The migration: one commit, then a day of
+repairs inside the next phase's first task. P38: planned, worked and gated in about a day and a half (09-29 → 09-30); T5 stopped once on
+the gate question and was reopened as T5.1, which took two expert attempts and ten coder runs (one red and reverted); every batch passed
+the 218-binary gate before its commit. Start to end: struct definitions 7,255 → 1,650; duplicate classes 206 → 53, all listed twins;
+file-scope definitions in `.c` files 4,085 → 0; variant camps 40 → 0; dead canonical names 141 → 0 (3 kept with cause); padding names
+26 → 0; hidden-type declarations ~1,106 → 0; lying declarations 1,796 / 76 callees → 229 / 24 (the census's `decls.lying`, printed by the
+gate as `types_floor_lying` — not the 98,648-based Phase 37 series, nor the plan's 1,537 "types floor"); levers 3,730, flat.
**What we would do sooner.** Read the target project's TREE before setting a policy on its precedent. Make every commit that advances a task
carry its log line, and checkpoint before any long wait past ~85 % context. Price a residue by distinct SHAPES, never by bodies. Put a
@@ -205,4 +235,9 @@ that skips a sibling's includes, a name where an identity was needed, an anchore
snapshot that stamps an empty build), and every repair moved the count by thousands while no rung got smarter — so a "stalling" number is
first a question about the instrument, then about the population, and only then about the method; and the stop rule for a mechanical
campaign is the remainder bucketed by its recorded cause, never the yield of the last two batches. In one line, the
-generation's rule so far: *the byte gate is still the only judge, and after 100 % it judges spelling.*
+generation's rule so far: *the byte gate is still the only judge, and after 100 % it judges spelling.* From the migration and
+Phase 38: in the migration itself, grep the tools and the build for every path it retires, and normalise every id it inherits; keep a
+published narrative's duty in a rule a task executes, not in a session habit. Test each milestone count against its instrument on the
+planning tree before approval — both directions: the starting tree fails it, and the target the plan describes can pass it — and write
+the gate's reading into the plan rather than discovering it at the fifth task. Put every attribute the compiler reads into a layout
+hash before the first fold, and let the byte gate, not a hash, say two types are one.
diff --git a/docs/story-timeline.md b/docs/story-timeline.md
index 922cc9ca8e..09cbcec788 100644
--- a/docs/story-timeline.md
+++ b/docs/story-timeline.md
@@ -99,5 +99,6 @@
| 2026-09-10 | 146 | | 218 | 100.0% | 100.0% | 100.0% | 100.0% | 0 | — | 4,603 | 414,148 |
| 2026-09-11 | 371 | P36 (v2.2.0) | 218 | 100.0% | 100.0% | 100.0% | 100.0% | 0 | — | 4,010 | 503,016 |
| 2026-09-12 | 111 | | 218 | 100.0% | 100.0% | 100.0% | 100.0% | 0 | 789 / 2091 | 3,730 | 502,985 |
+| 2026-09-29 | 40 | P37 (v2.3.0) | 218 | 100.0% | 100.0% | 100.0% | 100.0% | 0 | 789 / 2091 | 3,730 | — |
-78 dated rows · phase ticks from the 38 PhaseEnds · the chart: `docs/story-timeline.svg`.
+79 dated rows · phase ticks from the 38 PhaseEnds · the chart: `docs/story-timeline.svg`.
diff --git a/docs/story-timeline.svg b/docs/story-timeline.svg
index 95e26a8b7e..fe4d691c93 100644
--- a/docs/story-timeline.svg
+++ b/docs/story-timeline.svg
@@ -6,56 +6,57 @@
50%
75%
100%
-Jul 2026
-Aug 2026
-Sep 2026
+Jul 2026
+Aug 2026
+Sep 2026
-
-
-P7
-
-
-
-
-
-
-P15
-
-
-
-
-
-
-
-
-P23
-
-
-
-P26
-
-
-
-P29
-
-P30
-
-P31
-
-P32
-
-
-
-
-
-2026-07-22: main enters the denominators
-instruction-weighted (all binaries)
-distinct code (each body once)
-function count
+
+
+P7
+
+
+
+
+
+
+P15
+
+
+
+
+
+
+
+
+P23
+
+
+
+P26
+
+
+
+P29
+
+P30
+
+P31
+
+P32
+
+
+
+
+
+
+2026-07-22: main enters the denominators
+instruction-weighted (all binaries)
+distinct code (each body once)
+function count
After 100 % — the Gen3 debts as a share of their first measurement (every lever increment; the two raw-cast instruments)
0%
25%
@@ -63,26 +64,28 @@
75%
100%
the census (T1)
-mechanical strip — rung A/B (T4)
-the GTE header (T5)
-recipes + permuter — rungs R/D (T6)
-the guided search — rung G (S101)
-agents, one per class + the call-arity class (S102)
-agents, TU batches + regen (S103)
-agents, TU batches (S104)
-agents, TU batches (S105)
-P36
-P37
+mechanical strip — rung A/B (T4)
+the GTE header (T5)
+recipes + permuter — rungs R/D (T6)
+the guided search — rung G (S101)
+agents, one per class + the call-arity class (S102)
+agents, TU batches + regen (S103)
+agents, TU batches (S104)
+agents, TU batches (S105)
+P36
+P37
+other
09-09
-09-10
-09-11
-09-12
-
+09-10
+09-11
+09-12
+09-29
+
3,730
-register pins + asm statements (the lever series, 97 increments): 53,221 → 3,730
-
+register pins + asm statements (the lever series, 98 increments): 53,221 → 3,730
+
raw pointer-cast dereferences — the one-form regex count (until the census, 2026-09-12): 414,148 → 411,850
-
+
raw pointer-cast dereferences — the four-form census (2026-09-12 →; a new instrument, its own 100 %): 503,016 → 502,985
generated by tools/timeline.py — keyed by date
diff --git a/docs/story.md b/docs/story.md
index db5e03649c..5453e42685 100644
--- a/docs/story.md
+++ b/docs/story.md
@@ -170,11 +170,13 @@ revision, old hashes in historical documents replaced by inert tokens and mapped
would have corrupted history), proven pair by pair, and force-pushed. This document, the retrospective, the wiki and the
tooling releases are the last block; the visibility flip waits for GitHub to purge the old objects.
-## 10. After 100 %: making the code say what it means (Gen3 — Phases 35, 36, 37, 2026-09-08 →)
+## 10. After 100 %: making the code say what it means (Gen3 — Phases 35, 36, 37, 38, 2026-09-08 →)
-*From `PhaseEnd_Phase35.md`, `PhaseEnd_Phase36.md`, `phase-ends/CURRENT_PHASE.md` (Phase 37), decision log "P35", "P36 S99/S101/S104/S105",
-"P37 S106", `docs/levers.md`, `docs/readability.md`. This chapter is advanced at the end of every session (the owner's rule, 2026-09-12); the
-numbers are the two Gen3 series'.*
+*From `PhaseEnd_Phase35.md`, `PhaseEnd_Phase36.md`, `PhaseEnd_Phase37.md` and its log (`phase-ends/logs/Phase37.md`), decision log "P35",
+"P36 S99/S101/S104/S105", "P37 S106", `docs/levers.md`, `docs/readability.md`; for the migration and Phase 38, the Phase 38 plan's change
+lines, its task summaries (T1, T5, T5.1, T6, T7) and logs, and git. This chapter was advanced at the end of every session (the owner's rule,
+2026-09-12) until the migration of 2026-09-29 dropped that duty; since 2026-09-30 it is advanced by every phase's closing work (rule R121).
+The numbers are the two Gen3 series'.*
A hundred percent is a byte statement, not a code statement. The tree that rebuilt 218 binaries byte for byte was still the tree a machine had
drafted at speed: every shared engine function lived as a macro instantiated per level, tens of thousands of register pins and inline-asm
@@ -269,6 +271,87 @@ compiler from stealing an instruction into a branch's delay slot. The levers cou
hundred and thirty. One mistake of the day was mine: a restore run against a judge that had not yet finished stopping, which split a
batch's bookkeeping — erased cleanly, and the rule written down: wait for the exit.
+That night the declaration layer was called at its floor and the phase's fourth task closed: about 107,000 declarations made to say
+what their definitions say, 1,796 lying declarations left over 76 callees (98,648 over 1,609 at the phase's start), 5,262 call sites
+counted apart as the original's own calling convention and 418 as cross-binary calls, 133 of 149 alias-defined functions back under
+their names, ten of twelve parked signatures landed, and the levers at 3,730. Of the 1,796, 1,537 were judged a floor only the types
+could lower: a declaration cannot name a type its file cannot see. The checkpoint said the next task — one definition per struct type —
+would open in a fresh session. It did not open for seventeen days. The record says only that the phase sat at that boundary with a clean
+tree; it does not say why. On 2026-09-29 the owner re-scoped Phase 37 to the work already done (its tasks T0–T4) and carried the six
+unstarted tasks to the next phase word for word, so that the repository could change its working method between phases rather than in
+the middle of one. Phase 37 closed as v2.3.0 with the 218-binary gate green, and no tag.
+
+**The migration (2026-09-29, recorded as Phase 37.5).** The new method, Project Architect 3.0, replaced the long session-start reading with
+a written plan per phase, tasks that each run in a fresh context and end in a log and a short summary, and a one-page card of standing
+facts. One commit of 777 files installed it and moved the old operating documents — the setup reference, the 3.5-megabyte matching
+cookbook and its index, the effort map — into `docs/retired/`, where nothing loads them. Four things broke, and each was found only when
+something used it. The project's own tools still read the retired documents: the health chain's tool census took its tool table from the
+old setup file, a link checker and the wiki renderer pointed at it, and 36 links in the wiki, the README and the how-to pages led nowhere;
+the next phase's first task found four red health items with this one cause and repaired them as one defect. The new launcher, which
+sorts the closing reports by phase number, crashed: the half-phase from the first week is filed as `3_5`, with an underscore, beside
+`33.5` and `37.5` with dots, and the sort ended up comparing a number with a string. The migration had normalised none of the ids; the
+router could not build its starting state after the next plan was approved, and the fix — a sort that splits every id the same way — was
+made outside the task loop and reported upstream. The migration's ignore file gained a bare `.run/` line, which overrides the older lines
+that let chosen per-phase evidence under `.run/` be committed; from then on a census snapshot could be committed only by forcing it
+(the next phase met this as a commit helper's refusal). And the duty to advance this chapter and the retrospective at the end of every
+session had lived in the old method's memory and checkpoint routine; the migration carried it nowhere, and the next phase ran to its
+milestone with neither document touched. It came back the day after as rule R121, which hands it to every phase's closing work; this
+part of the chapter was written under it.
+
+**Phase 38 (2026-09-29 → 09-30) took up the carried task: one canonical definition per struct type.** Its first task repaired the
+migration (above), proved the gate green, re-took the byte oracle's baseline and measured the ground: 7,255 struct definitions — 1,179
+in the canonical header, 4,085 at file scope in `.c` files, 1,904 inside functions, 87 in other headers — in 206 classes of duplicates
+over 2,823 names, 40 "variant camps" (one name, several layouts), 141 canonical names nothing used, 26 legacy padding names, and the
+1,796 lying declarations. The second built the instrument the milestone would be read by, a `--check-structs` mode of the census, and
+its known-true case was that the starting tree fails with exactly those counts; it did, adding 1,106 declarations the engine had kept
+because a type was not visible. The third and fourth designed the three largest types — the entity, a 0x10C-byte block and the player
+block — into a new canonical header and folded their local copies onto it, every batch judged on the bytes.
+
+The fifth task, the long tail, did the mechanical part in one night — 1,308 types lifted into the canonical header, the padding names to
+0, the 40 variant camps to 0 by renaming each divergent local copy, 314 dead definitions deleted — and then stopped and asked, because
+three of the gate's counts could not reach zero without breaking the plan's own words. The duplicate count included every opaque layout
+twin: two placeholder-named structs with the same shape and no evidence that they are the same thing. Reaching 0 would have meant
+folding, for one example, 293 opaque eight-byte structs into one — "one name per layout", which the project's naming law forbids — while
+the same task said such twins stay apart and are listed. The outside-the-canonical-files count included 1,774 structs declared together
+with a variable inside one function (a stack frame, local by nature), and the lifting tool could reach only one kind of file scope. The
+dead-name count ignored uses inside the canonical header itself, so 114 of its 134 "dead" names were members of live types; deleting one
+single-letter type had broken a level. The census defined the milestone's zero as impossible, and the plan had asked for it anyway: the
+second task's known-true case had proved that the gate fails on the starting tree, and no one had asked whether it could pass on the
+intended one.
+
+The question went to the plan's critic, which reopened the task as T5.1 with the gate reading fixed, and the owner set that reading on
+09-30: a duplicate class still counts if its names are meaningful, or if it is a placeholder class not listed in a new record,
+`docs/struct-twins.md`, with per-class evidence from the struct map; a stack frame declared with its variable is reported, not counted;
+a canonical type used only inside another canonical type is alive. The milestone's own line stayed as written. The reasoning between the
+question and the answer was spoken rather than written: the plan keeps the decision as one change line, the fifth task's log keeps the
+conflict, and the discussion record opened that day is an empty template. What is said here is what those support.
+
+T5.1 took two attempts (the first handed off without leaving its progress file on disk) and ten coder runs, one of them red and
+reverted, and three things that had looked right failed on the way. The census counted a struct's tag and its typedef as two names,
+because C keeps them in two namespaces; the fifth task had split nine such pairs to satisfy it, and T5.1 taught the census that one
+definition's tag and typedef are one name. The layout hash that defines "the same layout" ignored alignment attributes, and the gate
+said so twice: folding a class whose members differed only by `packed` or `aligned` turned 142 of the 218 binaries red, and folding a
+packed four-byte struct onto its natural twin turned 148 red; the hash now carries those attributes, and two definitions that differ only
+in them are different types. And a fold by layout alone was not safe even then: a member name the canonical type spells at another
+offset compiles without a word from the compiler and reads the wrong field — a trial fold of that kind failed 142 binaries, and only the
+byte gate saw it. At the close every definition outside the canonical files had moved in (1,684 to 0), every meaningful-name class was
+folded, 942 placeholder names whose uses showed them to be one type were folded, and 53 placeholder classes were listed — 44 with
+evidence that they are separate, 9 with no use-site evidence either way, among them the shapes of Sony's `RECT`, `SVECTOR` and `DVECTOR`.
+Six layouts took Sony's own SDK names, three unused names were kept with their reason, and the 0x10C block's split was confirmed, with no
+unions.
+
+The sixth task put the declarations on the canonical types: nine redraw cycles, each through the gate, took the 1,106 hidden-type
+declarations to 0 and the "conflicting types" from 160 to 18 — all 18 stale rows of the engine's ledger whose source is already right,
+because the engine writes no new row for a unit it finds nothing to fix in. The shared-body conflicts went from 44 (the plan's 45 had
+counted a header line) to 40, each with its diagnostic. The seventh ran the gate on a fresh census on 09-30: exit 0, and 218 of 218
+binaries identical. From the phase's start to its end, struct definitions went from 7,255 to 1,650; duplicate classes from 206 to 53, all
+of them listed twins; file-scope definitions in `.c` files from 4,085 to 0; variant camps from 40 to 0; dead canonical names from 141 to 0
+(three kept with their cause); legacy padding names from 26 to 0; hidden-type declarations from about 1,106 to 0. Lying declarations fell
+from 1,796 over 76 callees to 229 over 24 — the census's `decls.lying` field at both ends, which the new gate prints as
+`types_floor_lying`; it is not the 98,648-to-1,796 series quoted above for Phase 37, nor the plan's 1,537 "types floor", and it is a
+different count from the hidden-type declarations, which are read from the engine's ledger. The levers stayed at 3,730: the phase moved no
+compiler pins, and none was asked of it.
+
## 11. By the numbers
| | |
diff --git a/phase-ends/current/logs/T9.md b/phase-ends/current/logs/T9.md
new file mode 100644
index 0000000000..a35a5484da
--- /dev/null
+++ b/phase-ends/current/logs/T9.md
@@ -0,0 +1,24 @@
+# T9 log — story §10 + retrospective §7 for P37 T5-close, PA3 migration, P38 (expert-opus55)
+
+## Timeline
+- Start: card slice, plan Context/Interfaces/Cookbook/Research, task T9, Changes lines (PHASE_PLAN.md:96-112 by range; whole read denied by guard), summaries T1/T5/T5.1/T6/T7.
+- Read targets by range: docs/story.md:173-286 (§10 ended at P37 S108, 2026-09-12), docs/retrospective.md:155-208 (§7 Phases 35-37).
+- Greps (no whole-log reads): logs/T5.1*.md for packed/aligned (c3b: attr fold DR_MODE 142 binaries red, member-at-other-offset trial B1 142 red; c5: a2e73 → packed_word 148 of 218 red; c6: lhash learns packed/aligned(N)); logs/T5.md:14-22 (stop: gate vs done-when, 293 opaque 8-byte names, 1,774 frames, 114+6+12 dead residual); logs/T6.md, T7.md, T2.md lying/types_floor lines.
+- Code fact: tools/type_census.py:1924 prints `types_floor_lying={summary['decls']['lying']}` → 1,796 (T1/T2) and 229 (T7) are the same field; .run/P38/census/type_census.txt:10 = 229 / 24 callees {K&R-empty 216, narrow 13}.
+- docs/levers.md:127-129, lever-progress.tsv:81-83: 3,730 at P37 T4, P37 close, P38 T7 → flat.
+- git: last P37 commit bbe54b9cc3 2026-09-12 (T4 CLOSED); no commits 09-13..09-28; migration b8a6cbc548 2026-09-29 (777 files; docs/{SETUP,cookbook-index,effort-map,matching-cookbook}.md → docs/retired/; .gitignore +`.run/`, `__pycache__/`); T0 303c045edf (launch natural_key, Phase3_5 crash); PhaseEnd_Phase3_5.md added 3207d52491 2026-06-14 (phase 3.5 spike).
+- `git check-ignore -v .run/giants/x.md` → .gitignore:595 `.run/` (older `!/.run/giants/` negations dead).
+- Story duty: rules/R121.md (2026-09-30) restores it; story.md/retrospective.md last changed a671f0abe9 2026-09-12.
+- Discussion record D1 (2026-09-29) is an empty template → the gate-reading reasoning is recorded only as Changes:104-105 + logs/T5.md.
+- Wrote story §10: heading +38, source/duty note, 8 new paragraphs (P37 T4 close + 17-day gap + re-scope; migration's four breakages; P38 T1-T4; T5 wall; gate-reading decision + thin record; T5.1 failures; T6/T7 + start-to-end counts with metric names).
+- Wrote retrospective §7: heading Phases 35-38 (R121), source line, believed/failed/cost(no tokens or dollars)/sooner additions.
+- `tools/timeline.py` → 79 rows, self-check OK; `--check` fresh. `doc_links.py docs/story.md docs/retrospective.md` → OK (2 pre-existing WARNs).
+
+## Retriever
+- retriever-digest (bg): P37 close + PA3 migration from PhaseEnd_Phase37.md, PhaseEnd_Phase37.5.md, R3.38-001, AUDIT.md, logs/Phase37.md tail. No report file written (could not create pending file) → no id. Key: re-scope words "rescope p37 and defer remaining tasks to p38" (P37:2,9-11,123); T4-close figures P37:59-60; 1,537 types floor P37:108-110; gap = "sat at the T4/T5 boundary" (P37:7-11), no reason recorded; v2.3.0, no tag; P37 believed/failed P37:134-153 (lying count 13,832→8,830→2,367→2,171→1,796; kill/restore 8 s); 3 sessions/115 commits; PhaseEnd_Phase37.5.md is a 189-byte stub.
+
+## Hypotheses rejected
+- "1,796 → 229 is two different metrics": tool source shows one field (`decls.lying`); story names both printed names and contrasts with the P37 98,648 series and the plan's 1,537.
+
+## Coder briefs
+- none (CODER: none; docs-only edit by the expert).
diff --git a/phase-ends/current/tasks/INDEX.md b/phase-ends/current/tasks/INDEX.md
index 6f943b1b05..735fd82a8f 100644
--- a/phase-ends/current/tasks/INDEX.md
+++ b/phase-ends/current/tasks/INDEX.md
@@ -9,3 +9,4 @@ T5.1 | done | long tail: one definition per type (gate reading fixed) | - | task
T6 | done | declaration layer on the canonical types | - | tasks/T6.md | logs/T6.md | -
T7 | done | milestone gate and record | - | tasks/T7.md | logs/T7.md | -
T8 | done | docs/formats.md: LIST.CD verified on US, runtime reader, repack note, LZSS ring + terminator rules | - | tasks/T8.md | logs/T8.md | -
+T9 | review | story §10 + retrospective §7 for P37 T5-close, PA3 migration and P38 | - | tasks/T9.md | logs/T9.md | -
diff --git a/phase-ends/current/tasks/T9.md b/phase-ends/current/tasks/T9.md
new file mode 100644
index 0000000000..57846d7762
--- /dev/null
+++ b/phase-ends/current/tasks/T9.md
@@ -0,0 +1,32 @@
+# T9 — story §10 + retrospective §7 for P37 T5-close, PA3 migration and P38
+
+Status: review | expert: expert-opus55 | ctx-at-completion: n/a | commit: see COMMIT | coder runs: none
+Done: story §10 and retrospective §7 now cover the end of old P37 (T4 close, the 17-day gap, the re-scope), the PA3 migration's four breakages, and P38 with start-to-end counts; §7 heading Phases 35-38; timeline regenerated (79 rows).
+Files:
+- docs/story.md — §10 heading/source note; 8 paragraphs after the S108 day
+- docs/retrospective.md — §7 heading (Phases 35-38, R121), source line, believed/failed/cost/sooner additions
+- docs/story-timeline.md, docs/story-timeline.svg — `tools/timeline.py` output
+Decisions:
+- §11 "By the numbers" left at its 2026-09-12 snapshot (not in T9's done-when; T10 publishes the P38 census).
+- Cost paragraph carries sessions, commits, attempts and coder runs only; no tokens or dollars.
+Deviations:
+- Brief says lying 1,796 → 229 is "a different metric": tools/type_census.py:1924 shows `types_floor_lying` IS `decls.lying`, so both ends are one field. Text names both printed names and says what it is NOT (the 98,648-based P37 series, the plan's 1,537 "types floor", the ledger-read hidden-type count).
+Findings:
+- The record holds no reason for the 09-13..09-28 gap (PhaseEnd_Phase37 says only "sat at the T4/T5 boundary"); stated as such.
+- Gate-reading reasoning: only Changes:104-105 and logs/T5.md:14-22; discussion D1 is an empty template; stated as thin.
+- The `.run/` blanket ignore: `git check-ignore` confirms it overrides `!/.run/giants/` etc.; "ended committing per-phase evidence" written as "could be committed only by force" (T1 gotcha), not as a claim that nothing was committed.
+Gotchas:
+- harness: retriever-digest could not write its pending report file; findings came inline, no research id.
+Next task needs: T10 may want §11's Gen3 row refreshed with the P38 census figures used here.
+Verified: verify: — (none in plan); `tools/timeline.py` → 79 rows, self-check OK; `timeline.py --check` → fresh; `doc_links.py docs/story.md docs/retrospective.md` → OK
+Review:
+ ran: tools/timeline.py, timeline.py --check, doc_links.py (local)
+ results: docs/story.md §10 (new paragraphs after "wait for the exit."), docs/retrospective.md §7
+ seen: story §10 and retrospective §7 now cover the end of old P37, the PA3 migration and P38
+ decisions:
+ - Lying-declaration wording: one field named twice (checked in source) vs the brief's "different metric" — Recommended: keep as written
+ - The 17-day gap and the decision reasoning are stated as unrecorded — Recommended: add the owner's own reason if wanted, else keep
+ - §11 snapshot left at 2026-09-12 — Recommended: refresh in T10
+ edits:
+ - none
+Full log: phase-ends/current/logs/T9.md