burn-down round 2: R-593 papra field copy, R-760 vikunja healthcheck reason, R-594 English allow-list, R-605 refusal exit 3, R-781 onboarding decoy clone, R-806 scheme; stale runner test fixed
gates / gates (push) Successful in 4s

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:15:02 +02:00
parent 29ac711d26
commit 4828dc754d
16 changed files with 536 additions and 50 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""R-760: every catalog service that has NO compose `healthcheck:` says why, in a comment inside its own block.
A missing healthcheck is sometimes right (the image has no shell; the image brings its own — adventurelog-frontend,
R-655), but a silent one cannot be told apart from a forgotten one: vikunja carried none for months with nothing
saying whether that was a choice. This test reads the committed composes as TEXT (catalog CI has no PyYAML) and
fails for a service block with neither a `healthcheck:` key nor a `# No healthcheck` comment.
COMPANION RED-PROOF: with the R-760 comment removed from templates/vikunja/docker-compose.yml this test fails and
names `vikunja/vikunja`."""
import glob
import io
import os
import re
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
EXPLAINED = re.compile(r"^\s*#\s*No healthcheck", re.I)
def service_blocks(text):
"""Yield (service name, [lines]) for each service under the top-level `services:` key."""
in_services = False
name, lines = None, []
for line in text.splitlines():
if re.match(r"^\S", line): # a top-level key (or a top-level comment) ends the services block
if name:
yield name, lines
name, lines = None, []
in_services = line.startswith("services:")
continue
if not in_services:
continue
m = re.match(r"^ ([A-Za-z0-9_.-]+):\s*$", line)
if m:
if name:
yield name, lines
name, lines = m.group(1), []
elif name:
lines.append(line)
if name:
yield name, lines
def unexplained(text):
out = []
for svc, lines in service_blocks(text):
if any(re.match(r"^ healthcheck:", l) for l in lines):
continue
if any(EXPLAINED.match(l) for l in lines):
continue
out.append(svc)
return out
class HealthcheckExplained(unittest.TestCase):
def test_parser_sees_a_missing_and_an_explained_service(self):
# decoys for the parser itself: a silent service convicts, an explained one and a checked one pass
text = ("services:\n a:\n image: x:1\n b:\n image: y:1\n # No healthcheck: no shell\n"
" c:\n image: z:1\n healthcheck:\n test: [\"CMD\", \"true\"]\nvolumes:\n d:\n")
self.assertEqual(unexplained(text), ["a"])
def test_every_catalog_service_has_a_healthcheck_or_says_why(self):
paths = sorted(glob.glob(os.path.join(ROOT, "templates", "*", "docker-compose.yml")))
self.assertGreater(len(paths), 40, "the template glob found too few composes — the test would pass empty")
bad = []
for p in paths:
app = os.path.basename(os.path.dirname(p))
with io.open(p, encoding="utf-8") as f:
text = f.read()
for svc in unexplained(text):
bad.append("%s/%s" % (app, svc))
self.assertEqual(bad, [], "service(s) with no compose healthcheck and no '# No healthcheck' comment "
"saying why: %s" % bad)
if __name__ == "__main__":
unittest.main()