Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 73 additions & 15 deletions skills/handoff/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,30 +59,55 @@ L=~/projects/xFactory/lanes-edit.sh

# EVERY READ IN STEP 1 GOES THROUGH THIS ONE FENCE, and Amendment 7(d) is the
# whole of it: `0` an answer · `8` NO ANSWER · `2` a helper predating the read —
# both of those fall to the next rung — and ANYTHING ELSE is a read that FAILED,
# which is neither of them (#26, the review of `c3ebcfe`, this file's `:28` and
# `:72`). All three of step 1's reads collapsed every code into the ordinary
# case, and what is beneath them is not a refusal: it is THE NEXT RUNG, and the
# last rung is the workstation's NEWEST SWAP — a different lane. A register that
# could not be read is not "this window is not a lane", and step 4 would then
# write a PAUSED record, a stamp and a restart command for a lane this window
# is not.
# both of those fall to the next rung, save the one `2` that is a REFUSAL (the
# last paragraph below) — and ANYTHING ELSE is a read that FAILED, which is
# neither of them (#26, the review of `c3ebcfe`, this file's `:28` and `:72`).
# All three of step 1's reads collapsed every code into the ordinary case, and
# what is beneath them is not a refusal: it is THE NEXT RUNG, and the last rung
# is the workstation's NEWEST SWAP — a different lane. A register that could
# not be read is not "this window is not a lane", and step 4 would then write a
# PAUSED record, a stamp and a restart command for a lane this window is not.
#
# IT IS `/restart`'s `lread` WITH ONE DIFFERENCE, STATED RATHER THAN LEFT TO BE
# FOUND: step 4's `dir` read does NOT come through here, because this fence
# IT IS `/restart`'s `lread` WITH TWO DIFFERENCES, STATED RATHER THAN LEFT TO
# BE FOUND. Step 4's `dir` read does NOT come through here, because this fence
# STOPS and that read may not — `R-A11-11`, *a swap is never left unwritten*.
# It drops the sub-field and names the read instead, where it stands.
# It drops the sub-field and names the read instead, where it stands. And an
# exit 2 carrying Amendment 15(d)'s words is a REFUSAL here (the last paragraph
# below), where `lread` still reads every 2 as no answer.
#
# IT SETS A VARIABLE RATHER THAN PRINTING ITS ANSWER, and that is load-bearing:
# a refusal inside `$( )` kills only the substitution's subshell and the skill
# would carry on with an empty answer.
#
# AND `2` IS TWO ANSWERS (#72 item 8; `lane-handoff`'s own fence took it in
# Copilot round 2 on openRepoTools#47, and this is that fence's rule). It is
# what a helper predating a subcommand spends on an unknown one — the rung this
# fence falls through for — and it is ALSO `canon_lane`'s refusal of a register
# holding two rows that differ only by case (Amendment 15(d)), which every read
# that takes a lane name now carries. Falling through THAT is binding this
# window to whichever lane the next rung happens to answer with, and then
# renaming the window and writing a PAUSED record and a restart command for it.
# So the refusal is asked for BY ITS OWN WORDS — no old helper emits them from
# an unknown subcommand, which is how `lane-handoff`, `lane-start`, `lane-end`
# and `lane` tell the two apart — and relayed, with the fall-through for a
# genuinely old helper left as it was. The words come from asking the same read
# a second time with only its stderr kept, as `lane-handoff` does: the first
# answer's stderr is discarded, and asking a read twice changes nothing.
sread() { # sread <var> "<what the failure is NOT>" <verb> [args…]
sr_var="$1"; sr_not="$2"; shift 2
sr_out=""; sr_rc=0
sr_out="$(LANES_NO_FETCH=1 "$L" "$@" 2>/dev/null)" || sr_rc=$?
case "$sr_rc" in
0) : ;;
8|2) sr_out="" ;;
2) sr_why="$(LANES_NO_FETCH=1 "$L" "$@" 2>&1 >/dev/null || :)"
case "$sr_why" in
*"Amendment 15(d)"*)
printf 'REFUSED: `%s %s` says the register holds two rows for this lane that differ only by case, so no read of it is unambiguous and this skill will not pick one (Amendment 15(d)). It will not bind a lane, rename the window, write a PAUSED record or print a restart command; merge the two rows by hand first. The helper said:\n%s\n' \
"$L" "$1" "$sr_why" >&2
exit 2 ;;
esac
sr_out="" ;;
8) sr_out="" ;;
*) printf 'REFUSED: `%s %s` failed (exit %s). That is NOT %s — a read that failed is never an answer (Amendment 7(d)), and this skill will not bind a lane, write a PAUSED record or print a restart command on one. Run it by hand to see what it says.\n' \
"$L" "$1" "$sr_rc" "$sr_not" >&2
exit 1 ;;
Expand Down Expand Up @@ -175,12 +200,32 @@ if [[ -z "$lane" ]]; then
[[ "$candidate" =~ ^[A-Za-z0-9_][A-Za-z0-9._-]*$ ]] && lane="$candidate"
fi
fi
# ONE NAME UNDER ANY CASE, WHICHEVER RUNG ANSWERED (Amendment 15; #72 item 8).
# Rung 1 reads the window's name through `register-row`, which resolves it
# through `canon_lane` and so refuses a register holding two rows that differ
# only by case. Rungs 2 and 3 answer with a record's lane field and the swap
# row's first cell, and nothing above reads either through the resolver, so a
# lane a case twin makes ambiguous bound from them. The bound name is therefore
# read once more, here, through `canon-lane` and the fence — `lane-handoff`'s
# own step after its rungs — before anything is renamed or written. Its 15(d)
# exit 2 refuses through the fence, an old helper's 2 keeps the name as it is,
# a read that failed stops the skill as every failed read does, and its answer
# is the register row's spelling, which the rename, the record and the restart
# line then carry. It is NOT a check that the lane has a row: `canon_lane`
# answers a name no row matches with the name as typed.
if [[ -n "$lane" ]]; then
sread lane_canon "'this lane has no row'" canon-lane "$lane"
[[ "$lane_canon" =~ ^[A-Za-z0-9_][A-Za-z0-9._-]*$ ]] && lane="$lane_canon"
fi
printf 'lane=%s\n' "${lane:-<none>}"
```

The window name answers first, the window's own RECORD second and the workstation's newest swap third —
the launcher's own order, with Amendment 11 clause (b)'s new zero-question step in the middle, so the skill
and the launcher never disagree about which lane this is. Then make the other two names match it, mechanically:
and the launcher never disagree about which lane this is. Whichever of the three answered, the name is then
read once more through `canon-lane`, as `lane-handoff` does: a register holding two rows for it that differ
only by case refuses there, and the row's own spelling is the name used below. Then make the other two names
match it, mechanically:

- window name ≠ lane → `tmux rename-window "$lane"`;
- Claude session name ≠ lane → type `/rename <lane>` (Amendment 2: the session name is the lane's
Expand Down Expand Up @@ -491,10 +536,23 @@ fi
# costs the record a `window` sub-field it already had in hand. Each is SHAPE
# CHECKED before it is taken, by the same two tests the live reads get, because
# an environment variable is no more trustworthy than a shim.
# A `<session>:<index>` IS CHECKED AS A WHOLE STRING (#80 item 4, the same
# finding `lane-handoff`'s `window_ref_ok` took as item 3). `*:[0-9]*` was a
# prefix glob, so `s:1junk` passed — from a shim and from the launcher's export
# alike — and went into the append-only `window` sub-field, where `window-lane`
# can never resolve it: a live `#S:#I` always ends in an index, which is digits.
# So the value is held to `^.+:[0-9]+$`, the same form as the `<@id>` test
# below — a session part that is not empty, then digits and nothing else after
# the last `:`. AND IT CARRIES NO LINE BREAK: the regex's `.` matches a newline,
# so `junk<LF>s:1` passes it, and `log PAUSED` refuses a payload carrying a
# newline or a carriage return — the whole record, not the sub-field. So a
# value with either is dropped here, beside each regex check.
win="$(tmux display-message -p '#S:#I' 2>/dev/null)"
case "$win" in *:[0-9]*) : ;; *) win="" ;; esac
[[ "$win" =~ ^.+:[0-9]+$ ]] || win=""
case "$win" in *$'\n'*|*$'\r'*) win="" ;; esac
[[ -n "$win" ]] || win="${WORKBENCHES_CLAUDE_WINDOW_REF:-}"
case "$win" in *:[0-9]*) : ;; *) win="" ;; esac
[[ "$win" =~ ^.+:[0-9]+$ ]] || win=""
case "$win" in *$'\n'*|*$'\r'*) win="" ;; esac
wid="$(tmux display-message -p '#{window_id}' 2>/dev/null)"
[[ "$wid" =~ ^@[0-9]+$ ]] || wid="${WORKBENCHES_CLAUDE_WINDOW_ID:-}"
# AN `<@id>` IS `@<digits>` AND NOTHING ELSE (F-X13(a), A11 Addendum 4 ruling 10).
Expand Down
Loading
Loading