feat: story_flags --bootstrap seeds skeletons into globals.toml (Task 5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-07 08:27:31 -04:00
parent 681737604c
commit f0d7775d15
2 changed files with 62 additions and 0 deletions

View File

@@ -126,6 +126,44 @@ def write_candidates(cands: dict[int, dict]) -> Path:
return p
import globals_build as _gb
def skeleton_toml(ev: dict) -> str:
consts = ", ".join(str(c) for c in ev["consts"][:8])
domain = "{0,1}" if set(ev["consts"]) <= {0, 1} and ev["consts"] else (f"one of {{{consts}}}" if consts else "?")
usage = (f"TODO: confirm. Branch-read in {ev['reach_scenes']} scenes / {ev['reach_total']} scripts; "
f"compared against [{consts}]; "
f"writers={ev['writers'][:4] or 'none (external/native?)'}.")
lines = ["[[global]]",
f'address = "{ev["address"]}"',
'name = ""',
f'category = "{ev["category"]}"',
'type = "int"',
f'value_domain = "{domain}"',
f'usage = "{usage}"',
'source = "auto-shape"',
f'confidence = "{ev["confidence"]}"',
"depends_on = []"]
return "\n".join(lines) + "\n"
def bootstrap(cands: dict[int, dict], toml_path=None) -> int:
toml_path = Path(toml_path) if toml_path else (paths.VM_MAP / "globals.toml")
present = set(_gb.load_toml(toml_path)[0]) if toml_path.exists() else set()
# only story-flag / ui-toggle / choice-output candidates are worth seeding for curation
seedable = {a: e for a, e in cands.items()
if e["category"] in ("story-flag", "ui-toggle", "choice-output") and a not in present}
if not seedable:
print(f"bootstrap: nothing new to add ({len(present)} already present).")
return 0
blocks = [skeleton_toml(seedable[a]) for a in sorted(seedable)]
with toml_path.open("a", encoding="utf-8") as f:
f.write("\n" + "\n".join(blocks))
print(f"bootstrap: appended {len(blocks)} skeletons -> {toml_path}")
return 0
def main(argv=None):
ap = argparse.ArgumentParser()
ap.add_argument("--bootstrap", action="store_true") # implemented in Task 5

View File

@@ -58,11 +58,35 @@ def test_miner_finds_known_flags():
check(cands[0xa57]["reach_scenes"] >= 70, "0xa57 high scene reach")
check(cands[0xa57]["category"] == "story-flag", "0xa57 classified story-flag")
def test_bootstrap_is_additive_and_idempotent():
import tempfile, pathlib, story_flags
# start from a copy of the real toml so curated entries are present
src = (paths.VM_MAP / "globals.toml").read_text(encoding="utf-8")
with tempfile.TemporaryDirectory() as d:
tp = pathlib.Path(d) / "globals.toml"
tp.write_text(src, encoding="utf-8")
before, _ = G.load_toml(tp)
cands = story_flags.mine()
story_flags.bootstrap(cands, toml_path=tp)
after, _ = G.load_toml(tp)
check(len(after) > len(before), "bootstrap adds new skeleton entries")
check(after[0xa57]["name"] == "lily_form_a", "bootstrap preserves curated 0xa57")
# idempotent: second run adds nothing
n1 = len(after)
story_flags.bootstrap(story_flags.mine(), toml_path=tp)
after2, _ = G.load_toml(tp)
check(len(after2) == n1, "second bootstrap is a no-op (idempotent)")
# every skeleton is auto-shape and not high-confidence
added = set(after) - set(before)
check(all(after[a]["source"] == "auto-shape" for a in added), "skeletons are source=auto-shape")
check(all(after[a]["confidence"] != "high" for a in added), "skeletons never high confidence")
if __name__ == "__main__":
test_load_and_lint()
test_lint_catches_bad_vocab()
test_merge_precedence()
test_sys4load_labels_from_registry()
test_miner_finds_known_flags()
test_bootstrap_is_additive_and_idempotent()
print(f"\n{len(FAILS)} failures")
sys.exit(1 if FAILS else 0)