shim: fix cbpin's wrapped header and six unnamed hooks; add a config check
Lane BP found both defects while copying cbpin for a determinism probe, and its own first draft reproduced the comment wrap -- which is what a trap looks like. The header ran on past the comment and left a stray aiseed.values ahead of the real one, and six registered hooks went unnamed, so under hooks=trace the config ran six more detours than it claimed. The check enforces the two syntax defects everywhere and exhaustiveness only where a template opts in with '# exhaustive'. hooks=trace legitimately means 'trace all but these', so a global exhaustiveness rule would be a rule about a style nobody agreed to; it matters only for a minimal-hook determinism probe.
This commit is contained in:
parent
989c692c53
commit
165478592a
2 changed files with 136 additions and 4 deletions
|
|
@ -1,7 +1,17 @@
|
|||
# Lane CB -- the pinned creation-turn capture. `shim.cfg.cbcapture` with `aiseed=pin
|
||||
# The seeds run C3 (unpinned, same save, same guest, same build) observed for itself. Pinning
|
||||
# them reproduces a turn that actually happened rather than inventing one.
|
||||
aiseed.values=32=e70a4703,496=0c63ca36,512=372be4df`.
|
||||
# Lane CB -- the pinned creation-turn capture. `shim.cfg.cbcapture` with `aiseed=pin`.
|
||||
#
|
||||
# 2026-09-09: this header used to wrap onto a LIVE `aiseed.values=` line carrying a trailing
|
||||
# backtick, so the file both contradicted its own "filled in per run" note and shipped a stray
|
||||
# setting ahead of the real one at the bottom. Lane BP found it while copying the file, and its
|
||||
# own first draft reproduced the same wrap -- which is what a trap looks like. The seeds now
|
||||
# appear exactly once, at the end, next to `aiseed=pin`.
|
||||
#
|
||||
# exhaustive
|
||||
#
|
||||
# ^ that marker is checked by tools/check_shim_configs.py: this is a determinism probe,
|
||||
# so it must name EVERY registered hook, because under `hooks=trace` one it forgets is
|
||||
# a detour it did not ask for -- and on a determinism probe an extra detour is exactly
|
||||
# what rule 19 says can move the bytes. Six were missing until lane BP found them.
|
||||
#
|
||||
# WHY PINNING IS NOT CHEATING. Lane L1 showed every AI client's generator is seeded with a
|
||||
# fresh per-process word, so `turn1-state -> turn2` has an outcome set of size k > 1 and NO
|
||||
|
|
@ -33,6 +43,16 @@ hook.Game::StrategyServer::NodeLineDecay=off
|
|||
hook.Game::StrategyServer::ProcessNodeSpaceTravel=off
|
||||
hook.Game::EncounterDetect::AssignContacts=off
|
||||
hook.Game::EncounterDetect::ProcessTeamRecord=off
|
||||
# These six were absent, and under `hooks=trace` an unnamed hook defaults back ON -- so this
|
||||
# config was quietly running six more detours than it claimed. Lane BP, 2026-09-09. Name every
|
||||
# registered hook; a template that lists most of them is worse than one that lists none, because
|
||||
# it reads as exhaustive.
|
||||
hook.Game::StrategyServer::BeginProcessTurn=off
|
||||
hook.Game::SVSOSwarmQueen::OnTurnBegin=off
|
||||
hook.Game::SVSOSwarmQueen::RegisterHives=off
|
||||
hook.Game::SVSOSwarmQueen::TickHives=off
|
||||
hook.Game::SVSOSlaversRefuel::UpdateDifficultyTier=off
|
||||
hook.Mars::RNG::Seed=off
|
||||
trace.path=C:\SOTS\shim.trace.jsonl
|
||||
trace.flush=always
|
||||
probes=off
|
||||
|
|
|
|||
112
tools/check_shim_configs.py
Normal file
112
tools/check_shim_configs.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Structural checks on the shim.cfg.* templates.
|
||||
|
||||
Lane BP found two defects in `shim.cfg.cbpin` while copying it for a determinism
|
||||
probe, and its own first draft reproduced one of them -- which is what a trap looks
|
||||
like. Both are checked here:
|
||||
|
||||
* A **wrapped header comment landing on a live setting.** `cbpin`'s first line ran
|
||||
on past the comment and left `aiseed.values=...\\u0060.` as a real setting ahead of the
|
||||
real one at the bottom, so the file contradicted its own "filled in per run" note.
|
||||
* A **key set twice.** The last one silently wins, which is exactly how a stale
|
||||
value hides in a config nobody re-reads.
|
||||
|
||||
There is deliberately **no global check that a template names every registered
|
||||
hook**. `hooks=trace` means "trace everything except what is turned off", and most
|
||||
templates rely on that on purpose -- an exhaustiveness rule applied to all of them
|
||||
would be a rule about a style nobody agreed to. It matters only where a lane wants
|
||||
a *minimal* hook set, and no file states that intent, so a template opts in by
|
||||
carrying a line reading exactly `# exhaustive` in its header. For those, an unnamed
|
||||
hook is a detour the config did not ask for, and on a determinism probe an extra
|
||||
detour is precisely what rule 19 says can move the bytes.
|
||||
|
||||
Exits non-zero with the offending file and what is wrong with it.
|
||||
"""
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
SRC = ROOT / "src" / "shim"
|
||||
|
||||
# A hook declares itself with `static constexpr const char* name = "...";`
|
||||
NAME_RE = re.compile(r'static\s+constexpr\s+const\s+char\*\s+name\s*=\s*"([^"]+)"')
|
||||
HOOK_RE = re.compile(r"^hook\.([^=]+)=")
|
||||
|
||||
# Not real hooks: the doc-comment's example in trace/hook.h, and the self-test hooks,
|
||||
# which exist to make the harness fail on purpose and are never part of a measurement.
|
||||
EXEMPT = {"Game::Foo"}
|
||||
EXEMPT_PREFIXES = ("Shim::SelfTest::",)
|
||||
|
||||
# Settings whose values legitimately end in a period or a path separator.
|
||||
PROSE_SAFE = ("path", "out", "dir", "file")
|
||||
|
||||
|
||||
def registered_hooks():
|
||||
names = set()
|
||||
for path in SRC.rglob("*"):
|
||||
if path.suffix in (".h", ".cpp"):
|
||||
for m in NAME_RE.finditer(path.read_text(encoding="utf-8", errors="replace")):
|
||||
names.add(m.group(1))
|
||||
return {n for n in names if n not in EXEMPT and not n.startswith(EXEMPT_PREFIXES)}
|
||||
|
||||
|
||||
def check(cfg, hooks):
|
||||
problems = []
|
||||
lines = cfg.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
settings = [l for l in lines if l.strip() and not l.lstrip().startswith("#")]
|
||||
|
||||
for l in settings:
|
||||
key = l.split("=", 1)[0].strip()
|
||||
if "=" not in l:
|
||||
problems.append(f"line is neither a comment nor a setting: {l!r}")
|
||||
elif l.rstrip().endswith(("`", "`.")) or (
|
||||
l.rstrip().endswith(".") and not any(p in key.lower() for p in PROSE_SAFE)
|
||||
):
|
||||
problems.append(f"setting looks like a wrapped comment: {l!r}")
|
||||
|
||||
seen = {}
|
||||
for l in settings:
|
||||
key = l.split("=", 1)[0].strip()
|
||||
if key in seen:
|
||||
problems.append(f"{key!r} set twice ({seen[key]!r} then {l!r})")
|
||||
seen[key] = l
|
||||
|
||||
if any(l.strip() == "# exhaustive" for l in lines):
|
||||
if not any(l.strip() == "hooks=trace" for l in settings):
|
||||
problems.append("declares `# exhaustive` but does not set `hooks=trace`, so it does nothing")
|
||||
named = {m.group(1) for m in (HOOK_RE.match(l) for l in settings) if m}
|
||||
missing = sorted(hooks - named)
|
||||
if missing:
|
||||
problems.append(
|
||||
f"declares `# exhaustive` but does not name {len(missing)} registered hook(s), "
|
||||
"which therefore default ON: " + ", ".join(missing)
|
||||
)
|
||||
return problems
|
||||
|
||||
|
||||
def main():
|
||||
hooks = registered_hooks()
|
||||
if not hooks:
|
||||
print("check_shim_configs: found no registered hooks -- the parser is wrong, not the configs")
|
||||
return 1
|
||||
|
||||
failures = []
|
||||
exhaustive = 0
|
||||
for cfg in sorted(SRC.glob("shim.cfg.*")):
|
||||
if any(l.strip() == "# exhaustive" for l in cfg.read_text(errors="replace").splitlines()):
|
||||
exhaustive += 1
|
||||
for p in check(cfg, hooks):
|
||||
failures.append(f"{cfg.name}: {p}")
|
||||
|
||||
if failures:
|
||||
print("check_shim_configs: FAILED")
|
||||
for f in failures:
|
||||
print(" " + f)
|
||||
return 1
|
||||
print(f"check_shim_configs: OK ({len(hooks)} registered hooks, {exhaustive} template(s) declared exhaustive)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Reference in a new issue