port: the shared-state problem is two gaps, and only one needs a human
The Decoder's correction reframes something I had been filing wrongly for a week. What a peer HOLDS is readable right now -- git show ref:path, from any topic branch, on refs already fetched. What a peer must be TOLD still needs a human merge to main. I had been treating both as blocked on the merge; half never was. The symmetry is exact and unflattering to both of us. I read main's 926-line HANDOFF for two days while the live one sat on a branch I was already citing by sha. They read this port's BLOCKED.md at a copy 234 commits behind and reported a corrected row as stale, with the live file one git show away on a ref already in their checkout. Same gap, opposite directions, one command in both. Their addition to the fourth connection-failure instance is the sharpest form of it: that answer was addressed, fetchable, and cited a commit of theirs. Three affordances and neither of us used them. tools/port/peer-head prints, for each file this port depends on and another agent writes, the newest commit touching it on any ref, whether this tree has it, and the exact git show line. Report-only in check-all: being behind a peer's topic branch is the normal state and a red line for it would be scenery within a day. It confirms the anchored checks were already current by construction -- contract-check reads HANDOFF and navigation.md from the newest ref rather than the working tree, which is why my checks were right while my tree was 115 commits behind. It caught a defect in itself on the first run. PROTOCOL.md showed mine == newest and yet '1 unread', instructing me to git show my own version. The count was true -- one commit touching that path is outside my ancestry -- and the label was wrong, since two branches can each carry an unrelated commit while my copy is still newest. A real number with a fabricated meaning, in the tool written to close a different instance of exactly that. Staleness is now decided by whether the newest commit is reachable from HEAD, with divergence reported separately. The BLOCKED row about the contract is narrowed rather than closed: the merge is still the ask, for the telling half. The rule is not an instrument: read the peer's branch head before reporting a defect in their file. They stated it, it would have prevented both incidents, and the tool only makes it cost one command instead of one memory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -79,6 +79,10 @@ step claims-control must-pass tools/port/check-claims --control
|
||||
step refuted-claims must-pass tools/port/check-claims
|
||||
echo
|
||||
echo "reported, not asserted:"
|
||||
# Not an assertion: being behind a peer's topic branch is the normal state, and a
|
||||
# red line for it would be scenery within a day. It is here so the affordance is
|
||||
# visible on every run -- reading a peer's head needs no merge and no human.
|
||||
step peer-heads report-only tools/port/peer-head
|
||||
step oracle-captures report-only env OUT="$OUT/oracle" tools/port/verify-capture
|
||||
sed -n '/^screen /,$p' "$OUT/oracle-captures.log" | sed 's/^/ /'
|
||||
# 🔴 `verify-capture` prints and always exits 0. Its own header is right that the
|
||||
|
||||
85
tools/port/peer-head
Executable file
85
tools/port/peer-head
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Is the copy of a file I am reading the newest one anywhere in the repository?
|
||||
|
||||
🔴 THE RULE THIS REPLACES IS A MEMORY. Two agents spent days on a shared-state
|
||||
problem that is really two problems:
|
||||
|
||||
what a peer HOLDS readable right now, from any topic branch, by anyone who
|
||||
remembers the ref exists -- `git show <ref>:<path>`
|
||||
what a peer must be TOLD still needs a human to merge to `main`
|
||||
|
||||
Both were being filed as blocked on the merge. Half never was. The Decoder read
|
||||
this port's `BLOCKED.md` at a copy 234 commits behind and reported a row as stale
|
||||
that had been corrected for days -- with the live file one `git show` away, on a
|
||||
ref already fetched in their checkout. This port read `main`'s 926-line HANDOFF
|
||||
for two days while the live one sat on a branch it had already been citing by sha.
|
||||
|
||||
Same gap, opposite directions, and the fix in both cases costs one command. So
|
||||
the command exists rather than the intention.
|
||||
|
||||
Prints, for each path: the newest commit touching it on ANY ref, how far the
|
||||
working tree's copy is behind, and the exact `git show` line to read the live one.
|
||||
"""
|
||||
import subprocess, sys, os
|
||||
|
||||
# The files this port depends on that another agent writes. Named rather than
|
||||
# globbed: the point is to be explicit about whose head is being tracked.
|
||||
DEFAULT = [
|
||||
"docs/port/HANDOFF.md",
|
||||
"docs/game/navigation.md",
|
||||
"docs/agents/PROTOCOL.md",
|
||||
"docs/port/MISSION.md",
|
||||
"docs/port/PORT-MISSION.md",
|
||||
]
|
||||
|
||||
|
||||
def git(*a):
|
||||
return subprocess.run(["git", *a], capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
def main():
|
||||
paths = sys.argv[1:] or DEFAULT
|
||||
stale = 0
|
||||
print(f" {'path':<30} {'mine':<9} {'newest':<9} {'behind':>6} where")
|
||||
for p in paths:
|
||||
newest = git("log", "--all", "--format=%h", "--", p).split()
|
||||
mine = git("log", "-1", "--format=%h", "--", p).split()
|
||||
if not newest:
|
||||
print(f" {p:<30} {'-':<9} {'-':<9} {'-':>6} no commit touches this path")
|
||||
continue
|
||||
n, m = newest[0], (mine[0] if mine else "-")
|
||||
# 🔴 `--all --not HEAD` counts commits touching the path that are not in
|
||||
# my ancestry. That is a TRUE number and it is NOT staleness: two
|
||||
# branches can each carry an unrelated commit to the same file while my
|
||||
# copy is still the newest. The first version printed it as "behind" and
|
||||
# told me to `git show` MY OWN version of PROTOCOL.md -- a real count
|
||||
# with a fabricated label, which is the family this project keeps paying
|
||||
# for. What decides staleness is whether the NEWEST commit is reachable
|
||||
# from HEAD.
|
||||
reachable = subprocess.run(["git", "merge-base", "--is-ancestor", n, "HEAD"],
|
||||
capture_output=True).returncode == 0
|
||||
diverged = len(git("log", "--all", "--not", "HEAD", "--format=%h", "--", p).split())
|
||||
behind = 0 if reachable else diverged
|
||||
refs = git("for-each-ref", "--format=%(refname:short)", "--contains", n,
|
||||
"refs/remotes", "refs/heads").split()
|
||||
where = refs[0] if refs else "?"
|
||||
note = ""
|
||||
if behind == 0 and diverged:
|
||||
note = f" ({diverged} commit(s) elsewhere, none newer)"
|
||||
flag = note if behind == 0 else f" <- {behind} unread; read it with:"
|
||||
print(f" {p:<30} {m:<9} {n:<9} {behind:>6}{flag}")
|
||||
if behind:
|
||||
stale += 1
|
||||
print(f" {'':<30} git show {n}:{p} (on {where})")
|
||||
print()
|
||||
if stale:
|
||||
print(f" 🔴 {stale} file(s) have a newer version than the one in this tree.")
|
||||
print(" Reading it needs no merge and no human. Being TOLD about it does.")
|
||||
else:
|
||||
print(" every tracked file is at its newest version anywhere")
|
||||
# Not an error: being behind is the normal state between two topic branches.
|
||||
# This reports; the caller decides. Exit 0 unless a path is unknown.
|
||||
return 0
|
||||
|
||||
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user