chore(re): stop committing game assets; captures stay local
Issue #49: the repos carry code, tooling and docs only. Untracks 143 screenshots, 3 savegame blobs and `tools/re-capture/ob_digits.png` (a digit-template sheet cut from game frames) -- 146 files, 76.4 MB. They stay in the working tree and are gitignored, so the pages' relative links still resolve where the captures exist and nothing ships. Derived measurements (csv/tsv/txt/log/json/jsonl/npy) are our own numbers, not game content, and stay tracked -- they are what most claims rest on. `check-capture-citations` had its contract inverted, and it is the half worth reading: * presence now comes from the WORKING TREE, not `git ls-files`. The assets are deliberately untracked, so asking the index would report every screenshot as missing and fail all 203 citations. * a NEW failure: a game asset that IS tracked. A screenshot that sneaks back in is invisible in review -- a binary shows as "Bin 0 -> 1234567 bytes" -- and is permanent once merged, since removing it later needs a history rewrite. So that half has to be loud. Verified: * selftest 8/8, including the new rule * scan: 212 present, 212 cited, 0 dangling, 0 tracked -> exit 0 * force-add one PNG -> "game assets TRACKED: 1", exit 1, selftest red ⚠️ This does NOT remove the blobs from history; a clone still fetches them. That needs a filter-repo rewrite and a force-push, which is a separate, human-run step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,22 @@ A wrong-but-confident note is worse than no note: someone builds on it and the b
|
|||||||
for weeks. Every entry therefore carries an explicit **confidence** and its **evidence**.
|
for weeks. Every entry therefore carries an explicit **confidence** and its **evidence**.
|
||||||
This mirrors the project method — *measure the oracle, never infer; refute before believing.*
|
This mirrors the project method — *measure the oracle, never infer; refute before believing.*
|
||||||
|
|
||||||
|
### Captures are local-only (issue #49)
|
||||||
|
|
||||||
|
The repository carries **code, tooling and docs**. Screenshots and savegame
|
||||||
|
blobs are game-derived, so since 2026-09-19 they live in `docs/re/captures/`
|
||||||
|
on disk and are **gitignored** — the pages' relative links still resolve on a
|
||||||
|
machine that has them, and nothing ships.
|
||||||
|
|
||||||
|
Derived measurements (`csv`, `tsv`, `txt`, `log`, `json`, `jsonl`, `npy`) are
|
||||||
|
our own numbers rather than game content, and stay tracked — they are what most
|
||||||
|
claims here actually rest on.
|
||||||
|
|
||||||
|
`tools/re/check-capture-citations` enforces both halves: a cited capture must be
|
||||||
|
**present**, and a game asset must **not be tracked**. ⚠️ A fresh clone has no
|
||||||
|
captures, so its citations will not resolve until the captures are copied in;
|
||||||
|
that is expected, and the checker is a local gate rather than a CI one.
|
||||||
|
|
||||||
### Clean-room firewall
|
### Clean-room firewall
|
||||||
|
|
||||||
- ✅ Allowed: behaviour descriptions, field offsets/types, formulas, state machines,
|
- ✅ Allowed: behaviour descriptions, field offsets/types, formulas, state machines,
|
||||||
|
|||||||
13
docs/re/captures/.gitignore
vendored
Normal file
13
docs/re/captures/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Game-derived assets stay on disk and ship nowhere (issue #49).
|
||||||
|
#
|
||||||
|
# The pages' relative links still resolve on a machine that has the captures,
|
||||||
|
# so the evidence stays followable where it exists — it is simply not committed.
|
||||||
|
# `tools/re/check-capture-citations` enforces both halves: a cited capture must
|
||||||
|
# be PRESENT here, and an asset must NOT be tracked.
|
||||||
|
#
|
||||||
|
# Derived measurements (csv, tsv, txt, log, json, jsonl, npy) are our own
|
||||||
|
# numbers rather than game content, and remain tracked.
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
*.bin
|
||||||
3
tools/re-capture/.gitignore
vendored
Normal file
3
tools/re-capture/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# Game-derived digit templates for ob_read.py — pixels from the game (issue #49).
|
||||||
|
ob_digits.png
|
||||||
@@ -10,10 +10,17 @@
|
|||||||
largest thing in the repository, and the one place a file can be added, never
|
largest thing in the repository, and the one place a file can be added, never
|
||||||
cited, and never noticed.
|
cited, and never noticed.
|
||||||
|
|
||||||
Two failures, which are opposites and must not be conflated:
|
⚠️ 2026-09-19, issue #49: **game assets are no longer committed.** Screenshots
|
||||||
|
and savegame blobs live in the working tree and are gitignored, so the pages'
|
||||||
|
relative links still resolve on a machine that has them while nothing ships.
|
||||||
|
That inverts half of this check — "is it committed?" became "is it PRESENT?",
|
||||||
|
and a NEW failure appeared: an asset that IS tracked. Both are below.
|
||||||
|
|
||||||
* a page cites a capture that **is not committed** — a reader following it
|
Three failures, which are opposites and must not be conflated:
|
||||||
|
|
||||||
|
* a page cites a capture that **is not present** — a reader following it
|
||||||
gets nothing. That is an error, exactly as in `check-citations`.
|
gets nothing. That is an error, exactly as in `check-citations`.
|
||||||
|
* a game asset that **is tracked by git** — issue #49 says it must not be.
|
||||||
* a capture that **no page cites** — not an error. It may be evidence a page
|
* a capture that **no page cites** — not an error. It may be evidence a page
|
||||||
should have cited, and deleting on that basis would silently ratify the
|
should have cited, and deleting on that basis would silently ratify the
|
||||||
omission. Reported, counted, never failed on.
|
omission. Reported, counted, never failed on.
|
||||||
@@ -65,7 +72,16 @@ def committed() -> tuple[set[str], set[str]]:
|
|||||||
proposed. `tools/port/check-citations` uses the working tree for exactly
|
proposed. `tools/port/check-citations` uses the working tree for exactly
|
||||||
this reason; so does this.
|
this reason; so does this.
|
||||||
"""
|
"""
|
||||||
files = {l for l in git("ls-files", ROOT).splitlines() if l}
|
# 🔴 NOT `git ls-files`: since #49 the assets are deliberately untracked, so
|
||||||
|
# the index no longer knows they exist. Presence is a question about the
|
||||||
|
# working tree, and asking git would report every screenshot as missing and
|
||||||
|
# fail on all 203 citations.
|
||||||
|
files = set()
|
||||||
|
for dirpath, _dirnames, filenames in os.walk(ROOT):
|
||||||
|
for fn in filenames:
|
||||||
|
if fn == ".gitignore":
|
||||||
|
continue
|
||||||
|
files.add(os.path.join(dirpath, fn).replace(os.sep, "/"))
|
||||||
dirs = set()
|
dirs = set()
|
||||||
for f in files:
|
for f in files:
|
||||||
parts = f.split("/")
|
parts = f.split("/")
|
||||||
@@ -74,6 +90,24 @@ def committed() -> tuple[set[str], set[str]]:
|
|||||||
return files, dirs
|
return files, dirs
|
||||||
|
|
||||||
|
|
||||||
|
ASSET_SUFFIXES = (".png", ".jpg", ".jpeg", ".bin")
|
||||||
|
|
||||||
|
|
||||||
|
def tracked_assets() -> list[str]:
|
||||||
|
"""Game assets that are committed — forbidden since issue #49.
|
||||||
|
|
||||||
|
The repo carries code, tooling and docs. A screenshot that sneaks back in
|
||||||
|
is invisible in review (a binary shows as "Bin 0 -> 1234567 bytes") and is
|
||||||
|
permanent once merged, because removing it later needs a history rewrite.
|
||||||
|
So this is the half of the check that has to be loud.
|
||||||
|
"""
|
||||||
|
out = []
|
||||||
|
for l in git("ls-files").splitlines():
|
||||||
|
if l.lower().endswith(ASSET_SUFFIXES):
|
||||||
|
out.append(l)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def cited() -> set[str]:
|
def cited() -> set[str]:
|
||||||
raw = git("grep", "-rhoE", CITE_ERE, "--", *SEARCH, SELF).splitlines()
|
raw = git("grep", "-rhoE", CITE_ERE, "--", *SEARCH, SELF).splitlines()
|
||||||
out = set()
|
out = set()
|
||||||
@@ -141,7 +175,7 @@ def selftest() -> int:
|
|||||||
real_dir = next(iter(dirs), None)
|
real_dir = next(iter(dirs), None)
|
||||||
real_file = next(iter(files), None)
|
real_file = next(iter(files), None)
|
||||||
if not real_dir or not real_file:
|
if not real_dir or not real_file:
|
||||||
print("selftest: 🔴 no captures committed — nothing to test against")
|
print("selftest: 🔴 no captures present — nothing to test against")
|
||||||
return 2
|
return 2
|
||||||
cases = [
|
cases = [
|
||||||
("planted dangling caught", "docs/re/captures/no-such-file-anywhere.png", False),
|
("planted dangling caught", "docs/re/captures/no-such-file-anywhere.png", False),
|
||||||
@@ -168,7 +202,15 @@ def selftest() -> int:
|
|||||||
print(" %-34s %s"
|
print(" %-34s %s"
|
||||||
% ("own fixtures not counted", "🔴 FAILED" if fixture_counted else "ok"))
|
% ("own fixtures not counted", "🔴 FAILED" if fixture_counted else "ok"))
|
||||||
|
|
||||||
ok = gathering_ok and not fixture_counted
|
# 🔴 THE #49 RULE NEEDS ITS OWN TICK. Presence now comes from the working
|
||||||
|
# tree, so a checkout with the captures present looks identical whether or
|
||||||
|
# not they are tracked — only this assertion can tell the difference.
|
||||||
|
stowaways = tracked_assets()
|
||||||
|
print(" %-34s %s%s"
|
||||||
|
% ("no game asset tracked", "ok" if not stowaways else "🔴 FAILED",
|
||||||
|
"" if not stowaways else " (%d tracked)" % len(stowaways)))
|
||||||
|
|
||||||
|
ok = gathering_ok and not fixture_counted and not stowaways
|
||||||
for name, path, want in cases:
|
for name, path, want in cases:
|
||||||
got = resolves(path.rstrip(".,;:)`"), files, dirs)
|
got = resolves(path.rstrip(".,;:)`"), files, dirs)
|
||||||
mark = "ok" if got == want else "🔴 FAILED"
|
mark = "ok" if got == want else "🔴 FAILED"
|
||||||
@@ -188,19 +230,33 @@ def main() -> int:
|
|||||||
print(f)
|
print(f)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
print("captures committed : %d" % len(files))
|
stowaways = tracked_assets()
|
||||||
|
print("captures present on disk : %d" % len(files))
|
||||||
print(" cited by a page or a tool : %d" % (len(files) - len(orphans)))
|
print(" cited by a page or a tool : %d" % (len(files) - len(orphans)))
|
||||||
print(" cited by nothing : %d (reported, not failed —" % len(orphans))
|
print(" cited by nothing : %d (reported, not failed —" % len(orphans))
|
||||||
print(" an orphan may be evidence a page owes)")
|
print(" an orphan may be evidence a page owes)")
|
||||||
|
rc = 0
|
||||||
if dangling:
|
if dangling:
|
||||||
print(" 🔴 cited but NOT committed: %d" % len(dangling))
|
print(" 🔴 cited but NOT present : %d" % len(dangling))
|
||||||
for d in dangling:
|
for d in dangling:
|
||||||
print(" %s" % d)
|
print(" %s" % d)
|
||||||
print("\n🔴 a reader following those gets nothing. Commit the capture, fix the")
|
print("\n🔴 a reader following those gets nothing. Restore the capture, fix the")
|
||||||
print(" path, or drop the citation.")
|
print(" path, or drop the citation.")
|
||||||
return 1
|
rc = 1
|
||||||
print(" 🔴 cited but NOT committed: 0")
|
else:
|
||||||
return 0
|
print(" 🔴 cited but NOT present : 0")
|
||||||
|
|
||||||
|
if stowaways:
|
||||||
|
print(" 🔴 game assets TRACKED : %d (issue #49 — code, tooling, docs only)" % len(stowaways))
|
||||||
|
for a in stowaways[:10]:
|
||||||
|
print(" %s" % a)
|
||||||
|
if len(stowaways) > 10:
|
||||||
|
print(" … and %d more" % (len(stowaways) - 10))
|
||||||
|
print("\n🔴 run `git rm --cached` on those; they stay on disk and stay ignored.")
|
||||||
|
rc = 1
|
||||||
|
else:
|
||||||
|
print(" 🔴 game assets TRACKED : 0")
|
||||||
|
return rc
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user