From 2d5496f75474e04755e201f16e97b10f48f87ef4 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Fri, 4 Sep 2026 18:27:39 +0200 Subject: [PATCH] protocol: findings before citing code, and checks that were kind once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two rules that look unrelated and are one failure, plus the change that makes the second enforceable. 1. A FINDING REACHES `main` BEFORE THE CODE THAT CITES IT. A citation resolving only on a peer branch is dead the moment it merges. Not hypothetical: 495 decoder and 366 port commits sit off `main`, and `port/scripts/boot.gd` already cites two docs/re pages present on neither its own branch nor main. 2. A CHECK MAY ONLY SOFTEN AGAINST A CONDITION IT CAN TEST -- the Pi agent's wording, and better than mine, because it is applicable while writing rather than a call to be vigilant. The mechanical form: Can this branch tell the difference between "not yet" and "no longer"? `gitea-protect --verify` printed ⚪ "not a collaborator (yet)" and continued, so the only instrument checking Write-not-Admin could not report that gate being REMOVED. `check-citations` reported peer citations instead of failing them, because under the old topology that was unfixable from the container. Both were correct AND kind when written; neither recorded that the kindness had a scope. Nobody edits these into being wrong -- the world moves and the allowance stays, which is why they survive review. The smell is leniency with an expiry date nobody set; the fix is the testable-condition rule. check-citations gains `--for-merge`, which turns the peer class into a failure. A flag rather than a new default because BOTH readings are still live: mid-work on a topic branch the peer class really is unfixable noise. What the old code could not express is where the code is GOING, and that is a condition the caller can state. Measured on this tree: 19 citations resolve only on a peer branch -- which is the size of the #7-depends-on-#8 edge, not the 2 I had counted in boot.gd. The selftest gains that third class, because a flag whose classification is unexercised is the shape this rule exists to catch. Controlled: emptying PEER_REFS makes the peer case collapse into "nowhere" and the selftest reports šŸ”“ BROKEN, rc=2. āš ļø Pre-existing and NOT from this change: the default run already exits 1 on 4 citations of `export/...` paths. Those are the generated tree, gitignored by design, and main's copy of the tool fails identically. The CITE regex treats `export/` as a repo prefix. Reported, not fixed -- it is the port's file and its call whether the regex or the citations are wrong. --- docs/agents/PROTOCOL.md | 34 ++++++++++++++++++++++++++++ tools/port/check-citations | 46 ++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/docs/agents/PROTOCOL.md b/docs/agents/PROTOCOL.md index 682db7e6..8f812bdf 100644 --- a/docs/agents/PROTOCOL.md +++ b/docs/agents/PROTOCOL.md @@ -155,6 +155,40 @@ rule is written here so you know it, not so it depends on you. A PR you cannot describe in a paragraph is an item that was too big. That is the signal to split it, not to write a longer description. +### šŸ”“ A finding reaches `main` before the code that cites it + +A citation that resolves only on a peer branch is **dead the moment it merges**. +Open the finding's PR first and make it a dependency of the code's. + +This is not hypothetical and it is not small: **495 decoder commits and 366 port +commits sit off `main`**, so nearly anything either agent re-proposes will hit +it. `port/scripts/boot.gd` already cites two `docs/re/` pages that exist on +neither its own branch nor `main`. + +## Checks that were kind once + +Two rules that look unrelated and are the same failure. + +**A check may only soften against a condition it can test.** + +`gitea-protect --verify` printed ⚪ *"not a collaborator (yet)"* and continued +without failing — so the one instrument that checks Write-not-Admin could not +report that gate being **removed**. `check-citations` reported peer-branch +citations rather than failing them, because under the old branch topology that +was a state nobody could fix. Both were **correct and kind when written**, and +neither recorded that the kindness had a scope. + +The test is mechanical, and you apply it to your own code: + +> **Can this branch tell the difference between *not yet* and *no longer*?** + +If it cannot, it does not get to be lenient. `--verify` could always ask whether +a collaborator exists, so the "yet" was never needed. + +šŸ“Œ **Nobody edits these into being wrong** — the world moves and the allowance +stays. That is why they survive review, and why the smell is worth naming: +*leniency with an expiry date nobody set.* + `share put --note "…" --for port` records the sender, the time, **the commit they were on**, and whether their tree was dirty. A capture with no provenance is not evidence, it is a picture. diff --git a/tools/port/check-citations b/tools/port/check-citations index 53c824b3..465aefc0 100755 --- a/tools/port/check-citations +++ b/tools/port/check-citations @@ -77,11 +77,28 @@ def main() -> int: open(bad, "w").write("see `docs/port/this-file-does-not-exist-anywhere.md`\n") good = os.path.join(tmp, "good.md") open(good, "w").write("see `docs/port/PORT-MISSION.md`\n") + # The THIRD class, which `--for-merge` turns into a failure. It has to be + # told apart from both others: a peer citation is not dangling (the file + # exists) and does not resolve here (the reader still gets nothing), and + # a scanner that collapsed it into either would make the flag meaningless + # while still passing the two checks above. + peerfile = os.path.join(tmp, "peer.md") + open(peerfile, "w").write("see `docs/re/f5-a-press-snaps-the-plate.md`\n") + rp, pp, np_ = scan([peerfile]) + _, _, nb = scan([bad]) r, _, ng = scan([good]) - ok = len(nb) == 1 and len(ng) == 0 and r == 1 - print("selftest: planted dangling caught=%s, real citation passed=%s -> %s" - % (len(nb) == 1, len(ng) == 0 and r == 1, "ok" if ok else "šŸ”“ BROKEN")) + caught = len(nb) == 1 + passed = len(ng) == 0 and r == 1 + peer_ok = len(pp) == 1 and rp == 0 and len(np_) == 0 + ok = caught and passed and peer_ok + print("selftest: planted dangling caught=%s, real citation passed=%s, " + "peer-branch classed separately=%s -> %s" + % (caught, passed, peer_ok, "ok" if ok else "šŸ”“ BROKEN")) + if not peer_ok: + print(" šŸ”“ --for-merge cannot mean anything if the peer class is " + "not distinguished; got resolves=%d peer=%d nowhere=%d" + % (rp, len(pp), len(np_))) return 0 if ok else 2 files = sorted(glob.glob("docs/port/*.md")) @@ -89,9 +106,30 @@ def main() -> int: total = resolves + len(peer) + len(nowhere) print("citations of repo paths in docs/port/*.md: %d" % total) print(" resolve here : %d" % resolves) - print(" on a peer branch, not merged: %d (reported, not failed)" % len(peer)) + # šŸ”“ --for-merge TURNS THE PEER CLASS INTO A FAILURE. + # + # Reporting-not-failing was right when it was written: a peer-branch + # citation was "a state nobody in this container can fix", so failing on it + # would have been red for something unactionable. Under the pull-request + # workflow that stopped being true -- a PR into `main` is EXACTLY where it + # becomes fixable, by opening the finding's PR first and depending on it. + # The citation is dead the moment this merges, so the merge is the last + # place the leniency can still be withdrawn. + # + # Left as a flag rather than made unconditional, because both readings are + # still live: mid-work on a topic branch the peer class really is unfixable + # noise. The difference the old code could not express is WHERE the code is + # going, and that is a condition the caller can state. + merging = "--for-merge" in sys.argv + label = "šŸ”“ FAILS (--for-merge)" if merging else "reported, not failed" + print(" on a peer branch, not merged: %d (%s)" % (len(peer), label)) for m, (src, ref) in sorted(peer.items()): print(" %-52s %s <- %s" % (m, ref.split("/")[-1], os.path.basename(src))) + if peer and merging: + print("\nšŸ”“ %d citation(s) resolve only on a peer branch." % len(peer)) + print(" After this merges they resolve NOWHERE -- the reader gets a dead") + print(" path. Land the finding first and make it a dependency of this PR.") + return 1 if nowhere: print(" šŸ”“ resolve NOWHERE : %d" % len(nowhere)) for m, src in sorted(nowhere.items()):