3c49dc8ea4
gates / gates (push) Successful in 12s
R-399: monitoring.integrity.read_data_subset defaults to 100%. A pack damaged without changing its size made plain `restic check` report "no errors were found" on demo-hp 2026-08-30; every read-data form caught it. Cost on that 134 MB store: 35.0s structure vs 39.2s at 100%. "off" (any case) is the off token; empty means not-configured, therefore the default; a malformed value falls back to the DEFAULT, never to structure. A completed check over 5 minutes logs a WARN naming the duration, the depth and R-401 — operator log only, no hub event, no depth change. The depth is now recorded with the verdict (LastIntegrityDepth; empty = NOT RECORDED, never "structure"). R-400: 24 debug-page references, 17 dispatched, 7 dead — three of which fetched on page LOAD, so those panels were permanently blank. backup/crossdrive implemented; backup/infra, hub/infra-push, dr/infra-status, storage/watchdog-status and both storage/simulate-* deleted with their panels and JavaScript. scripts/debug_route_gate.py fails in both directions and is registered after the seven were resolved. 18 referenced, 18 dispatched, none orphaned. Corrections: the dead-field warning in report/types.go said the controller runs no integrity check and the notifiers are called from nowhere — both false since v0.227.0. controller.yaml.example gains its missing integrity: block. integrityCheckTimeout's "ships OFF" comment rewritten.
66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Debug-route gate (R-400) — every control on the debug page must resolve to a handler, and every
|
|
handler must be reachable from the page.
|
|
|
|
WHY IT EXISTS. On 2026-08-31 the shipped debug page referenced 24 `/api/debug/...` addresses and the
|
|
dispatcher answered 17. Seven controls did nothing, and three of those seven were not buttons at all:
|
|
they fetch on page LOAD, so whole panels had been permanently empty and nobody had to click anything
|
|
to be misled. This is the page an operator opens when something is already wrong.
|
|
|
|
BOTH DIRECTIONS FAIL. A reference with no case is a dead control. A case with no reference is a
|
|
handler nothing reaches — the same defect mirrored, and the shape this project has now shipped eight
|
|
times. Neither is a warning here.
|
|
|
|
DELIBERATELY TEN LINES OF LOGIC. Two lists and a difference. Its value is that it cannot rot: a
|
|
cleverer gate that understood routing would need maintaining, and an unmaintained gate is how the
|
|
class hides in the first place. The dispatcher's EXACT-match switch with a NotFound default is what
|
|
made the original defect visible, and this gate assumes exactly that shape — do not make either clever.
|
|
|
|
Run from controller/: python3 scripts/debug_route_gate.py
|
|
"""
|
|
import io
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
TEMPLATE = os.path.join("internal", "web", "templates", "debug.html")
|
|
DISPATCH = os.path.join("internal", "web", "handler_debug.go")
|
|
|
|
REF_RE = re.compile(r"/api/debug/([A-Za-z0-9/_-]+)")
|
|
CASE_RE = re.compile(r'subpath\s*==\s*"([A-Za-z0-9/_-]+)"')
|
|
|
|
|
|
def read(path):
|
|
if not os.path.exists(path):
|
|
print("DEBUG ROUTE GATE INCONCLUSIVE: %s not found (run from controller/)" % path)
|
|
sys.exit(2)
|
|
return io.open(path, encoding="utf-8").read()
|
|
|
|
|
|
def main():
|
|
# Sets, not lists: the same address referenced by two controls is satisfied by one case (§8).
|
|
refs = set(REF_RE.findall(read(TEMPLATE)))
|
|
cases = set(CASE_RE.findall(read(DISPATCH)))
|
|
|
|
dead = sorted(refs - cases)
|
|
unreached = sorted(cases - refs)
|
|
|
|
for name in dead:
|
|
print("DEAD CONTROL %s references /api/debug/%s and %s has no case for it"
|
|
% (TEMPLATE, name, DISPATCH))
|
|
for name in unreached:
|
|
print("UNREACHED %s dispatches %r and %s never references it"
|
|
% (DISPATCH, name, TEMPLATE))
|
|
|
|
if dead or unreached:
|
|
print("DEBUG ROUTE GATE FAILED: %d dead control(s), %d unreached handler(s)"
|
|
% (len(dead), len(unreached)))
|
|
sys.exit(1)
|
|
print("debug route gate OK - %d referenced address(es), all dispatched, none orphaned"
|
|
% len(refs))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|