T3.c2: rung S plan reads a pinned base->type (--bbt) and skips finished files

This commit is contained in:
Drew T
2026-10-01 14:40:49 -06:00
parent 61a3382532
commit 06da215a66
6 changed files with 190 additions and 18 deletions
+74 -7
View File
@@ -3245,11 +3245,39 @@ def fold_report(heads):
# ----------------------------------------------------------------------------------------------------------------------
# planning and --apply (the batch: files in leverage order, workers own whole TUs, headers serial and last)
# ----------------------------------------------------------------------------------------------------------------------
BBT_PIN = os.environ.get("RESTRUCT_BBT") or None # P3.39 T3.c2: a pinned body_base_type.json (--bbt / RESTRUCT_BBT)
def merge_bbt(cur, pin, can):
"""cur with each base whose type is NOT canonical replaced by the pinned entry for the same (body, base) when that one is canonical.
T3.c2: the cycle's per-batch census rewrites body_base_type.json and the map's names drift as sites become members (after t3_S5 the
ov bases' top type was Unkstruct_800D3888, not in a canonical header: 0 of 103469 ov bases canonical, plan_S drew 0 files)."""
out, n = {}, 0
for key, bases in cur.items():
pb = pin.get(key, {})
nb = {}
for bk, ent in bases.items():
pe = pb.get(bk)
if ent["type"] not in can and pe and pe["type"] in can:
nb[bk], n = pe, n + 1
else:
nb[bk] = ent
out[key] = nb
return out, n
def bbt():
p = CENSUS / "body_base_type.json"
if not p.exists():
sys.exit("restruct: no body_base_type.json — run `tools/type_census.py -j 16 --sites` first (R43)")
return json.loads(p.read_text())
cur = json.loads(p.read_text())
if BBT_PIN:
pp = pathlib.Path(BBT_PIN)
pp = pp if pp.is_absolute() else REPO / pp
if not pp.exists():
sys.exit(f"restruct: the pinned body_base_type ({BBT_PIN}) does not exist")
cur, _ = merge_bbt(cur, json.loads(pp.read_text()), canon_types())
return cur
def matches_only(only, tu, unit, extra=()):
@@ -3263,14 +3291,28 @@ def matches_only(only, tu, unit, extra=()):
or (o in h and o.startswith(("Unkstruct_", "func_", "D_"))) for o in only for h in hay if h)
def plan_S(headers, batch, only, done):
"""[(tu, {fn: bases})] — files with typed bases whose type is a CANONICAL definition (visible), ordered by the map's leverage."""
def s_finished(tu, fns, latest):
"""True when every body in fns has a latest ("S", tu, fn) ledger row whose nhash_after is the body's CURRENT nhash (T3.c2: t3_S1–S5
re-drew finished files — drawable stayed ~2088 after 250 files done)."""
if not all((latest.get(("S", tu, fn)) or {}).get("nhash_after") for fn in fns):
return False
path = REPO / tu
if not path.exists():
return False
masked, defs, _, _, _ = file_view(tu, path.read_text(errors="surrogateescape"))
cur = {d["name"]: body_nhash(masked, d) for d in defs if d["name"] in fns}
return all(cur.get(fn) == latest[("S", tu, fn)]["nhash_after"] for fn in fns)
def plan_S(headers, batch, only, done, latest=None, data=None):
"""[(tu, {fn: bases})] — files with typed bases whose type is a CANONICAL definition (visible), ordered by the map's leverage.
latest = ledger_latest(rows): files whose every body is finished at its current text (s_finished) are not drawn; data: a bbt() dict (selftest)."""
smap = struct_map()
can = canon_types()
files = collections.defaultdict(dict)
weight = collections.Counter()
skipped = collections.Counter()
for key, bases in bbt().items():
for key, bases in (bbt() if data is None else data).items():
tu, fn = key.split("|", 1)
if tu.endswith(".h") != headers:
continue
@@ -3288,6 +3330,10 @@ def plan_S(headers, batch, only, done):
weight[tu] += smap.get(ent["type"], {}).get("sites", 0)
if usable:
files[tu][fn] = usable
if latest:
for tu in [t for t in files if s_finished(t, set(files[t]), latest)]:
del files[tu]
skipped["file-finished"] += 1
ordered = sorted(files, key=lambda t: (-weight[t], t))
return [(tu, files[tu]) for tu in ordered[:batch]], len(ordered), skipped
@@ -3520,7 +3566,7 @@ def apply_batch(a):
log_f.close()
return 0 if v == "IDENTICAL" else 1
if a.rung == "S":
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done)
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done, ledger_latest(rows))
print(describe_plan("S", plan, total, skipped), flush=True)
elif a.rung == "D":
if a.redraw:
@@ -4706,6 +4752,23 @@ def selftest(real=False):
ok("rung D supersede: only lifted latest rows with no lying declaration left (.c only; cause key; latest row)",
[(r["tu"], r["unit"]) for r in st_] == [("src/a.c", "decl:f1")] and sup_[0]["verdict"] == "DECL-NONE" and "stale DECL-KEPT" in sup_[0]["cause"]
and stale_lifted(sl_led, [], ["DECL-KEPT:conflicting"], True, None)[0]["tu"] == "src/c.h", f"{st_}")
# T3.c2: the pinned base→type fallback, and finished files left out of the plan
key_ = f"{tu}|fx_a"
cur_ = {key_: {"param:a0": dict(type="Unkstruct_DRIFT", sites=12), "param:a1": dict(type="Unkstruct_DRIFT2", sites=1)}}
pin_ = {key_: {"param:a0": dict(type="Unkstruct_TEST", sites=12), "param:a1": dict(type="Unkstruct_GONE", sites=1)}}
mg_, nfb_ = merge_bbt(cur_, pin_, _canon)
ok("bbt pin: a drifted non-canonical type falls back to the pinned canonical one; a non-canonical pin is not taken",
nfb_ == 1 and mg_[key_]["param:a0"]["type"] == "Unkstruct_TEST" and mg_[key_]["param:a1"]["type"] == "Unkstruct_DRIFT2", f"{mg_}")
ok("plan_S: drifted bbt draws nothing; the pinned merge draws the file",
plan_S(False, 50, None, set(), {}, cur_)[1] == 0 and plan_S(False, 50, None, set(), {}, mg_)[1] == 1)
m_, defs_, _, _, _ = file_view(tu, text)
nh_ = body_nhash(m_, next(x for x in defs_ if x["name"] == "fx_a"))
fin_ = ledger_latest([dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after=nh_)])
old_ = ledger_latest([dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after=nh_),
dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after="stale")])
p_fin, p_old = plan_S(False, 50, None, set(), fin_, mg_), plan_S(False, 50, None, set(), old_, mg_)
ok("plan_S: a file whose bodies' latest S rows are at the current nhash is not drawn; a stale latest hash draws it",
p_fin[1] == 0 and p_fin[2]["file-finished"] == 1 and p_old[1] == 1, f"{p_fin[1:]} {p_old[1:]}")
ok("the selftest never touches the live inflight.json", True)
ok("struct_layout selftest", sl.selftest() == 0)
JUDGE_STUB = None
@@ -4774,8 +4837,11 @@ def main():
ap.add_argument("--rerun-differs", action="store_true", help="re-run only the DIFFERS rows of the last probe (with S2) and re-render")
ap.add_argument("--probe-decls", action="store_true", help="probe (ii): the definition's signature as the in-scope declaration, per TU x callee")
ap.add_argument("--probe-defs", action="store_true", help="probe (iii): fold a TU's duplicate file-scope definitions onto the canonical header")
ap.add_argument("--bbt", help="a pinned body_base_type.json: a base whose current type is not canonical falls back to its pinned entry (env RESTRUCT_BBT)")
ap.add_argument("-j", "--jobs", type=int, default=16)
a = ap.parse_args()
global BBT_PIN
BBT_PIN = a.bbt or BBT_PIN
if a.selftest:
sys.exit(selftest(real=a.real))
if a.restore:
@@ -4819,9 +4885,10 @@ def main():
return
if a.plan:
recipes()
done, _ = ledger_index(load_ledger())
led = load_ledger()
done, _ = ledger_index(led)
if a.rung == "S":
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done)
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done, ledger_latest(led))
print(describe_plan("S", plan, total, skipped))
elif a.rung == "D":
if a.redraw:
@@ -11,7 +11,7 @@
# still in place: inspect, then `tools/restruct.py --restore` (never git checkout, R102). Stops cleanly when nothing is drawable.
# With CENSUS=0 too: a final X/Y with X != Y exits 1 BEFORE R22/commit and the tree stays exactly as restruct left it (no auto-revert) —
# the OPERATOR must restore it (`tools/restruct.py --restore`, or revert the named files) before the next cycle (T4.c2).
# Usage: [LABEL_PREFIX=t4_] [TASK=T4] [REDRAW="DECL-KEPT"] [CENSUS=0] tools/restruct_cycle.sh START END [BATCH=50] [RUNG=D|S|L] [ONLY="alias1 alias2 …"] [HEADERS=1]
# Usage: [LABEL_PREFIX=t4_] [TASK=T4] [REDRAW="DECL-KEPT"] [CENSUS=0] [RESTRUCT_BBT=.run/P39/census/body_base_type.json] tools/restruct_cycle.sh START END [BATCH=50] [RUNG=D|S|L] [ONLY="alias1 alias2 …"] [HEADERS=1]
# RUN IT DETACHED from the Claude Code harness (R115 — its low-memory guard kills a long BACKGROUND task; a SIGKILL skips a restore):
# setsid nohup bash -c 'TASK=T4 tools/restruct_cycle.sh 1 8 50 D; echo "cycle exit=$?"' > .run/P37/restruct/cycle_<x>.log 2>&1 &
# and watch the log (`until grep -q 'cycle exit=' <log>; do sleep 60; done`). A killed batch: `tools/restruct.py --restore`.
@@ -25,6 +25,7 @@ case "$RUNG" in D|S|L) ;; *) echo "RUNG must be D, S or L"; exit 2;; esac
ONLYFLAG=""; [ -n "$ONLY" ] && ONLYFLAG="--only $ONLY"
REDRAWFLAG=""; [ -n "${REDRAW:-}" ] && REDRAWFLAG="--redraw $REDRAW"
HFLAG=""; [ -n "${HEADERS:-}" ] && HFLAG="--headers"
BBTFLAG=""; [ -n "${RESTRUCT_BBT:-}" ] && BBTFLAG="--bbt $RESTRUCT_BBT" # T3.c2: a pinned body_base_type.json (the census rewrites the live one)
FLEET=$(ls config/check.*.sha | wc -l)
PY=.venv/bin/python
MARKER='## 🛑 SESSION CHECKPOINT'
@@ -41,7 +42,7 @@ for k in $(seq "$START" "$END"); do
$PY tools/delever_oracle.py --calibrate ov_SC04_011 ov_SC03_015 md_SC07_004 main -j 16 > .run/P37/restruct/calibrate_${label}.log 2>&1 \
|| { echo "cycle: calibration FAILED"; tail -5 .run/P37/restruct/calibrate_${label}.log; exit 1; }
}
{ /usr/bin/time -f "$label wall=%e s" $PY tools/restruct.py --apply --rung "$RUNG" --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
{ /usr/bin/time -f "$label wall=%e s" $PY tools/restruct.py --apply --rung "$RUNG" --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG $BBTFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
grep -q '^exit=0$' "$log" || { echo "cycle: batch $label — restruct exited non-zero"; tail -4 "$log"; exit 1; }
if grep -qE 'nothing to do' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
vline=$(grep -oE "restruct: batch .*" "$log" | tail -1)
+30
View File
@@ -0,0 +1,30 @@
# T3.c2 — rung S plan: pinned base→type + finished files out
## Cause (confirmed)
- Hypothesis held. `.run/P39/t3c2/cmp_bbt.py` (scratch) over ov bases (non-.h):
- pinned `.run/P39/census/body_base_type.json` (T1, 10:25): 116484 bases, 31408 canonical; top `Unkstruct_80126B58`, `Unkstruct_800B5CB8`.
- current `.run/P37/census/body_base_type.json` (14:18, after t3_S5 census): 103469 bases, **0 canonical**; top `Unkstruct_800D3888` (22682).
- untouched file `src/ov_MAIN_012/ov_MAIN_012.c|func_8012E9C0` (no t3_ S row): `param:param_1` pinned `Unkstruct_800B5CB8` (canon) → current `Unkstruct_800D3888` (not canon).
- So the per-batch `type_census --sites` renames types in body_base_type.json; plan_S's canonical filter drops every base → t3_S6 `0 of 0`.
- Drawable never fell: plan_S had no finished-file exclusion (work_file_S skips done bodies but the file is still drawn).
## Change
- `tools/restruct.py`:
- `BBT_PIN` (env `RESTRUCT_BBT`, CLI `--bbt PATH`); `merge_bbt(cur, pin, can)`: per (body, base), a non-canonical current type falls back to the pinned entry when that is canonical; keys come from current only. `bbt()` applies it when pinned (exits if the pin path is missing).
- `s_finished(tu, fns, latest)`: every body's latest ("S",tu,fn) row has `nhash_after` == current `body_nhash` (file parsed only when every body has a latest S row with a hash). Any verdict counts (latest row = verdict, R70); TOOL-ERROR rows (nhash_after None) keep the file drawable.
- `plan_S(..., latest=None, data=None)`: drops finished files, counts `skipped["file-finished"]`; both callers (`--plan`, `--apply`) pass `ledger_latest(rows)`.
- selftest +3 controls: merge fallback (canonical pin taken, non-canonical pin not); drifted bbt draws 0 / merged draws 1; finished at current nhash → not drawn, stale latest hash → drawn.
- `tools/restruct_cycle.sh`: `RESTRUCT_BBT=… ` → `--bbt` on the apply line; usage line documents it.
## Verified
- `.venv/bin/python tools/restruct.py --selftest` → 88/88 OK; `/usr/bin/python3 tools/restruct.py --selftest` → 88/88 OK.
- `make kit-corpus` → corpus materialised (409 copies); `make tools-health` → OK.
- `restruct.py --plan --rung S --only "ov_*"` (no pin) → `0 of 0 drawable files … skipped bases {'type-not-canonical': 103469}` (.run/logs/t3c2_plan_nopin.log).
- same with `--bbt .run/P39/census/body_base_type.json` → `50 of 1901 drawable files · 340 bodies · 397 typed bases · 2063 deref sites · skipped {'type-not-canonical': 83385, 'file-finished': 187}` (.run/logs/t3c2_plan_pin.log).
- full counts (`.run/P39/t3c2/plan_counts.py`, batch unlimited, pinned): no finish filter 2088 files / 17165 bodies; with filter 1901 files / 14621 bodies (187 finished files out).
## Expert notes
- The pin `.run/P39/census/body_base_type.json` is gitignored and untracked (11.5 MB); nothing in tools/ writes it. Losing it loses the fix's input: copy or `git add -f` if wanted.
- 225 TUs carry t3_ S rows; 187 count as finished; the rest have a body with no / stale / hashless latest row (e.g. TOOL-ERROR) or a body judged by an earlier label at an older hash.
- Bases new since the pin (not in it) stay non-canonical: newly typed bodies are not drawn until a canonical census is pinned again.
- Sweep resume: `RESTRUCT_BBT=.run/P39/census/body_base_type.json LABEL_PREFIX=t3_ TASK=T3 tools/restruct_cycle.sh 6..60 50 S "ov_*"` (not run).
+6
View File
@@ -7,3 +7,9 @@
- retriever-code (inline, no report): rung S mechanics. Unit = file; plan_S :3119 from CENSUS/body_base_type.json; S1 MEMBERS / S2 greedy_kept :753 / KEPT-ALL / REFUSED; site verdict `KEPT(<body hint>)` (one hint per body, :1117-1122); kept sites are left raw (no respell); no-field skips never add fields; `--apply` allow_local=False; no code writes macro spellings.
- restruct_cycle.sh read: per batch calibrate-if-stale → apply -j 12 → R22 → census → commit; S body ≈ 2 s.
- T3.c1 briefed (opus55): per-site class, KEPT(<class>) / RESIDUAL(<cause>), CAST_* respell of kept P sites, `--only` glob, calibrate, exemplar ov_SC05_010_jr_80180F84.c.
- T3.c1 done (3ff15ee462, 3507628e40, fe2e814776): selftest 85/85, --real 90/90, exemplar apply 1/1 IDENTICAL 25.5 s, R22 218/218, check-casts macros CAST_ALIAS=3 CAST_SIGN=10 CAST_WIDTH=6 unbacked 0. Deviation (coder): BACKED_S_VERDICTS += MEMBERS, S+A; new body verdict KEPT. Exemplar +0x34 store = RESIDUAL(TYPE-NOT-CANONICAL) in real apply (KEPT(SCHED-ALIAS) only under try-file local types).
- cycle batch 1 (`LABEL_PREFIX=t3_ TASK=T3 restruct_cycle.sh 1 1 50 S "ov_*"`, log .run/P39/restruct/cycle_t3_a.log): 08b5b03dd0, 50 files/2088 drawable, 3803 bodies, 24546 members / 2835 kept / 9346 skipped, 1707 s apply, R22 218/218 112 s. Batch kept at 50 files though > 10 min: detached (R115), heaviest first; deviation.
- cycle 2..60 launched (.run/P39/restruct/cycle_t3_b.log).
- retriever-code (inline): --write-types is preview-only; canonical = struct defined in engine_types.h / struct_types.h; no-field = no leaf at exact off; head_types.py regenerates only its marked region; no tool adds fields to hand-authored structs.
- cycle 2..60 (cycle_t3_b.log): t3_S2..S5 green (R22 218/218 each, last 69f9da8c0f); members 12384/7786/7878/6694; then t3_S6 `0 of 0 drawable … type-not-canonical 103469` → cycle exit 0 "nothing drawable". Drawable count never fell (2088→2089). Probe: current body_base_type.json 109,359 bases, top type Unkstruct_800D3888 (absent from canonical headers). Hypothesis: census rerun renames struct-map types as sites become members; plan reads drifting map; plan also re-draws finished files.
- T3.c2 briefed: confirm, pin base→type (pre-sweep .run/P39/census/body_base_type.json), exclude finished files.
+74 -7
View File
@@ -3245,11 +3245,39 @@ def fold_report(heads):
# ----------------------------------------------------------------------------------------------------------------------
# planning and --apply (the batch: files in leverage order, workers own whole TUs, headers serial and last)
# ----------------------------------------------------------------------------------------------------------------------
BBT_PIN = os.environ.get("RESTRUCT_BBT") or None # P3.39 T3.c2: a pinned body_base_type.json (--bbt / RESTRUCT_BBT)
def merge_bbt(cur, pin, can):
"""cur with each base whose type is NOT canonical replaced by the pinned entry for the same (body, base) when that one is canonical.
T3.c2: the cycle's per-batch census rewrites body_base_type.json and the map's names drift as sites become members (after t3_S5 the
ov bases' top type was Unkstruct_800D3888, not in a canonical header: 0 of 103469 ov bases canonical, plan_S drew 0 files)."""
out, n = {}, 0
for key, bases in cur.items():
pb = pin.get(key, {})
nb = {}
for bk, ent in bases.items():
pe = pb.get(bk)
if ent["type"] not in can and pe and pe["type"] in can:
nb[bk], n = pe, n + 1
else:
nb[bk] = ent
out[key] = nb
return out, n
def bbt():
p = CENSUS / "body_base_type.json"
if not p.exists():
sys.exit("restruct: no body_base_type.json — run `tools/type_census.py -j 16 --sites` first (R43)")
return json.loads(p.read_text())
cur = json.loads(p.read_text())
if BBT_PIN:
pp = pathlib.Path(BBT_PIN)
pp = pp if pp.is_absolute() else REPO / pp
if not pp.exists():
sys.exit(f"restruct: the pinned body_base_type ({BBT_PIN}) does not exist")
cur, _ = merge_bbt(cur, json.loads(pp.read_text()), canon_types())
return cur
def matches_only(only, tu, unit, extra=()):
@@ -3263,14 +3291,28 @@ def matches_only(only, tu, unit, extra=()):
or (o in h and o.startswith(("Unkstruct_", "func_", "D_"))) for o in only for h in hay if h)
def plan_S(headers, batch, only, done):
"""[(tu, {fn: bases})] — files with typed bases whose type is a CANONICAL definition (visible), ordered by the map's leverage."""
def s_finished(tu, fns, latest):
"""True when every body in fns has a latest ("S", tu, fn) ledger row whose nhash_after is the body's CURRENT nhash (T3.c2: t3_S1–S5
re-drew finished files — drawable stayed ~2088 after 250 files done)."""
if not all((latest.get(("S", tu, fn)) or {}).get("nhash_after") for fn in fns):
return False
path = REPO / tu
if not path.exists():
return False
masked, defs, _, _, _ = file_view(tu, path.read_text(errors="surrogateescape"))
cur = {d["name"]: body_nhash(masked, d) for d in defs if d["name"] in fns}
return all(cur.get(fn) == latest[("S", tu, fn)]["nhash_after"] for fn in fns)
def plan_S(headers, batch, only, done, latest=None, data=None):
"""[(tu, {fn: bases})] — files with typed bases whose type is a CANONICAL definition (visible), ordered by the map's leverage.
latest = ledger_latest(rows): files whose every body is finished at its current text (s_finished) are not drawn; data: a bbt() dict (selftest)."""
smap = struct_map()
can = canon_types()
files = collections.defaultdict(dict)
weight = collections.Counter()
skipped = collections.Counter()
for key, bases in bbt().items():
for key, bases in (bbt() if data is None else data).items():
tu, fn = key.split("|", 1)
if tu.endswith(".h") != headers:
continue
@@ -3288,6 +3330,10 @@ def plan_S(headers, batch, only, done):
weight[tu] += smap.get(ent["type"], {}).get("sites", 0)
if usable:
files[tu][fn] = usable
if latest:
for tu in [t for t in files if s_finished(t, set(files[t]), latest)]:
del files[tu]
skipped["file-finished"] += 1
ordered = sorted(files, key=lambda t: (-weight[t], t))
return [(tu, files[tu]) for tu in ordered[:batch]], len(ordered), skipped
@@ -3520,7 +3566,7 @@ def apply_batch(a):
log_f.close()
return 0 if v == "IDENTICAL" else 1
if a.rung == "S":
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done)
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done, ledger_latest(rows))
print(describe_plan("S", plan, total, skipped), flush=True)
elif a.rung == "D":
if a.redraw:
@@ -4706,6 +4752,23 @@ def selftest(real=False):
ok("rung D supersede: only lifted latest rows with no lying declaration left (.c only; cause key; latest row)",
[(r["tu"], r["unit"]) for r in st_] == [("src/a.c", "decl:f1")] and sup_[0]["verdict"] == "DECL-NONE" and "stale DECL-KEPT" in sup_[0]["cause"]
and stale_lifted(sl_led, [], ["DECL-KEPT:conflicting"], True, None)[0]["tu"] == "src/c.h", f"{st_}")
# T3.c2: the pinned base→type fallback, and finished files left out of the plan
key_ = f"{tu}|fx_a"
cur_ = {key_: {"param:a0": dict(type="Unkstruct_DRIFT", sites=12), "param:a1": dict(type="Unkstruct_DRIFT2", sites=1)}}
pin_ = {key_: {"param:a0": dict(type="Unkstruct_TEST", sites=12), "param:a1": dict(type="Unkstruct_GONE", sites=1)}}
mg_, nfb_ = merge_bbt(cur_, pin_, _canon)
ok("bbt pin: a drifted non-canonical type falls back to the pinned canonical one; a non-canonical pin is not taken",
nfb_ == 1 and mg_[key_]["param:a0"]["type"] == "Unkstruct_TEST" and mg_[key_]["param:a1"]["type"] == "Unkstruct_DRIFT2", f"{mg_}")
ok("plan_S: drifted bbt draws nothing; the pinned merge draws the file",
plan_S(False, 50, None, set(), {}, cur_)[1] == 0 and plan_S(False, 50, None, set(), {}, mg_)[1] == 1)
m_, defs_, _, _, _ = file_view(tu, text)
nh_ = body_nhash(m_, next(x for x in defs_ if x["name"] == "fx_a"))
fin_ = ledger_latest([dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after=nh_)])
old_ = ledger_latest([dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after=nh_),
dict(rung="S", tu=tu, unit="fx_a", verdict="MEMBERS", nhash_after="stale")])
p_fin, p_old = plan_S(False, 50, None, set(), fin_, mg_), plan_S(False, 50, None, set(), old_, mg_)
ok("plan_S: a file whose bodies' latest S rows are at the current nhash is not drawn; a stale latest hash draws it",
p_fin[1] == 0 and p_fin[2]["file-finished"] == 1 and p_old[1] == 1, f"{p_fin[1:]} {p_old[1:]}")
ok("the selftest never touches the live inflight.json", True)
ok("struct_layout selftest", sl.selftest() == 0)
JUDGE_STUB = None
@@ -4774,8 +4837,11 @@ def main():
ap.add_argument("--rerun-differs", action="store_true", help="re-run only the DIFFERS rows of the last probe (with S2) and re-render")
ap.add_argument("--probe-decls", action="store_true", help="probe (ii): the definition's signature as the in-scope declaration, per TU x callee")
ap.add_argument("--probe-defs", action="store_true", help="probe (iii): fold a TU's duplicate file-scope definitions onto the canonical header")
ap.add_argument("--bbt", help="a pinned body_base_type.json: a base whose current type is not canonical falls back to its pinned entry (env RESTRUCT_BBT)")
ap.add_argument("-j", "--jobs", type=int, default=16)
a = ap.parse_args()
global BBT_PIN
BBT_PIN = a.bbt or BBT_PIN
if a.selftest:
sys.exit(selftest(real=a.real))
if a.restore:
@@ -4819,9 +4885,10 @@ def main():
return
if a.plan:
recipes()
done, _ = ledger_index(load_ledger())
led = load_ledger()
done, _ = ledger_index(led)
if a.rung == "S":
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done)
plan, total, skipped = plan_S(a.headers, a.batch, a.only, done, ledger_latest(led))
print(describe_plan("S", plan, total, skipped))
elif a.rung == "D":
if a.redraw:
+3 -2
View File
@@ -11,7 +11,7 @@
# still in place: inspect, then `tools/restruct.py --restore` (never git checkout, R102). Stops cleanly when nothing is drawable.
# With CENSUS=0 too: a final X/Y with X != Y exits 1 BEFORE R22/commit and the tree stays exactly as restruct left it (no auto-revert) —
# the OPERATOR must restore it (`tools/restruct.py --restore`, or revert the named files) before the next cycle (T4.c2).
# Usage: [LABEL_PREFIX=t4_] [TASK=T4] [REDRAW="DECL-KEPT"] [CENSUS=0] tools/restruct_cycle.sh START END [BATCH=50] [RUNG=D|S|L] [ONLY="alias1 alias2 …"] [HEADERS=1]
# Usage: [LABEL_PREFIX=t4_] [TASK=T4] [REDRAW="DECL-KEPT"] [CENSUS=0] [RESTRUCT_BBT=.run/P39/census/body_base_type.json] tools/restruct_cycle.sh START END [BATCH=50] [RUNG=D|S|L] [ONLY="alias1 alias2 …"] [HEADERS=1]
# RUN IT DETACHED from the Claude Code harness (R115 — its low-memory guard kills a long BACKGROUND task; a SIGKILL skips a restore):
# setsid nohup bash -c 'TASK=T4 tools/restruct_cycle.sh 1 8 50 D; echo "cycle exit=$?"' > .run/P37/restruct/cycle_<x>.log 2>&1 &
# and watch the log (`until grep -q 'cycle exit=' <log>; do sleep 60; done`). A killed batch: `tools/restruct.py --restore`.
@@ -25,6 +25,7 @@ case "$RUNG" in D|S|L) ;; *) echo "RUNG must be D, S or L"; exit 2;; esac
ONLYFLAG=""; [ -n "$ONLY" ] && ONLYFLAG="--only $ONLY"
REDRAWFLAG=""; [ -n "${REDRAW:-}" ] && REDRAWFLAG="--redraw $REDRAW"
HFLAG=""; [ -n "${HEADERS:-}" ] && HFLAG="--headers"
BBTFLAG=""; [ -n "${RESTRUCT_BBT:-}" ] && BBTFLAG="--bbt $RESTRUCT_BBT" # T3.c2: a pinned body_base_type.json (the census rewrites the live one)
FLEET=$(ls config/check.*.sha | wc -l)
PY=.venv/bin/python
MARKER='## 🛑 SESSION CHECKPOINT'
@@ -41,7 +42,7 @@ for k in $(seq "$START" "$END"); do
$PY tools/delever_oracle.py --calibrate ov_SC04_011 ov_SC03_015 md_SC07_004 main -j 16 > .run/P37/restruct/calibrate_${label}.log 2>&1 \
|| { echo "cycle: calibration FAILED"; tail -5 .run/P37/restruct/calibrate_${label}.log; exit 1; }
}
{ /usr/bin/time -f "$label wall=%e s" $PY tools/restruct.py --apply --rung "$RUNG" --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
{ /usr/bin/time -f "$label wall=%e s" $PY tools/restruct.py --apply --rung "$RUNG" --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG $BBTFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
grep -q '^exit=0$' "$log" || { echo "cycle: batch $label — restruct exited non-zero"; tail -4 "$log"; exit 1; }
if grep -qE 'nothing to do' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
vline=$(grep -oE "restruct: batch .*" "$log" | tail -1)