PE: close phase 3.38: RECAP, R122, C0514, three skills, long-tail decisions

This commit is contained in:
Drew T
2026-09-30 20:22:03 -06:00
parent 78515137ab
commit 385dca118c
15 changed files with 2034 additions and 5655 deletions
@@ -0,0 +1,21 @@
---
name: commit-task-autostage
description: Commit logs and research deliberately; force-add ignored tracked paths before commit_task.sh
---
# Committing alongside commit_task.sh's auto-stage
Captured 2026-09-30 from .run/P38/phaseend/sk_commit.md#Workflow.
## When to use
committing while coder runs or reports are pending
## Steps
- `tools/commit_task.sh` auto-stages everything pending under `phase-ends/current/{tasks,logs,research,discussions}`;
whatever runs next sweeps another run's logs or a new research report into its commit (P38 T5.1: R3.38-002 rode in
a coder commit; T5: a coder log rode in another's).
- Commit research and logs deliberately, in the commit that owns them, before the next coder commit runs.
- Gitignored-but-tracked paths (`.run/P3x/census`, `.run/P37/restruct/ledger.jsonl`) are refused: `git add -f` them
first, then call commit_task.sh with the other paths.
- Messages: one line, at most 100 chars, no task id (the script prefixes it).
Origin: P38 T1, T5, T5.1, T10 summaries.
@@ -0,0 +1,21 @@
---
name: restruct-cycle-watch
description: Launch, find, wake on and stop a detached restruct_cycle.sh without racing inflight.json
---
# Watching a detached restruct cycle
Captured 2026-09-30 from .run/P38/phaseend/sk_cycle.md#Workflow.
## When to use
running or relaunching tools/restruct_cycle.sh
## Steps
- Launch detached (the form in the phase plan's Context): `setsid nohup bash -c '... tools/restruct_cycle.sh ...' > .run/P<N>/restruct/cycle_<x>.log 2>&1 &`.
- The log stays 0 bytes until the first batch line; an empty log is not a dead cycle.
- Before any relaunch, look for a live one: `pgrep -af '^bash tools/restruct_cycl[e].sh'`. An unanchored
`pgrep -f` matches the calling shell's own `bash -c` line; a double launch races `inflight.json` (P38 T4).
- To wake on exit without polling: `tail --pid=<pid> -f /dev/null` as a background task; its completion re-invokes you.
- Stop with `kill -INT <pid>`, wait for the exit, only then `--restore` (R118).
- On a stop with final X != Y the cycle leaves edited files as restruct left them: revert unjudged files before R22.
Origin: P38 T4, T6 summaries.
@@ -0,0 +1,20 @@
---
name: retire-doc-consumers
description: Grep and repoint tools, Makefile and docs that read a doc being retired, in the same task
---
# Retiring a doc without breaking its consumers
Captured 2026-09-30 from .run/P38/phaseend/sk_retire.md#Workflow.
## When to use
moving a doc to docs/retired or deleting it
## Steps
- For every doc a migration or cleanup moves to `docs/retired/` (or deletes), grep its path in `tools/`, `Makefile`
and `docs/` in the same task: `grep -rn '<path>' tools Makefile docs`.
- Repoint each consumer to the successor (or to the retired copy when none exists) and record the mapping.
- Then run `make tools-health` (needs a complete build/: R22 first) and `make kit-corpus`; both read docs.
- P38 T1: the PA3 migration retired SETUP.md, matching-cookbook.md, cookbook-index.md and effort-map.md; four
tools-health reds and 36 broken links came from that one cause.
Origin: P38 T1 summary.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+20 -20
View File
@@ -1,18 +1,18 @@
type_census — 2026-09-30T02:49:11Z at 42a9cd7f5 (src stamp a777e79c16d8d251, tool a7b191db86); 7305 files (4121 TUs + 3181 headers), 99113 function definitions; walk 91.5 s, total 292.5 s
type_census — 2026-10-01T02:18:15Z at 78515137ab (src stamp 287e6f521d908e6d, tool 17a2928ffd); 7306 files (4121 TUs + 3181 headers), 99109 function definitions; walk 65.7 s, total 271.3 s
DEFINITIONS 7255 struct/union bodies (+0 enums): canonical header 1179 · .c file-scope 4085 · .c block-scope 1904 · per-function shared headers 63 · other headers 24 · include/ 0
3143 distinct names · 527 distinct layouts (0 unresolved) · 206 duplicate classes covering 2823 names (tier 1 122 same-named, tier 2 84 opaque; 46 layout-twin classes / 1867 names kept apart) · 585 exact-text classes / 4460 copies · 40 VARIANT names (>1 layout) · 141 dead canonical names · 74 canonical names redefined in .c · 1781 scalar/alias typedefs · 157 forward decls
CAST SITES deref 502985 = P 408978 + I 60664 + X 13800 + M 19543 (+ A address-of 18888; + C typed cast-member 36681) in 69493 bodies · coverage OK (raw {'P': 408978, 'I': 60664, 'X': 13800, 'A': 18888, 'C': 36681, 'M': 19543}) · refused 0 · absolute 0 · readability regex over raw text 411820
by base: param 276839 · local 149904 · gaddr 40123 · nested 24076 · global 8861 · other 1678 · addr-of 1348 · ident 156
by width: s4 145148 · u2 142773 · s2 131805 · u1 43174 · agg 13472 · p4 12019 · u4 11165 · s1 3429
top bases: a0(param) 92255 · param_1(param) 81577 · arg0(param) 67704 · p(local) 18043 · p(param) 11784 · s0(local) 10368 · cach(local) 8246 · s1(local) 7137 · buf(local) 7060 · flag(local) 5453 · *(s32 *)(a0 + 0x20)(nested) 5215 · iVar2(local) 5160 · dst(local) 4874 · iVar1(local) 4836 · iVar3(local) 4693 · e(local) 3918
DECLARATIONS 99113 function definitions (1894 K&R) · 4279270 extern function declarations over 12088 names (3777 spelled >1 way) · 6415 names with >1 body text · 1078766 data externs over 55548 symbols (18493 typed >1 way) · asm-label aliases 5158 (165 names) · builtins {'memcpy': 301, 'abs': 1} · attributes {'packed': 63, 'aligned': 12}
lying declarations 1796 over 76 callees {'K&R-empty': 1373, 'narrow': 423} · K&R sites (marked, counted apart) 5262 over 199 callees · cross-binary (the fleet's definitions disagree, this binary has none) 418 over 96 callees
STRUCT MAP 18739 types explain 498866/502985 deref sites (99.2 %); unassigned 4119; 4064 types span ≥2 bodies (441211 sites), 14675 single-body; types with conflicts 727, suspect 11
merges by evidence {'param': 36454, 'assign': 14664, 'ret-of': 3373, 'arg': 10168, 'return': 1524, 'assign&': 1050, 'arg&': 270, 'field': 9613}; vetoed {'arg-conduit': 115277, 'ret-of-multisource': 6686, 'assign-multisource': 34568, 'arg&-conduit': 14628, 'field-multisource': 7008, 'ret-of': 679, 'assign&-multisource': 12501, 'arg&-no-overlap': 3455, 'assign': 347, 'arg-no-overlap': 43013, 'assign&': 69, 'arg': 182, 'return': 12, 'field': 3224}
Unkstruct_800B5CB8 sites 107580 bodies 11184 fields 134 size≥0x10c width-conflicts 50 sign-mixed 55 merges {'param': 9448, 'assign': 7399, 'arg': 6952, 'field': 2622} at D_800B5CB8,D_800D3888,D_80181448 ptr D_8006386C,D_80074778,D_80074780
Unkstruct_80126B58 sites 65397 bodies 17047 fields 175 size≥0x24c width-conflicts 50 sign-mixed 60 merges {'param': 2100, 'arg': 1086, 'assign&': 106, 'field': 51} at D_80126B58 ptr D_80126B58
Unkstruct_8018E71C sites 6745 bodies 396 fields 7 size≥0x10 width-conflicts 0 sign-mixed 0 merges {'param': 18, 'arg': 15, 'assign&': 2} at D_8018E71C
DEFINITIONS 1650 struct/union bodies (+0 enums): canonical header 1021 · .c file-scope 0 · .c block-scope 600 · per-function shared headers 29 · other headers 0 · include/ 326
1024 distinct names · 523 distinct layouts (0 unresolved) · 53 duplicate classes covering 496 names (tier 1 0 same-named, tier 2 53 opaque; 44 layout-twin classes / 472 names kept apart) · 42 exact-text classes / 546 copies · 0 VARIANT names (>1 layout) · 56 dead canonical names · 0 canonical names redefined in .c · 4300 scalar/alias typedefs · 145 forward decls
CAST SITES deref 502983 = P 408974 + I 60666 + X 13800 + M 19543 (+ A address-of 18892; + C typed cast-member 36853) in 69492 bodies · coverage OK (raw {'P': 408974, 'I': 60666, 'X': 13800, 'A': 18892, 'C': 36853, 'M': 19543}) · refused 0 · absolute 0 · readability regex over raw text 411816
by base: param 276840 · local 149900 · gaddr 40123 · nested 24076 · global 8861 · other 1677 · addr-of 1350 · ident 156
by width: s4 145146 · u2 142772 · s2 131805 · u1 43174 · agg 13475 · p4 12018 · u4 11164 · s1 3429
top bases: a0(param) 92255 · param_1(param) 81578 · arg0(param) 67704 · p(local) 18039 · p(param) 11784 · s0(local) 10368 · cach(local) 8246 · s1(local) 7137 · buf(local) 7060 · flag(local) 5453 · *(s32 *)(a0 + 0x20)(nested) 5215 · iVar2(local) 5160 · dst(local) 4874 · iVar1(local) 4836 · iVar3(local) 4693 · e(local) 3918
DECLARATIONS 99109 function definitions (1894 K&R) · 4279260 extern function declarations over 12088 names (3773 spelled >1 way) · 6413 names with >1 body text · 1078766 data externs over 55542 symbols (18498 typed >1 way) · asm-label aliases 5148 (159 names) · builtins {'memcpy': 301, 'abs': 1} · attributes {'packed': 26, 'aligned': 6}
lying declarations 229 over 24 callees {'K&R-empty': 216, 'narrow': 13} · K&R sites (marked, counted apart) 5293 over 200 callees · cross-binary (the fleet's definitions disagree, this binary has none) 418 over 96 callees
STRUCT MAP 19122 types explain 498863/502983 deref sites (99.2 %); unassigned 4120; 4046 types span ≥2 bodies (440088 sites), 15076 single-body; types with conflicts 857, suspect 11
merges by evidence {'param': 38862, 'assign': 16777, 'ret-of': 4334, 'arg': 10823, 'return': 1524, 'assign&': 1183, 'arg&': 270, 'field': 9896}; vetoed {'arg-conduit': 115274, 'ret-of-multisource': 6678, 'assign-multisource': 33062, 'arg&-conduit': 14636, 'field-multisource': 7008, 'ret-of': 675, 'assign&-multisource': 12102, 'arg&-no-overlap': 3455, 'assign': 347, 'arg-no-overlap': 43617, 'assign&': 337, 'arg': 182, 'return': 12, 'field': 3224}
Unkstruct_800B5CB8 sites 107580 bodies 11184 fields 134 size≥0x10c width-conflicts 50 sign-mixed 55 merges {'param': 9704, 'assign': 8078, 'arg': 7080, 'field': 2622} at D_800B5CB8,D_800D3888,D_80181448 ptr D_8006386C,D_80074778,D_80074780
Unkstruct_80126B58 sites 65397 bodies 17047 fields 175 size≥0x24c width-conflicts 50 sign-mixed 60 merges {'param': 2102, 'arg': 1087, 'assign&': 106, 'field': 51} at D_80126B58 ptr D_80126B58
Unkstruct_cb638a45 sites 6477 bodies 381 fields 6 size≥0x10 width-conflicts 0 sign-mixed 0 merges {'param': 10, 'arg': 4}
Unkstruct_dac960ce sites 4464 bodies 124 fields 13 size≥0x24c width-conflicts 0 sign-mixed 2 merges {'param': 2}
Unkstruct_0caecd54 sites 4340 bodies 124 fields 23 size≥0xfe width-conflicts 0 sign-mixed 1 merges {'param': 2, 'assign': 1, 'return': 1}
Unkstruct_f8ed2b9f sites 4061 bodies 4061 fields 1 size≥0x217 width-conflicts 0 sign-mixed 0 merges {'param': 2}
@@ -20,14 +20,14 @@ STRUCT MAP 18739 types explain 498866/502985 deref sites (99.2 %); unassigned
Unkstruct_8e3dbe51 sites 3930 bodies 131 fields 5 size≥0x4c width-conflicts 0 sign-mixed 1 merges {}
Unkstruct_8014A1B0 sites 3778 bodies 542 fields 13 size≥0xfe width-conflicts 0 sign-mixed 1 merges {'field': 13, 'arg': 10, 'param': 10, 'return': 1}
Unkstruct_80128EA8 sites 3311 bodies 632 fields 18 size≥0x38 width-conflicts 0 sign-mixed 13 merges {'arg': 23, 'ret-of': 19, 'field': 7, 'param': 2}
Unkstruct_ade2e8a0 sites 3271 bodies 196 fields 26 size≥0xa4 width-conflicts 3 sign-mixed 9 merges {'param': 268, 'arg': 133}
Unkstruct_57a28935 sites 3271 bodies 196 fields 26 size≥0xa4 width-conflicts 3 sign-mixed 9 merges {'param': 268, 'arg': 133}
Unkstruct_1fa9e810 sites 3144 bodies 131 fields 3 size≥0x49 width-conflicts 0 sign-mixed 0 merges {}
GLOBAL BLOCKS 2124 runs of ≥4 adjacent scalar D_ symbols (14253 symbols): 0x800cf10c+0x34 ×29 (D_800CF10C..D_800CF13C) · 0x800a6580+0x3a ×27 (D_800A6580..D_800A65B8) · 0x801998a4+0x57 ×24 (D_801998A4..D_801998FA) · 0x80199840+0x56 ×23 (D_80199840..D_80199895) · 0x801a9608+0x50 ×22 (D_801A9608..D_801A9654) · 0x801ab708+0x54 ×21 (D_801AB708..D_801AB758) · 0x801b0df0+0x4b ×21 (D_801B0DF0..D_801B0E3A) · 0x801b83a0+0x4e ×21 (D_801B83A0..D_801B83ED)
PARKED (P36) 24 classes / 453 bodies / 691 needed sites → .run/P37/census/parked.tsv; needs {'signature': 13, 'struct': 7, '?': 4}; 22 PARK names are minimum-lever banks
CONTROLS 4/4: player_block_widths OK (D_80078E8C:4/4 D_80078EB2:2/2 D_80078EB4:2/2 D_80078EB6:2/2 D_80078EB8:2/2 D_80078F08:4/4 D_80078F0C:4/4) · matrix_layouts OK (MATRIX=32 MATRIX_L48=48) · prim_layout_class OK (defs in 1192 files, 2 spellings, 1192/1192 in one layout (64 B)) · tu_conflict_D_800A651C OK (s32:1202 | u8[]:9 | OtBlk[]:9 | OtBlk_80016450[]:8 | s32[]:7)
CONTROLS 4/4: player_block_widths OK (D_80078E8C:4/4 D_80078EB2:2/2 D_80078EB4:2/2 D_80078EB6:2/2 D_80078EB8:2/2 D_80078F08:4/4 D_80078F0C:4/4) · matrix_layouts OK (MATRIX=32 MATRIX_L48=48) · prim_layout_class OK (defs in 1 files, 1 spellings, 1/1 in one layout (64 B)) · tu_conflict_D_800A651C OK (s32:1202 | u8[]:9 | OtBlk[]:9 | OtBlk_80016450[]:8 | s32[]:7)
duplicate-layout classes (top 12): 8B×293 names (488 defs): GTESVec, GVec8, SIn_8017E95C, SV… | 8B×202 names (0 defs): Blk8L, Blk8S16, Blk8U, Blk8_80126940… | 32B×170 names (277 defs): M32_8017F154, M32_80188094, MATRIX, MATRIX2… | 8B×153 names (341 defs): Blk8_801807DC, Blk8_8018453C, COL_8001EFE0, MoveT… | 32B×129 names (0 defs): B20, Blk20, Blk20_800CB68C, Blk20_8016DF5C… | 4B×111 names (0 defs): B4, B4_80165140, B4_8017F154, B4_80181970… | 64B×91 names (1428 defs): Prim_8016E7C8, Prim_8016E7C8_8016B6BC, Prim_8016E7C8_8016B9F8, Prim_8016E7C8_8016C2C4… | 8B×82 names (0 defs): B8, B8_17F474, B8_80165140, B8_801851A8… | 8B×63 names (211 defs): Ang_8017E95C, DVEC_80183534, SOut_8017E95C, SVEC… | 4B×60 names (68 defs): PTAG_85910, PTag, PTag293C, PTag_1412A8… | 8B×59 names (0 defs): Bc8_80180E0C, CdFileLoc, Cmd_801805C8, M8_8016B6BC… | 16B×54 names (0 defs): B16, B16_8017CDE4, B16_80183ED8, B16_801841C8…
VARIANT names (top 12): Prim×5, Rec×4, Blk8×4, St×4, S×4, V8×4, Vec8×3, Obj×3, Ent×3, Blk16×2, CdReq×2, Blk4×2
duplicate-layout classes (top 12): 4B×56 names (0 defs): B4_80165140, B4_8017F154, B4_8018750C, B4_8018AD9C… | 8B×46 names (0 defs): Blk8S16, Blk8_80126940, Blk8_80180A38, D_80195AF4_t… | 8B×45 names (0 defs): B8, B8_17F474, B8_80165140, B8_801851A8… | 8B×34 names (0 defs): Args_80182194, Blk8017FD54, Blk8_801A6AD0, Blk8_801AD914… | 20B×32 names (0 defs): Blk20_8017D110, Buf8017BF34_8017BF34, Buf8017BF50_8017C234, Loc_80180E10… | 16B×28 names (0 defs): Blk16X, Blk16_8017ECD4, Blk16_8018A4C4, Blk16_8018A8B8… | 6B×25 names (0 defs): D80190448_t, Pt6_8017D65C, ResMapCol, S6… | 32B×25 names (0 defs): Blk20_8016DF5C, Blk20_80180B78, Blk20_80184694, Blk32L_80133298… | 8B×22 names (22 defs): B8014, Blk8_80133CD4, Blk8_801463A0, Blk8_8017FF80… | 12B×19 names (19 defs): Blk12, Prim126, PrimC59C, PrimC974… | 8B×14 names (0 defs): CdFileLoc, Cmd_801805C8, M8_8016B6BC, Pair8_8017DDC4… | 4B×14 names (0 defs): D_801D0660_s, Ent_80186AE4, P4, Pair4_801B02DC…
VARIANT names (top 12):
type_census: 7255 definitions (527 layouts, 206 duplicate classes, 40 variant camps) · 502985 cast sites in 69493 bodies over 8 base classes (4 forms + A, coverage OK) · 1796 lying declarations / 76 callees (+5262 K&R sites, +418 cross-binary apart) · controls 4/4
struct_map: 18739 types over 18739 clusters explain 498866/502985 sites (99.2 %); 4119 unassigned sites; 727 types with conflicts
type_census: 1650 definitions (523 layouts, 53 duplicate classes, 0 variant camps) · 502983 cast sites in 69492 bodies over 8 base classes (4 forms + A, coverage OK) · 229 lying declarations / 24 callees (+5293 K&R sites, +418 cross-binary apart) · controls 4/4
struct_map: 19122 types over 19122 clusters explain 498863/502983 sites (99.2 %); 4120 unassigned sites; 857 types with conflicts
+3
View File
@@ -43,6 +43,9 @@ PY = /usr/bin/python3
## Skills <!-- roles: expert planner -->
<!-- one line per captured workflow; the SKILL.md is the canonical text -->
- <!-- TODO SKILL_NAME: no source found --> — <what it automates, when to invoke it>
- restruct-cycle-watch — Launch, find, wake on and stop a detached restruct_cycle.sh without racing inflight.json
- retire-doc-consumers — Grep and repoint tools, Makefile and docs that read a doc being retired, in the same task
- commit-task-autostage — Commit logs and research deliberately; force-add ignored tracked paths before commit_task.sh
## Paths <!-- roles: expert coder router planner review critic discuss auditor curator -->
- Oracles (the ground truth X3 names): <!-- TODO ORACLES: no source found -->
+17
View File
@@ -0,0 +1,17 @@
# C0514 — Run a repo-wide check on unedited HEAD first; mirrored edits need make kit-corpus
tags: docs,doc_links,tools-health,kit-corpus,baseline · date: 2026-09-30 · phase/task: 38/PE · origin: P38 T8, T10 summaries
# Run a repo-wide check on unedited HEAD before blaming (or crediting) your edit
A done-when like "docs link check passes" or "tools-health OK" is a whole-repo check; it can be red for reasons
outside the task (P38 T8: `tools/doc_links.py` was red on two UNCOVERED docs added by the structs tasks, not by the
formats.md edit under test).
- Before editing, run the check on unedited HEAD and keep its output: that separates pre-existing failures from yours.
- A pre-existing red is either fixed in the task (smallest change, listed as a deviation outside `files:`) or
reported as such; never leave the done-when red and never call it green.
- Mirrors are the common source: any tool or doc copied into `decomp-architect/corpus` makes `make tools-health`
red (`tool_census --check` gaps) until `make kit-corpus` is run; run it in the same task that edits the
original (P38 T10).
Origin: P38 T8 (doc_links), T10 (kit corpus).
+1
View File
@@ -963,3 +963,4 @@ C0510 | §396(a) correction: a zero-byte barrier CAN fence an slti;beqz cross-ju
C0511 | Type-census instrument pitfalls: attrs in layout hash, tag+typedef one name, intra-canon liveness, stale ledger rows | census,structs,types,gate,ledger | 2026-09-30 | 38/T7 | P38 T2-T6 summaries
C0512 | Folding duplicate struct definitions onto one canonical type, byte-safely | structs,fold,types,restruct,lift_types | 2026-09-30 | 38/T7 | P38 T4-T6 summaries
C0513 | Makefile health-gate traps: make -n on .ONESHELL executes; health needs a complete build/ | make,tools-health,oneshell,build | 2026-09-30 | 38/T7 | P38 T1
C0514 | Run a repo-wide check on unedited HEAD first; mirrored edits need make kit-corpus | docs,doc_links,tools-health,kit-corpus,baseline | 2026-09-30 | 38/PE | P38 T8, T10 summaries
+7
View File
@@ -30,6 +30,13 @@
- Why split, not union: the head is `suspect` (a merge of real types through argument conduits, line 15); every refused unit is a whole coherent layout disagreeing at several offsets, which is the signature of a separate type (G62 "per type"), and a union would give one name two types. The byte judge refusing the fold is the evidence that these sites belong to another type.
- Consequence: `Ent_8017FD58` and `Ent_8018D3A4` stay distinct canonical types (their own same-layout copies fold onto them in T5.1); unions stay deferred (binding line 13) until a unit is found whose single field is read at two widths in one body.
## Long-tail rules (P38 T5, T5.1; recorded at the phase close)
- binding: a divergent-layout local of a VARIANT name is renamed `<Name>_<suffix>` (TU suffix); the canonical or most-used layout keeps the name (T5).
- binding: a struct tag and a typedef of one name with different layouts are split `N_tag` / `N_td`; a tag and typedef of ONE definition count as one name (T5, T5.1).
- binding: the census layout hash includes alignment attributes (`packed`, `aligned`); two definitions differing only in attributes are different types (T5.1; cookbook C0511).
- binding: a tier-2 (all-placeholder) duplicate layout is either folded on SHARED use-site evidence or listed in `docs/struct-twins.md` with its per-class cause; `tools/struct_twins.py --write` regenerates that list after every fold (T5.1; rule R122).
## Per-offset records
Written by `tools/head_types.py` (max-site split). Losers' sites keep their casts; the byte oracle judges them later.
+1 -1
View File
@@ -1,6 +1,6 @@
# The struct map — the types the fleet's cast sites need (Phase 37 T1)
> **Generated by `tools/type_census.py` at `c5591de124` (2026-10-01T01:07:38Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_<addr>`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk<HEX>`) until Phase 38 cites evidence.
> **Generated by `tools/type_census.py` at `78515137ab` (2026-10-01T02:18:15Z); never typed (R75).** Every raw pointer-cast dereference in `src/` was assigned to a base (a parameter, a local, a global by value or by address, an absolute address) and the bases were clustered by EVIDENCE only — the same global symbol, an argument passed to a callee's parameter, an assignment or return, a pointer field read, a shared body's fleet-wide identity — never by a similar offset signature. Each cluster is one type the tree needs. Names are placeholders (`Unkstruct_<addr>`: the lowest global instance's address, else the lowest member function's); fields are placeholders (`unk<HEX>`) until Phase 38 cites evidence.
**19122 types explain 498863 of 502983 dereference sites (99.2 %); 4120 sites have a base the census could not classify; 857 types carry a width conflict.** Merges by evidence: param 38862, assign 16777, arg 10823, field 9896, ret-of 4334, return 1524, assign& 1183, arg& 270.
+76
View File
@@ -0,0 +1,76 @@
MILESTONE: green
Verified: `PY tools/phaseend_index.py verify --verbose` → GREEN 1/1 (.run/logs/pe_verify.log, .run/logs/verify1.log) at 78515137ab: `.venv/bin/python tools/type_census.py --check-structs` exit 0 and `make clean && make extract-all JOBS=16 && make check-all JOBS=16` exit 0 (`extract-all: 217 extracted, 0 failed of 217`, `check-all: 218 passed, 0 failed of 218`). Fresh census of record, same tree (.run/logs/census_cs.log): `dup_classes=0 outside_canon=0 dead=0 twins_stale=0 variants=0 pad_names=0 parse_error_decls=0 controls=4/4`; non-gating `conflicting_types=18 tu_conflict=0 types_floor_lying=229 twins_listed=53 dead_kept=3 block_frames=629`. Gate reading = the ratified 2026-09-30 Changes line (listed tier-2 twins, block-scope frames and intra-canon liveness do not gate).
## Recap
Phase 3.38 was the second half of the "structs" work in the readability generation: the game's C already compiles to the original bytes, and this phase made its data types readable by giving every struct (a C record type) one definition in a small set of shared header files instead of thousands of private copies. It started from 7,255 struct definitions with 206 groups of duplicates and 40 names that meant different things in different files, and ended with 1,650 definitions, all in the canonical headers except records declared inside function bodies, with no unexplained duplicates. The two biggest types, a 0x24C-byte entity record and a 0x10C-byte record used across the engine, were designed once in `include/struct_types.h` and folded in wherever the byte check allowed; the 53 duplicate groups that remain have no evidence that they are the same type and are listed with their reason in `docs/struct-twins.md`. Along the way the function declarations were rebuilt on the new types, which took the declarations that could not see their types from 1,106 to 0 and the declarations that disagree with their function from 1,796 to 229. Every change was checked by recompiling all 218 game binaries and comparing them byte for byte, and the phase closed with that check and the struct census both green.
## Decisions that still bind
- norm: every latest-ledger-verdict reader goes through `restruct.ledger_latest` (T2) — already rule R100; no new rule.
- norm: tier-2 (all-placeholder) duplicate layouts are folded on SHARED evidence or listed in docs/struct-twins.md with a cause; `struct_twins.py --write` after every fold (T5.1) — rule R122 added.
- contract: head-type width conflicts resolve by the max-site split, unions deferred (T3) — docs/struct-map-decisions.md `## Resolution rule`.
- contract: cross-space canonical types live in `include/struct_types.h` via `include/common.h`; `CANON_HEADERS` = engine_types.h + struct_types.h (T3) — docs/struct-map-decisions.md `## Canonical file set`.
- contract: a head fold is keyed by canonical NAME and layout SIZE; `HF_EXCLUDE` globs carry a cause (T4) — docs/ops/p38-t4-2026-09-29-restruct-head-fold-mode.md:3.
- contract: VARIANT locals renamed `<Name>_<suffix>`; tag/typedef split `N_tag`/`N_td`; layout hash includes packed/aligned (T5, T5.1) — docs/struct-map-decisions.md `## Long-tail rules`, cookbook C0511.
- contract: repacking (TOC, LIST.CD, ISO records, EDC/ECC; type-4 terminator in the entry's last payload sector) (T8) — docs/formats.md §2.1, §4.6.
- environment: `tool_census` builds the kit corpus from git-tracked files only (T1) — not yet in docs/ops/tooling-inventory.md; see Next task needs.
- rules ratified this phase: R98, R99, R100, R101, R102, R103, R104, R105, R118, R119, R120, R121; added at close: R122.
- Next task needs: 53 listed tier-2 twins in docs/struct-twins.md (SEPARATE 44, UNBOUND 9) stay apart until use-site evidence binds them; refold with `lift_types --fold-class`, then `struct_twins.py --write`.
- Next task needs (3.39/3.40): 18 stale `conflicting types` ledger rows (func_800D20C0 15 .c + 1 .h, func_8012C098 1 .c + 1 .h; sources already canonical; restruct writes no row for a hand-fixed unit, so the census over-counts).
- Next task needs (3.39/3.40): 40 TU-CONFLICT rows in config/dedup_exceptions.tsv (census `tu_conflict=0` reads the restruct ledger, not these); biggest groups are the PsyQ prototypes ApplyMatrixSV (8) and RotTransPers (5); per-row causes logs/T6.c2.md.
- Next task needs (3.40): types_floor_lying 229 over 24 callees (K&R-empty 216, narrow 13); func_80146C3C left K&R-marked (zero-arg `$a0` callers in shared headers).
- Next task needs: block_frames 629 (block-scope stack frames that declare objects, non-gating) and dead_kept 3 (Unkstruct_80126B58, LineF2_8018F694, actor4c) are reported, not owed by any phase yet.
- Next task needs: docs/memory-map.md §3.2 LIST.CD RAM cache still TBD; close it from docs/formats.md §2.1 (0x80180000 staging, 0xE40 B, resolved into cdFileLocTable 0x800AE830).
- Next task needs: the tools/launch.py PhaseEnd-sort workaround (natural_key) is local; an upstream fix is expected; tools/launch.py and tools/commit_task.sh drift was declined at install; re-check both on the next PA update.
- Next task needs: regenerate the timeline (`tools/timeline.py`) and README (`tools/progress.py --readme`) after the phase's last commit (`timeline.py --check` counts same-day commits); README Levers reads 3,729.
- Next task needs: `restruct --selftest --real` reads red after any commit until R22 → `delever_oracle --snapshot-baseline` → `--calibrate ov_SC04_011 ov_SC03_015 md_SC07_004 main -j 16`.
## Deviations (H7 check)
- Summaries naming tools without HOW_WE_WORK.md or docs/ops/ in `Files:`: T2 (`type_census --check-structs`, `restruct.ledger_latest`), T3 (new `tools/head_types.py`, `delever.includers` -I, restruct overlap edits), T5 (`lift_types` scope), T6 (`share_body --only` comma prefixes), T10 (`progress.py`/`readability_progress.py` newest-census rule, `timeline.py` struct_defs column). T5.1 later added tooling-inventory rows for lift_types and struct_twins; T10 added tool_dictionary rows for head_types/struct_twins; `docs/ops/tooling-inventory.md` still has no row for head_types, ledger_latest or the T10 changes. Owed to the next phase's first tooling task.
## Audit
- `phaseend_index.py assemble` generates `## Agent runs` and the carry audit from the usage ledger; the CLI (`~/.claude/pa3/pa_ledger.py audit --phase 3.38`) was run at close and produced the `### Carry audit — phase 3.38` section (1 session, 2,620 requests; .run/logs/carry_audit.log), so the PhaseEnd_Phase37.5 audit's "no ledger" blocker (R105) does not recur. Tables as printed (chars), rows for scratch and harness spill files omitted:
### Carry audit — phase 3.38
| file (read) | chars | n |
|---|---|---|
| restruct.py | 270.4k | 54 |
| type_census.py | 198.6k | 44 |
| lift_types.py | 190.4k | 19 |
| 800.c | 65.1k | 12 |
| formats.md | 41.8k | 9 |
| PHASE_PLAN.md | 34.9k | 3 |
| struct_twins.py | 33.2k | 8 |
| struct_layout.py | 26.7k | 7 |
| story.md | 25.2k | 2 |
| file (write) | chars | n |
|---|---|---|
| lift_types.py | 65.2k | 14 |
| restruct.py | 56.0k | 48 |
| struct_twins.py | 30.8k | 21 |
| type_census.py | 29.6k | 34 |
| story.md | 11.7k | 4 |
| head_types.py | 10.5k | 1 |
| result kind | chars | n |
|---|---|---|
| Read | 1.3M | 248 |
| bash other | 1.1M | 794 |
| run.sh | 292.3k | 367 |
| tools/card.py | 228.8k | 18 |
| tools/type_census.py | 129.1k | 72 |
| tools/restruct.py | 127.1k | 57 |
| Grep | 125.4k | 56 |
| tools/outline.py | 119.9k | 18 |
| tools/plan_edit.py | 95.1k | 38 |
| tools/lift_types.py | 78.8k | 38 |
- whole reads over 20.0k: 0; spilled results read whole: 0; median requests after a read: 3; carry/request 1,540 chars.
- flag: 159 tool-source reads (coders and retrievers on restruct.py, type_census.py, lift_types.py, struct_twins.py; 2 critic reads of type_census.py); list in the ledger output.
- retriever re-asks: 3 of 10 retriever briefs repeated a lookup of the same run.
+13
View File
@@ -0,0 +1,13 @@
# Phase 3.38 close — log (closing expert)
- Read: card slice expert; plan header, Context, Rationale, Changes; tasks/T1..T10 (+T5, T5.1); AUDIT.md.
- `PY tools/phaseend_index.py verify --verbose` (run.sh --bg pe_verify) → GREEN 1/1; clause log .run/logs/verify1.log: extract-all 217/0/217, check-all 218/0/218 at 78515137ab.
- `.venv/bin/python tools/type_census.py --check-structs` (run.sh census_cs) → exit 0; gating counts all 0, controls 4/4; non-gating conflicting_types=18 tu_conflict=0 types_floor_lying=229 twins_listed=53 dead_kept=3 block_frames=629. Regenerated tracked outputs: .run/P37/census/{struct_map_top.json,type_census.json,type_census.txt}, docs/struct-map.md (stamp).
- `PY tools/task_log.py gotchas` → 51 tagged lines in 11 summaries.
- generalizable: T1-T6 + T5.1 already promoted by T7 (C0511-C0513; C0512 holds the fold-sdk line). New: T8:20 + T10:24 → cookbook C0514.
- workflow: T4:26 + T6:26 → skill restruct-cycle-watch; T1:34 → retire-doc-consumers; T5.1:33 → commit-task-autostage (HOW_WE_WORK.md ## Skills lines written by skill_add).
- binding: T5.1 tier-2 rule → rules R122; T5/T5.1 contracts → docs/struct-map-decisions.md `## Long-tail rules` (my one edit, 6 lines); others already in their docs (RECAP Decisions).
- H7: misses T2, T3, T5, T6, T10 → RECAP Deviations.
- Carry audit: `~/.claude/pa3/pa_ledger.py audit --phase 3.38 --md` (run.sh carry_audit) → section produced; tables copied to RECAP `## Audit` without cost lines.
- harness: `phaseend_index.py assemble --help` does not say that assemble generates the carry audit and Agent runs; learned from a grep of the module docstring.
- No retrievers, no coders.
+2
View File
@@ -147,3 +147,5 @@ R119 | Snapshot/refresh helpers refuse empty or shrunken input | - | active | Ph
R120 | Issue reply drafts are never committed or mentioned in commit text | - | active | developer 2026-09-30
R121 | Phases name the counts they move and publish them at close | - | active | developer 2026-09-30
R122 | Tier-2 struct twins are folded on evidence or listed with a cause | structs,census,twins | active | PhaseEnd_Phase3.38 binding (T5.1)
+12
View File
@@ -0,0 +1,12 @@
# R122 — Tier-2 struct twins are folded on evidence or listed with a cause
id: R122 · group: - · status: active · tags: structs,census,twins · origin: PhaseEnd_Phase3.38 binding (T5.1) · added: 2026-09-30
A duplicate struct layout whose names are all placeholders (census tier 2) is never left apart silently: it is
either folded onto one canonical type on SHARED use-site evidence (`tools/struct_twins.py`), or listed in
`docs/struct-twins.md` with its per-class cause (SEPARATE, UNBOUND). The list is generated: rerun
`tools/struct_twins.py --write` as its own call after every fold, so `type_census --check-structs` never reads a
stale twin (`twins_stale` gates).
Why: G62/S106 keep layout twins without evidence apart ("per type, not per layout"), but an unlisted twin is
indistinguishable from forgotten work; P38 T5 stopped at 198 classes because the gate had no list to read.
Evidence: phase-ends/phase-3.38 tasks/T5.md, T5.1.md; docs/struct-twins.md (53 classes at P38 close).