R-758: mem_limit is the sum of the compose limits — eight figures corrected, gate mem-limit-sum (--fast, stdlib) with decoys
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:
@@ -57,6 +57,7 @@ COVERS = {
|
||||
"probe-measured": "the measurement written in the TAGLINE or another comment block, not directly above setup_done_probe:; a date with no before/after; before/after with no date; 'read upstream' instead of 'measured' - vs a genuine measured comment (R-715)",
|
||||
"family-gate": "family_gate written only in a COMMENT (not gated, no min_controller owed); min_controller only in a comment; a golden DIRECTORY named 0.287.0 with no bake log (the mkdir shape, R-410); a sibling with no golden (stated NOT CHECKED, never a pass of rule 3) - vs the facts: an unanchorable exception (regex, '/', '..'), an exception list with no gate, min_controller below 0.287.0, the newest baked golden below 0.287.0; and a genuine family app passes (decisions 63/64, finding F1)",
|
||||
"onboarding": "a NEW template with no record; a record missing an id, or carrying it only inside an HTML comment; a `done` whose path does not exist, is an EMPTY directory (the mkdir shape, R-410) or names an absent sibling-repo file; an `n/a` with an empty or two-word reason; an `open` row; `opened:` backdated before the checklist; the template a new app copies lacking a new id - vs a complete record, an id added after `opened:`, and an exempt app's record with open rows (NEW-APP-CHECKLIST.md)",
|
||||
"mem-limit-sum": "the right figure only in a COMMENT (the compose header's 'mem_limit: 640M', the .felhom.yml arithmetic) while the field is wrong; a `memory:` under reservations: (not a limit) making the sum come out right; a `mem_limit:` outside resources:; a steps/ file with the old figure (not judged) - vs the facts: a field under the sum, a service with no limit, an unreadable size (R-758)",
|
||||
"copy-i18n": "Hungarian edited in a COMMENT/README/display_name (label, not copy) vs a real frozen string changed; an English block that is not English, is not matched to a Hungarian twin, or rewrites a credential (R-560). A retrieval promise REGISTERED in ALLOWLIST_EN passes only for its own app+path+sentence with a real reason; an entry for another app, a rewritten sentence, a stale entry or a two-word reason convicts (R-594). Also the DEGRADED mode CI actually runs — PyYAML shadowed out, freeze only (R-595)",
|
||||
}
|
||||
|
||||
@@ -686,6 +687,66 @@ def family_gate_cases():
|
||||
shutil.rmtree(ws, ignore_errors=True)
|
||||
|
||||
|
||||
def mem_sum_cases():
|
||||
"""check-mem-limit-sum.py reads FILES: templates/*/docker-compose.yml + .felhom.yml, via --root (R-758)."""
|
||||
global ran
|
||||
import tempfile
|
||||
ws = tempfile.mkdtemp(prefix="catalog-memsum-")
|
||||
try:
|
||||
COMPOSE = (
|
||||
"# demo - header\n# RAM: ~100M (mem_limit: 640M)\n"
|
||||
"services:\n"
|
||||
" demo:\n image: demo/demo:1.0\n container_name: demo\n environment:\n - X=1\n"
|
||||
" deploy:\n resources:\n limits:\n memory: 384M\n"
|
||||
" demo-db:\n image: postgres:16-alpine\n command: |\n memory: 9999M\n"
|
||||
" deploy:\n resources:\n limits:\n memory: 256M # the DB\n"
|
||||
"volumes:\n demo_data:\n")
|
||||
META = ('display_name: "Demo"\n# mem_limit: "999M" is not this line\n'
|
||||
'resources:\n mem_request: "100M"\n mem_limit: "640M" # 384+256\n pi_compatible: true\n')
|
||||
|
||||
def run(name, compose, meta, expect_rc, must=(), extra=None):
|
||||
global ran
|
||||
cat = os.path.join(ws, "cat")
|
||||
shutil.rmtree(cat, ignore_errors=True)
|
||||
d = os.path.join(cat, "templates", "demo")
|
||||
os.makedirs(d)
|
||||
io.open(os.path.join(d, "docker-compose.yml"), "w", encoding="utf-8").write(compose)
|
||||
io.open(os.path.join(d, ".felhom.yml"), "w", encoding="utf-8").write(meta)
|
||||
if extra:
|
||||
extra(d)
|
||||
r = sh([sys.executable, os.path.join(ROOT, "scripts", "check-mem-limit-sum.py"), "--root=" + cat], ROOT)
|
||||
out = r.stdout + r.stderr
|
||||
ran += 1
|
||||
ok = r.returncode == expect_rc and all(m in out for m in must)
|
||||
print(" %s %-70s rc=%d (expected %d)" % ("ok" if ok else "XX", name, r.returncode, expect_rc))
|
||||
if not ok:
|
||||
fails.append("%s: rc=%d expected %d; missing %s\n%s" % (
|
||||
name, r.returncode, expect_rc, [m for m in must if m not in out], out[-400:]))
|
||||
|
||||
def steps(d):
|
||||
os.makedirs(os.path.join(d, "steps"))
|
||||
io.open(os.path.join(d, "steps", "abc.felhom.yml"), "w").write('resources:\n mem_limit: "384M"\n')
|
||||
|
||||
print("\n-- mem-limit-sum: genuine and decoys")
|
||||
run("GENUINE: 384+256 = 640M, the field says 640M", COMPOSE, META, 0, ("mem-limit-sum gate OK",))
|
||||
run("GENUINE: 1G is 1024M (1G + 256M = 1280M)", COMPOSE.replace("memory: 384M", "memory: 1G"),
|
||||
META.replace('"640M"', '"1280M"'), 0, ("gate OK",))
|
||||
run("DECOY: a steps/ file with an old figure is not judged", COMPOSE, META, 0, ("gate OK",), extra=steps)
|
||||
print("-- mem-limit-sum: the facts (each MUST be refused)")
|
||||
run("FACT: the field under the sum; the right figure only in comments", COMPOSE,
|
||||
META.replace('mem_limit: "640M" # 384+256', 'mem_limit: "384M" # 384+256=640M'), 1, ("not the sum", "384+256=640M"))
|
||||
run("FACT: reservations: memory makes nothing a limit (service w/o limit)",
|
||||
COMPOSE.replace(" limits:\n memory: 256M", " reservations:\n memory: 256M"),
|
||||
META.replace('"640M"', '"384M"'), 1, ("NO deploy.resources.limits.memory", "demo-db"))
|
||||
run("FACT: mem_limit only OUTSIDE resources: (top level) is no declaration", COMPOSE,
|
||||
META.replace(' mem_limit: "640M" # 384+256\n', '').replace('display_name: "Demo"\n', 'display_name: "Demo"\nmem_limit: "640M"\n'),
|
||||
1, ("declares no resources.mem_limit",))
|
||||
run("FACT: an unreadable size is INCONCLUSIVE, never a pass", COMPOSE.replace("memory: 384M", "memory: lots"), META, 2,
|
||||
("INCONCLUSIVE",))
|
||||
finally:
|
||||
shutil.rmtree(ws, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
gate = os.path.join(ROOT, "scripts", "check-engine-major.py")
|
||||
if not os.path.isfile(gate):
|
||||
@@ -1197,6 +1258,7 @@ i18n:
|
||||
|
||||
onboarding_cases()
|
||||
family_gate_cases()
|
||||
mem_sum_cases()
|
||||
|
||||
if fails:
|
||||
print()
|
||||
|
||||
Reference in New Issue
Block a user