970616b72b
gates / gates (push) Failing after 11m41s
The catalogue side is app-catalog-felhom.eu b7f0f7c. Here: the evidence, the website, the register and the operator's view. Website - Two cards in the Otthon & Eletmod / Home & Lifestyle section of BOTH apps pages, with assets: grocy-logo.svg is grocy's own icon with its single fill made white like the other logos, lubelogger-logo.png is the app's own icon with its dark background dropped and the mark made white (the rule SparkyFitness's PNG follows), and six screenshots of each app's own UI with a household's own data, taken headless on the bench from the published template. - The app count moved 56 -> 58 in 15 places per language set, both languages, and the open-source tile 49 -> 51. Checked by asking the same patterns for the new number afterwards. marketing/facebook/COPY.md still says 56 and is NOT changed: the post it carries is already scheduled. Evidence - documentation/audits/new-apps-2026-10-10/ — FIT.md (checklist group 0 for all three, with the Hungarian-UI column), the bench and box transcripts, the memory samples, the screenshots and the gate runs. Register: 137 -> 139 rows, 2 opened, 0 closed. - R-926 after a remove-keeping-backups and restore, nobody has checked what the app page shows for an after_install app's generated password. Measured here: LubeLogger is safe (its login is derived from the environment at every start, the new password signs in, the data is back); grocy's install password still signs in from the restored database but the deployed environment no longer carries ADMIN_PASSWORD at all. Seven apps are in the class. - R-927 Monica, stopped at checklist 0.2 with the measurements, waiting on the operator. Gate scripts: the same console trap in nineteen of them and in repo_gates.py itself, where it ABORTED THE WHOLE RUNNER at the first gate — printing a non-ASCII character on this workstation's cp1250 console raised UnicodeEncodeError before the gate had decided anything, and reuse_refs_check.py died while printing a NOTE. All now reconfigure their own streams. Red-proof that the remaining script-test failures are not mine: test_due_checks_gate.py fails the same 5 of 42 with the change reverted.
130 lines
6.6 KiB
Python
130 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""one_register_gate.py — ONE register. Operator ruling, 2026-08-22 (R-369).
|
|
|
|
WHAT IT CONVICTS ON (a FAIL, exit 1): a row in `ROADMAP.md` that is
|
|
* neither an IDEA (a proposal for something that does not exist yet), nor
|
|
* DONE (shipped / closed / killed / ruled / moved),
|
|
and that has **no counterpart row in `OPEN-ITEMS.md`**.
|
|
|
|
WHY IT EXISTS, and the cost that bought it. Two files held open work and only one of them called
|
|
itself the source of truth. R-107 — "no offsite action unpacks the named-volume tars Tier-3
|
|
captures" — was written up properly on 2026-07-28, marked READY, and put here. Every standing rule
|
|
says "grep the register before minting", and every one of them greps the other file. So it was
|
|
invisible, and on 2026-08-21 an overnight drill rediscovered it from scratch by planting files and
|
|
watching them not come back. **25 days.**
|
|
|
|
THE PREDICATE IS THE ROADMAP'S OWN STATE COLUMN, deliberately — it already distinguishes `idea` from
|
|
`READY`, so this gate reads data that exists rather than asking anyone to maintain a new marker.
|
|
|
|
WHAT THIS GATE CANNOT SEE — the residual holes, named rather than implied:
|
|
|
|
1. **A finding filed with the state `idea` escapes.** The state column is a human judgement, and a
|
|
defect written under `idea` looks exactly like a proposal to this gate. It is the same shape as
|
|
the problem it fixes, one level up. **Accepted by the operator 2026-10-05 (R-424, closed): no
|
|
mechanical fix exists; a person reads the roadmap.** test_gate_decoys.py asserts it BY DESIGN.
|
|
2. **A finding written in prose with no `R-` identifier escapes entirely.** This gate matches ids.
|
|
A survey that enumerates a gap and never numbers it is invisible here — that is the previous
|
|
session's sweep territory and the PROMPT-TEMPLATE rule "an enumerated gap becomes a row".
|
|
3. **A finding that never reaches the roadmap at all escapes.** Nothing here reads audits, spikes
|
|
or inventories.
|
|
4. It checks that a counterpart EXISTS, never that the two say the same thing. A stale register row
|
|
beside a live roadmap row passes.
|
|
|
|
Run: python3 scripts/one_register_gate.py [--fast]
|
|
"""
|
|
import io
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import register_table # noqa: E402
|
|
|
|
# A gate's own console must not decide its verdict. These scripts quote back Hungarian copy, file
|
|
# paths and arrow characters; a Windows console here is cp1250, and printing one of them raised
|
|
# UnicodeEncodeError *before the gate had decided anything* — reuse_refs_check.py died while printing
|
|
# a NOTE, which the runner then reported as a failure (2026-10-10). Same trap class as
|
|
# poster_facts_gate.py's, which was found by its own red-proof.
|
|
for _stream in (sys.stdout, sys.stderr):
|
|
try:
|
|
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
except (AttributeError, ValueError, OSError): # pragma: no cover - old Python, or a pipe
|
|
pass
|
|
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
ROADMAP = os.path.join(ROOT, "documentation", "backlog", "ROADMAP.md")
|
|
REGISTER = os.path.join(ROOT, "documentation", "backlog", "OPEN-ITEMS.md")
|
|
|
|
ROW = re.compile(r"^\|\s*\*{0,2}R-(\d+[a-z]?)\*{0,2}\s*\|") # 2026-10-03: a suffix id (R-27b, R-27c, R-50b) was skipped unchecked
|
|
# DONE and IDEA are matched against the LEADING VERDICT of the row's STATE cell only, never the whole
|
|
# row (the body of a finding routinely contains the word "shipped" while describing something else) and,
|
|
# since 2026-10-06, never the whole cell either (a state can name another row's verdict).
|
|
# BANKED and PROVEN-LIVE are this project's own done-words and were found by running the gate: a row
|
|
# reading "BANKED in full" or "first slice PROVEN-LIVE" is shipped work, not an open finding.
|
|
DONE = re.compile(r"\b(SHIPPED|CLOSED|KILLED|DONE|SUPERSEDED|OBSOLETE|WITHDRAWN|MERGED|DISCHARGED|"
|
|
r"RULED|MOVED|BANKED|PROVEN-LIVE)\b", re.I)
|
|
IDEA = re.compile(r"\b(IDEA|SPIKED|PARKED|DEFERRED|BACKLOG|PROPOSAL)\b", re.I)
|
|
|
|
|
|
def rows(path):
|
|
"""Yield (id, state_cell, whole_line) for every R- row in a pipe table."""
|
|
for line in io.open(path, encoding="utf-8"):
|
|
line = line.rstrip("\n")
|
|
m = ROW.match(line)
|
|
if not m:
|
|
continue
|
|
# 2026-10-03: split on pipes OUTSIDE backticks (`register_table.split_cells`). R-50b's item
|
|
# carries `create\|reconcile\|grant` in code, and the naive split read its STATE from the
|
|
# middle of that — so the row was judged on words that were not its state at all.
|
|
cells = register_table.split_cells(line) or []
|
|
state = cells[3].strip() if len(cells) > 3 else ""
|
|
yield m.group(1), state, line
|
|
|
|
|
|
def main():
|
|
if not os.path.exists(ROADMAP) or not os.path.exists(REGISTER):
|
|
print("one-register gate: a backlog file is missing — FAILURE, never a skip")
|
|
return 1
|
|
|
|
have = set(rid for rid, _, _ in rows(REGISTER))
|
|
offenders, ideas, done, ok = [], 0, 0, 0
|
|
for rid, state, line in rows(ROADMAP):
|
|
# R-426 (2026-10-06): the LEADING VERDICT of the state cell, not the whole cell — R-378's lesson,
|
|
# one file over. A cell reading `READY — split out after R-86 CLOSED` matched DONE on the other
|
|
# row's word and the open finding escaped as shipped (and `READY — was an idea` would escape as
|
|
# an idea). Decoy: test_gate_decoys.py `one-register/done-word-about-another-row`.
|
|
verdict = register_table.leading_verdict(state)
|
|
if DONE.search(verdict):
|
|
done += 1
|
|
continue
|
|
if IDEA.search(verdict):
|
|
ideas += 1
|
|
continue
|
|
if rid in have:
|
|
ok += 1
|
|
continue
|
|
title = re.sub(r"\s+", " ", line.split("|")[2] if len(line.split("|")) > 2 else line)
|
|
offenders.append((rid, state, title.strip()[:120]))
|
|
|
|
print("one-register gate — ROADMAP rows: %d done, %d ideas, %d open-with-a-register-row, "
|
|
"%d WITHOUT" % (done, ideas, ok, len(offenders)))
|
|
|
|
if offenders:
|
|
print()
|
|
print("CONVICTED — open work in ROADMAP.md with no row in OPEN-ITEMS.md:")
|
|
for rid, state, title in offenders:
|
|
print(" R-%-5s state=%-24s %s" % (rid, state[:24], title))
|
|
print()
|
|
print("OPEN-ITEMS.md is the single source of truth for open work (its own first line).")
|
|
print("Either move the row there keeping its id and filing date, or — if it is a proposal for")
|
|
print("something that does not exist yet — mark its state `idea`, which is what it is.")
|
|
return 1
|
|
|
|
print("one-register gate OK — every open ROADMAP row has a register counterpart.")
|
|
return 0
|
|
|
|
|
|
sys.exit(main())
|