opencode-thrum-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/assets/commands/thrum-_deviations-protocol.md +87 -0
  2. package/assets/commands/thrum-_generator-idempotency-protocol.md +15 -0
  3. package/assets/commands/thrum-_project-rules-protocol.md +18 -0
  4. package/assets/commands/thrum-_snapshot-protocol.md +265 -0
  5. package/assets/commands/thrum-_stamp-protocol.md +112 -0
  6. package/assets/commands/thrum-_verify-before-write-protocol.md +27 -0
  7. package/assets/commands/thrum-inbox.md +22 -0
  8. package/assets/commands/thrum-load-context.md +17 -0
  9. package/assets/commands/thrum-overview.md +17 -0
  10. package/assets/commands/thrum-prime.md +30 -0
  11. package/assets/commands/thrum-quickstart.md +14 -0
  12. package/assets/commands/thrum-reply.md +28 -0
  13. package/assets/commands/thrum-restart-extended.md +195 -0
  14. package/assets/commands/thrum-restart.md +293 -0
  15. package/assets/commands/thrum-send.md +31 -0
  16. package/assets/commands/thrum-sleep-extended.md +186 -0
  17. package/assets/commands/thrum-sleep.md +212 -0
  18. package/assets/commands/thrum-team.md +12 -0
  19. package/assets/commands/thrum-update-project.md +259 -0
  20. package/assets/commands/thrum-wait.md +14 -0
  21. package/assets/skills/adversarial-critique/SKILL.md +242 -0
  22. package/assets/skills/brainstorm-queue-aware/SKILL.md +167 -0
  23. package/assets/skills/brainstorm-steward/SKILL.md +181 -0
  24. package/assets/skills/choosing-subagent-models/SKILL.md +244 -0
  25. package/assets/skills/closing-findings-while-warm/SKILL.md +104 -0
  26. package/assets/skills/configure-roles/SKILL.md +229 -0
  27. package/assets/skills/coordinator-assessing-agent-completion/SKILL.md +205 -0
  28. package/assets/skills/coordinator-assessing-agent-completion/scan.py +117 -0
  29. package/assets/skills/coordinator-branch-split-on-block/SKILL.md +168 -0
  30. package/assets/skills/coordinator-context-monitoring/SKILL.md +674 -0
  31. package/assets/skills/coordinator-dispatching-work/SKILL.md +288 -0
  32. package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +522 -0
  33. package/assets/skills/coordinator-interacting-with-the-owner/SKILL.md +171 -0
  34. package/assets/skills/coordinator-maintaining-memory/SKILL.md +89 -0
  35. package/assets/skills/coordinator-managing-state-and-lifecycle/SKILL.md +192 -0
  36. package/assets/skills/coordinator-merging-code/SKILL.md +331 -0
  37. package/assets/skills/coordinator-philosophy-merge-gate/SKILL.md +442 -0
  38. package/assets/skills/coordinator-plan-reconcile-gate/SKILL.md +198 -0
  39. package/assets/skills/coordinator-post-restart-sweep/SKILL.md +135 -0
  40. package/assets/skills/coordinator-running-autonomous-feature-development/SKILL.md +199 -0
  41. package/assets/skills/coordinator-running-brainstorm-cycles/SKILL.md +654 -0
  42. package/assets/skills/coordinator-running-review-cycles/SKILL.md +153 -0
  43. package/assets/skills/coordinator-single-responsibility-merge-gate/SKILL.md +14 -0
  44. package/assets/skills/diagnosing-fleet-and-daemon/SKILL.md +255 -0
  45. package/assets/skills/efficient-multi-agent-research/SKILL.md +177 -0
  46. package/assets/skills/fixing-documentation-errors/SKILL.md +258 -0
  47. package/assets/skills/implementer-maintaining-memory/SKILL.md +84 -0
  48. package/assets/skills/implementer-orchestrating-execution/SKILL.md +125 -0
  49. package/assets/skills/implementer-receiving-dispatch/SKILL.md +127 -0
  50. package/assets/skills/implementer-receiving-review-feedback/SKILL.md +125 -0
  51. package/assets/skills/implementer-status-and-handoff/SKILL.md +150 -0
  52. package/assets/skills/implementer-tdd-and-quality/SKILL.md +99 -0
  53. package/assets/skills/memory-audit-recover/SKILL.md +153 -0
  54. package/assets/skills/memory-curate/SKILL.md +139 -0
  55. package/assets/skills/memory-maintenance/SKILL.md +87 -0
  56. package/assets/skills/memory-read-discipline/SKILL.md +164 -0
  57. package/assets/skills/memory-search-advanced/SKILL.md +217 -0
  58. package/assets/skills/memory-write-discipline/SKILL.md +135 -0
  59. package/assets/skills/orchestrate/SKILL.md +795 -0
  60. package/assets/skills/persistent-watcher-archetype/SKILL.md +225 -0
  61. package/assets/skills/prime-agent/SKILL.md +92 -0
  62. package/assets/skills/project-hotpath-gate/SKILL.md +255 -0
  63. package/assets/skills/project-philosophy/SKILL.md +418 -0
  64. package/assets/skills/project-philosophy/resources/philosophy-template.md +155 -0
  65. package/assets/skills/project-philosophy/resources/reliability-class-library.md +653 -0
  66. package/assets/skills/project-setup/SKILL.md +1007 -0
  67. package/assets/skills/project-setup/resources/implementation-agent.md +826 -0
  68. package/assets/skills/researcher-answering-queries/SKILL.md +100 -0
  69. package/assets/skills/researcher-investigating/SKILL.md +152 -0
  70. package/assets/skills/researcher-maintaining-memory/SKILL.md +176 -0
  71. package/assets/skills/state-migration/SKILL.md +54 -0
  72. package/assets/skills/thrum/SKILL.md +161 -0
  73. package/assets/skills/thrum/resources/ANTI_PATTERNS.md +132 -0
  74. package/assets/skills/thrum/resources/BOUNDARIES.md +56 -0
  75. package/assets/skills/thrum/resources/CLI_REFERENCE.md +1066 -0
  76. package/assets/skills/thrum/resources/IDENTITY.md +49 -0
  77. package/assets/skills/thrum/resources/LISTENER_PATTERN.md +64 -0
  78. package/assets/skills/thrum/resources/MESSAGING.md +163 -0
  79. package/assets/skills/thrum/resources/TMUX_SESSIONS.md +118 -0
  80. package/assets/skills/thrum/resources/WORKTREES.md +117 -0
  81. package/assets/skills/thrum-memory/SKILL.md +138 -0
  82. package/assets/skills/trace-spec-to-plan/SKILL.md +104 -0
  83. package/assets/skills/trace-spec-to-plan/resources/sweep-agent-prompt.md +57 -0
  84. package/assets/skills/update-queue/SKILL.md +149 -0
  85. package/assets/skills/using-the-queue/SKILL.md +172 -0
  86. package/assets/skills/using-thrum-state/SKILL.md +104 -0
  87. package/assets/skills/verify-against-plan/SKILL.md +323 -0
  88. package/assets/skills/verify-against-source/SKILL.md +237 -0
  89. package/assets/skills/watcher-archetype/SKILL.md +84 -0
  90. package/assets/skills/writing-durable-markdown/SKILL.md +66 -0
  91. package/dist/hooks/block-sync-cd.d.ts +9 -0
  92. package/dist/hooks/block-sync-cd.js +71 -0
  93. package/dist/hooks/block-sync-cd.test.d.ts +1 -0
  94. package/dist/hooks/block-sync-cd.test.js +61 -0
  95. package/dist/hooks/shell-env.d.ts +8 -0
  96. package/dist/hooks/shell-env.js +22 -0
  97. package/dist/index.d.ts +6 -0
  98. package/dist/index.js +25 -0
  99. package/dist/installer.d.ts +2 -0
  100. package/dist/installer.js +86 -0
  101. package/package.json +49 -0
