hub v0.137.0 source + burn-down round 2 in felhom.eu: R-277 R-581 R-600 R-544 R-855 R-134 R-92 R-292 R-599 R-725 R-728 (hub), R-819 R-857 R-555 R-364 R-587 (gates/tools), R-571 R-129 R-124-runbook (docs); 28 rows closed incl. catalog + agent v0.147.0 rows, R-350 merged into R-132, R-888 opened, R-887 mechanism (249 -> 222)
gates / gates (push) Successful in 2m3s

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 19:43:37 +02:00
parent e0d884565f
commit 557629d2bf
65 changed files with 2680 additions and 122 deletions
+70 -1
View File
@@ -280,6 +280,30 @@ ALLOWLIST = {
"and a fact displayed with nothing to compare it to is decoration."),
(_CH, "backup.last_db_dump"): _R264,
(_CH, "backup.last_integrity_check"): _R264,
# ---- R-555 (2026-10-05): surfaced when comments stopped counting as receivers ----
#
# Each of these passed for months because its NAME occurred hub-side only in a comment. No hub struct
# decodes any of them. Listed so the drop is visible; none is known to be needed by a hub surface.
# **Delete an entry when a hub struct starts decoding the field.**
(_AH, "audit_tail"): (
"R-555: ignored on purpose — hub/internal/api/handler.go's hostReportPayload comment names it "
"among the fields the hub does not decode (backups/restore_tests/pbs_snapshots/audit_tail)."),
(_AH, "guests.spec"): (
"R-555: the parent of guests.spec.disk_bytes / memory_bytes above — sizing is hub-owned intent, "
"not mirrored reality, so the object is not decoded either."),
(_CH, "backup.integrity_ok"): (
"R-555 / R-331: no producer since disk-tier restic moved to the agent (slice 8C); the Backup card "
"dropped it rather than re-sourcing it (hub/internal/web/backup_card.go)."),
(_CH, "backup.repo_size_mb"): (
"R-555 / R-331: no producer since slice 8C; the Backup card reads offsite.repo_size_bytes "
"instead (hub/internal/web/backup_card.go)."),
(_CH, "stacks.deployed"): (
"R-555: surfaced 2026-10-05 — the hub decodes no deployed-app list from the report; whether a hub "
"surface needs it is not decided. Filed as R-888 for the operator to decide."),
(_CH, "storage.decommissioned"): (
"R-555: surfaced 2026-10-05 — the hub decodes no per-drive decommissioned marker from the report; "
"whether a hub surface needs it is not decided. Filed as R-888 for the operator to decide."),
}
# A node whose IMMEDIATE CHILDREN are still checked but whose DEEPER descendants are not, because the
@@ -446,6 +470,48 @@ def walk(by_dir, by_name, start_dir, start_type, seen=None, prefix=""):
TOKEN_RE = re.compile(r"[A-Za-z0-9_]+")
TEMPLATE_COMMENT_RE = re.compile(r"\{\{-?\s*/\*.*?\*/\s*-?\}\}", re.S)
def strip_go_comments(src):
"""Go source with every // and /* */ comment removed; string, raw-string and rune literals kept.
R-555: a field whose name appeared hub-side only in a COMMENT counted as received — `language`
passed because configs.go said "this page's existing language". A comment is prose, not a decoder.
A small lexer rather than a regex, because a regex cannot tell `//` in "http://…" or a backtick
struct tag from a comment, and the struct tags are exactly what this gate reads.
"""
out, i, n = [], 0, len(src)
while i < n:
c = src[i]
if c == "/" and i + 1 < n and src[i + 1] == "/":
j = src.find("\n", i)
i = n if j < 0 else j # keep the newline
continue
if c == "/" and i + 1 < n and src[i + 1] == "*":
j = src.find("*/", i + 2)
i = n if j < 0 else j + 2
out.append(" ")
continue
if c in "\"'`":
j = i + 1
while j < n and src[j] != c:
if c != "`" and src[j] == "\\":
j += 1
elif c != "`" and src[j] == "\n":
break # an unterminated literal ends at the line
j += 1
out.append(src[i:j + 1])
i = j + 1
continue
out.append(c)
i += 1
return "".join(out)
def strip_template_comments(src):
"""An HTML template with every {{/* … */}} action removed (R-555)."""
return TEMPLATE_COMMENT_RE.sub(" ", src)
def receiver_tokens(repo_root):
@@ -479,7 +545,10 @@ def receiver_tokens(repo_root):
p = os.path.join(dp, f)
try:
with open(p, encoding="utf-8", errors="replace") as fh:
toks.update(TOKEN_RE.findall(fh.read()))
src = fh.read()
# R-555: comments are stripped first — a name in prose is not a decoder.
src = strip_go_comments(src) if f.endswith(".go") else strip_template_comments(src)
toks.update(TOKEN_RE.findall(src))
except OSError as e:
# never swallowed: an unreadable source file makes the answer unknown, not "absent"
die("wire-contract gate INCONCLUSIVE: cannot read %s: %s" % (p, e))