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,674 @@
1
+ ---
2
+ name: coordinator-context-monitoring
3
+ description: "Use when managing live implementer/brainstormer agents during a long coordination session, at epic merge gates, after a busy dispatch hour, or whenever you suspect an agent is approaching context limits. Prevents 97%-context silent blow-ups by running a sweep + pre-emptive restart before the agent degrades. Safe to wire into a recurring cron that INVOKES this skill — the skill applies tier-ladder judgment, surfacing (and, only when explicitly opted in via config, autonomously restarting) at the >85% tier. What's forbidden is a cron/script that fires restarts unconditionally without going through this skill's tier ladder."
4
+ ---
5
+
6
+ # Coordinator: Context Monitoring and Pre-emptive Restart
7
+
8
+ ## When to invoke
9
+
10
+ Trigger this pattern at each of:
11
+
12
+ - Receipt of an `ALERT: flagged=…` message from the `context-monitoring` thrum
13
+ monitor (the canonical scheduled sweep, outer tick `@every 30m`, self-gated
14
+ so effective rate is slower; see "How the scheduled sweep works")
15
+ - Epic merge gates (after merging a sub-epic — E6.4, E6.5, etc.)
16
+ - After dispatching 3+ tasks in quick succession
17
+ - After any agent has been running for 60+ minutes without a restart
18
+ - On observing slow or degraded responses from an implementer
19
+ - Manually whenever the session feels "intense" (lots of cycles in a short
20
+ window)
21
+
22
+ The skill applies tier-ladder judgment: the >85% tier surfaces a
23
+ `recommend-restart-extended` reason (or, when `restart_actuation=true` in
24
+ config, runs a snapshot-gated restart) only on actual ctx %, not on every sweep.
25
+ What's forbidden is a script that bypasses this skill and fires
26
+ `thrum tmux restart --force` unconditionally (that violates
27
+ `feedback_restart_discipline` — burn the runway, don't restart on schedule).
28
+
29
+ ## Coordinator self-restart — ALWAYS `/thrum:restart-extended`
30
+
31
+ This skill's tier ladder (Steps 3–5) governs how the coordinator restarts
32
+ **other** agents. When the **coordinator restarts ITSELF**, a different rule
33
+ applies and it is absolute:
34
+
35
+ > **The coordinator ALWAYS self-restarts via `/thrum:restart-extended`, NEVER
36
+ > `/thrum:restart`.**
37
+
38
+ Rationale: the coordinator is the nerve center. Its handoff must carry the full
39
+ 16-section snapshot (wire contracts, capability matrix, cycle history, open
40
+ L-questions, anticipated Q&A, design rationale) so the next session resumes
41
+ steady-state coordination without re-deriving fleet state. The standard
42
+ `/thrum:restart` compact format drops that load-bearing context and strands the
43
+ next session. This holds regardless of which tier the coordinator's own context
44
+ sits in — there is no ctx-band at which the coordinator downgrades to the
45
+ compact restart.
46
+
47
+ Practical trigger points for coordinator self-restart:
48
+
49
+ - A warn-tier context nudge fires on the coordinator's own pane (e.g. 70%+).
50
+ - The coordinator hits a clean checkpoint and elects a fresh session.
51
+ - Rate-limit / stuck-state recovery on the coordinator itself.
52
+
53
+ In every one of these, run `/thrum:restart-extended`. (Captured as role-rule
54
+ `coord-always-restart-extended` /
55
+ `Coordinator ALWAYS uses /thrum:restart-extended`; this skill is the
56
+ always-loaded home so the rule survives even when memory isn't consulted.)
57
+
58
+ Note the distinction from Steps 3–5 below: those steps drive `/thrum:restart`
59
+ (Step 4) and `/thrum:restart-extended` (Step 5) into
60
+ **implementer/brainstormer** panes per their ctx tier — that tiering is correct
61
+ for worker agents. The carve-out here is ONLY about the coordinator's own
62
+ restart, which is unconditionally the extended form.
63
+
64
+ ## How the scheduled sweep works (v0.10.6+ — thrum monitor)
65
+
66
+ The sweep runs as a daemon-managed `thrum monitor` job named
67
+ `context-monitoring`, registered with an `@every 30m` schedule. The 30-min tick
68
+ is the outer envelope — the sweep is self-gated: daytime ticks only fire
69
+ when the fleet is busy (`agent_status=working`), and overnight ticks fire only
70
+ every other tick (effective 60-min rate), so most ticks may be silent no-ops.
71
+ There is a SINGLE monitor for ALL lenses in v1 — not one per lens. The job
72
+ invokes
73
+ `scripts/error-and-context-agent-sweep.sh --no-nudge --out /tmp/agent-sweep.txt`.
74
+ The script emits a single consolidated `ALERT:` line to stdout when ANY agent
75
+ crosses a threshold (ctx >= 50% OR api-error OR capture-fail); when the fleet is
76
+ clean, the script is silent so no message fires. The monitor's
77
+ `--match '^ALERT:'` filter routes the ALERT line as a message to the
78
+ `coordinator` role (fanned out to every LIVE local coordinator — if no
79
+ coordinator is currently live, the sweep is a silent no-op rather than queuing
80
+ for an offline one), which triggers this skill.
81
+
82
+ Format of the ALERT line:
83
+
84
+ ```text
85
+ ALERT: flagged=N stuck=S stuck_working=W tier3=T tier2=U — agent_a(92%,api-err,STUCK,stuck-working); agent_b(88%); …
86
+ ```
87
+
88
+ or, on a benign-only tick once the periodic heartbeat interval has elapsed:
89
+
90
+ ```text
91
+ ALERT: all-clear — N agents healthy, 0 actionable (sweep alive)
92
+ ```
93
+
94
+ - `flagged` — total agents needing attention
95
+ - `stuck` — api-errored on TWO consecutive sweeps (state file tracks this across
96
+ runs)
97
+ - `stuck_working` — agent_status=working AND tmux quiet > threshold AND no
98
+ recent JSONL tool calls; threshold tunable via `--silence-threshold-min`,
99
+ default 10 min
100
+ - `tier3` — count with ctx >= 85% (snapshot-gated restart candidates; see
101
+ Step 5)
102
+ - `tier2` — count with 70-84% ctx (tmux-send nudge candidates)
103
+ - Per-agent segment: `name(ctx%,reason-if-any,classifier)` joined by `;`
104
+
105
+ ### RUN vs DELIVER
106
+
107
+ The sweep now ALWAYS computes flags every scheduled tick (daytime; overnight
108
+ keeps its even-tick-only cadence) — RUN is no longer gated on a coarse
109
+ fleet-busy signal, which used to silently suppress genuinely actionable signals
110
+ before flags were even computed.
111
+
112
+ DELIVERY of the `ALERT:` line is a separate decision, made AFTER flags are
113
+ computed:
114
+
115
+ - **ACTIONABLE** (delivers the real ALERT) — tier2/tier3 ctx, workstall, stuck /
116
+ stuck_working, waiting-on-coord, blocked-on-human, capture-fail,
117
+ ctx-unknown-idle, api-err, idle-mid-task (abandoned), finished-impl (recommend
118
+ teardown), awaiting-restart, pending-human.
119
+ - **BENIGN-ONLY** (never delivers on its own) — idle-no-task and/or bare ctx
120
+ tier1 (50-69%) alone. `flagged=20 tier3=0` where every one of those 20 is
121
+ benign-only does **not** fire an ALERT — `flagged` counts everything written
122
+ to the per-agent report; it does not imply delivery.
123
+ - **Periodic all-clear heartbeat** — configurable via `.thrum/config.json`
124
+ `heartbeat.allclear_interval_min` (default 60). If no actionable ALERT has
125
+ fired for that long, the sweep emits the minimal `all-clear` line above so a
126
+ benign-quiet fleet is never mistaken for a dead monitor. Any delivered ALERT
127
+ (actionable or all-clear) resets this clock.
128
+
129
+ The full per-agent report stays at `/tmp/agent-sweep.txt` (overwritten each
130
+ sweep) for on-demand drill-down — read it AFTER receiving an ALERT to see which
131
+ specific panes are at risk.
132
+
133
+ The previous keepalive-cron pattern (CronCreate `<id>`) is deprecated in
134
+ favor of this scheduled monitor. The bookkeeping responsibility moves out of the
135
+ coordinator's per-session re-add chore and into the daemon's durable monitors
136
+ table (survives daemon restart, no per-session re-init needed for this monitor —
137
+ though OTHER CronCreate jobs may still require it per
138
+ `feedback_cron_reinit_each_session`).
139
+
140
+ ## Lenses
141
+
142
+ Every lens SURFACES or RECOMMENDS by default — none take autonomous action on
143
+ their own. The ONLY exception is `context_tiers`' snapshot-gated restart, and
144
+ even that requires an explicit `restart_actuation: true` opt-in in config. There
145
+ is a single monitor for all lenses in v1, not one per lens.
146
+
147
+ ### Default-ON lenses (implemented, E1–E6)
148
+
149
+ | Lens | Default | Disposition |
150
+ | --------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
151
+ | `context_tiers` | ON | The ctx%/stuck-working ladder (L1). Sole autonomous actuator when `restart_actuation=true`; otherwise RECOMMEND via `recommend-restart-extended` reason in ALERT. |
152
+ | `idle_mid_task` | ON | L2 — 30-min idle-with-open-task detection (bead cross-ref). RECOMMEND `/thrum:sleep-extended`; a no-task variant REPORTs `idle-no-task` at lower priority. |
153
+ | `snapshot_awaiting_restart` | ON | L3 — pane text + fresh-snapshot two-signal heuristic. RECOMMEND `thrum tmux restart <agent>` (never autonomous). |
154
+ | `blocked_on_human_modal` | ON | L4 — detects spend-limit/permission/consent modal prompts. DETECTION ONLY, never auto-answers; routes into the L5 ledger. |
155
+ | `pending_human_ledger` | ON | L5 — flat JSONL ledger of items awaiting a human. EXEMPT from D5 backoff — surfaces every tick. `hb_ledger_resolve` (the resolution path) is defined but has zero call sites — nothing marks an entry resolved, so the ledger is append-only-forever, not "until resolved." |
156
+ | `waiting_on_coord` | ON | L9 — 21-rule pattern match (folded in from the standalone waiting-on-coord sweep) + warm-hold exemption. RECOMMEND coordinator answer. |
157
+
158
+ ### Default-OFF lenses (E8, flag-gated — NOT YET IMPLEMENTED as of E7)
159
+
160
+ These lenses are reserved for E8. They exist as config flags for future work but
161
+ produce no output until enabled.
162
+
163
+ | Lens | Default | Disposition |
164
+ | ----------------------- | ------- | -------------------------------- |
165
+ | `merge_ready_queue` | OFF | L6 — REPORT |
166
+ | `merge_reconciliation` | OFF | L7 — REPORT |
167
+ | `base_deploy_staleness` | OFF | L8 — REPORT |
168
+ | `ready_vs_capacity` | OFF | L10 — RECOMMEND |
169
+ | `stale_agent_gc` | OFF | L11 — propose-only GC, RECOMMEND |
170
+ | `daemon_wedge_trend` | OFF | L12 — REPORT |
171
+
172
+ ## Configuration
173
+
174
+ The heartbeat system is configured under the `heartbeat` key in
175
+ `.thrum/config.json` (Go type: `HeartbeatConfig` in
176
+ `internal/config/heartbeat.go`). Example:
177
+
178
+ ```json
179
+ {
180
+ "heartbeat": {
181
+ "schema_version": 1,
182
+ "enabled": true,
183
+ "cadence_minutes_day": 30,
184
+ "cadence_minutes_overnight": 60,
185
+ "busy_only_daytime": true,
186
+ "busy_signal": "working_or_activity_30m",
187
+ "overnight_window": {
188
+ "from": "22:00",
189
+ "to": "07:00",
190
+ "tz": "America/New_York"
191
+ },
192
+ "backoff": { "start_after": 2, "multiplier": 2, "floor_every": 4 },
193
+ "lenses": {
194
+ "context_tiers": {
195
+ "enabled": true,
196
+ "params": {
197
+ "restart_actuation": false,
198
+ "degrading_flag_after_ticks": 3
199
+ }
200
+ },
201
+ "idle_mid_task": { "enabled": true },
202
+ "...": "..."
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ **Field reference:**
209
+
210
+ | Field | Purpose |
211
+ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
212
+ | `cadence_minutes_day` | Daytime cadence target (default 30 min). The underlying monitor runs `@every 30m` and self-gates so only busy-fleet daytime ticks fire. |
213
+ | `cadence_minutes_overnight` | Overnight cadence target (default 60 min). The monitor fires every other `@every 30m` tick overnight (effective 60-min rate). |
214
+ | `busy_only_daytime` | When `true`, daytime ticks are skipped unless any agent has `agent_status=working` (D3 busy-signal gate). |
215
+ | `busy_signal` | Algorithm for "is the fleet busy?" Default `working_or_activity_30m`: any agent working OR activity in the last 30 min. |
216
+ | `overnight_window.tz` | **MUST be an explicit IANA timezone** (e.g. `"America/New_York"`). If unset, the sweep warns and falls back to always-daytime-rate rather than silently guessing. |
217
+ | `backoff.start_after` | Number of consecutive unchanged detections of the same problem before D5 progressive backoff begins. |
218
+ | `backoff.multiplier` | Each backoff step multiplies the surface interval by this factor (default 2×). |
219
+ | `backoff.floor_every` | The surface interval is capped at this many ticks — the problem is never fully silenced (always surfaces at least once per `floor_every` ticks). |
220
+ | `lenses.<name>.enabled` | Enable/disable a lens individually. |
221
+ | `lenses.<name>.params` | Per-lens parameters. The only actuation opt-in in the whole system is `context_tiers.params.restart_actuation` (default `false`). |
222
+ | `lenses.context_tiers.params.degrading_flag_after_ticks` | After this many consecutive tier3 surfaced ticks without a successful restart, a `degrading-no-restart` reason is added to the ALERT (default 3). |
223
+
224
+ ### Editing the config (there is NO `thrum config set`)
225
+
226
+ `thrum config` only has a `show` subcommand — you **hand-edit
227
+ `.thrum/config.json`** to configure the heartbeat. Two gotchas make this
228
+ non-obvious; both bite silently.
229
+
230
+ **1. All-or-nothing merge (the sharp footgun).** The loader
231
+ (`internal/config/daemon.go`, `LoadThrumConfig`) applies
232
+ `DefaultHeartbeatConfig()` **only when the stanza is effectively absent**
233
+ (`schema_version == 0` AND zero lenses):
234
+
235
+ ```go
236
+ if cfg.Heartbeat.SchemaVersion == 0 && len(cfg.Heartbeat.Lenses) == 0 {
237
+ cfg.Heartbeat = DefaultHeartbeatConfig()
238
+ }
239
+ ```
240
+
241
+ There is **no field-level deep merge.** The moment you write a stanza with
242
+ `"schema_version": 1`, every field you _omit_ takes its Go zero-value —
243
+ `cadence_minutes_day: 0`, `enabled: false`, and **all 12 lenses disabled**. So
244
+ "I'll just set the timezone" with a three-line stanza silently turns the whole
245
+ sweep off. **Always write the COMPLETE stanza** — copy
246
+ `DefaultHeartbeatConfig()` (`internal/config/heartbeat.go`) in full, then change
247
+ only what you need.
248
+
249
+ **2. `overnight_window.tz` MUST be an explicit IANA zone.** Default is `""` (the
250
+ source comment reads "TZ set by operator"). Left unset, the sweep prints
251
+ `warning: heartbeat.overnight_window.tz not configured — using local time for gating`
252
+ and falls back to the box's local clock (warns rather than silently
253
+ guessing). Set it to the operator's zone, e.g. `"America/Los_Angeles"` (PST/PDT)
254
+ or `"America/New_York"` (EST/EDT). Never `"local"`.
255
+
256
+ **Procedure** — back up, write the full stanza, validate, done (no restart):
257
+
258
+ ```bash
259
+ cp .thrum/config.json .thrum/config.json.bak
260
+ # Build a COMPLETE heartbeat block mirroring DefaultHeartbeatConfig with your tz,
261
+ # then splice it in (jq keeps the rest of config.json intact):
262
+ jq --slurpfile hb /tmp/hb-block.json '.heartbeat = $hb[0]' .thrum/config.json > /tmp/c.json \
263
+ && mv /tmp/c.json .thrum/config.json
264
+ jq -e '.heartbeat.overnight_window.tz != "" and (.heartbeat.lenses|length)==12' .thrum/config.json
265
+ ```
266
+
267
+ **No daemon restart is required.** The scheduled monitor invokes the sweep
268
+ _script_, which reads `.thrum/config.json` directly on every tick — the new tz
269
+ takes effect on the next sweep. (`thrum config show` reflects the daemon's
270
+ in-memory config, cached at boot, so it will lag until the next daemon start;
271
+ that lag does not affect the script-driven sweep.) Avoid a
272
+ restart-just-for-config — it needlessly risks a cold-boot WAL-replay
273
+ brick.
274
+
275
+ ## Subagent model selection
276
+
277
+ > **Model tiers:** pass an explicit `model:` on every dispatch — `sonnet`
278
+ > (low effort) mechanical, `sonnet` (medium effort) judgment, Opus only on
279
+ > operator-ask or a skill step that names it. See the
280
+ > `choosing-subagent-models` skill for the full policy.
281
+
282
+ ## Step 1 — Run the sweep
283
+
284
+ ```bash
285
+ bash scripts/error-and-context-agent-sweep.sh --out /tmp/agent-sweep.txt
286
+ grep -E 'ctx_used:|^===== ' /tmp/agent-sweep.txt
287
+ ```
288
+
289
+ The script emits one `ctx_used: X.X%` line per live agent. It captures the
290
+ Claude Code status bar footer, normalizing UTF-8 non-breaking spaces
291
+ (`\xc2\xa0`) before matching `Ctx Used: X.X%`. Runtimes without that footer
292
+ (Codex, Cursor) fall back to `(n/a)`.
293
+
294
+ ## Step 2 — Threshold logic
295
+
296
+ ### 🔴 ORCHESTRATORS ARE NOT ON THE CONTEXT LADDER — they restart on a PLAN BOUNDARY
297
+
298
+ **The context thresholds below are a COORDINATOR rule. Orchestrators inherited it
299
+ rather than being chosen for it, and it is not cost-optimal for them.** Do not
300
+ hold an orchestrator open to reach 70–75%, and do not restart one mid-plan to
301
+ chase the cost curve.
302
+
303
+ **The orchestrator rule:**
304
+
305
+ > **Restart an orchestrator at PLAN COMPLETION — where completion means the plan
306
+ > is MERGED *and* the follow-up beads filed during that plan have been FIXED.**
307
+ > Not at the merge report. Not at the merge.
308
+
309
+ **Why the follow-ups clause is load-bearing, not a detail.** The standing
310
+ fix-while-warm rule says a bug found mid-work gets FIXED while warm, because
311
+ file-and-move-on costs ~10x — the research has to be redone before the fix can
312
+ start. Bugs found during a plan are therefore filed and swept up at the END of
313
+ the plan, while the orchestrator *and its implementers* still hold the context.
314
+ **A restart at the merge boundary destroys exactly that warmth, and does so
315
+ INVISIBLY — the follow-ups still get done, just cold and at the 10x price.** A
316
+ restart rule keyed to "the merge" does not merely mistime a restart; it silently
317
+ repeals fix-while-warm.
318
+
319
+ **Why the split is real, not stylistic**:
320
+
321
+ - Per-turn cost scales LINEARLY with context size (the whole prefix is re-sent
322
+ and billed at the cache-read rate every turn), so cumulative session cost is
323
+ roughly QUADRATIC in how far context is allowed to grow. Break-even for a
324
+ restart is **under one turn** once context is meaningfully above the re-prime
325
+ floor.
326
+ - **Orchestrators re-prime far cheaper than coordinators, and it is structural:**
327
+ `internal/cli/prime_filter.go` gives ONLY `role=="coordinator"` the full
328
+ project-state passthrough. Measured orchestrator re-prime ≈ 30k tokens.
329
+ - Coordinators keep the ladder below for a stated reason — larger re-entry cost,
330
+ plus decision-dense context a snapshot cannot faithfully reconstruct.
331
+
332
+ **What the plan boundary buys:** it is the one point where the cost argument and
333
+ the preserve-in-flight-judgment argument AGREE. The work has just been handed off
334
+ in a merge report and the follow-ups are closed, so in-flight judgment is at its
335
+ minimum and state is externalised into beads.
336
+
337
+ **Applying it in this sweep:** an orchestrator flagged in the 50–85% bands is
338
+ NOT a restart candidate on context alone. Check whether its plan is complete
339
+ (merged + follow-ups closed). If it is, restart regardless of how low its
340
+ context is. If it is not, leave it alone regardless of how high.
341
+
342
+
343
+
344
+ | ctx_tier | stuck_working | Action |
345
+ | -------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
346
+ | < 50% | N | No action — agent has runway |
347
+ | < 50% | Y | Tmux-send "are you stuck? `/continue` if waiting" nudge; if not recovered in next sweep, surface to operator |
348
+ | 50-70% | N | **NOT a warning** — OPTIONAL restart-if-IDLE only: directed inbox restart request when the agent is idle. A BUSY agent (or a coordinator, or a warm-hold/on-call/parked consultant) at 50-70% gets no action — see role/state carve-out below. |
349
+ | 50-70% | Y | Tmux-send nudge; defer the tier-1 directed restart request until the next sweep confirms the pane is active again AND ctx is still in this band |
350
+ | 70-85% | N | Tmux-send `/thrum:restart` (bypasses inbox; more forceful) — for coordinators/warm-hold consultants ACTIVELY WORKING, this band doesn't apply until the role/state carve-out's higher floor (75%, see below) |
351
+ | 70-85% | Y | Surface to operator immediately (degraded + stuck → human-eyes-needed) |
352
+ | > 85% | any | Snapshot-gated restart (opt-in): surface `recommend-restart-extended` by default; autonomous restart only when `restart_actuation=true` in config (see Step 5) |
353
+ | `(n/a)` | any | Pane capture failed OR runtime has no Ctx footer — check tmux session manually |
354
+
355
+ **Role/state-aware carve-out:** coordinators, and any agent explicitly standing by as a
356
+ warm-hold:/on-call:/parked: consultant in an ongoing impl, are treated exactly
357
+ like a coordinator — while ACTIVELY WORKING (not idle) they only flag at
358
+ **ctx >= 75%**, not the normal 50% `CTX_THRESHOLD`. An IDLE coordinator (or idle
359
+ consultant) still flags at the normal threshold — idle restarts are cheap, no
360
+ exemption needed there. Implemented as `hb_effective_ctx_threshold` in
361
+ `scripts/heartbeat-lib.sh` — the 75% floor is a named var
362
+ (`HB_COORD_CTX_THRESHOLD_WORKING`), a one-line swap if it ever changes. Locked
363
+ test scenarios: coord@72%+working → NOT flagged; coord@76%+working → flagged;
364
+ coord@60%+idle → flagged; non-coordinator roles are unaffected (still 50% while
365
+ working or idle).
366
+
367
+ **Warm-hold/on-call/parked exemption:** if the agent's `intent` field starts with `^warm-hold:`,
368
+ `^on-call:`, or `^parked:`, skip nudge + restart for all tiers below >85% (and
369
+ apply the coordinator-equivalent 75% floor above while working). The >85%
370
+ bracket still fires regardless. These are EXPLICIT DECLARED states, not a
371
+ heuristic guess at coordination intent — a consultant/on-call/done-planning
372
+ researcher must set one of these prefixes ITSELF via
373
+ `thrum agent set-intent "warm-hold: <reason>"` (or `on-call:`/`parked:`).
374
+
375
+ **A coordinator CANNOT set another agent's intent.** `thrum agent set-intent`
376
+ takes TEXT only — it sets the intent for the CURRENT SESSION and accepts no
377
+ agent argument and no `--to`. To get an intent onto another agent, message that
378
+ agent and have it set its own; then verify by reading `thrum agent list --json`
379
+ rather than by counting acks.
380
+
381
+ **Background-child-aware busy detection:** an agent with an OPEN in-progress bead, a recent JSONL tool_use
382
+ (`state == "working"`), or a pane showing it's driving background sub-agents
383
+ (`Waiting for N background agent...`) is BUSY, not idle — even if pane-silent.
384
+ This is checked BEFORE the idle/abandoned classification (`hb_idle_classify`'s
385
+ `is_busy` param) so an orchestrator quietly waiting on its own dispatched
386
+ sub-agents is never misread as abandoned just because it hasn't committed
387
+ recently.
388
+
389
+ **Finished-impl teardown:** a closed bead + idle + no
390
+ new dispatch classifies as `finished`, which surfaces a `recommend-teardown`
391
+ ALERT to the coordinator — it is a RECOMMENDATION only, never autonomous;
392
+ teardown itself stays coordinator-actuated (kill-tmux → worktree-teardown per
393
+ lifecycle discipline in `implementer-status-and-handoff`).
394
+
395
+ ## Step 2.5 — Stuck-working axis
396
+
397
+ `stuck_working` is **orthogonal** to `ctx_tier`. They can compound. Treat the
398
+ table above as a composite lookup: the action depends on the _cell_, not on
399
+ either axis alone.
400
+
401
+ `stuck_working` measures "is this agent currently unable to make progress?"
402
+ while `ctx_tier` measures "how soon will this agent degrade if left alone?" An
403
+ agent can be:
404
+
405
+ - ctx-fine but stuck-working (waiting on a hung tool call) → nudge to unstick
406
+ - ctx-degraded but not stuck-working (working normally but burning context) →
407
+ restart cleanly
408
+ - both → composite action per the table; high-degradation cases skip the nudge
409
+ step since the agent likely can't recover from the nudge alone
410
+
411
+ The sweep's STUCK-WORKING classification (sweep script's per-agent
412
+ `stuck_working` line + ALERT-line `stuck_working=N` axis) fires when:
413
+
414
+ 1. `agent_status = "working"` (the agent claims to be mid-work; set by the
415
+ Pattern D self-write in role skills), AND
416
+ 2. tmux silence > `SILENCE_THRESHOLD_MIN` minutes (default 10; tunable via
417
+ `--silence-threshold-min N`), AND
418
+ 3. JSONL transcript's last assistant message has `stop_reason = tool_use` AND
419
+ was emitted more than `SILENCE_THRESHOLD_MIN` minutes ago (Claude runtime
420
+ only; non-Claude runtimes use tmux silence alone)
421
+ 4. `intent` does NOT start with `^warm-hold:` (warm-hold exemption)
422
+
423
+ The classification is sweep-side only — it does NOT write to identity files. The
424
+ identity-file `agent_status="stuck"` semantic stays reserved for
425
+ `permission.markAgentStuck` writes (permission-cadence give-up).
426
+
427
+ **Reliability ladder rationale:** the inbox → tmux-send → snapshot-gated restart
428
+ progression goes from polite to forceful. Inbox messages can fail (delivery
429
+ bugs, agent too degraded to check inbox at high ctx, or the documented self-echo
430
+ regression). Tmux-send types literally into the agent's input field — bypasses
431
+ inbox entirely. The snapshot-gated restart (Step 5) bypasses the agent's
432
+ willingness but not the worktree safety: it first solicits a snapshot, then
433
+ restarts gracefully only after one lands.
434
+
435
+ Agents degrade _silently_ before they blow; 50% is the "still coherent enough to
436
+ write a good snapshot" window.
437
+
438
+ ## Step 3 — Directed inbox restart request for 50% – 70% agents
439
+
440
+ ```bash
441
+ thrum send --to @<agent_name> --stdin <<'EOF'
442
+ Your context is at ~X%. Please run /thrum:restart now — write your snapshot and I will re-dispatch after.
443
+ EOF
444
+ ```
445
+
446
+ The agent writes their restart snapshot to
447
+ `.thrum/agents/<agent>/sessions/<timestamp>-restart.md` and goes idle.
448
+ Coordinator waits for the "snapshot written" acknowledgement before
449
+ re-dispatching the next task.
450
+
451
+ If the agent acks without writing the snapshot first (anti-pattern), gently
452
+ remind them to write the snapshot before going idle so the next session restores
453
+ cleanly.
454
+
455
+ If the agent doesn't respond within ~5 minutes OR you see them keep working,
456
+ escalate to Step 4 (tmux-send nudge).
457
+
458
+ ## Step 4 — Tmux-send nudge for 70% – 85% agents
459
+
460
+ The inbox path may not be reaching them at this context level. Bypass the inbox
461
+ by typing the restart command directly into their input field:
462
+
463
+ ```bash
464
+ thrum tmux send <tmux_session_name> '/thrum:restart'
465
+ ```
466
+
467
+ (Find the tmux session name in sweep output — it's typically the worktree
468
+ basename, e.g. `myepic-impl`, NOT the agent_id.)
469
+
470
+ This causes the runtime to immediately execute `/thrum:restart` as if the user
471
+ typed it. The agent writes their snapshot + restarts.
472
+
473
+ If the tmux-send doesn't trigger a restart within ~5 minutes (agent may be too
474
+ degraded to process input), escalate to Step 5 (snapshot-gated restart).
475
+
476
+ ## Step 5 — Snapshot-gated restart for >85% agents
477
+
478
+ The >85% tier is the ONLY autonomous actuator in the whole heartbeat system —
479
+ and even it is **opt-in, off by default**. The behavior is controlled by
480
+ `heartbeat.lenses.context_tiers.params.restart_actuation` in config (default:
481
+ `false`).
482
+
483
+ ### When `restart_actuation` is `false` (the default)
484
+
485
+ The sweep does NOT restart anything. It emits a `recommend-restart-extended`
486
+ reason in the ALERT line, surfacing the situation to the coordinator role. When
487
+ this reason appears, decide whether to run `/thrum:restart-extended` on the
488
+ agent — a deliberate, human-in-the-loop action. No automatic restart fires.
489
+
490
+ ### When `restart_actuation` is `true` (explicitly enabled by the operator)
491
+
492
+ The sweep runs a 4-step snapshot-gated procedure automatically:
493
+
494
+ 1. **Capture pre-send timestamp** — record the current wall-clock time.
495
+ 2. **Send `/thrum:restart-extended` via raw tmux send-keys** — types the restart
496
+ command directly into the agent's pane (bypasses inbox).
497
+ 3. **Sleep a settle window** — default 180 s, tunable via `HB_SNAPSHOT_SETTLE`
498
+ env var.
499
+ 4. **Check for a FRESH snapshot file** — looks for a `*-restart.md` file in
500
+ `.thrum/agents/<agent>/sessions/` written within the freshness window
501
+ (default 200 s, tunable via `HB_SNAPSHOT_WINDOW` — a distinct env var from
502
+ Step 3's settle window, `HB_SNAPSHOT_SETTLE`):
503
+ - **Fresh snapshot found:** runs `thrum tmux restart <agent_id>` (graceful).
504
+ **NEVER `--force`.** The `thrum tmux restart` call in this path takes no
505
+ `--force` flag, by design — if the agent wrote a snapshot, there is no
506
+ reason to force-interrupt mid-call.
507
+ - **No fresh snapshot:** does NOT force anything. The agent is tracked in a
508
+ `no_snapshot` backstop instead (see A2 note below).
509
+
510
+ **NEVER `--force`.** This path does not use `--force` under any circumstances.
511
+ Forceful interruption of a mid-tool-call agent is not worth the risk when the
512
+ graceful path is available and the agent has runway before catastrophic
513
+ degradation.
514
+
515
+ ### Safety checks that still apply
516
+
517
+ The following guards from "Pre-restart safety checks" carry forward and are
518
+ checked BEFORE the snapshot-gated procedure fires:
519
+
520
+ - **Warm-hold exemption** — if `intent` starts with `^warm-hold:`, skip ALL
521
+ restart actions for this agent (the >85% bracket is NOT exempt from warm-hold
522
+ in the snapshot-gated model; an operator who sets warm-hold is taking explicit
523
+ responsibility).
524
+ - **Cooldown** — do not restart the same agent twice within 30 minutes. If an
525
+ agent crosses threshold again that fast, surface to operator rather than
526
+ restart-loop.
527
+ - **No-restart-mid-merge** — never restart an agent whose pane shows a Git merge
528
+ conflict or active rebase. Surface to operator instead.
529
+
530
+ ### "degrading-no-restart" backstop
531
+
532
+ If an agent stays at tier3 without a successful restart landing — whether
533
+ because `restart_actuation` is `false`, or because a snapshot never appeared
534
+ within the settle window — for N consecutive surfaced ticks (default: 3,
535
+ config-tunable via
536
+ `heartbeat.lenses.context_tiers.params.degrading_flag_after_ticks`), the sweep
537
+ adds a distinct `degrading-no-restart` reason to the ALERT. This ensures the
538
+ coordinator still sees that the agent needs eyes even when the system alone
539
+ cannot fix it.
540
+
541
+ ### After a successful restart
542
+
543
+ See "Post-restart procedure" below.
544
+
545
+ ## Step 6 — API-error recovery (who nudges depends on how the sweep runs)
546
+
547
+ When you run the sweep **manually** (the command in Step 1, no flags), it still
548
+ auto-nudges every agent whose pane shows an `API Error` line — it types
549
+ `continue` into the affected pane via `tmux send-keys` (bypassing the
550
+ `thrum tmux send` wrapper queue, which stalls on fully-silent panes). The report header lists who was auto-nudged:
551
+
552
+ ```text
553
+ # auto-nudged 3 agent(s) on api_errors with 'continue':
554
+ # - impl_foo @ foo-impl:0.0
555
+ # ...
556
+ ```
557
+
558
+ Pass `--no-nudge` (alias `--report-only`) to get a pure detection run with **no
559
+ pane writes** — useful to inspect before acting.
560
+
561
+ **The daemon does NOT auto-nudge from the sweep.** The daemon-hosted built-in
562
+ sweep always runs the script with `--no-nudge`: a daemon must not silently type
563
+ into agent panes as a side-effect of a _detection_ sweep. It reports flagged
564
+ agents to the coordinator role only.
565
+
566
+ **Deliberate daemon auto-remediation is a separate, opt-in feature**
567
+ (`daemon.auto_remediation.enabled`, **default OFF**). When an operator turns it
568
+ on, the daemon — not this skill — applies the recovery tier ladder:
569
+
570
+ - 1st detection of an agent's API error → one `continue` nudge.
571
+ - Still erroring on the next tick (nudge didn't clear it) → it escalates
572
+ SUSPECTED-STUCK to the operator chain instead of re-nudging (no nudge-loops).
573
+ - It skips panes mid git merge/rebase, audits every nudge/escalation, and resets
574
+ an agent's state on recovery.
575
+
576
+ So if `daemon.auto_remediation.enabled` is on, you don't manage API-error
577
+ recovery by hand — the daemon does, with the same no-repeat + escalate
578
+ safeguards described here.
579
+
580
+ Anthropic 529s and rate limits are transient (typically resolve in
581
+ seconds-to-minutes); the agent's previous tool call is queued in-session, so a
582
+ single `continue` reactivates them without losing in-flight state.
583
+
584
+ **When to escalate (manual runs / daemon off):** if the same agent shows an API
585
+ error on TWO consecutive sweeps despite a nudge, the issue isn't transient —
586
+ surface to operator as SUSPECTED-STUCK and investigate manually
587
+ (status.claude.com, network, account limits). (When daemon auto-remediation is
588
+ on, it raises this escalation for you.)
589
+
590
+ **When NOT to nudge:** before shipping a release, to fold one more fix into the
591
+ current cycle while an agent is stuck, run with `--no-nudge` (or hold the
592
+ manual sweep). Once `continue` fires, the agent
593
+ resumes its previous tool call immediately — there's no recovery window.
594
+
595
+ ## Pre-restart safety checks
596
+
597
+ Whether triggered by the scheduled `context-monitoring` thrum monitor or by the
598
+ coordinator manually invoking the skill, run these guards BEFORE firing a
599
+ restart:
600
+
601
+ 1. **Verify the daemon is reachable**:
602
+ `thrum team --json | jq '.members | length'` — if 0 or error, daemon is down;
603
+ skip the sweep, surface to operator.
604
+ 2. **Confirm the monitor is alive**: `thrum monitor list` should show
605
+ `context-monitoring` in `running` status with a non-empty `SCHEDULE` column.
606
+ If absent or dead, the scheduled ALERTs aren't firing and the skill must be
607
+ invoked manually from a recurring cron until the monitor is restored. (See
608
+ "Re-register the monitor" below.)
609
+ 3. **Check if any agent is mid-commit** (active tool call): look for
610
+ `Running bash` or active spinner in the sweep pane lines — if so, defer
611
+ restart for that agent until the tool completes.
612
+ 4. **Never restart (graceful or otherwise) an agent whose pane shows a Git merge
613
+ conflict or active rebase** — that corrupts the worktree. Surface to operator
614
+ instead.
615
+ 5. **Cooldown**: do not restart the same agent twice within 30 minutes. If an
616
+ agent crosses threshold again that fast, something's wrong with their
617
+ workload — surface to operator rather than restart-loop.
618
+
619
+ ### Re-register the monitor
620
+
621
+ If `thrum monitor list` doesn't show `context-monitoring`, register it from the
622
+ main repo. The script path must be absolute (the daemon runs the command from a
623
+ clean env, not the coordinator's shell), so resolve it from the repo root first:
624
+
625
+ ```bash
626
+ SCRIPT="$(git -C /path/to/thrum/main-repo rev-parse --show-toplevel)/scripts/error-and-context-agent-sweep.sh"
627
+ thrum monitor add \
628
+ --name context-monitoring \
629
+ --schedule "@every 30m" \
630
+ --match '^ALERT:' \
631
+ --to coordinator \
632
+ -- bash "$SCRIPT" --no-nudge --out /tmp/agent-sweep.txt
633
+ ```
634
+
635
+ (Or substitute the literal absolute path if no shell is handy in the
636
+ daemon's environment.) The monitor fires one-shot per scheduled tick; in between
637
+ ticks the child does not run, so there's no continuous CPU cost from the sweep.
638
+ Note that `@every 30m` is the outer envelope — the sweep script self-gates
639
+ so most ticks produce no ALERT output and thus no message fires.
640
+
641
+ ## Post-restart procedure
642
+
643
+ When a restart fires (Step 4 or 5 — tmux-send nudge or snapshot-gated restart):
644
+
645
+ 1. Surface a brief status note to the coordinator: "Restarted @\<agent_name\>
646
+ at \<ctx\>% — snapshot at
647
+ `.thrum/agents/<agent>/sessions/<timestamp>-restart.md`".
648
+ 2. Wait for the agent to come back online (`thrum team` shows them active again,
649
+ or their pane shows the runtime prompt).
650
+ 3. Re-send their current dispatch with the full scope + plan refs + AC targets —
651
+ treat them as a fresh implementer who needs the full briefing again.
652
+ 4. Note any WIP files they may have left in their worktree from the prior
653
+ attempt (they'll audit salvage-vs-discard before substantive code).
654
+ 5. If they had a partial DONE, the coordinator's git log should still have it;
655
+ the agent may need a pointer to commits they shipped before the blow.
656
+
657
+ ## Reference
658
+
659
+ - **Sweep script**: `scripts/error-and-context-agent-sweep.sh` (captures
660
+ `ctx_used: X.X%` from Claude JSONL transcript; falls back to pane scan for
661
+ non-Claude runtimes). Now part of a sweep-script family (sibling:
662
+ `waiting-on-coord-agent-sweep.sh`).
663
+ - **Memory key**: project-local `coordinator-rule-context-check-broadcast` may
664
+ capture project-specific tweaks; load via
665
+ `thrum memory list --kind agent_rule --scope role`.
666
+ - **Related discipline**:
667
+ - `feedback_restart_discipline` — burn the runway; don't preempt-restart at
668
+ clean checkpoints
669
+ - `feedback_byte_equality_pane_detection` — pane-snapshot byte diffs are
670
+ unreliable; use structural anchors + settle windows
671
+ - **Why thresholds matter**: agents at 97% context silently produce degraded
672
+ output (missed instructions, partial tool calls, slow responses) before they
673
+ blow. The 70%/85% thresholds give the system 15-30% runway to extract a
674
+ snapshot or trigger a graceful restart cleanly.