burn-down Part B: 14 small rows fixed and closed across four repos (306 -> 292); no :latest in the hub build; gate list pinned; closed-id duplicates refused; R-262 subset pinned
gates / gates (push) Failing after 1m40s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-05 17:10:38 +02:00
parent f5a0aeb0b8
commit b26d292a64
19 changed files with 220 additions and 35 deletions
+8
View File
@@ -1,3 +1,11 @@
## build + gates — no `:latest`; the gate list pinned; duplicate closed ids refused (2026-10-05, burn-down)
- **R-345:** `build-hub.sh` no longer tags or pushes `felhom-hub:latest` (`--push`, `--multiarch`, local); nothing
pulls it. `test_no_latest_push.py` walks `hub/` and `scripts/` build files (red-proofs: the old Makefile, the old
build script).
- **R-418:** `repo_gates.py`'s docstring lists all 17 gates; `test_repo_gates_docstring.py` keeps list == `GATES`.
- **R-416:** `closed_register_gate.py` RULE 4 — an id twice in `CLOSED-ITEMS.md`; decoy in `test_gate_decoys.py`.
## gates — `script-tests`: every Python test suite under scripts/ runs on every push (R-885) (2026-10-05)
- `scripts/script_tests_gate.py` (registered in `repo_gates.py`, fast): walks `scripts/` for `test_*.py` and runs each;
+1 -4
View File
@@ -147,12 +147,11 @@ case "${ACTION}" in
info "Building for current platform + pushing..."
docker build "${BUILD_ARGS[@]}" \
-t "${IMAGE}:${VERSION}" \
-t "${IMAGE}:latest" \
.
info "Pushing..."
docker push "${IMAGE}:${VERSION}"
docker push "${IMAGE}:latest"
# never :latest (R-345): the manifest pins a version, and nothing pulls hub:latest (checked 2026-10-05)
;;
--multiarch)
@@ -169,7 +168,6 @@ case "${ACTION}" in
docker buildx build "${BUILD_ARGS[@]}" \
--platform linux/amd64,linux/arm64 \
-t "${IMAGE}:${VERSION}" \
-t "${IMAGE}:latest" \
--push \
.
;;
@@ -178,7 +176,6 @@ case "${ACTION}" in
info "Building for current platform (local only)..."
docker build "${BUILD_ARGS[@]}" \
-t "${IMAGE}:${VERSION}" \
-t "${IMAGE}:latest" \
.
;;
esac
+13 -3
View File
@@ -50,9 +50,9 @@ WHAT THIS GATE CANNOT SEE — the residual holes, named rather than implied:
printed as a WARNING.
3. **A closed-sounding verdict that is not true escapes.** `PARTLY CLOSED` leads with no open word.
This gate checks where a row FILED, never whether the verdict is honest.
4. **A duplicate id WITHIN one register escapes.** `OPEN-ITEMS.md` carries two unrelated findings
both numbered R-133 (filed as R-406). Adding that rule would fail the gate on a pre-existing
defect, and a registered-but-failing gate refuses every push, so it was deliberately left out.
4. ~~A duplicate id WITHIN one register escapes.~~ **Closed 2026-10-05 (R-416):** in `OPEN-ITEMS.md`
`register_shape_gate.py` RULE 3 refuses it; in `CLOSED-ITEMS.md` this gate's RULE 4 does (no duplicates
existed when it was added, so it was registered green).
5. Nothing here reads audits, spikes or inventories. A finding that never reaches either register
is invisible to this gate, as it is to `one_register_gate.py`.
@@ -153,6 +153,16 @@ def main():
for rid in sorted(closed_ids & set(open_ids), key=lambda r: (int(re.sub(r"\D", "", r)), r)):
convicted.append((open_ids[rid], rid, "", "has a row in BOTH registers"))
# RULE 4 — a duplicate id WITHIN CLOSED-ITEMS.md (2026-10-05, R-416). The open register's duplicates are
# register_shape_gate.py's RULE 3; this file had no such rule, so two closed rows under one id (the R-133/R-406
# shape) would make `git show` of "the row that closed R-n" ambiguous. Suffixed ids (R-88a, R-88b) are distinct.
first_seen = {}
for n, rid, _, _ in rows(CLOSED):
if rid in first_seen:
convicted.append((n, rid, "", "CLOSED-ITEMS.md has this id twice (first at line %d)" % first_seen[rid]))
else:
first_seen[rid] = n
# RULE 3 — a finished row left in the OPEN register (2026-10-03)
finished_in_open = []
for n, rid, columns, cells, _ in register_table.rows(OPEN):
+2
View File
@@ -24,6 +24,8 @@ Gates, in order (all must pass; **non-zero exit on any failure**):
13. observations a REPORT.md observation with no register row behind it (R-389)
14. register-shape a register row whose state cell was eaten, a duplicated id, or a blank line
splitting the table — the register mis-stating how many findings exist (R-627)
15. script-tests every Python test suite under scripts/ (found by a walk), by exit code (R-885)
16. decoy-coverage every registered gate in all four repos has a decoy, or a named exemption (R-421)
**THE `GATES` TABLE BELOW IS THE LIST; THIS IS A POINTER TO IT.** It drifted once already —
it read eleven while thirteen were registered, from 2026-08-24 until 2026-09-01, so
+15
View File
@@ -25,6 +25,7 @@ Run from the repo root: python3 scripts/test_gate_decoys.py
Exit 0 all decoys rejected · 1 a decoy passed (a live hole).
"""
import io
import re
import json
import os
import shutil
@@ -217,6 +218,20 @@ decoy("closed-register/unreadable-row", "closed_register_gate.py",
append_to(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"),
u"\n| **R-905** | A row with no state cell at all. |\n"))
# --- closed-register RULE 4 (2026-10-05, R-416): the same id twice in CLOSED-ITEMS.md ---------------
# The id is read from the file's FIRST closed row at run time, so the decoy duplicates a real id rather than an
# invented one that could never collide.
def _first_closed_id():
for line in io.open(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"), encoding="utf-8"):
m = re.match(r"^\| \*\*(R-\d+[a-z]?)\*\* \|", line)
if m:
return m.group(1)
decoy("closed-register/duplicate-closed-id", "closed_register_gate.py",
append_to(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"),
u"\n| **%s** | The same id again. | CLOSED 2026-10-05 | none |\n" % _first_closed_id()))
# --- closed-register RULE 3 (2026-10-03): a FINISHED row left in the OPEN register ---------------
# On 2026-10-03 the open register held 113 rows whose leading verdict was finished — a quarter of the
# file. The decoy is that exact shape: a row a session closed in place and never moved. The genuine
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""R-345: nothing in this repo's build tooling tags or pushes an image as `:latest`.
Scans hub/Makefile, scripts/build-hub.sh and every Dockerfile/Makefile/*.sh under hub/ and scripts/ (a walk).
Run: python3 scripts/test_no_latest_push.py"""
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BAD = re.compile(r"^(?![ \t]*#)[^\n]*?(\bdocker\s+(tag|push)\b[^#\n]*:latest\b|-t\s+\S*:latest\b)", re.M)
hits, scanned = [], 0
for top in ("hub", "scripts"):
for dp, dns, fns in os.walk(os.path.join(ROOT, top)):
dns[:] = [d for d in dns if d not in (".git", "__pycache__", "node_modules")]
for f in fns:
if f in ("Makefile", "Dockerfile") or f.endswith(".sh"):
p = os.path.join(dp, f)
scanned += 1
for m in BAD.finditer(open(p, encoding="utf-8", errors="replace").read()):
hits.append("%s: %s" % (os.path.relpath(p, ROOT), m.group(0).strip()))
if scanned < 5:
print("FAIL: scanned only %d build files — the scope is wrong" % scanned)
sys.exit(1)
if hits:
print("FAIL: a :latest tag/push in build tooling (R-345):\n " + "\n ".join(hits))
sys.exit(1)
print("OK: %d build files, no docker tag/push of :latest" % scanned)
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""R-418: repo_gates.py's docstring list of gates names exactly the gates in GATES, in the same order.
It drifted twice (eleven listed while thirteen ran, 2026-08-24..09-01; then fifteen while seventeen ran). Run:
python3 scripts/test_repo_gates_docstring.py"""
import os
import re
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import repo_gates # noqa: E402
listed = re.findall(r"^ {1,3}\d+b?\. +([a-z][a-z0-9-]+) ", repo_gates.__doc__, re.M)
registered = [g[0] for g in repo_gates.GATES]
if listed != registered:
print("FAIL: the docstring lists %d gate(s), GATES registers %d" % (len(listed), len(registered)))
print(" only listed: %s" % sorted(set(listed) - set(registered)))
print(" only registered: %s" % sorted(set(registered) - set(listed)))
if set(listed) == set(registered):
print(" (same set, different order)")
sys.exit(1)
print("OK: the docstring lists the %d registered gates, in order" % len(registered))