opencode-thrum-pro 0.1.0 → 0.1.2
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.
- package/assets/commands/thrum-_stamp-protocol.md +65 -0
- package/assets/skills/brainstorm-steward/SKILL.md +15 -0
- package/assets/skills/choosing-subagent-models/SKILL.md +19 -11
- package/assets/skills/conversation-only-context/SKILL.md +186 -0
- package/assets/skills/coordinator-dispatching-work/SKILL.md +28 -8
- package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +68 -0
- package/assets/skills/coordinator-merging-code/SKILL.md +24 -0
- package/assets/skills/coordinator-running-brainstorm-cycles/SKILL.md +11 -7
- package/assets/skills/efficient-multi-agent-research/SKILL.md +7 -6
- package/assets/skills/persistent-watcher-archetype/SKILL.md +68 -9
- package/assets/skills/persistent-watcher-archetype/resources/thrum-capture-fallback.sh +123 -0
- package/assets/skills/persistent-watcher-archetype/resources/thrum-watch-pane-capture.sh +379 -0
- package/assets/skills/prime-agent/SKILL.md +2 -4
- package/assets/skills/project-setup/SKILL.md +118 -21
- package/assets/skills/update-queue/SKILL.md +3 -1
- package/assets/thrum_allowlist.json +34 -0
- package/dist/allowlist-sync.test.d.ts +1 -0
- package/dist/allowlist-sync.test.js +44 -0
- package/dist/index.js +24 -1
- package/dist/installer.d.ts +29 -0
- package/dist/installer.js +356 -1
- package/dist/installer.permissions.test.d.ts +1 -0
- package/dist/installer.permissions.test.js +602 -0
- package/dist/permission-fixture.integration.test.d.ts +1 -0
- package/dist/permission-fixture.integration.test.js +506 -0
- package/package.json +3 -2
|
@@ -84,6 +84,71 @@ parseable with `grep -F` semantics without regex. The reader-side regex in
|
|
|
84
84
|
`verify-against-plan` anchors on this fixed format; do not reorder or recase any
|
|
85
85
|
field.
|
|
86
86
|
|
|
87
|
+
## Review-object binding (THRUM-REVIEW stamp — plan/prompt binding)
|
|
88
|
+
|
|
89
|
+
A review stamp's PRESENCE proves a review happened; it does not prove the
|
|
90
|
+
stamp still binds the artifact that was reviewed. An edit made after a
|
|
91
|
+
`Ready:Yes` verdict leaves a stamp that still reads `Ready:Yes` while
|
|
92
|
+
describing a document that no longer exists — the same class of staleness the
|
|
93
|
+
Authored-against stamp solves for the tree, applied here to the artifact
|
|
94
|
+
itself.
|
|
95
|
+
|
|
96
|
+
The `THRUM-REVIEW` stamp carries two extra keys per stage, binding it to the
|
|
97
|
+
exact git blob it reviewed and the exact source it was reviewed against:
|
|
98
|
+
|
|
99
|
+
| Stage | Self-binding key | Source-lock key |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `stage=plan` | `plan_obj` | `src_lock` |
|
|
102
|
+
| `stage=prompt` | `prompt_obj` | `plan_lock` |
|
|
103
|
+
|
|
104
|
+
### Derivation (strip-then-hash, run at stamping time)
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
# self-object blob: strip EVERY THRUM-REVIEW line, hash the remainder, filters OFF
|
|
108
|
+
obj=$(grep -v '^<!-- THRUM-REVIEW:' "$FILE" | git hash-object --no-filters --stdin)
|
|
109
|
+
# source-lock blob: the source file is not mutated by the stamp -> plain hash
|
|
110
|
+
lock=$(git hash-object --no-filters "$SOURCE_FILE")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- Strip set = every line matching `^<!-- THRUM-REVIEW:` (all cycles) — NOT
|
|
114
|
+
`THRUM-GATE`/`THRUM-DEFER`. The stamp cannot bind a hash that includes
|
|
115
|
+
itself, so its own lines are removed before hashing (chicken-and-egg).
|
|
116
|
+
- `--no-filters --stdin` bypasses gitattributes clean filters / autocrlf so
|
|
117
|
+
the value emitted here and the value re-derived at read time agree
|
|
118
|
+
byte-for-byte.
|
|
119
|
+
- Hash the **working-tree** file, not a committed blob — an uncommitted
|
|
120
|
+
post-review edit must be caught before it is ever committed.
|
|
121
|
+
- For `stage=plan`: `$SOURCE_FILE` is the LOCKED brainstorm (plus design spec,
|
|
122
|
+
if any); `src_lock` proves the plan was reviewed against the source that was
|
|
123
|
+
current at review time.
|
|
124
|
+
- For `stage=prompt`: `$SOURCE_FILE` is the LOCKED plan; `plan_lock` proves the
|
|
125
|
+
prompt was reviewed against the plan that was current at review time.
|
|
126
|
+
- When multiple cycles' `THRUM-REVIEW: stage=<S>` lines coexist, the
|
|
127
|
+
**highest-cycle (last-appended)** line is the binding verdict; earlier
|
|
128
|
+
lines are history, never re-checked. Stamps are only ever appended, never
|
|
129
|
+
reordered, so append-order == cycle-order — select with `tail -1`, never a
|
|
130
|
+
sort (`cycle=10` must beat `cycle=9`, which a lexical sort inverts).
|
|
131
|
+
|
|
132
|
+
### Null/unresolvable guard
|
|
133
|
+
|
|
134
|
+
Same failure class as the Authored-against stamp above: if either `obj` or
|
|
135
|
+
`lock` comes back empty or `null`, STOP and report — do NOT emit a stamp with
|
|
136
|
+
an empty/`null` binding key. A `THRUM-REVIEW` line with `plan_obj=` (or any of
|
|
137
|
+
the four binding keys) empty or literal `null` still matches a reader's regex
|
|
138
|
+
and reads as a valid binding — the value a reader would trust to prove binding
|
|
139
|
+
is exactly the value that never resolved.
|
|
140
|
+
|
|
141
|
+
### Extended stamp shape (fixed key order preserved)
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
<!-- THRUM-REVIEW: stage=plan verdict=Ready:Yes cycle=<N> date=<YYYY-MM-DD> verify=<...> plan_obj=<blob> src_lock=<blob> [rev=... notes="..."] -->
|
|
145
|
+
<!-- THRUM-REVIEW: stage=prompt verdict=Ready:Yes cycle=<N> date=<YYYY-MM-DD> prompt_obj=<blob> plan_lock=<blob> -->
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`plan_obj`/`src_lock` and `prompt_obj`/`plan_lock` are appended after the
|
|
149
|
+
stage's existing keys, in this order — keeping the fixed-key-order convention
|
|
150
|
+
this file already establishes for the Authored-against stamp above.
|
|
151
|
+
|
|
87
152
|
## Read-time provenance re-derivation
|
|
88
153
|
|
|
89
154
|
Both checks below are computable from the two fields the stamp already carries
|
|
@@ -26,6 +26,10 @@ tree rule (a roster member that is a coordinator escalates to the human, not
|
|
|
26
26
|
to that coordinator). Everything below is additive — the brainstorm-specific
|
|
27
27
|
duties layered on top of that base.
|
|
28
28
|
|
|
29
|
+
Your roster is entirely remote (other boxes), so the base skill's remote
|
|
30
|
+
modal-unblock note applies to every roster member by default — never reach
|
|
31
|
+
for `thrum tmux key`, which is local-socket-only.
|
|
32
|
+
|
|
29
33
|
## Roster = brainstormers
|
|
30
34
|
|
|
31
35
|
Your `watch_params.roster` is the list of brainstormer agent names you
|
|
@@ -63,6 +67,17 @@ thread:
|
|
|
63
67
|
detection would miss it entirely if the send failed, was mis-addressed,
|
|
64
68
|
or the brainstormer simply froze before sending.
|
|
65
69
|
|
|
70
|
+
This backstop's actual capture loop runs as the same `thrum monitor`-
|
|
71
|
+
scheduled script the base `persistent-watcher-archetype` skill ships —
|
|
72
|
+
`resources/thrum-watch-pane-capture.sh` — pointed at your own roster of
|
|
73
|
+
brainstormers via your own `watch_params.json`. One parameterized script
|
|
74
|
+
serves both archetypes; only the roster contents and cadence differ, and
|
|
75
|
+
those are already `watch_params.json` fields, not script forks. Follow
|
|
76
|
+
that skill's "Driving your cycle" section for the setup-copy step and the
|
|
77
|
+
exact `thrum monitor start` registration — **`--notify-on-success` is
|
|
78
|
+
mandatory there too**, for the identical reason: a `--schedule`d job
|
|
79
|
+
delivers nothing on `--match` alone (thrum-ruz1z §5c).
|
|
80
|
+
|
|
66
81
|
## The queue (your pane console)
|
|
67
82
|
|
|
68
83
|
Render one unified list across every roster member, oldest-first:
|
|
@@ -52,24 +52,30 @@ These are the tiers now:
|
|
|
52
52
|
|
|
53
53
|
## Worked examples — LITERAL ARGUMENTS, AND EFFORT IS TOOL-DEPENDENT
|
|
54
54
|
|
|
55
|
-
**
|
|
56
|
-
per mechanism — re-check the schema in front of you rather than restating
|
|
57
|
-
memory, because it can change:
|
|
55
|
+
**Pin `model` on every spawn. Pass `effort` wherever the mechanism exposes it.**
|
|
56
|
+
Syntax per mechanism — re-check the schema in front of you rather than restating
|
|
57
|
+
from memory, because it can change:
|
|
58
58
|
|
|
59
59
|
| Mechanism | `model` | `effort` |
|
|
60
60
|
|---|---|---|
|
|
61
|
-
| **Agent tool** | ✅ settable |
|
|
61
|
+
| **Agent tool** | ✅ settable | ✖ **not exposed — do not pass it** |
|
|
62
62
|
| **Workflow `agent()`** (opts: label, phase, schema, model, effort, isolation, agentType) | ✅ settable | ✅ settable |
|
|
63
63
|
| **Agent DEFINITION** (`.claude/agents/*.md` / plugin `agents/*.md` frontmatter) | ✅ | ✅ — sets the default for that agent type |
|
|
64
|
+
| **`thrum tmux create` / `launch`** | ✅ `--model` | ✅ `--effort` — pass on BOTH |
|
|
64
65
|
|
|
65
|
-
**Agent tool —
|
|
66
|
+
**Agent tool — `model` only. It takes no `effort` argument.**
|
|
66
67
|
|
|
67
68
|
```python
|
|
68
|
-
Agent(subagent_type="general-purpose", model="sonnet",
|
|
69
|
+
Agent(subagent_type="general-purpose", model="sonnet",
|
|
69
70
|
description="Code-quality review of <branch>",
|
|
70
71
|
prompt="...")
|
|
71
72
|
```
|
|
72
73
|
|
|
74
|
+
**The tier philosophy still governs even where the Agent tool cannot express it:**
|
|
75
|
+
sonnet-low for investigation and mechanical work, sonnet-medium for reviewers,
|
|
76
|
+
verifiers and implementers. Where effort is not settable, carry the intent through
|
|
77
|
+
the agent definition, the Workflow opts, or the tmux launch flags.
|
|
78
|
+
|
|
73
79
|
An agent definition can also carry a default:
|
|
74
80
|
|
|
75
81
|
```yaml
|
|
@@ -87,8 +93,9 @@ agent(prompt, { model: "sonnet", effort: "low" }) // mechanical sub-
|
|
|
87
93
|
```
|
|
88
94
|
|
|
89
95
|
**THE DIRECTIVE, imperative and not a comment: REVIEWERS RUN SONNET AT MEDIUM
|
|
90
|
-
EFFORT.** Pass `effort: "medium"` literally
|
|
91
|
-
|
|
96
|
+
EFFORT.** Pass `effort: "medium"` literally under Workflow, in the agent
|
|
97
|
+
definition, or on `thrum tmux create`/`launch`. Never dispatch a reviewer "from
|
|
98
|
+
memory".
|
|
92
99
|
|
|
93
100
|
**AND VERIFY, DO NOT ASSERT:** any claim about what a tool does or does not expose
|
|
94
101
|
must be checked against the schema in front of you.
|
|
@@ -112,9 +119,10 @@ plus brainstormer → opus-medium and brainstormer's own subagents → sonnet-lo
|
|
|
112
119
|
|
|
113
120
|
## Pin every spawn explicitly — the floor depends on the ROLE, not the depth
|
|
114
121
|
|
|
115
|
-
Every orchestrator MUST pass an explicit `model:`
|
|
116
|
-
it
|
|
117
|
-
Opus orchestrator that forgets the pin just
|
|
122
|
+
Every orchestrator MUST pass an explicit `model:` on EVERY subagent it spawns, and
|
|
123
|
+
`effort` wherever the mechanism exposes it. An unspecified subagent SILENTLY
|
|
124
|
+
INHERITS THE PARENT'S MODEL — so an Opus orchestrator that forgets the pin just
|
|
125
|
+
spent Opus tokens on a grep.
|
|
118
126
|
|
|
119
127
|
**The floor is set by what the agent DOES, not by how deep it sits** — see the
|
|
120
128
|
Agent tiers table above. This applies recursively: an implementer spawning its
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conversation-only-context
|
|
3
|
+
description: "Use at the START of any long-lived agent session (researcher, coordinator, orchestrator, brainstormer), and whenever the operator says we are going to have a long conversation, get a lot done, or keep many threads open - also when context is climbing, when several investigations are in flight at once, when you are about to read code or a multi-file doc set to investigate, or when you catch yourself opening a log to just check quickly."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Conversation-only context — spend your own context on the human, dispatch everything else
|
|
7
|
+
|
|
8
|
+
Your context is the session's scarcest resource. It buys conversation with the
|
|
9
|
+
operator, messaging, queue and state — nothing else. Every investigation,
|
|
10
|
+
verification, root-cause, code read, doc summary and VM job goes to a named
|
|
11
|
+
persistent subagent that writes its result to a file.
|
|
12
|
+
|
|
13
|
+
`<scratch>` below means `/private/tmp` on macOS (where `/tmp` is a symlink) and
|
|
14
|
+
`/tmp` on Linux. Check with `[ -L /tmp ]`; never assume from the platform name.
|
|
15
|
+
|
|
16
|
+
## Wake procedure
|
|
17
|
+
|
|
18
|
+
0. Caller binding decays to anonymous across a restart. Before the first mutating
|
|
19
|
+
thrum command, re-warm it: `thrum prime >/private/tmp/rw.txt 2>&1` — output to
|
|
20
|
+
a file, **not** back into context.
|
|
21
|
+
1. Read the **full** prime briefing with the Read tool, every page, in order. Name
|
|
22
|
+
one load-bearing fact from the **middle** of it before taking any action.
|
|
23
|
+
2. Execute the resume plan from the newest restart snapshot.
|
|
24
|
+
3. Reconcile `thrum queue list` against `thrum state show personal_state:current`.
|
|
25
|
+
4. Read `thrum inbox --unread` **and** run `thrum message search` — stale unread
|
|
26
|
+
sorts last and is missed by the inbox page. Mark each processed message read
|
|
27
|
+
(`thrum message read <id>`).
|
|
28
|
+
5. Where the snapshot lists live subagents (§ restart handoff), respawn each one
|
|
29
|
+
with its result file as seed before doing its work yourself.
|
|
30
|
+
|
|
31
|
+
## The dispatch rule
|
|
32
|
+
|
|
33
|
+
**The parent never reads code, logs, or multi-file doc sets to investigate.**
|
|
34
|
+
Ask a subagent a smaller question instead. "I will just check quickly" is the
|
|
35
|
+
failure this skill exists to prevent.
|
|
36
|
+
|
|
37
|
+
| Parent keeps inline | Parent dispatches |
|
|
38
|
+
|---|---|
|
|
39
|
+
| Reading and replying to messages (`thrum reply --stdin <<'EOF'`) | Any code read, grep sweep, or call-site audit |
|
|
40
|
+
| Queue and state reconcile | Root-cause investigation |
|
|
41
|
+
| Talking to the human at the pane | Verification of an implementer's work |
|
|
42
|
+
| Owner-only decisions, surfaced with a recommendation | Summarizing a doc set |
|
|
43
|
+
| Tiny mechanical git **in its own worktree** (commit/push docs) | Any VM job |
|
|
44
|
+
| Reading a subagent's ≤20-line summary | Anything whose tool output runs to pages |
|
|
45
|
+
| The ONE file a question names — a subagent's report, or a deliverable you were asked to review | Everything the named file cites |
|
|
46
|
+
|
|
47
|
+
That row is purpose, not size: a file handed to you to act on is conversation,
|
|
48
|
+
not investigation.
|
|
49
|
+
|
|
50
|
+
Never spawn a subagent to operate inside another agent's worktree.
|
|
51
|
+
|
|
52
|
+
## Dispatch shape
|
|
53
|
+
|
|
54
|
+
Every dispatch carries: an explicit `name:`, an explicit `model:`, an output file
|
|
55
|
+
path, a ≤15-20 line reply cap, and the pasted CONSTRAINTS block. Launch
|
|
56
|
+
independent subagents **in one message** so they run in parallel.
|
|
57
|
+
|
|
58
|
+
Pin `model:` on every spawn. Pass `effort` only where the mechanism exposes it —
|
|
59
|
+
Workflow `agent()` opts, agent-definition frontmatter, `thrum tmux launch
|
|
60
|
+
--effort`. **The Agent tool does not take `effort`.**
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
Agent(subagent_type="general-purpose",
|
|
64
|
+
name="<prefix>_<slug>",
|
|
65
|
+
model="sonnet",
|
|
66
|
+
description="<3-5 words>",
|
|
67
|
+
prompt="""<task>
|
|
68
|
+
|
|
69
|
+
WRITE your result to <scratch>/<prefix>-<slug>.md. Reply to me with a
|
|
70
|
+
summary of at most 20 lines - no logs, no transcripts, no command dumps.
|
|
71
|
+
|
|
72
|
+
=== CONSTRAINTS ... === # pasted verbatim, see below
|
|
73
|
+
""")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Write is fine for a NEW result file; to update an existing one, Read it first or
|
|
77
|
+
append with a heredoc (`cat >> <path> <<'EOF'`) — Write refuses to overwrite a
|
|
78
|
+
file it has not read this session.
|
|
79
|
+
|
|
80
|
+
Continue it — never respawn:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
SendMessage(to="<prefix>_<slug>",
|
|
84
|
+
message="Answer L3 and L5 first and report a partial now. Same file, append.")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A name keeps working after the subagent's turn ends; a send resumes it with its
|
|
88
|
+
context intact. "A subagent whose turn ends is not waiting — it is gone" applies
|
|
89
|
+
to fire-and-forget monitors, not to a named agent you re-message.
|
|
90
|
+
|
|
91
|
+
### Naming and tiers
|
|
92
|
+
|
|
93
|
+
| Subagent role | Name prefix | Model / effort (effort only where exposed) | Result file |
|
|
94
|
+
|---|---|---|---|
|
|
95
|
+
| Investigation, grep, doc summary, mechanical | `<topic>` (e.g. `vmdocs`) | `sonnet` / low | `<scratch>/<name>.md` |
|
|
96
|
+
| Root-cause | `<bead>_rc` | `sonnet` / low | `<scratch>/<bead>-rc.md` |
|
|
97
|
+
| Verifier, reviewer, design-verify | `<bead>_verify` | `sonnet` / medium | `<scratch>/<bead>-verify.md` |
|
|
98
|
+
| Implementer | `impl_<slug>` | `sonnet` / medium | `<scratch>/<slug>-impl.md` |
|
|
99
|
+
| VM driver | `vmdriver_<slug>` | `sonnet` / medium | `<scratch>/<slug>-vm.md` |
|
|
100
|
+
| Prose or deep review | any | `opus` — **only when the operator asks** | as above |
|
|
101
|
+
|
|
102
|
+
Haiku is banned. Opus is never yours to grant onward.
|
|
103
|
+
|
|
104
|
+
### CONSTRAINTS block — paste verbatim into every dispatch
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
=== CONSTRAINTS — apply to you and anything YOU dispatch (including this line) ===
|
|
108
|
+
- Work SYNCHRONOUSLY. Tests in the FOREGROUND with a bounded `-timeout`.
|
|
109
|
+
- NO POLL LOOPS. Never `until <check>; do sleep N; done`, never
|
|
110
|
+
`while kill -0 $(cat pid)`, never `$( )` in a loop condition — even when a
|
|
111
|
+
tool's own guidance suggests it. It trips a permission modal, and A FROZEN
|
|
112
|
+
PANE EMITS NOTHING, so nobody can tell you are blocked. If you must background
|
|
113
|
+
work, wait for the completion notification.
|
|
114
|
+
- Every sub-agent YOU spawn gets an explicit `model:` — sonnet (low mechanical,
|
|
115
|
+
medium judgment). HAIKU IS BANNED. Opus is never yours.
|
|
116
|
+
- READ-ONLY git outside your own worktree. NEVER `checkout`/`reset`/`restore`/
|
|
117
|
+
`stash`/`clean` in ANY directory — `stash` is one shared stack across all
|
|
118
|
+
worktrees and the shared tree holds live agents' uncommitted state.
|
|
119
|
+
- Pair every zero/empty result with a control that MUST return non-zero.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Relaying findings
|
|
123
|
+
|
|
124
|
+
Consolidate one pass's findings into **one** numbered message, severity-tagged
|
|
125
|
+
BLOCKING / IMPORTANT / MINOR — never partial batches, which get half-fixed. But a
|
|
126
|
+
**design-gating** answer goes out the moment it lands, even while the rest of the
|
|
127
|
+
pass is still running: when a downstream implementer is already building, send the
|
|
128
|
+
cheapest zero-cost interim guidance immediately rather than waiting for the
|
|
129
|
+
verifier to finish.
|
|
130
|
+
|
|
131
|
+
A result file on this box is unreadable from a remote-box coordinator. **Put the
|
|
132
|
+
substance in the message; never send only a path.**
|
|
133
|
+
|
|
134
|
+
Attribute what you did not verify yourself: forwarding a subagent's finding as
|
|
135
|
+
your own claim launders an unchecked assertion into your authority. Either read
|
|
136
|
+
the cited line, or write "from my verifier, cites `file:line`".
|
|
137
|
+
|
|
138
|
+
## Pane discipline
|
|
139
|
+
|
|
140
|
+
Write to the pane only when the human asked, or when the decision is one only the
|
|
141
|
+
human can make. Everything else goes through `thrum send` / `thrum reply`.
|
|
142
|
+
Surface an owner-only decision crisply, with a recommendation attached.
|
|
143
|
+
|
|
144
|
+
## VM work
|
|
145
|
+
|
|
146
|
+
Use a named persistent driver (`vmdriver_<slug>`) per
|
|
147
|
+
`dev-docs/daemon-wedge-sync-audit/execution-pattern-driver-subagents-in-vm.md`:
|
|
148
|
+
reach the VM only through the repo's tmux-ssh script, state the three-clause GO
|
|
149
|
+
(execute now · drive to completion yourself · then stop and report), require the
|
|
150
|
+
isolation proof and the 3-section report. VM specifics — profiles, first-boot
|
|
151
|
+
re-key, isolation legs — are in `dev-docs/vm-dev-patterns/`. Never do VM work
|
|
152
|
+
yourself.
|
|
153
|
+
|
|
154
|
+
## Restart handoff
|
|
155
|
+
|
|
156
|
+
Subagents do not survive a restart. Before restarting:
|
|
157
|
+
|
|
158
|
+
1. Tell every live subagent to finish and write its file.
|
|
159
|
+
2. Record in the restart snapshot, per subagent: **name · what it owned · result
|
|
160
|
+
file path · model/effort**.
|
|
161
|
+
3. State which subagents have already exited.
|
|
162
|
+
|
|
163
|
+
The successor respawns each one with its result file as seed.
|
|
164
|
+
|
|
165
|
+
Restart on a number read from the pane footer, never on a felt sense.
|
|
166
|
+
|
|
167
|
+
## See Also
|
|
168
|
+
|
|
169
|
+
- `efficient-multi-agent-research` — the fan-out sibling: partition, investigate to
|
|
170
|
+
disk, consolidate, for N>6 items. This skill sets the session's posture; that one
|
|
171
|
+
runs a single pass.
|
|
172
|
+
- `choosing-subagent-models` — the tier policy and source of the CONSTRAINTS block.
|
|
173
|
+
|
|
174
|
+
## Failure modes
|
|
175
|
+
|
|
176
|
+
- Respawning a fresh single-shot subagent for a follow-up when a named one already holds the context — `SendMessage` it; a respawn loses the context and pays the re-read.
|
|
177
|
+
- A fire-and-forget "monitor until done" agent that arms a monitor and ends its turn — measured at 70 min, 122k tokens, zero output. Put the drive-to-completion clause in every dispatch.
|
|
178
|
+
- The parent reading code or docs "to just check quickly" — message the subagent that already read them and ask for the one field.
|
|
179
|
+
- One subagent handed ten tasks — partition it and dispatch the parts in parallel.
|
|
180
|
+
- A spawn with no `name:` — it cannot be re-messaged. Name every spawn.
|
|
181
|
+
- A spawn with no `model:` — it silently inherits Opus. Pin `model:` on every spawn, and `effort` wherever the mechanism takes it.
|
|
182
|
+
- Holding a design-gating answer behind the rest of the pass while an implementer builds on the old premise — that one goes immediately; the rest ships as one consolidated message.
|
|
183
|
+
- Acting on the resume plan alone and skipping the middle of the prime — the falsified premise sits in the part you skipped.
|
|
184
|
+
- Trusting an older "not started" message over a newer "already running" one — verify, do not recall; that cuts both directions.
|
|
185
|
+
- Restarting without listing every live subagent's name and result file in the snapshot — the successor cannot respawn what is not written down.
|
|
186
|
+
- Forwarding a subagent's BLOCKING finding to a third party as your own claim — check the cited line, or attribute it; never state as yours what you did not verify.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coordinator-dispatching-work
|
|
3
|
-
description: "Use when starting an epic, dispatching to an implementer, creating a worktree for an agent, or spawning a sub-agent. Loads coordinator-specific discipline for kicking off implementation work."
|
|
3
|
+
description: "Use when starting an epic, dispatching to an implementer or orchestrator, choosing or re-routing which orchestrator gets a task, creating a worktree for an agent, or spawning a sub-agent. Loads coordinator-specific discipline for kicking off implementation work - routing by requirement and checking the target's load first."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Coordinator: Dispatching Work
|
|
@@ -72,8 +72,10 @@ optimization.
|
|
|
72
72
|
|
|
73
73
|
## Check the target's queue before you dispatch (R3)
|
|
74
74
|
|
|
75
|
-
Before dispatching,
|
|
76
|
-
<target>` (
|
|
75
|
+
Before dispatching, read the target's load two ways: its queue AND its pane. `thrum
|
|
76
|
+
queue list --agent <target>` (a well-run orchestrator keeps its queue current) plus a
|
|
77
|
+
quick `thrum tmux capture <target>` to see what it is mid-task on (message it to confirm
|
|
78
|
+
if its state is unclear). An empty result does NOT
|
|
77
79
|
mean free — it also shows for an agent that keeps no queue or that you mis-named;
|
|
78
80
|
confirm by message before assigning. `assign` appends to `assigned_to` with no
|
|
79
81
|
duplicate check, so this pre-dispatch read is the only guard against double-loading a
|
|
@@ -236,11 +238,21 @@ grep -E 'THRUM-REVIEW: stage=prompt verdict=Ready:Yes([[:space:]]|-->|$)' "$PROM
|
|
|
236
238
|
```
|
|
237
239
|
|
|
238
240
|
- **Stamp present** → the pre-dispatch review is already satisfied; SKIP
|
|
239
|
-
re-running it and dispatch.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
re-running it and dispatch. (The binding check itself — does the stamp still
|
|
242
|
+
bind the exact prompt object and locked plan — is `project-setup`'s job, not
|
|
243
|
+
this gate's; see its canonical contract, `project-setup` SKILL.md § "Step 5:
|
|
244
|
+
Stamp the prompt for the review loop" + Phase 0. Do not restate that recipe
|
|
245
|
+
here.)
|
|
246
|
+
- **Stamp absent on a project-setup-produced prompt is an ANOMALY, not a
|
|
247
|
+
sanctioned shortcut.** After the prompt-stamp fix (`project-setup` Step 5),
|
|
248
|
+
project-setup cannot report completion or dispatch-readiness without
|
|
249
|
+
appending this stamp — so a project-setup prompt reaching dispatch without
|
|
250
|
+
one means something skipped a step. Do not treat its absence as normal; run
|
|
251
|
+
the full pre-dispatch dual review AND surface the missing stamp as a finding.
|
|
252
|
+
The one legitimate, narrow carve-out: **pre-feature prompts and prompts from
|
|
253
|
+
non-project-setup flows** genuinely never carry this stamp — for those, fall
|
|
254
|
+
through to the normal pre-dispatch dual review same as before, with no
|
|
255
|
+
anomaly flag.
|
|
244
256
|
|
|
245
257
|
This is the prose side of the boundary; the post-DONE dual-review (which reviews
|
|
246
258
|
the implementer's CODE, a different artifact) always runs separately. The prompt
|
|
@@ -251,6 +263,14 @@ post-DONE side.)
|
|
|
251
263
|
|
|
252
264
|
When the fleet has standing orchestrators and the work is a full plan execution:
|
|
253
265
|
|
|
266
|
+
🔴 **Route by what the work REQUIRES, not by which orchestrator is easiest to reach.**
|
|
267
|
+
An orchestrator co-located with special resources - a build/signing/release box, the
|
|
268
|
+
shared data store, a particular OS - is RESERVED for work that needs those resources;
|
|
269
|
+
all other work goes to a general-purpose orchestrator, addressed BY NAME. The
|
|
270
|
+
locally-visible orchestrator is the one you reach for by reflex, and that reflex
|
|
271
|
+
overloads it. Enumerate the FULL set first (local and remote alike), then pick by
|
|
272
|
+
requirement and by load - never by visibility.
|
|
273
|
+
|
|
254
274
|
### Step 1: Select an orchestrator
|
|
255
275
|
|
|
256
276
|
```bash
|
|
@@ -420,6 +420,74 @@ circuit-breaker — **BLOCKING**. Peer call with timeout but no circuit-breaker
|
|
|
420
420
|
**Triage:**
|
|
421
421
|
`git diff main...HEAD --name-only | xargs grep -n '{peer_call_patterns}'`
|
|
422
422
|
|
|
423
|
+
## Lens 11 — Census classification coverage (mandatory test, not a pattern check)
|
|
424
|
+
|
|
425
|
+
**Why:** `go test ./...` never runs in Pass-3 — targeted-test selection is
|
|
426
|
+
scoped to files the diff touches, and `internal/daemon/rpc/testcensus` is an ad
|
|
427
|
+
hoc test target nothing routes to. A merge that adds a `state.NewState(...)`
|
|
428
|
+
call site in an `internal/daemon/rpc/*_test.go` file (test-fixture setup, not a
|
|
429
|
+
handler) can leave it unclassified and desync the frozen census snapshot with
|
|
430
|
+
no gate catching it — trunk went RED post-merge on 573mo v87 for exactly this
|
|
431
|
+
reason (thrum-ykmly).
|
|
432
|
+
|
|
433
|
+
**Relationship to Lenses 1–10:** every other lens in this file is an
|
|
434
|
+
LLM-judgment pattern check — grep a pattern, read the surrounding code, apply
|
|
435
|
+
judgment. This lens is MECHANICAL: run the command, read PASS/FAIL. It shares
|
|
436
|
+
its evidentiary discipline with the philosophy gate's coincidence-detector-tests
|
|
437
|
+
lens and preamble rule 3 (RUN any test you make a claim about) rather than with
|
|
438
|
+
Lenses 1–10's pattern-matching shape.
|
|
439
|
+
|
|
440
|
+
**Own narrower trigger — this lens does NOT fire on every gate run.**
|
|
441
|
+
`internal/daemon/` is already a gate-wide `trigger_directories` entry, so this
|
|
442
|
+
gate already RUNS on any `internal/daemon/rpc/` diff. This lens carries its OWN
|
|
443
|
+
trigger on top of that (see `config.lenses.census_classification_coverage` and
|
|
444
|
+
`lens_scope_semantics`): it fires only when the diff (against merge-base,
|
|
445
|
+
preamble rule 4) either (a) touches `internal/daemon/rpc/testcensus/**`
|
|
446
|
+
directly, or (b) touches an `internal/daemon/rpc/*_test.go` file AND an
|
|
447
|
+
added/changed line matches `trigger_content_pattern` (bare `NewState(`,
|
|
448
|
+
alias-agnostic on purpose — a renamed import of the state package would defeat
|
|
449
|
+
a `state.`-prefixed match, and over-firing here costs one fast test run while
|
|
450
|
+
under-firing reproduces the exact hole this lens exists to close). A diff
|
|
451
|
+
elsewhere under `internal/daemon/` (state/, safedb/, scheduler/, or an
|
|
452
|
+
`rpc/*_test.go` edit that never touches a `NewState(` call) fires the GATE but
|
|
453
|
+
not THIS lens — record it walked-and-N/A. A non-test `.go` change in
|
|
454
|
+
`internal/daemon/rpc/` alone (e.g. a handler edit with no test change) does not
|
|
455
|
+
fire this lens either: `census.go`'s `Run()` only scans `*_test.go` source
|
|
456
|
+
non-recursively, so a handler-only edit cannot desync the census by
|
|
457
|
+
construction.
|
|
458
|
+
|
|
459
|
+
**How to apply:** Read `config.lenses.census_classification_coverage`. Check
|
|
460
|
+
the trigger per the paragraph above. If it fires, run
|
|
461
|
+
`config.lenses.census_classification_coverage.mandatory_test_command` in the
|
|
462
|
+
gate's throwaway worktree (preamble rule 2) and record the literal output.
|
|
463
|
+
|
|
464
|
+
**Gates `TestCensusClassificationCoverage` ONLY — deliberately NOT the whole
|
|
465
|
+
package.** `mandatory_test_command` is `-run`-scoped to
|
|
466
|
+
`^TestCensusClassificationCoverage$`. The sibling test in the same package,
|
|
467
|
+
`TestCensusRegeneration`, is an orthogonal count-snapshot drift detector — it
|
|
468
|
+
reds on ANY new top-level test function added anywhere in
|
|
469
|
+
`internal/daemon/rpc`, unrelated to whether any `NewState` site is classified.
|
|
470
|
+
Gating the whole package would false-block the next unrelated census-surface
|
|
471
|
+
diff on `TestCensusRegeneration`'s brittleness with zero added
|
|
472
|
+
defect-prevention (`ClassificationCoverage` alone already catches the defect
|
|
473
|
+
this lens exists for). `TestCensusRegeneration`'s own staleness is tracked
|
|
474
|
+
separately (nd2j1/P1 owns the classification data; the brittle count-snapshot
|
|
475
|
+
assertion itself is tracked as **thrum-33zzh** — "make advisory or drop the
|
|
476
|
+
raw-count assertion"), not by this lens.
|
|
477
|
+
|
|
478
|
+
**Severity:** Trigger fires and `mandatory_test_command` was not run before
|
|
479
|
+
merge, OR was run and is RED — **BLOCKING**. A RED result means an unclassified
|
|
480
|
+
`NewState` site: confirm `testdata/classifications.json` was regenerated per
|
|
481
|
+
`internal/daemon/rpc/testcensus/README.md` and re-committed on the same branch
|
|
482
|
+
before merge.
|
|
483
|
+
|
|
484
|
+
**Triage:**
|
|
485
|
+
```bash
|
|
486
|
+
git diff <merge-base>...<tip> --name-only | grep -E 'internal/daemon/rpc/testcensus/|internal/daemon/rpc/.*_test\.go$'
|
|
487
|
+
# for any *_test.go hit outside testcensus/, confirm content match:
|
|
488
|
+
git diff <merge-base>...<tip> -- <file> | grep -E '^\+.*NewState\('
|
|
489
|
+
```
|
|
490
|
+
|
|
423
491
|
## Cross-gate consolidation with the philosophy gate
|
|
424
492
|
|
|
425
493
|
This gate runs **in parallel** with `coordinator-philosophy-merge-gate` (both
|
|
@@ -176,6 +176,30 @@ executed half is **scoped to the change**:
|
|
|
176
176
|
- Any `*_tripwire_test.go` the change touches must stay green or the diff must
|
|
177
177
|
state why its premise changed; a tripwire edited to accommodate the change is a
|
|
178
178
|
finding, not a fix.
|
|
179
|
+
- 🔴 **The CLASS-firing governance gates, on the merged tree — they are BLIND to
|
|
180
|
+
changed-package scoping, exactly like the `//go:embed` asset in §5.**
|
|
181
|
+
`TestFixtureDriftGuard_NoUnlistedSchemaMirrors` (`internal/schema`) and
|
|
182
|
+
`TestEveryMirrorWriterIsSanctioned` (`tests/gate`) fire on a CLASS — a hand-rolled
|
|
183
|
+
`CREATE TABLE` for a real production table name, or a new durablelane / mirror
|
|
184
|
+
writer — so a diff adds the violation **without touching `internal/schema` or
|
|
185
|
+
`tests/gate`**, and the changed-package `-race` never runs them. Cheap trigger, run
|
|
186
|
+
it every merge: `git diff <base> <tip> | grep -iE 'CREATE TABLE|ALTER TABLE|durablelane|MirrorWriter|sanctionedMirrorWriters'`
|
|
187
|
+
— ANY hit ⇒ also run `go test ./internal/schema/ -run TestFixtureDriftGuard` and
|
|
188
|
+
`go test ./tests/gate/ -run TestEveryMirrorWriterIsSanctioned` on the MERGED tree.
|
|
189
|
+
Measured 2026-08-28: kgi7t (`TestEveryMirrorWriterIsSanctioned`) and xo58f
|
|
190
|
+
(`TestFixtureDriftGuard`) BOTH escaped per-merge Pass-3 this way and surfaced only
|
|
191
|
+
in the daily gate as trunk-reds. The changed-package rule below is necessary, not
|
|
192
|
+
sufficient — a class gate is a property of the TREE, not the neighbourhood.
|
|
193
|
+
🔴 **Also always-run, same class-gate reasoning:**
|
|
194
|
+
`TestNoMainStateDBWriteOwnerBypasses` (`internal/testgate`; corpus 5.1
|
|
195
|
+
owner-bypass-check) — a scanner ratchet over every raw main-State-DB write
|
|
196
|
+
site, blind to changed-package scoping the same way. Measured 2026-09-04:
|
|
197
|
+
thrum-mjmu3 Part a introduced a raw `r.db.ExecContext` outside
|
|
198
|
+
`safedb.WriteCoordinator` in a durable-lane cold-boot restore step; the
|
|
199
|
+
ratchet went 213→214 and gate_thrum's Pass-3 on that merge was CLEAN because
|
|
200
|
+
this test was not yet in the always-run set (thrum-wdt97). Run it on the
|
|
201
|
+
MERGED tree every merge: `go test ./internal/testgate/... -run
|
|
202
|
+
TestNoMainStateDBWriteOwnerBypasses`.
|
|
179
203
|
|
|
180
204
|
**Establish branch-attributable vs pre-existing from the changed-package `-race`
|
|
181
205
|
result** — a regression adds failures the same packages did not have before. Do
|
|
@@ -519,8 +519,11 @@ contract-drift and quality issues the internal reviewer misses.
|
|
|
519
519
|
stamp. Any FEASIBILITY finding (see Loop semantics) routes to the
|
|
520
520
|
coordinator instead of being folded inline.
|
|
521
521
|
6. Researcher repeats only if cycle-1 introduces new design surface (rare for
|
|
522
|
-
bounded mechanical plans); otherwise v2 LOCKED,
|
|
523
|
-
|
|
522
|
+
bounded mechanical plans); otherwise v2 LOCKED. Before stamping, compute
|
|
523
|
+
`plan_obj`/`src_lock` per `_stamp-protocol.md` § "Review-object binding"
|
|
524
|
+
(strip-then-hash the plan against itself for `plan_obj`; plain-hash the
|
|
525
|
+
LOCKED source for `src_lock`), then stamp
|
|
526
|
+
`<!-- THRUM-REVIEW: stage=plan verdict=Ready:Yes cycle=<N> date=<YYYY-MM-DD> verify=Ready:Yes plan_obj=<blob> src_lock=<blob> -->`.
|
|
524
527
|
7. Researcher signals plan LOCKED back to coord, citing both review passes
|
|
525
528
|
and the Deviations block.
|
|
526
529
|
|
|
@@ -561,11 +564,12 @@ review catches translation errors (plan → prompt) plus prompt-specific quality
|
|
|
561
564
|
`requesting-code-review` SUPPLEMENTARY — clarity, scope language,
|
|
562
565
|
dispatch-readiness, sub-agent model guidance, DONE-shape spec.
|
|
563
566
|
3. Researcher consolidates → folds inline → re-issues prompt; `project-setup`
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
567
|
+
OWNS and applies the prompt-stage stamps — the gate footer, the
|
|
568
|
+
binding-bound verdict stamp, and the hard completion requirement that it
|
|
569
|
+
not exist without one — per that skill's canonical contract (`project-setup`
|
|
570
|
+
SKILL.md § "Step 5: Stamp the prompt for the review loop" + Phase 0). This
|
|
571
|
+
step does not restate that mechanics; it exists only to confirm the stamp
|
|
572
|
+
is applied within the same project-setup session, before signaling back.
|
|
569
573
|
4. Researcher signals "project-setup complete + post-setup dual-review applied"
|
|
570
574
|
back to coord, citing both review passes + final artifact paths.
|
|
571
575
|
|
|
@@ -125,10 +125,10 @@ hard to consolidate. Specify table columns in every prompt.
|
|
|
125
125
|
**Not specifying output file paths** - Agents may write to the working directory
|
|
126
126
|
or not at all. Always include the exact output path in every prompt.
|
|
127
127
|
|
|
128
|
-
**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
**Write is fine for a NEW findings file; a heredoc is the safe form for updating
|
|
129
|
+
one.** Write refuses to overwrite a file it has not Read this session, so a
|
|
130
|
+
round-2 agent updating an existing findings file fails unless it Reads it first.
|
|
131
|
+
Prefer `cat >> <path> <<'EOF'` to append on later rounds.
|
|
132
132
|
|
|
133
133
|
**Put this in every prompt:**
|
|
134
134
|
|
|
@@ -136,8 +136,9 @@ The filesystem is writable; the tool is what refuses.
|
|
|
136
136
|
READ-ONLY EVERYWHERE EXCEPT YOUR OUTPUT FILE.
|
|
137
137
|
Do not modify source, tests, config, git state, or the issue tracker.
|
|
138
138
|
You MUST write your findings to exactly: dev-docs/<topic>/findings_N.md
|
|
139
|
-
Write
|
|
140
|
-
|
|
139
|
+
Write is fine for creating it. To UPDATE it on a later round, Read it first or
|
|
140
|
+
append with a heredoc (cat >> <path> <<'EOF' ... EOF) — Write refuses to
|
|
141
|
+
overwrite a file it has not Read. That single write is expected and authorized.
|
|
141
142
|
```
|
|
142
143
|
|
|
143
144
|
If an agent still returns text instead of a file, persist it yourself verbatim
|