@@ -0,0 +1,205 @@
1
+ ---
2
+ name: coordinator-assessing-agent-completion
3
+ description: "Use when deciding which live agents are FINISHED and can be stood down - finding obsolete agents, idle agents, agents nobody is waiting on, agents still running after their work landed, reaping candidates, tidying up the fleet, 'who can I shut down', 'which agents are done', auditing a box before a deploy or restart - AND when reducing the worktree count or reaping/freeing worktrees to relieve load (too many worktrees, the identity or collision scan timing out, tmux create or launches failing, 'reduce the worktree count', 'reap dead worktrees'), because a worktree is an agent's home and reaping one is an agent-lifecycle decision, never disk cleanup. Produces a candidate list from the transcript plus a DIRECT-OBSERVATION liveness check (live tmux pane / ppid, not a snapshot-presence proxy), never a kill list."
4
+ ---
5
+
6
+ # Assessing Whether an Agent Is Finished — and Whether a Worktree Is Safe to Reap
7
+
8
+ `thrum tmux connect` tells you which agents are **alive**. Nothing tells you which
9
+ are **alive but done**. This is that second question.
10
+
11
+ **This produces INFORMATION. You make the judgment call.**
12
+
13
+ The scan ranks and quotes; it does not conclude. It has no flag that reaps,
14
+ kills, or changes a phase, and it must not grow one — a script that recommends
15
+ standing an agent down is read as having decided, and its phrasing then carries
16
+ weight its evidence does not.
17
+
18
+ Apply the gate and containment checks below and reach your own
19
+ conclusion. Destructive steps still follow the standing reap preconditions and
20
+ pacing rules; nothing here shortcuts them.
21
+
22
+ ## The instruments, in order of trust
23
+
24
+ | Question | Instrument |
25
+ |---|---|
26
+ | Is it alive? | `thrum tmux connect < /dev/null` · `tmux list-sessions` |
27
+ | What did it last say? | its transcript JSONL — **the primary signal** |
28
+ | How long has it been quiet? | transcript mtime — **weak, see below** |
29
+ | What is it waiting for? | its own last message, read as prose |
30
+
31
+ **Do NOT use `thrum team`, `thrum agent list`, `phase`, or `last_seen_at` for any
32
+ of this.** They are stale, and they fail toward
33
+ reporting a live agent as absent.
34
+
35
+ ## Locating an agent's transcript
36
+
37
+ The project directory is the agent's worktree path with `/` and `.` replaced by `-`:
38
+
39
+ ```
40
+ /Users/<you>/.thrum/worktrees/thrum/<session>
41
+ -> ~/.claude/projects/-Users-<you>--thrum-worktrees-thrum-<session>/*.jsonl
42
+ ```
43
+
44
+ Newest `.jsonl` by mtime is the live session. Records carry `timestamp`, `type`,
45
+ and `message.content`.
46
+
47
+ ## The scan
48
+
49
+ ```bash
50
+ thrum tmux connect < /dev/null | awk 'NR>1 && $1 ~ /^[0-9]+$/ {print $2"|"$3}'
51
+ ```
52
+ gives `session|@agent` for every alive agent. For each, open the newest transcript
53
+ and take the last record of `type == "assistant"` with a non-empty text block.
54
+ That text is the signal.
55
+
56
+ ## Reading the signal
57
+
58
+ **Terminal-state language** — the agent declaring it holds nothing:
59
+
60
+ > *standing by* · *standing down* · *task done* · *idle* · *no further action* ·
61
+ > *completion report sent* · *held pending X* · *inbox clear, nothing to act on*
62
+
63
+ **Not terminal**: a question to another agent, a partial result, a stated next
64
+ step, an error it has not resolved.
65
+
66
+ ## ⚠️ Two false positives the phrase match produces, both measured
67
+
68
+ The scan is a **pre-filter**. It matches anywhere in the last message, which
69
+ catches two agents that are not finished:
70
+
71
+ 1. **A long active report that happens to contain a terminal phrase mid-text.**
72
+ The agent is mid-work; the phrase is describing something else.
73
+ 2. **A freshly-primed agent.** *"Inbox empty; standing by"* is part of its prime,
74
+ not a completion. Check its session start — minutes old means it just woke.
75
+
76
+ ⇒ **Read the END of the message, not a keyword hit.** A finished agent's last
77
+ sentence IS the terminal statement; a working agent's merely contains one.
78
+
79
+ ## 🔴 THE GATE — an agent saying it is finished is not evidence that it is
80
+
81
+ **Name what it is waiting for, then prove that thing is dead or landed.**
82
+
83
+ *"Held pending the coordinator merge"* is finished only if the merge landed or was
84
+ abandoned. If the merge is still queued, the agent is **correctly** waiting and
85
+ reaping it destroys live context that nobody else holds.
86
+
87
+ **If you cannot name the blocker, it is not a candidate.**
88
+
89
+ ## Containment, before anything is stood down
90
+
91
+ - branch pushed, or no commits to lose
92
+ - no only-copy untracked state: `git -C <wt> status --porcelain --untracked-files=all --ignored -- .thrum` — **both flags**; the narrow form returns a false zero
93
+ - no restart snapshot under `<worktree>/.thrum/restart/` — a snapshot means a
94
+ sleeping agent that expects to wake with its context
95
+
96
+ ## Reaping many worktrees at once — the batch path
97
+
98
+ The single-agent gate above still governs EACH worktree; batching changes only how you
99
+ GATHER the evidence and EXECUTE, never the judgment.
100
+
101
+ **Gather with read-only examiner sub-agents.** For dozens of candidates, partition them
102
+ across background sub-agents that examine each worktree read-only and RETURN a verdict —
103
+ they never reap. Per worktree each reports: sleeping-agent marker present?; branch
104
+ containment (ancestor of trunk / pushed / UNMERGED-with-commit-list); only-copy authored
105
+ content (docs/plans/specs, undelivered reports, restart snapshots), ignoring regenerable
106
+ dirt (`scripts/thrum-*.sh`, `.thrum/redirect`, `runtime_config.json`). Fence them READ-ONLY
107
+ (no checkout/reset/stash/clean/rebase/commit/push, no `worktree remove`, no `agent delete`,
108
+ no `rm`). You consolidate and decide.
109
+
110
+ **Cross-check the reap set against the live + sleeping roster before ANY deletion** — one
111
+ protected agent in the set aborts the batch. Build the exclusion from the live-pid set +
112
+ sleeping set + locked worktrees, not from names.
113
+
114
+ **Salvage only-copy state FIRST, and diff by NAME.** Copy with `cp -a`, then compare source
115
+ vs salvage with `find -type f | sort` (set-diff by NAME) BEFORE trusting per-file `cmp` — a
116
+ flat copy of two same-basename files (e.g. `.thrum/restart/<a>.md` and
117
+ `.thrum/context/<a>.md`) silently clobbers one, and `cmp` on what copied passes clean. Give
118
+ salvaged files distinct names and land the salvage somewhere durable (the main repo, not
119
+ `/private/tmp`).
120
+
121
+ ## 🔴 The reap is worktree-removal-only — and TEST every step on ONE item first
122
+
123
+ **`thrum agent delete --force` is REFUSED by the CAS guard on a stale agent with an empty
124
+ `agent_pid_start_time`** (*"expected-state premise is required … refusing rather than treating
125
+ an empty/never-read premise as a match"*). `--force` does NOT bypass it; the guard is
126
+ correct. So the reap that actually frees load is **`git worktree remove --force <path>`** —
127
+ the delete best-effort-fails and leaves a **benign orphan DB row**, which you LEAVE (filtered
128
+ at query per the fleet ruling; never `agent cleanup --force` to tidy them).
129
+
130
+ ⚠️ **After a large reap you may see a `DeadAgentSweeper` skip alert + elevated write-RPC
131
+ latency — do NOT assume the reap caused it.** This box carries a standing single-writer
132
+ contention condition (one serialized write conn, `SetMaxOpenConns(1)`) that spikes write-RPC
133
+ latency recurrently and **reap-independently**. A large reap is at most ONE coincident spike: stranding dozens of stale
134
+ agents (worktreeless, empty `pid_start_time` → always "due") stretches the sweep's
135
+ evidence-gathering run-length (that is what `held=` measures — a run duration, mostly off-lock
136
+ syscalls, not a lock hold or a deadlock; the skip is correct, the guard releases via `defer`),
137
+ but it drives almost no writes of its own. The
138
+ discriminator that it is contention and not poison: `write_pool in_use=0`,
139
+ `selfheal_write_poison_detected=0`. **So do not chase the reap as the lever** — pacing/batching
140
+ large reaps is mild hygiene at best (it trims run-length and a little pressure), a restart is
141
+ transient relief only, and the durable fix is **writer-decontention**, owned by the
142
+ daemon-perf agent. Symptom to recognize (reads and `daemon status` stay ~0.5s while
143
+ `status --self` climbs — writes slow, not reads): hand it to the daemon-perf owner, don't
144
+ re-diagnose the internals yourself.
145
+
146
+ **Before running any batched destructive loop, run it on ONE item and verify the EFFECT**
147
+ (agent gone from the registry / worktree gone from `git worktree list` and disk), not just
148
+ the exit code. A batch can fail wholesale for a reason a single test surfaces instantly — a
149
+ minimal-`PATH` loop/background context where `git`/`thrum`/`sleep` are `command not found`
150
+ (set `PATH` explicitly or use absolute paths); a `read` that collapses a leading empty field.
151
+ Batched worktree removal is also SLOW on a loaded box (seconds each) — run it in the
152
+ background or in chunks, never one 2-minute foreground loop.
153
+
154
+ ## ⚠️ Idle time is corrupted by your own traffic
155
+
156
+ A coordinator broadcast resets every recipient's transcript mtime. After any
157
+ fleet-wide message, **every agent reads as freshly active** and the idle column
158
+ is meaningless for hours.
159
+
160
+ ⇒ **Rank by the last message's CONTENT, not by elapsed time.** A nudged-then-idle
161
+ agent re-states that it is standing by, so the content signal survives what the
162
+ timing signal does not.
163
+
164
+ ## ⚠️ Two absences that are not evidence
165
+
166
+ - **No transcript** does not mean no agent. Alive agents exist with no transcript
167
+ directory.
168
+ - **Absent from `connect`** means only "no tmux session on THIS box". `connect` is
169
+ box-local; remote agents can never appear in it.
170
+
171
+ ## Idle-gopls RAM reclaim
172
+
173
+ Once you've classified an agent as idle, its `gopls` process (if any) is a
174
+ second, independent reclaim candidate — an idle agent's gopls is dead weight
175
+ sitting in RAM.
176
+
177
+ `scripts/reap-idle-gopls.sh` matches each running `gopls` process to its
178
+ owning agent (ppid chain → tmux pane_pid → `thrum team --json` session) and
179
+ uses `tmux_state` as a cheap first-pass filter — anything not idle by that
180
+ signal is skipped outright. That filter is bounded, not authoritative: it
181
+ does not replace this skill's own idle/finished judgment, which still comes
182
+ from the transcript per the rules above. Run the script alongside the scan
183
+ above, apply the same transcript-based gate to whatever it surfaces, and
184
+ fold the result into a single combined table: idle agent · gopls PID ·
185
+ RSS-MB.
186
+
187
+ - Killing an idle agent's gopls is a no-op — it respawns on the agent's next
188
+ LSP request. Never destructive to the agent itself.
189
+ - **NEVER kill a pool or active-worker gopls.** The script excludes by ROLE
190
+ (orchestrator, gate, coordinator, brainstormer, researcher — the roles
191
+ that are never reap targets) plus the non-idle-state skip above, plus one
192
+ named exception for a role-uncovered pool agent. It also runs a canary
193
+ self-check before printing anything: if it can't prove the exclusion still
194
+ resolves correctly, it aborts loudly instead of printing a kill-list.
195
+ Do not override or bypass any of this.
196
+ - Same framing as the rest of this skill: the script **produces information,
197
+ you decide**. It is read-only and dry-run — it prints a kill-list, it never
198
+ kills anything.
199
+
200
+ ## Why this fails dangerously if you skip the gate
201
+
202
+ The failure direction is **false-GONE**: concluding a live agent is finished. It
203
+ authorises abandonment, and **nobody audits an agent nobody believes exists**, so
204
+ the error has no natural discovery path. A false-ALIVE costs one wasted check.
205
+ Prefer it.
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+ """Rank alive agents by whether their last message reads as terminal.
3
+
4
+ This is an INFORMATION source. It ranks and quotes; it does not conclude, and it
5
+ deliberately has no flag that reaps, kills, or changes a phase. The reading agent
6
+ makes the judgment call using the gate and containment checks in SKILL.md.
7
+
8
+ Usage:
9
+ thrum tmux connect < /dev/null | python3 scan.py [worktree-root]
10
+
11
+ Reads `session|@agent` lines on stdin (see SKILL.md for the awk that produces
12
+ them). Default worktree root is ~/.thrum/worktrees/thrum.
13
+ """
14
+
15
+ import datetime
16
+ import glob
17
+ import json
18
+ import os
19
+ import re
20
+ import sys
21
+
22
+ # Phrases an agent uses to declare it holds nothing. Matching one makes an agent
23
+ # a CANDIDATE, never a decision -- the blocker check in SKILL.md is what decides.
24
+ TERMINAL = (
25
+ "standing by", "standing down", "stand down", "task done", "no further action",
26
+ "nothing to add", "nothing further", "completion report sent", "inbox is clear",
27
+ "inbox clear", "no action needed", "no reply needed", "idle", "held pending",
28
+ "awaiting", "nothing owed", "work is complete", "done and reported",
29
+ )
30
+
31
+
32
+ def slug(path):
33
+ """Claude Code's project-dir naming: every character that is
34
+ not [A-Za-z0-9] becomes '-' -- not just '/' and '.'.
35
+ Mirrors Claude Code's own path-encoding algorithm exactly for ASCII input -- must stay byte-for-byte in sync with it.
36
+ """
37
+ return re.sub(r"[^A-Za-z0-9]", "-", path)
38
+
39
+
40
+ def last_assistant_text(path):
41
+ """Last non-empty assistant text block, or '' if none."""
42
+ try:
43
+ with open(path, encoding="utf-8") as fh:
44
+ lines = fh.read().splitlines()
45
+ except OSError:
46
+ return ""
47
+ for line in reversed(lines):
48
+ try:
49
+ rec = json.loads(line)
50
+ except ValueError:
51
+ continue
52
+ if rec.get("type") != "assistant":
53
+ continue
54
+ for block in rec.get("message", {}).get("content", []):
55
+ if isinstance(block, dict) and block.get("type") == "text":
56
+ text = " ".join(block.get("text", "").split())
57
+ if text:
58
+ return text
59
+ return ""
60
+
61
+
62
+ def main():
63
+ root = sys.argv[1] if len(sys.argv) > 1 else os.path.expanduser(
64
+ "~/.thrum/worktrees/thrum")
65
+ projects = os.path.expanduser("~/.claude/projects")
66
+ now = datetime.datetime.now(datetime.timezone.utc)
67
+
68
+ rows, no_transcript = [], []
69
+ for line in sys.stdin:
70
+ line = line.strip()
71
+ if "|" not in line:
72
+ continue
73
+ session, agent = line.split("|", 1)
74
+ files = glob.glob(os.path.join(projects, slug(os.path.join(root, session)), "*.jsonl"))
75
+ if not files:
76
+ # NOT evidence the agent is gone -- see SKILL.md.
77
+ no_transcript.append(agent)
78
+ continue
79
+ newest = max(files, key=os.path.getmtime)
80
+ mtime = datetime.datetime.fromtimestamp(os.path.getmtime(newest),
81
+ datetime.timezone.utc)
82
+ text = last_assistant_text(newest)
83
+ low = text.lower()
84
+ hits = [p for p in TERMINAL if p in low]
85
+ rows.append(((now - mtime).total_seconds() / 3600.0, agent, hits, text))
86
+
87
+ rows.sort(key=lambda r: (not r[2], -r[0]))
88
+
89
+ print(f"alive with transcripts: {len(rows)} no transcript: {len(no_transcript)}")
90
+ print("\nLAST MESSAGE READS AS TERMINAL -- an observation, not a recommendation.")
91
+ print("Name each one's blocker and prove it dead before concluding. See SKILL.md.\n")
92
+ any_candidate = False
93
+ for idle, agent, hits, text in rows:
94
+ if not hits:
95
+ continue
96
+ any_candidate = True
97
+ print(f" {agent:<28} idle {idle:5.1f}h [{', '.join(hits)}]")
98
+ print(f" {text[:150]}")
99
+ if not any_candidate:
100
+ print(" (none)")
101
+
102
+ print("\nNOT terminal -- still working or mid-exchange:")
103
+ for idle, agent, hits, _ in rows:
104
+ if not hits:
105
+ print(f" {agent:<28} idle {idle:5.1f}h")
106
+
107
+ if no_transcript:
108
+ print("\nNo transcript found (NOT evidence of absence):")
109
+ for agent in no_transcript:
110
+ print(f" {agent}")
111
+
112
+ print("\nIdle hours are unreliable after any fleet broadcast -- your own "
113
+ "traffic resets every recipient's clock. Rank by content, not time.")
114
+
115
+
116
+ if __name__ == "__main__":
117
+ main()
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: coordinator-branch-split-on-block
3
+ description: "Use when a Pass-3 merge gate (hotpath, philosophy) or dual review BLOCKS on ONE concern of a branch that bundles multiple independent concerns, while the other concern(s) are clean - on a live multi-writer shared branch. Walks the split-land-recut sequence - separate the blocked concern from the clean one(s), land the clean half first, re-cut the blocked half onto the now-moved base, and verify the dropped commits are NOT ancestors of the re-cut tip before re-gating and merging."
4
+ ---
5
+
6
+ # Coordinator: Branch-Split When One Concern Blocks a Multi-Concern Branch
7
+
8
+ ## When to use
9
+
10
+ - A Pass-3 dual merge-gate (`coordinator-hotpath-merge-gate` or
11
+ `coordinator-philosophy-merge-gate`) — or dual review — **BLOCKS on one
12
+ identifiable concern** of a branch, while the branch also carries one or
13
+ more genuinely independent, unrelated concerns that are clean.
14
+ - The clean concern(s) are ready to land on their own merits and gain nothing
15
+ from waiting on the blocked concern's fix.
16
+ - The branch lives on a **live multi-writer shared branch** — other
17
+ coordinators/agents are also pushing, so the base moves under you and a
18
+ naive re-cut needs an explicit safety check, not just a rebase.
19
+
20
+ **Don't use when:**
21
+
22
+ - The BLOCK concern and the "clean" concern actually share code or intent —
23
+ if splitting would require cherry-picking around interdependent commits,
24
+ this is not an independent-concerns split; fix the blocker in place
25
+ instead.
26
+ - Only one concern exists on the branch. A single-concern BLOCK just goes
27
+ back to the implementer normally — there is nothing to separate.
28
+
29
+ ## The split decision
30
+
31
+ When a gate names a BLOCKING finding scoped to one part of the diff, and the
32
+ rest of the diff is unrelated to that finding, split the branch into:
33
+
34
+ - **The clean half** — the concern(s) with zero BLOCKING findings.
35
+ - **The blocked half** — the concern the gate flagged.
36
+
37
+ State the split explicitly in the merge report before doing anything else:
38
+ name which commits belong to which half. If a commit touches both concerns,
39
+ that commit belongs to the blocked half (never guess a commit apart) or the
40
+ split isn't clean enough to attempt this play.
41
+
42
+ ## Land the clean half first
43
+
44
+ Do not hold the clean half hostage to the blocked half's fix cycle. Route the
45
+ clean half through its own merge report to the merge-king (coordinators
46
+ never self-merge) and get it merged **first**. This advances the mainline
47
+ tip by the clean half's commits.
48
+
49
+ ## Re-cut the blocked half onto the now-moved base
50
+
51
+ > **This is not the banned rebase.** The project-wide ban (see
52
+ > `coordinator-dispatching-work` and `orchestrate` — "merge forward, never
53
+ > rebase") targets rebasing a still-live, in-flight branch onto a moved base
54
+ > as the pre-MERGE-READY sync step: a stale-local-ref rebase there can
55
+ > silently revert commits landed since your last fetch, with no check
56
+ > catching it. The re-cut below is a different operation on a different
57
+ > object — you are not syncing an existing branch, you are **building a new
58
+ > branch from scratch** out of only the blocked half's own unpushed commits,
59
+ > and the operation is made safe by an explicit invariant check (below), not
60
+ > by care alone. Rebasing (or cherry-picking) a branch's own local, unpushed
61
+ > commits onto a fresh base during a deliberate re-cut is legitimate; do not
62
+ > "fix" this section by replacing it with merge-forward.
63
+
64
+ Once the clean half is merged, the blocked half must be **re-cut** — not
65
+ merely rebased — onto the new tip, so it carries **only its own commits**,
66
+ not a copy of the clean half's commits riding along from the original
67
+ branch-off point. Concretely: create the new branch from the merged tip
68
+ (the clean half's merge SHA, announced by SHA unprompted, per base-mover
69
+ discipline), then cherry-pick or rebase only the blocked half's own commits
70
+ onto it. The goal state is a
71
+ branch whose full commit range is exactly "the blocked concern's fix,"
72
+ nothing else.
73
+
74
+ ## The not-ancestor invariant — the load-bearing check
75
+
76
+ **Pin exactly which SHAs are the "dropped set" — this is the detail the check
77
+ lives or dies on.** The dropped set is the clean half's commits **as they
78
+ existed on the original pre-split bundled branch** — their ORIGINAL SHAs,
79
+ before the clean half was landed. It is **not** the clean half's merged
80
+ form: landing the clean half can rewrite its commits (a rebase, a squash, a
81
+ merge-time correction), which mints **new, distinct git objects** with new
82
+ SHAs. Those merged-form SHAs are *expected*, *correct* ancestors of the
83
+ re-cut tip, because construction branched the re-cut FROM the merged tip —
84
+ seeing them as ancestors is not a failure, it's the point.
85
+
86
+ **After re-cutting, verify the ORIGINAL pre-split SHAs of the clean half's
87
+ commits are NOT ancestors of the re-cut branch's tip:**
88
+
89
+ ```bash
90
+ git merge-base --is-ancestor <original-pre-split-clean-half-sha> <recut-blocked-tip>
91
+ # exit 1 (NOT an ancestor) is the PASS condition
92
+ ```
93
+
94
+ Run this for every commit in the dropped set (the original pre-split SHAs),
95
+ not just the first one — and never substitute the merged-form SHAs here, or
96
+ the check inverts and fails a safe re-cut.
97
+
98
+ **Why this is the check that matters:** the risk isn't that the re-cut tip
99
+ descends from the clean half's *landed* fix — it must, by construction. The
100
+ risk is that the re-cut branch was built sloppily (e.g. a naive rebase of
101
+ the whole original bundle instead of a cherry-pick of only the blocked
102
+ half's commits) and ended up carrying the clean half's **original,
103
+ pre-correction commit objects** as ancestors alongside the merged tip. If
104
+ that happens, merging the re-cut branch feeds those original objects into a
105
+ future merge-base calculation on the same files, and git's three-way merge
106
+ can resolve back toward the older, pre-correction content — silently
107
+ reverting whatever the clean half's merge fixed. There is no conflict, no
108
+ warning, no gate that catches this by construction: the merge just succeeds
109
+ and quietly re-applies the older version. Verification (ancestry) has a
110
+ shelf life, and re-deriving it AT THIS SPECIFIC HANDOFF (the re-cut) is not
111
+ optional.
112
+
113
+ Run the check at **re-cut time** and again immediately before the merge-king
114
+ executes the re-cut branch's merge (the base can move again in between on a
115
+ live shared branch — re-run, don't relay a stale result).
116
+
117
+ ## Three-pass re-gate on the re-cut branch
118
+
119
+ The re-cut branch is a new artifact (different base, different commit set)
120
+ and gets a full re-gate, not a diff-against-the-old-gate. Follow the
121
+ three-pass order, in order:
122
+
123
+ 1. **UNFRAMED first** — no defect brief, read the full changed functions,
124
+ flag anything nobody asked about. Run this before the other two; a
125
+ framed pass run first anchors every subsequent pass to the same blind
126
+ spot.
127
+ 2. **CODE-DERIVED enumeration** — terms and symbols pulled from the code
128
+ itself (not from the original defect description), enumerate every call
129
+ site / consumer.
130
+ 3. **BY-EFFECT** — assert the specific defect the original BLOCK named
131
+ cannot occur, phrased as a checkable behavior. Example from the worked
132
+ case: "an inconclusive liveness probe produces neither a tombstone nor a
133
+ repair" — a concrete, testable claim, not "the fix looks right."
134
+
135
+ A code-derived enumeration is not a substitute for the unframed pass — it is
136
+ still aimed at symbols the code review chose to look at. Both are required.
137
+
138
+ ## Hold the implementer's retirement until BOTH halves merge
139
+
140
+ Do not retire (agent.delete / worktree teardown) the implementer who owns
141
+ this work until **both** the clean half and the blocked half have merged.
142
+ The implementer's context on the blocked half's fix is exactly what the
143
+ three-pass re-gate cycle needs if it surfaces further findings; retiring
144
+ early forces a cold restart mid-fix-cycle.
145
+
146
+ ## Gate that closes it
147
+
148
+ Both halves merged, both efficacy rows recorded (one row per merged branch,
149
+ not one row for the original bundled branch), the not-ancestor invariant's
150
+ PASS result is part of the merge report the merge-king relied on (not
151
+ asserted after the fact).
152
+
153
+ ## See also
154
+
155
+ - `coordinator-hotpath-merge-gate` / `coordinator-philosophy-merge-gate` —
156
+ the Pass-3 gates whose BLOCK verdict triggers this play.
157
+ - `coordinator-running-review-cycles` — the dual-review cycle that may also
158
+ originate a scoped BLOCK this play applies to.
159
+
160
+ ## Project-specific rules (already loaded)
161
+
162
+ Read the shared partial at the absolute path:
163
+ `claude-plugin/commands/_project-rules-protocol.md`
164
+
165
+ If you accumulate a new rule mid-session about branch-splitting, capture it
166
+ via the `coordinator-maintaining-memory` skill — it references the
167
+ `memory-write-discipline` common for the canonical `thrum memory create`
168
+ shape.