mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-04 16:28:11 -04:00
plan: Phase 3.39 plan approved
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# Phase 3.39 — Casts to declared data (implements GENERATION_PLAN.md phase 3.39)
|
||||
Milestone: 0 raw address casts in the 4 census forms (reinterpret macros counted apart, each with its instrument); `PY tools/lever_census.py --check --strict` 0 (from 3,730); lying-decl census 0 outside ledgered exceptions; R22 218/218 — verified by: `PY tools/type_census.py --check-casts` exit 0 (T2's gate: P+I+X+M raw = 0, every reinterpret-macro site backed by its instrument row, `types_floor_lying` 0 outside `config/lying_exceptions.tsv`), `PY tools/lever_census.py --check --strict` exit 0, and `make clean && make extract-all JOBS=16 && make check-all JOBS=16` → `check-all: 218 passed, 0 failed of 218`, all at the closing commit
|
||||
Approved: 2026-10-01 Planner: claude-opus-5-5/medium Plan-hash: 00193ec39d21ec7defe14c16613989494a42a50a1054e4c850b3c287dacca59c
|
||||
|
||||
## Context
|
||||
- Product: the matched C of Brave Fencer Musashi (218/218 binaries byte-identical). Gen 3 makes it legible; source may be rewritten freely, the object bytes are the gate (R22). This phase = legacy T6 "the cast campaign".
|
||||
- State at 3.38 close: 1,650 struct defs in `src/shared/engine_types.h` + `include/struct_types.h` (via `include/common.h`); `type_census --check-structs` 0; parse_error_decls 0; R22 218/218.
|
||||
- Raw casts (instrument `tools/type_census.py`, regexes :134-137): 503,016 at P37 T1 = P `*(T *)(base+k)` 409,007 + I `*(T *)ident` / `&D_x` / `0x80…` 60,666 + X `((T *)e)[i]` 13,800 + M `M2C_FIELD(` 19,543; 502,983 in 69,492 bodies at P38 close. Counted apart, not gated: A address-of `(T *)(base+k)` 18,912 (:138), C typed cast-member 36,681 (:139), ABS_ADDR :140. Struct map explains 99.2% of sites by base; legacy estimate of the floor: ~4% kept casts (S2) + up to ~24% the current layouts cannot hold (misaligned, pointer fields needing the pointee, sign-mismatch loads, non-struct bases). Per-form/per-binary split lives in `.run/P37/census/*` (regenerate in T1).
|
||||
- `type_census --check` (:1905-1918) already fails on any raw deref and any lying decl, but also on block-scope defs (629 block_frames, non-gating under the ratified 3.38 reading) — hence a new `--check-casts` gate (T2), not a re-reading of `--check`.
|
||||
- Only reinterpret macro today: `M2C_FIELD(expr, type_ptr, offset)` `include/common.h:43`, which IS form M (raw). Legacy also proved LOBU/LOH/HIH/LOHU/HIHU/LOW/LOWU byte-equal (PhaseEnd_Phase37:36-37); retriever found none of them in `include/` — T2 confirms and decides.
|
||||
- Byte law behind kept casts (C0507 §458): in gcc 2.7.2 `*(T*)(p+k)` carries no MEM_IN_STRUCT_P; `p->f`, `((S*)p)->f`, `q[k]` do (expr.c:4568-4577); sched.c:837-865 and the cse kill table differ, so cast → member IS a byte edit. Ladder: rung S1 = convert every cast on one base to member spelling (126/139 bodies IDENTICAL in the probe); S2 = greedy leave-one-out/revert in ≤ 2n+1 compiles, minimal kept set ledgered `KEPT` (27 of 716 kept). Recipes for globals: R1 pointer global as struct member, R2 `extern T X[]`, R3 walked pointer (`restruct --try … --recipes`). A parameter cannot be retyped while its body still has `*(T*)(a0+k)` (pointer arithmetic scales): signature change is a per-callee S → T → D unit.
|
||||
- Oracle hazard: the whole-object oracle reports DIFFERS on a correct global-block edit (relocation spelling only); use the linked oracle (R110, `delever_oracle --linked-control`, SHA vs `config/check.*.sha`).
|
||||
- Header economics: a header is judged on every includer (~35 s per judgement at -j16, `tools/restruct_cycle.sh:19`); a canonical-header edit re-fans ~3,900 objects (~6 min full). Header edits are batched: dozens of field additions per fan-out, never one per site.
|
||||
- Levers (`tools/lever_census.py`): `--check` (:1188-1210) fails on any class A/B site (pins + asm statements) without a `// !FAKE:` marker (FAKE_MARK :83) and on orphan markers; `--strict` (:1190-1204) fails on ANY pin/asm site, any direct GTE statement in a body, any per-TU asm macro definition. 3,730 = pins 1,868 + asm 1,862 over 1,838 bodies (`docs/levers.md:127-128`); also GTE levers 450, per-TU asm macros 314; direct GTE statements 6,717 (decision-log:3843). P36 composition: 691 parked signature/carve, ~1,150 in five proven head classes, 450 GTE clobber variants, 94 missing-parameter pins, 1,548 drawable singletons. P37 T4 removed 280.
|
||||
- GTE: `include/gte_inline.h` = one definition per signature (51 #defines, majority spelling, generated by `tools/gte_consolidate.py --header`); a def differing only by an extra clobber lives in its TU as `<name>_m` (~60 files, e.g. `src/ov_SC06_030/ov_SC06_030_jr_801380E0.c`). Hazards: C0209 §187 (inline `sqr` emits two hazard nops, SDK Square0 one) and C0217 §195-J (call vs inline is a per-site source fact; tell = target opcodes `lwc2/sqr/swc2` vs `jal`). Never unify by TU style; prove per site.
|
||||
- Lying decls (`type_census.py:1354-1362`, via `tools/argcheck.py`): `lying` = argcheck rows not K&R-marked and not cross-binary; 229 at 3.38 close over 24 callees (K&R-empty 216, narrow 13); `func_80146C3C` left K&R-marked (zero-arg `$a0` callers in shared headers). Declaration byte facts C0508 §459: "pin was missing argument" = unit of def + all callers; a def in a shared header is per-definition. 40 TU-CONFLICT rows already ledgered in `config/dedup_exceptions.tsv` (PsyQ ApplyMatrixSV 8, RotTransPers 5, …).
|
||||
- Carry from 3.38: 18 stale `conflicting types` ledger rows (func_800D20C0 15 .c + 1 .h, func_8012C098 1 .c + 1 .h; sources canonical; restruct writes no row for a hand-fixed unit); `restruct --selftest --real` is red after any commit until R22 → `delever_oracle --snapshot-baseline` → `--calibrate ov_SC04_011 ov_SC03_015 md_SC07_004 main -j 16`; `lever_progress --check` goes red after a census-moving commit until `--snapshot`; timeline and README regenerate after the previous phase's last commit (README Levers should read 3,729; `timeline.py --check` reports STALE).
|
||||
- Gates: `make check BINARY=<alias>` per touched binary; main only through `tools/gate_main.py` (never incremental); R22 clean fleet run after any shared body/header/executable edit; read exit codes (R53); `restruct_cycle.sh` runs R22 itself and commits each green batch (R42), detached via setsid nohup (R115), batches < 10 min.
|
||||
- Mutation discipline: restruct writes `inflight.json` (originals) under a lock; recover with `restruct --restore`, never `git checkout` (R102). Every latest-verdict reader goes through `restruct.ledger_latest` (R100).
|
||||
- Hand-edited: `src/**`, `include/common.h`, `config/*.tsv`. Generated (regenerate, never hand-edit): `include/struct_types.h` between `head_types:begin/end` (`tools/head_types.py`), `include/gte_inline.h` (`gte_consolidate.py --header`), `docs/levers.md`, `docs/readability.md` series, `docs/struct-twins.md` (`struct_twins.py --write`).
|
||||
- Out of scope: `tools/` harness files of Project Architect, `.claude/`, hooks, templates (harness gotchas only). The decomp's own tools under `tools/` (type_census, restruct, lever_census, delever, gte_consolidate) are in scope.
|
||||
|
||||
## Rationale
|
||||
- Order: instruments first (T2), then the biggest form by leverage (P on struct-map bases, T3/T4), then globals and absolute addresses (I, M; T5), declarations (T6), GTE (T7), then pins (T8) — the pins "the unified types now explain" only become removable after the bodies are re-spelled, so T8 runs last on the rewritten tree. Legacy order (S → S+A → X → R, largest leverage first) kept.
|
||||
- Kept casts: the milestone allows reinterpret macros counted apart, each with its instrument. A kept cast is respelled as a named macro whose expansion is the identical cast (preprocessor-identical, so the respelling is byte-neutral by construction, still gated), and every macro site must match a restruct ledger row (`KEPT` + cause) — the instrument. A macro site without its row fails `--check-casts`. This keeps "kept only where the gate proves the spelling moves bytes" machine-checked rather than a relabel. Unions stay deferred (3.38 binding decision); a width-conflict loser offset is a kept-cast cause, not a union.
|
||||
- T2 is `effort: high` (expert-fable): the judgment is the design of the reinterpret-macro set, the cause classes and the gate's reading of "raw" — a definition no test can arbitrate, and every later task inherits it. It is the only high mark (1 of 9).
|
||||
- Split T3/T4 by fleet segment (overlays vs md_*/main/shared) so each expert's context holds one segment's batches; main needs `gate_main.py` and shared bodies re-fan the fleet, which makes T4 the riskier half, run second with T3's ledger as its model.
|
||||
- Lying decls are drawn here (the milestone needs 0 outside a ledger), not left to 3.40; what cannot be drawn mechanically is ledgered with cause in a new `config/lying_exceptions.tsv` and becomes 3.40 residue. Kept apart from `dedup_exceptions.tsv`, which keys TU conflicts, not callee lies.
|
||||
- `--strict` 0 conflicts with the phase's own scope ("the surviving pins that the unified types now explain") and with 3.40's existence: removing all 3,730 levers is the agent-priced lane legacy P36 measured at 9 sessions / ~15-20M tokens, and the Standing constraints allow agent waves only at the developer's cap. That is money and scope — the one Developer-decides item. The plan is drafted for the recommended reading (A); reading B changes only T8's cap and the milestone line.
|
||||
- No new dependency. New code only in the decomp's own tools: `type_census --check-casts` (the existing census already computes every count; delta = the macro/ledger cross-check and the lying ledger) and, under reading A, a `lever_census --residue` exclusion keyed by a ledger.
|
||||
- Coders: opus55 everywhere code or sweeps run; T9 is the record and gate, still coder opus55 (the clean R22 and the snapshots are coder-run loops).
|
||||
- Triage P3.38-1 lands in T1: the previous phase's last commit has happened, and the INBOX asks for it first.
|
||||
|
||||
## Interfaces
|
||||
- `tools/type_census.py`: forms `FORM_P` :134, `FORM_I` :135, `FORM_X` :136, `FORM_M` :137, `FORM_A` :138, `FORM_C` :139, `ABS_ADDR` :140; `parse_base(expr)` :189; `find_sites(masked, rel, span_of_line, line_of, params_of)` :275; `walk_file(raw, rel)` :593; `run_census(jobs, use_cache=True, out_dir, want_sites=False)` :1171; lying :1354-1362; `summary['casts']` = {deref_total, by_form{P,I,X,M}, addr_form, typed_cast_member, bodies, by_bclass, coverage_ok} :1425; render :1635, :1669; `main()` :1879, flags `-j --out-dir(.run/P37/census) --no-cache --sites --check --check-structs --selftest --quiet` :1881-1888; `--check` verdict :1905-1918; `--check-structs` :1919-1926 (`check_structs_inputs()`, `check_structs_verdict(summary, pad, parse)`); struct map JSON written :1468 (`.run/P37/census/struct_map.json`).
|
||||
- `tools/argcheck.py`: `definitions()` :71; `scan(defs, only_needed_argpins=False)` :164; `main` :230.
|
||||
- `tools/lever_census.py`: `classify_asm_body` :176; `walk_file` :434; `run_census` :774; controls :991; selftest :1109; `main` :1159; `--check` :1188-1210; `--strict` :1190-1204; `FAKE_MARK` :83; `C_FAKE_RX` :84; `NON_LEVER_KINDS` {gte, verbatim-body, gte-unsigned} :85; `GTE_LEVER_KIND "gte-lever"` :86; `GTE_HEADER_DEFAULT include/gte_inline.h` :67; out `.run/P36/census` :53. Flags `--check --strict --gte-header --out-dir --sites -j --no-cache --selftest --quiet`.
|
||||
- `tools/restruct.py`: docstring :1-52 (rungs S struct spelling, D declaration redraw, L definition fold; residual classes :31-33); `RUN=.run/P37/restruct` :85; `LEDGER=RUN/ledger.jsonl` :86, row {rung, tu, unit, verdict, cause, before/after hashes}, key (rung, tu, unit); `INFLIGHT` :87 (fcntl lock :429-452); `ledger_latest(rows)` :402 → {(rung,tu,unit): row}; `ledger_index` :411; argparse `main()` :4490-4532: `--try TU FN` :4492, `--plan/--apply` :4495-4496, `--rung S|D|L` :4497, `--batch --label --only --headers`, `--redraw` :4503, `--try-file` :4504, D flags `--callee --signature --body --all-defs --map-data --with --tu --unalias` :4505-4512, `--restore` :4513, `--status` :4514, `--check-ledger` :4515, `--audit-types --write-types --type --top` :4516-4519, `--selftest [--real]` :4523 (body :4054-4484), `--fanout-cost` :4528, `-j` :4532.
|
||||
- `tools/restruct_cycle.sh:14`: `[LABEL_PREFIX=] [TASK=] [REDRAW=] [CENSUS=0] START END [BATCH=50] [RUNG=D|S|L] [ONLY] [HEADERS=1]`; clean src/config required :36; calibrate when red :38-43; apply `-j 12` :44, needs `final N/N` :45-50; skips R22 if src unchanged :52-54; R22 :56 (`.run/P37/baseline/r22_<label>.log`); FLEET = count of `config/check.*.sha`.
|
||||
- `tools/delever_oracle.py`: `main()` :608; `--recipes` :610, `--calibrate <aliases>` :611/:639, `--status` :612, `--snapshot-baseline` :613 (:241-280, `.run/P36/delever/baseline/`), `--linked-control` :615, `-j` :616.
|
||||
- `tools/delever.py`: `main()` :6002; `--plan --apply --batch --headers --only --rejudge --redraw --restore --scrub --propagate --recipes [--cap --control --limit] --apply-body` :6027 `--rung` (default E) :6028 `--allow-residue`; apply refuses without current calibration :1115, :5872 (R56).
|
||||
- `tools/gte_consolidate.py`: `inventory` :246; `canonical_table` :303; `render_header` :398; `write_header` :421; `plan_files` :457; `file_edits` :532; `apply_batch` :627; `sweep` :783; `remark` :915; `status` :979; `selftest` :987; `main` :1115. Flags `--inventory --header --apply --sweep --remark --status --selftest --batch N(400) --label --only --rejudge --dirty-ok -j(12)`.
|
||||
- `tools/head_types.py`: `resolve` :124, `main` :202 (writes `include/struct_types.h` region from `struct_map.json`). `tools/struct_layout.py:370` `field_offsets(res, d)`.
|
||||
- `include/common.h:43` `M2C_FIELD(expr, type_ptr, offset)`. `include/gte_inline.h:1-7` header contract.
|
||||
- Gates: `Makefile` `kit-corpus` :255, `tools-health` :269, `extract-all` :1219 (JOBS ?= 16 :1215), `check-all` :1241, `check` :1262 (`make check BINARY=<alias>`). `tools/gate_main.py` `main()` :870: `gate_main.py <slate.json> [--apply] [--no-bisect] [--no-resume] [--allow-dirty] [--assert-baseline]`.
|
||||
- Also used: `tools/cast_call_sites.py` main :293; `tools/conform_decls.py` main :209 (`--fn --draft --check --apply --cast-zero-arg-calls`); `tools/decl_from_use.py` main :1178.
|
||||
- New in this phase (T2 owns the exact shape; later tasks read T2's summary):
|
||||
- `type_census.py --check-casts` → prints `check-casts: raw P=<n> I=<n> X=<n> M=<n>; macros <name>=<n>… (unbacked <n>); lying=<n> (ledgered <n>)` and exits 1 on any raw > 0, unbacked > 0 or unledgered lying > 0.
|
||||
- reinterpret macros in `include/common.h`, each expanding to the identical cast; instrument = restruct ledger row `{rung:"S", verdict:"KEPT", cause:<class>}` keyed (binary, address, body) per R99.
|
||||
- `config/lying_exceptions.tsv`: `callee \t def_file \t kind \t cause \t task`.
|
||||
- Reading A only: `lever_census.py --strict --residue config/lever_residue.tsv` (`file \t func \t kind \t pass \t instrument \t cause`), rows excluded from `--strict`, still counted.
|
||||
|
||||
## Cookbook
|
||||
C0507 (§458 struct spelling is a per-access dial; S1/S2 ladder), C0508 (§459 declaration byte facts), C0503 (§454 rungs D/R), C0504 (§455 rung G), C0505 (§456 lever-removal move catalog), C0506 (§457 S105 TU batches), C0420 (§379 MEM_IN_STRUCT_P member vs cast), C0392 (§351 /s per access), C0209 (§187 GTE nops SDK vs game), C0217 (§195-J GTE call vs inline per site), C0368 (§328 volatile alias must be an object), C0383 (§342 void* param cast not byte-neutral), C0502 (§453 one source per unique function), C0510-C0513 (phase 3.38 struct entries), C0036 (§33 reconcile_decls fleet-majority oracle), C0081/C0086 (§73/§75c decl + call-site cast pair).
|
||||
Rules: R22, R40 (retry a fan-out failure once), R42, R53, R56, R99, R100, R102, R110, R112, R113 (marker names pass and instrument), R115, R122, G53 (producer census before a spelling sweep), G61.
|
||||
|
||||
## Research
|
||||
R3.38-001 (remaining work after P37; Gen3 constraints), R3.38-002 (type_census / lift_types mechanics). Pending, for T1 to adopt: `phase-ends/current/research/pending/retriever-code-p39-cast-instruments.md` (cast forms, lever_census flags, lying source, gte_consolidate CLI).
|
||||
|
||||
## Developer decides
|
||||
1. How `lever_census --check --strict` 0 is read at this phase's close. Strict 0 means every one of the 3,730 pins and asm statements, every direct GTE statement and every per-TU asm macro is gone. The phase scope only owes the pins the new types explain; the rest is the agent-priced work that 3.40 (the toggleable residue lane) exists for, and the generation's rules allow agent waves only at your cap. (A) Recommended: 3.39 removes everything the mechanical rungs reach and ledgers the rest in `config/lever_residue.tsv`, each row with pass, instrument and cause; the gate reads `--strict --residue` 0 and the literal `--strict` 0 moves to 3.40's milestone. Cost stays at the planned 1-2 sessions and the decision to spend on 3.40 stays yours once the ledger size is known. (B) Keep the literal `--strict` 0 here and name an agent cap for a lever wave inside T8; the legacy comparable cost 9 sessions and about 15-20M tokens. Recommended: A.
|
||||
|
||||
## Triage
|
||||
- P3.38-1: T1 -- the 3.38 close has landed; regenerating the timeline and README is phase-start bookkeeping (INBOX asks it first)
|
||||
|
||||
## Tasks
|
||||
|
||||
- T1 | next | expert-opus55 | title: phase-start health, carry cleanup and baselines | coder: opus55 | effort: medium | files: docs/story-timeline.md, README.md, .run/P37/restruct/ledger.jsonl, docs/readability-progress.tsv | done-when: timeline.py --check fresh and README Levers reads the live lever count; the 18 stale conflicting-types ledger rows removed through restruct (--check-ledger clean); make tools-health OK; R22 218/218 on the start commit; delever_oracle --snapshot-baseline + --calibrate ov_SC04_011 ov_SC03_015 md_SC07_004 main -j 16 green and restruct --selftest --real green; fresh type_census (--sites) and lever_census baselines recorded in the summary: raw per form, per binary, per base class (struct-map-explained vs not, top 20 bases), lever split (pins/asm/GTE statements/asm macros/gte-levers), lying 229 by callee; pending report adopted | verify: PY tools/timeline.py --check && make tools-health && PY tools/restruct.py --check-ledger && PY tools/type_census.py --check-structs --quiet | reads: R3.38-001, phase-3.38/tasks/T6.md, phase-3.38/tasks/T7.md | deps: — | est-ctx: 90k | review: no | wait-for: —
|
||||
Timeline: `PY tools/timeline.py` then `PY tools/progress.py --readme`; commit before R22. The census baselines are the denominators every later task and the closing record quote; key sites by (binary, address, body) (R99).
|
||||
- T2 | queued | expert-fable | title: cast and lying gate instruments, reinterpret-macro set | coder: opus55 | effort: high | files: tools/type_census.py, tools/lever_census.py, include/common.h, config/lying_exceptions.tsv, config/lever_residue.tsv, docs/gen3-standards.md | done-when: `type_census --check-casts` exists with the Interfaces output line; it FAILS on the T1 tree with T1's counts; selftest has positive/negative controls for each form, for a macro site with and without its ledger row, and for a ledgered vs unledgered lying callee; the reinterpret-macro set (names, expansion = identical cast, one cause class each: at minimum SCHED-ALIAS, SIGN, WIDTH/overlap loser, MISALIGNED, NON-STRUCT base) is defined in include/common.h and documented in docs/gen3-standards.md, with one byte-proof per macro (an existing kept site respelled → make check IDENTICAL); M2C_FIELD stays raw form M; under reading A, lever_census --strict --residue with its selftest; the census caches invalidated; type_census --check-structs still 0 | verify: PY tools/type_census.py --selftest && PY tools/lever_census.py --selftest && PY tools/type_census.py --check-structs --quiet && (PY tools/type_census.py --check-casts; test $? -eq 1) | reads: T1 | deps: T1 | est-ctx: 130k | review: no | wait-for: —
|
||||
Judgment: what counts as raw vs a backed reinterpret macro, and the cause classes; decide whether legacy LOBU/LOH/HIH… are reinstated as macros of this set. Header edit to common.h re-fans the fleet: land the macro set in ONE edit + one R22. The gate cross-checks macro sites against `restruct.ledger_latest` rows (R100), never a second filter.
|
||||
- T3 | queued | expert-opus55 | title: rung S sweep, overlays (forms P and X) | coder: opus55 | effort: medium | files: src/ov_*/**/*.c, include/struct_types.h, src/shared/engine_types.h, .run/P37/restruct/ledger.jsonl | done-when: every ov_* body with a P or X site on a struct-map base processed by restruct --rung S (S1 then S2) through restruct_cycle.sh batches, each batch R22-green and committed; kept casts respelled with T2 macros backed by KEPT rows; missing fields added to the canonical types in batched header edits (≤ 1 fan-out per batch of field additions); residual P/X on ov_* each ledgered with a cause class; summary records before/after per form, kept count by cause, bodies refused and why | verify: PY tools/type_census.py --check-casts --quiet; PY tools/type_census.py --check-structs --quiet && make clean && make extract-all JOBS=16 && make check-all JOBS=16 | reads: T1, T2 | deps: T2 | est-ctx: 160k | review: no | wait-for: —
|
||||
Isolate on one TU first (C0507 exemplar `func_801814AC`, ov_SC05_010_jr_80180F84.c:3030: the +0x34 store must stay cast). Parameter retyping is a per-callee S → T → D unit (C0507). Use the linked oracle for global-block edits (R110). Retry a fan-out failure once (R40). Run cycles detached (skill restruct-cycle-watch). `--check-casts` still exits 1 here (other segments); read its P/X counts for ov_*.
|
||||
- T4 | queued | expert-opus55 | title: rung S sweep, md_*, main and shared bodies (forms P and X) | coder: opus55 | effort: medium | files: src/md_*/**/*.c, src/main/**/*.c, src/shared/**, include/struct_types.h, src/shared/engine_types.h | done-when: as T3 for md_*, main and shared bodies; main gated only by tools/gate_main.py; shared-body edits followed by R22; P and X raw = 0 fleet-wide except sites ledgered with a cause that T5 owns (non-struct base) | verify: PY tools/type_census.py --check-casts --quiet; make clean && make extract-all JOBS=16 && make check-all JOBS=16 | reads: T1, T2, T3 | deps: T3 | est-ctx: 160k | review: no | wait-for: —
|
||||
Shared bodies (C0502 §453 include-at-site share) re-fan every includer: batch them, one R22 per batch.
|
||||
- T5 | queued | expert-opus55 | title: globals and absolute addresses to declared symbols (forms I and M, residual non-struct bases) | coder: opus55 | effort: medium | files: src/**/*.c, src/shared/engine_types.h, include/common.h, config/symbols.*.txt | done-when: form I (`*(T *)D_x`, `&D_x` reinterpret, absolute `0x80…`) and form M (`M2C_FIELD`) at 0 raw: each site is a typed extern / declared symbol (recipes R1 pointer global as struct member, R2 `extern T X[]`, R3 walked pointer), a member access, or a T2 macro backed by its row; absolute addresses become symbols named in the binary's symbol file (unnamed `D_<addr>` form, no invented names, G5); every touched binary make check / gate_main green and R22 green | verify: PY tools/type_census.py --check-casts --quiet; make clean && make extract-all JOBS=16 && make check-all JOBS=16 | reads: T1, T2, T3, T4 | deps: T4 | est-ctx: 150k | review: no | wait-for: —
|
||||
`restruct --try TU FN --base global:D_x --recipes` per class before fanning out; a global's declared type change is a header/def edit seen by every user — batch. Symbol-file edits follow the symbol-file path (R15), never generated asm.
|
||||
- T6 | queued | expert-opus55 | title: lying declarations to 0 outside the ledger | coder: opus55 | effort: medium | files: src/**/*.c, src/**/*.h, config/lying_exceptions.tsv | done-when: types_floor_lying 0 outside config/lying_exceptions.tsv; each of the 24 callees either drawn by restruct --rung D (--callee/--signature/--all-defs, def + all callers as one unit, C0508) and R22-green, or ledgered with kind and cause (e.g. func_80146C3C zero-arg `$a0` callers); --check-structs still 0 | verify: PY tools/type_census.py --check-casts --quiet; PY tools/type_census.py --check-structs --quiet | reads: T1, T2 | deps: T2 | est-ctx: 110k | review: no | wait-for: T5
|
||||
Runs after T5 so declarations are drawn on the final symbol types (wait-for keeps the tree single-writer; no file overlap with T3-T5 otherwise). Shared-header prototypes conform in every includer at once.
|
||||
- T7 | queued | expert-opus55 | title: GTE to one spelling | coder: opus55 | effort: medium | files: include/gte_inline.h, src/**/*.c, tools/gte_consolidate.py | done-when: 0 direct GTE statements in bodies and 0 per-TU asm macro definitions (lever_census --strict counts); every `_m` variant either replaced by its canonical macro (byte-proven IDENTICAL) or kept as a header-level variant in include/gte_inline.h with a `// !FAKE:` marker naming pass and instrument (R113), one definition per variant; call vs inline left per site (§195-J); gte_inline.h regenerated by gte_consolidate --header, not hand-edited; R22 green | verify: PY tools/gte_consolidate.py --selftest && PY tools/lever_census.py --check && make clean && make extract-all JOBS=16 && make check-all JOBS=16 | reads: T1, T2 | deps: T6 | est-ctx: 130k | review: no | wait-for: —
|
||||
If gte_consolidate cannot express a header-level variant, extend it (decomp tool, in scope). gte-lever sites after this task are marked levers; under reading A they enter T8's residue ledger, under B T8 removes them.
|
||||
- T8 | queued | expert-opus55 | title: pins and asm the unified types explain; residue ledger | coder: opus55 | effort: medium | files: src/**/*.c, config/lever_residue.tsv, docs/levers.md | done-when: delever (rungs A/B strip, R recipes with --propagate) run over every lever-carrying body rewritten by T3-T7, removals R22-green and committed; reading A: every remaining pin/asm/gte-lever site has a config/lever_residue.tsv row (pass, instrument, cause; agent-shaped ones flagged for 3.40) and `lever_census --check --strict --residue config/lever_residue.tsv` exits 0; reading B: literal --strict 0 under the developer's cap; lever_census --check 0 UNMARKED | verify: PY tools/lever_census.py --check && PY tools/lever_census.py --check --strict --residue config/lever_residue.tsv | reads: T1, T2, T7 | deps: T7 | est-ctx: 140k | review: no | wait-for: —
|
||||
Calibration current before delever --apply (R56). Seed rung D only with the tree's own lever-free body (C0503). The residue ledger is 3.40's input: the 3.40 planner reads it, so causes must be specific (head class, missing parameter, carve, singleton).
|
||||
- T9 | queued | expert-opus55 | title: milestone gate and record | coder: opus55 | effort: medium | files: docs/readability.md, docs/readability-progress.tsv, docs/levers.md, docs/story-timeline.md, README.md, cookbook/ | done-when: on one commit from a clean tree: type_census --check-casts 0, --check-structs 0, lever_census --check --strict (as the developer's reading) 0, R22 218/218, make tools-health OK; delever snapshot + calibrate and restruct --selftest --real green; lever_progress --snapshot taken; readability and lever series rendered; timeline/README regenerated; cookbook entries for the phase's byte facts added; summary states before/after per form, kept casts by cause, lying ledger size, lever residue size | verify: PY tools/type_census.py --check-casts && PY tools/lever_census.py --check --strict --residue config/lever_residue.tsv && make clean && make extract-all JOBS=16 && make check-all JOBS=16 && make tools-health | reads: T1, T2, T3, T4, T5, T6, T7, T8 | deps: T8 | est-ctx: 100k | review: no | wait-for: —
|
||||
Under reading B, verify drops `--residue …`. Do not edit the plan between the R22 and the commit.
|
||||
|
||||
## Risks
|
||||
- The ~24% of sites the layouts cannot hold blows up kept-cast counts or header churn. Detection: T1's per-base split and T3's first batches; if kept > ~25% of a segment, T3 returns `question` with the cause histogram before fanning out.
|
||||
- A reinterpret macro reads as relabeling. Mitigation: every macro site needs its KEPT row (T2 selftest proves an unbacked site fails); the closing summary reports kept by cause.
|
||||
- False DIFFERS from the whole-object oracle on global edits (relocation spelling) — use the linked oracle (R110); a correct edit refused shows in the ledger as REFUSED with a relocation-only diff.
|
||||
- Header fan-out cost (~6 min per canonical-header edit): unbatched field additions multiply wall time. Detection: R22 logs per cycle in `.run/P37/baseline/`.
|
||||
- Main regresses silently if gated incrementally — gate_main.py only.
|
||||
- `--selftest --real` and `lever_progress --check` read red after commits by design; a task that treats that as a failure stalls. Each task refreshes snapshot/calibration after its R22.
|
||||
- Reading B without a cap leaves T8 open-ended; the critic should stop T8 at the cap and ledger the rest.
|
||||
- GTE respellings that differ only in hazard nops (C0209) pass a careless check on one binary and fail another; T7 gates fleet-wide.
|
||||
|
||||
## Changes
|
||||
@@ -0,0 +1,16 @@
|
||||
# P39 cast/lever/lying-decl instruments
|
||||
task: find instruments for phase 3.39
|
||||
agent: retriever-code
|
||||
tags: type_census, lever_census, argcheck, gte_inline
|
||||
|
||||
## Answer
|
||||
- Four forms = tools/type_census.py:134-137 FORM_P `*(T *)(`, FORM_I `*(T *)ident|&D|0x80..`, FORM_X `((T *)e)[`, FORM_M `M2C_FIELD(`. 503,016 = P 409,007 + I 60,666 + X 13,800 + M 19,543 (tools/progress.py:1248; docs/readability-progress.tsv:4).
|
||||
- Apart: FORM_A (:138) address-of casts 18,912; FORM_C (:139) cast-member 36,681. ABS_ADDR (:140) marks 0x80xxxxxx literals in FORM_I.
|
||||
- Entry: find_sites(masked, rel, span_of_line, line_of, params_of) :275; parse_base(expr) :189; run_census(jobs,use_cache,out_dir,want_sites) :1171; main :1879.
|
||||
- Flags: -j --out-dir (.run/P37/census) --no-cache --sites --check --check-structs --selftest --quiet.
|
||||
- No M2C_FIELD reinterpret macro beyond include/common.h:43 (form M). No other reinterpret macro found in include/.
|
||||
- lever_census: main :1159; --check = unmarked (no `// !FAKE:` on line or line above) pins/asm, GTE header excluded; --strict = any pin/asm, direct GTE stmt in bodies, per-TU asm macro def. Output .run/P36/census; run_census :774; classify_asm_body :176; walk_file :434.
|
||||
- Lying: type_census.py:1354-1362 via argcheck.definitions() :71 / scan() :164 (tools/argcheck.py); kr_marked and cross_binary excluded. Gate :1914 in --check-structs. Exceptions: config/dedup_exceptions.tsv (41 TU-CONFLICT matches).
|
||||
- GTE: include/gte_inline.h (51 #defines; `_m` clobber variants noted line 6, per-TU in src/ov_*/..._jr_801380E0.c). tools/gte_consolidate.py: main :1115, flags --inventory --header --apply --sweep --remark --status --selftest --batch --label --only --rejudge --dirty-ok -j.
|
||||
|
||||
sources: tools/type_census.py, tools/lever_census.py, tools/gte_consolidate.py, tools/argcheck.py, include/common.h, include/gte_inline.h
|
||||
Reference in New Issue
Block a user