diff --git a/tools/README.md b/tools/README.md index de01be0..488ddc4 100644 --- a/tools/README.md +++ b/tools/README.md @@ -225,7 +225,7 @@ of them, which is why it is stated here rather than in a docstring. | `daintree-control.py` | is the fleet-status instrument answering, or blind? | 0 control passes · **2 VOID** | | `doctrine-watch.py` | which roles' doctrine moved under them, and who has not read it? | 0 nothing to tell · 1 a role is behind · **2 established nothing** | | `doctrine-uncommitted.py` | which doctrine is the fleet READING that main does not carry? | 0 reads == landed · 1 drift in any of three directions · **2 established nothing (no repo, bad ref, no file)** | -| `label-precedence.py` | when `role:` and `dev:N` disagree, which does a pane obey? | 0 no hazard · 1 a `dev:N` beside a reserved queue · **2 established nothing** | +| `label-precedence.py` | when `role:` and `dev:N` disagree, which does a pane obey? | 0 no HAZARD collisions · 1 at least one HAZARD -- a finding, established · 2 established nothing: forge unreadable, or the buckets did not sum | ⚙ GENERATED-FROM: --states | | `label-exists.py` | does the label you are about to query actually exist? | 0 all exist · 1 one is absent · **2 established nothing** | | `verdict-census.py` | has each indexed instrument ever produced a verdict? (`--ledger` keeps the record · `--stale-check` asks in 0.1s whether it is current) | 0 no finding · 1 a finding · **2 established nothing** · ⚠ `--stale-check`'s 0 means *the record is current*, NOT *they all produce verdicts* | | `wake-yield.py` | did that interruption produce work, or churn? | 0 | diff --git a/tools/label-precedence.py b/tools/label-precedence.py index e29406b..efd7701 100644 --- a/tools/label-precedence.py +++ b/tools/label-precedence.py @@ -147,7 +147,24 @@ def main(argv=None): help="print the exit states this tool can return, and stop") a = ap.parse_args(argv) if a.states: - print("0 no HAZARD collisions · 1 at least one HAZARD · 2 established nothing") + # ⛔ The contract is machine-readable and TAB-separated -- `EXIT\t\t`. + # A first version printed the same information as prose separated by "·". It read + # correctly to a human and `states-index-check.py` could not parse a single line of it, + # so this tool declared --states and was counted, correctly, as not exposing it. Copying + # the README row's LOOK instead of the producer's CONTRACT is #39's shape from the other + # side: a new producer speaking a dialect its consumer does not read. + # ⚠ EXIT lines ONLY, and that is deliberate. A first version also declared five VERDICT + # states, one of which (NO-DEV-LABEL) exists only on the branch of a DIFFERENT open PR -- + # so merging this one first would have made the tool DECLARE A STATE IT DOES NOT HAVE, + # which is #39's defect inside the PR about #39. `emit_row` reads EXIT lines only, so the + # VERDICT lines bought nothing and cost a merge-order dependency. ⇒ Removed rather than + # documented: an order a reader must remember is the weakest carrier on the ladder. + # VERDICT declarations can be added once the bucket set is settled on `main`. + for kind, code, meaning in ( + ("EXIT", "0", "no HAZARD collisions"), + ("EXIT", "1", "at least one HAZARD -- a finding, established"), + ("EXIT", "2", "established nothing: forge unreadable, or the buckets did not sum")): + print(f"{kind}\t{code}\t{meaning}") return 0 if a.self_test: return self_test() diff --git a/tools/states-index-check.py b/tools/states-index-check.py index a7ce0ea..3ff05e6 100644 --- a/tools/states-index-check.py +++ b/tools/states-index-check.py @@ -185,7 +185,7 @@ def main(): # fix already available. ⇒ Adoption becomes monotonic: nothing can regress, and the # covered population — the one in which #39's class is actually retired — can only # grow. Same shape as SUBJ_BASELINE, which records a debt that reds if it GROWS. - capable, ungenerated = [], [] + capable, ungenerated, nonconforming = [], [], [] tdir = os.path.join(a.repo, "tools") for fn in sorted(os.listdir(tdir)): if not fn.endswith(".py") or fn.startswith("test_"): @@ -199,6 +199,14 @@ def main(): # ratchet was demanding a fix that does not exist. ⇒ Ratchet on the PROPERTY # (emit_row succeeds), never on the flag. #403's population leg, in a guard. if emit_row(os.path.join(tdir, fn), "probe") is None: + # ⛔ #466: this branch used to `continue` in silence, so a tool that REGISTERS + # --states and emits an unparseable format was skipped without being counted. + # That is the silent complement -- and it hid a real case for an hour: + # label-precedence.py declared --states as prose separated by "·", which reads + # as a declaration to a human and yields not one parseable line. ⇒ Named, so + # the population where #39's class is NOT yet retired is visible rather than + # inferred from a smaller number. + nonconforming.append(fn) continue capable.append(fn) if not any(f"`{fn}`" in l for l in marked): @@ -207,6 +215,14 @@ def main(): print(f" instruments exposing --states: {len(capable)} · of those, row NOT generated:" f" {len(ungenerated)}" + (f" ⇒ {' '.join(ungenerated)}" if ungenerated else "")) + print(f" registers --states but emits no parseable row: {len(nonconforming)}" + + (f" ⇒ {' '.join(nonconforming)}" if nonconforming else "")) + print(f" ⇒ partition {len(capable)} + {len(nonconforming)} = " + f"{len(capable) + len(nonconforming)} tools registering --states") + if nonconforming: + print("⚠ NOT a defect in those tools by itself — a format may predate this contract." + "\n It is the population where #39's class is NOT retired, and --emit cannot" + "\n help them until they emit EXITcodemeaning.") if ungenerated: print("⛔ RATCHET — a tool that CAN generate its row and has not is a future drift with" "\n the fix already written. Run --emit and commit the result.")