Backlog triage Part C: every open row has a Category (11) and a Sev (P1-P4); OPEN-ITEMS grouped by category, severity order; register_shape_gate RULES 5-8 (columns by header, category, sev, defined state) with five decoys seen red; CLAUDE.md + PROMPT-TEMPLATE: new rows filed into their category
gates / gates (push) Successful in 29s
gates / gates (push) Successful in 29s
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:
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""register_shape_gate.py — OPEN-ITEMS.md is a TABLE, and a table has a shape (R-627, 2026-09-22).
|
||||
|
||||
WHAT IT CONVICTS ON (a FAIL, exit 1), four rules and nothing else:
|
||||
WHAT IT CONVICTS ON (a FAIL, exit 1), seven rules (RULE 2 is reserved) and nothing else:
|
||||
|
||||
RULE 1 — every register row (`| **R-nnn** | …`) ends with `|`, so it HAS a state cell.
|
||||
RULE 2 — reserved. **There is deliberately no cell-COUNT rule, and that is a measurement, not an
|
||||
@@ -17,6 +17,23 @@ WHAT IT CONVICTS ON (a FAIL, exit 1), four rules and nothing else:
|
||||
the rows after it render as a separate table — and, worse, a script that walks "the
|
||||
table" stops there.
|
||||
|
||||
RULE 5 — (2026-10-03) every register row sits under a table header and splits — on pipes OUTSIDE
|
||||
backticks, `register_table.split_cells` — into exactly as many cells as that header declares.
|
||||
RULE 2's measurement still stands for a naive split; a backtick-aware split CAN count, and on
|
||||
2026-10-03 it did for 437 of 442 rows — the five it could not were real defects (a stray extra
|
||||
cell, rows written five columns wide), repaired before this rule was registered.
|
||||
RULE 6 — (2026-10-03) the `Category` cell is EXACTLY one of the eleven names in `CATEGORIES`.
|
||||
RULE 7 — (2026-10-03) the `Sev` cell is exactly `P1`, `P2`, `P3` or `P4`.
|
||||
RULE 8 — (2026-10-03) the `State` cell LEADS with one of `register_table.OPEN_STATES`. A finished word
|
||||
there is `closed_register_gate.py` RULE 3's conviction; any OTHER word is convicted here,
|
||||
because a state nobody defined ("PARTLY CLOSED", "PLUMBING COMPLETE") is how 18 rows became
|
||||
unreadable to every script before the 2026-10-03 triage.
|
||||
|
||||
WHY RULES 5–8 (2026-10-03, operator request). Every open row now carries one category and one severity
|
||||
so the register can be read as a list of priorities, and a new row is filed into its category from the
|
||||
start. A column that a script cannot read reliably is not a column — so the shape is enforced, by the
|
||||
header's column NAMES, not by positions.
|
||||
|
||||
WHY IT EXISTS, and the cost that bought it. On 2026-09-21 the update night appended measured
|
||||
results to eight existing rows with a regex that matched a row's trailing state cell. On two rows
|
||||
(**R-446** and **R-458**) it consumed the state cell and did not put it back; the cell reappeared as
|
||||
@@ -45,10 +62,24 @@ USAGE
|
||||
python3 scripts/register_shape_gate.py <file> # any file, for the red-proof
|
||||
Exit: 0 clean · 1 convicted.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import register_table # noqa: E402 — the shared row reader (2026-10-03)
|
||||
|
||||
# The eleven categories (operator request 2026-10-03; the reviewer's default, operator may reverse).
|
||||
# The same list, in the same order, heads OPEN-ITEMS.md's "How a row is filed" section.
|
||||
CATEGORIES = (
|
||||
"Install & onboarding", "Apps & catalog", "App updates", "Backup & restore", "Storage & devices",
|
||||
"Security & access", "Box system & updates", "Monitoring & notifications", "Hub & operator",
|
||||
"Business & legal", "Process & tooling",
|
||||
)
|
||||
SEVERITIES = ("P1", "P2", "P3", "P4")
|
||||
REQUIRED_COLUMNS = ("ID", "Category", "Sev", "What", "State", "Owner")
|
||||
|
||||
ROW = re.compile(r'^\| \*\*(R-\d+[a-z]?)\*\* \|') # 2026-09-30: a letter suffix (R-88a, R-88b, R-209a) is a row too — it was skipped, 3 rows unchecked
|
||||
REG = Path(__file__).resolve().parent.parent / "documentation" / "backlog" / "OPEN-ITEMS.md"
|
||||
|
||||
@@ -108,6 +139,34 @@ def check(path):
|
||||
f"{ROW.match(lines[j]).group(1)} at line {j+1}. A blank line ends "
|
||||
f"the markdown table."))
|
||||
|
||||
# RULES 5–8 — by the table's own header (2026-10-03)
|
||||
for n, rid, columns, cells, _line in register_table.rows(path):
|
||||
if columns is None:
|
||||
bad.append((n, rid, "RULE 5 — the row sits under no `| ID | … |` table header, so no column "
|
||||
"of it can be read by name"))
|
||||
continue
|
||||
missing = [c for c in REQUIRED_COLUMNS if c not in columns]
|
||||
if missing:
|
||||
bad.append((n, rid, "RULE 5 — its table header lacks the column(s) %s" % ", ".join(missing)))
|
||||
continue
|
||||
if cells is None:
|
||||
continue # RULE 1 has already convicted it
|
||||
if len(cells) != len(columns):
|
||||
bad.append((n, rid, "RULE 5 — %d cells where the header declares %d. A literal `|` outside "
|
||||
"backticks makes a cell; put it inside `code`." % (len(cells), len(columns))))
|
||||
continue
|
||||
cat = register_table.cell(columns, cells, "Category")
|
||||
if cat not in CATEGORIES:
|
||||
bad.append((n, rid, "RULE 6 — Category %r is not one of the eleven (%s)"
|
||||
% (cat, " · ".join(CATEGORIES))))
|
||||
sev = register_table.cell(columns, cells, "Sev")
|
||||
if sev not in SEVERITIES:
|
||||
bad.append((n, rid, "RULE 7 — Sev %r is not P1, P2, P3 or P4" % sev))
|
||||
word = register_table.first_word(register_table.cell(columns, cells, "State") or "")
|
||||
if word not in register_table.OPEN_STATES and not register_table.CLOSED_FAMILY.match(word):
|
||||
bad.append((n, rid, "RULE 8 — the State leads with %r, which is not a defined state (%s)"
|
||||
% (word, " · ".join(register_table.OPEN_STATES))))
|
||||
|
||||
return rows, len(seen), bad
|
||||
|
||||
|
||||
@@ -119,8 +178,9 @@ def main():
|
||||
if rows != ids:
|
||||
print(f"register-shape: {rows - ids} row(s) are DUPLICATES")
|
||||
if not bad:
|
||||
print("register-shape: OK — every row ends with a state cell, every id is unique, "
|
||||
"and no blank line splits the table")
|
||||
print("register-shape: OK — every row ends with a state cell, every id is unique, no blank "
|
||||
"line splits the table, and every row has its columns, a known category, a P1–P4 "
|
||||
"severity and a defined state")
|
||||
return 0
|
||||
print(f"register-shape: CONVICTED — {len(bad)} problem(s)")
|
||||
for line_no, rid, why in bad:
|
||||
|
||||
Reference in New Issue
Block a user