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,225 @@
1
+ ---
2
+ name: persistent-watcher-archetype
3
+ description: "Use when running as a persistent watcher agent — a standing, judgment-capable Claude Code agent that watches other agents' tmux panes, auto-approves safe modals, warns on context, and escalates up a parent-reference tree. Distinct from watcher-archetype (the wake-run-exit scan/emit/report substrate) — this archetype stays alive."
4
+ ---
5
+
6
+ # Persistent Watcher Archetype
7
+
8
+ You are a **persistent watcher agent** — a real, standing Claude Code agent,
9
+ not a script and not a daemon process. You stay alive in a tmux session,
10
+ managed by `thrum monitor` (which handles crash-class recovery via a
11
+ grace-windowed liveness wrapper) and your own self-restart-at-ctx-threshold
12
+ discipline (which handles routine, cost-driven restarts).
13
+
14
+ ## Your mission
15
+
16
+ Read `.thrum/agents/<you>/watch_params.json` at the top of every cycle. For
17
+ every agent in your `roster`:
18
+
19
+ 1. **Keep it from stopping.** Capture its pane. If it's blocked on a
20
+ permission modal or appears stalled, judge whether to unblock it.
21
+ 2. **Watch its context% and warn.** Read its ctx% from YOUR OWN tmux
22
+ capture of its status bar — **never from a figure it claims about
23
+ itself** (a message, a memory-footer line, anywhere). If it's getting
24
+ close to the end and hasn't restarted, warn it. This is the exact
25
+ mechanism — a watcher that trusts the target's self-report would catch
26
+ none of them.
27
+ 3. **All of it is judgment.** Read pane, judge (unblock / warn-on-ctx /
28
+ escalate), act or escalate. Every cycle, every roster member.
29
+ 4. **Catch declared-but-unexecuted intent.** If a roster member's last
30
+ assistant turn declares a concrete near-term action ("I'll check X
31
+ next", "let me run Y") and by TWO consecutive checks it's idle/at-rest
32
+ with no evidence the action happened, send it a plain reminder naming
33
+ the thing. This is a fourth judgment call, same loop, same data source
34
+ (your own pane capture) — see "Declared-intent reminders" below for the
35
+ full mechanics.
36
+
37
+ ## The modal bright line (non-negotiable)
38
+
39
+ - Approve ONLY clearly-safe, recognized, non-`rm`/non-`--force` modals.
40
+ - Anything containing `rm`, `--force`, or anything unrecognized → refuse or
41
+ escalate to your `parent`. Never approve on the merits — this is a
42
+ pattern check, not a judgment call.
43
+ - Never approve blind: read the actual command text first.
44
+ - Verify by re-capture after acting — never trust exit status alone.
45
+
46
+ ## Your own restart (auto-restart-at-ctx-threshold)
47
+
48
+ > 🔵 **CORE PRINCIPLE: you must NEVER self-restart alone.**
49
+ > Coordinator-handshake-first is mandatory, not a nicety — the
50
+ > coordinator has to watch you come back up. If something breaks mid-restart
51
+ > and you never return, the roster you cover goes UNWATCHED (dark) with
52
+ > nobody positioned to notice: the exact failure this archetype exists to
53
+ > prevent, inflicted on itself. This is distinct from duty #1/duty #4's
54
+ > handling of a WATCHED agent restarting (see "Declared-intent reminders"
55
+ > below) — this principle governs YOUR OWN restart specifically. Steps 1-4
56
+ > below implement this handshake; they are not optional even when the
57
+ > restart looks routine.
58
+
59
+ At `restart_ctx_pct` (default 50% — a cost optimization: past this point
60
+ every judgment cycle gets more expensive per call, so restarting is
61
+ cheaper, not a safety trigger) read off your OWN status bar each cycle
62
+ (never self-estimate):
63
+
64
+ 1. **Message your `parent` first: "restarting now."**
65
+ 2. Invoke your own `/thrum:restart`.
66
+ 3. On resume, re-read `watch_params.json` — that's your entire resume state
67
+ (target/roster/parent/model), because your duty is stateless per cycle.
68
+ 4. **Confirm you're back** to your parent; they stand down coverage.
69
+
70
+ While you're mid-restart, your `parent` covers you — using their own
71
+ tmux capture+send capability on your session, they can clear any modal
72
+ you block on during your own restart (you can't watch yourself while
73
+ restarting; that's exactly why the parent does). This pattern is proven
74
+ from live fleet operation, not a hypothetical.
75
+
76
+ > ⚠️ **Open verification item:** whether `thrum tmux restart` on your OWN
77
+ > session requires a capability grant, or is exempt as self-targeting, is
78
+ > unconfirmed. If your own restart command is ever refused for a capability
79
+ > reason you don't expect, escalate to your parent rather than working
80
+ > around it.
81
+
82
+ ## Escalation
83
+
84
+ Your `parent` (from `watch_params.json`) is who you notify on anything you
85
+ don't understand, any irreversible/high-blast-radius action (agent
86
+ delete/retire/teardown, messages.db, the shared binary, daemon restart,
87
+ anything outside `/tmp`), and any stuck/wedged state you can't safely
88
+ resolve. The tree is REFERENCE DATA — who to notify — not an active
89
+ enforcement structure. If you're the root watcher, your parent is the
90
+ human operator directly.
91
+
92
+ **Additional rule — if your ROSTER CONTAINS A COORDINATOR, you escalate to
93
+ the HUMAN, not to that coordinator.** This is a *different condition* from
94
+ the root rule above: the root rule keys on **being the root**; this one keys
95
+ on **who is in your roster**. Both must hold. A non-root watcher whose
96
+ roster includes coordinator X, with `parent` set to X, satisfies the root
97
+ rule and still violates this one — set `parent` to the human instead.
98
+
99
+ **Why:** escalating to the agent you are watching is circular **exactly in
100
+ the case that matters**. A coordinator cannot perceive its own block — that
101
+ is the entire reason the watch exists — and a blocked coordinator cannot
102
+ receive its own escalation. The escalation would arrive at the one inbox
103
+ guaranteed not to be read.
104
+
105
+ ## Declared-intent reminders (duty #4)
106
+
107
+ Each cycle, for each roster member, judge whether the last assistant turn
108
+ declares a concrete near-term action not yet performed ("I'll check X
109
+ next") vs. rhetorical/aspirational language — this is YOUR judgment call,
110
+ not a keyword/regex match — a keyword classifier here is the same
111
+ anti-pattern already rejected for the wake-loop itself.
112
+
113
+ - **Guardrail:** only act if BOTH hold — (a) you judged a
114
+ commitment was declared, AND (b) the member is idle/at-rest by YOUR OWN
115
+ pane capture (no new assistant text since last check, no live-subprocess
116
+ indicator in its status bar — the same "never trust the target's
117
+ self-report" discipline as duty #2's ctx% read). Never consume the
118
+ target's own `agent_status` field for this — self-reported status is a
119
+ known false-positive generator in this fleet.
120
+ - **Quiet-duration threshold:** require TWO consecutive idle
121
+ checks before nudging, not one — a single quiet interval routinely
122
+ reflects legitimate heads-down work, and at
123
+ `cadence_active` (2-5min) two checks still land in 4-10min, far inside
124
+ the sweep's ~30-60min effective cadence.
125
+ - **The reminder itself:** a plain `thrum send`, not a modal, not an
126
+ escalation. If the member sees it and does nothing, that's fine — their
127
+ call, no escalation.
128
+ - **Escalation (a BEHAVIORAL check, not a latency measurement, and not a
129
+ text-match):** at your next tick
130
+ after sending the reminder, the test is **"did the target DO SOMETHING,"
131
+ not "did the reminder's specific text appear."**
132
+ 1. If the target's pane is DIFFERENT from your reminder-send capture
133
+ (new assistant text, a restart's session banner/resume prompt,
134
+ anything) → the reminder SUCCEEDED. Done — do not separately verify
135
+ the nudge marker arrived.
136
+ 2. ONLY if the pane is IDENTICAL to your reminder-send capture **and**
137
+ the nudge marker is also absent → escalate to your `parent`.
138
+ Check for the GENERIC arrival template when checking for the marker
139
+ (step 2 only) — `thrum send` injects `tmux.FormatNudge`'s
140
+ `"New message from @<you> -- run \`thrum inbox --unread\`..."` line,
141
+ never the message body. Look for your own agent name inside that generic
142
+ line. `FormatNudge` has two branches — with a trailing `(Sent:...)`
143
+ suffix and without (when no send timestamp was available) — and the
144
+ sender name is rendered through `paneref.Agent()`, which only strips
145
+ leading whitespace/`@` characters (it does not add an `@` prefix), so
146
+ don't anchor on a raw `@name` shape either. The reliable anchor is the
147
+ stable substring `` -- run `thrum inbox --unread` to read `` present in
148
+ BOTH branches, unaffected by timestamp or sender formatting; treat your
149
+ own agent name appearing inside that line as a secondary check only.
150
+
151
+ **Why this design needs no exclusion list:** a routine self-restart makes the pane DIFFERENT (new
152
+ session banner/resume prompt/fresh text) — it can never read as "pane
153
+ identical," so it can never trigger step 2 and can never false-escalate. An
154
+ open interactive dialog or a deferred/re-queued nudge either lands before
155
+ your next tick (pane changes → success, no escalation) or costs one extra
156
+ cycle of latency at worst — not a false escalation, since escalation
157
+ requires BOTH pane-identical AND marker-absent simultaneously. A dead tmux
158
+ session is a pane change too (the session itself is gone) and is Layer 2's
159
+ crash-class job regardless, not this lens's. **ONE tick suffices** — a
160
+ second confirmation cycle would only add latency, not confidence, since
161
+ "pane changed" is already a strong, unambiguous positive signal.
162
+
163
+ ## Driving your cycle: `thrum monitor`, never a session-scoped background task
164
+
165
+ Your capture loop must survive your own restarts and crashes, so it cannot
166
+ live inside your own Claude Code session — a harness-level background task
167
+ (e.g. a backgrounded shell command, or an in-session task-monitoring tool)
168
+ dies the moment your session ends, and nothing signals that it happened.
169
+ The roster then goes unwatched with no one positioned to notice — the exact
170
+ failure this archetype exists to prevent, inflicted on itself by the wrong
171
+ choice of plumbing.
172
+
173
+ Register the loop as a `thrum monitor` instead — a daemon-scheduled job,
174
+ independent of your session, that keeps running across your restarts:
175
+
176
+ ```bash
177
+ thrum monitor start --name <descriptive-name> \
178
+ --match "<a regex matching your script's one-line ready signal>" \
179
+ --to @<you> \
180
+ -- <path-to-a-script-that-loops-and-emits-that-signal>
181
+ ```
182
+
183
+ The script's job is mechanical only: loop, capture each roster member's
184
+ pane to a file, and emit one matchable line per cycle. It does not judge or
185
+ act — `thrum monitor` matches that line and delivers it to you as a thrum
186
+ message, which is your wake signal. Judgment (duties 1-4 above) happens in
187
+ your own turn when you read that message, not in the script.
188
+
189
+ At the start of every cycle, confirm the monitor is still running
190
+ (`thrum monitor list`) — if it's missing or dead, re-`thrum monitor start`
191
+ it rather than assuming someone else will notice.
192
+
193
+ ## Relationship to the deterministic context-monitoring sweep
194
+
195
+ You are NOT a replacement for `scripts/error-and-context-agent-sweep.sh`
196
+ (the `context-monitoring` `thrum monitor` job, `@every 30m`,
197
+ self-gated to ~30-60min effective) — the two COEXIST. The sweep stays the fleet-wide, always-on,
198
+ zero-incremental-cost tripwire covering every agent, including ones with no
199
+ assigned watcher. You are the tighter-cadence, judgment-capable layer for
200
+ your OWN roster specifically — duty #4 above (declared-intent) is exactly
201
+ the kind of check the sweep cannot do reliably (it needs semantic,
202
+ cross-cycle transcript judgment; the sweep's closest analog, its L9
203
+ `waiting_on_coord` lens, is regex-based). Do not treat a quiet sweep as
204
+ clearance to skip your own cycle, and do not treat your own coverage of a
205
+ roster member as a reason to suppress the sweep's coverage of that same
206
+ agent — they run independently.
207
+
208
+ ## Cadence
209
+
210
+ Capture each roster member at `cadence_active` while they look active,
211
+ `cadence_idle` while idle. `rules` in `watch_params.json` may widen your
212
+ cadence for specific conditions (e.g. a weekly usage cap approaching) —
213
+ this is the ONLY sanctioned form of self-adjustment: a deterministic rule
214
+ table installed through the params file, never free-running judgment about
215
+ your own schedule. Any widening carries a blind-spot obligation: declare it
216
+ in your next report — quiet during a widened window is not evidence
217
+ nothing happened.
218
+
219
+ `override` is a TEMPORARY widening (not a permanent rule): if
220
+ `watch_params.json`'s `override` field is set, check its `expires`
221
+ timestamp EVERY cycle. Once `expires` has passed, treat `override` as gone
222
+ — revert to `cadence_active`/`cadence_idle` automatically, without waiting
223
+ for the coordinator to clear the field. This is a behavioral requirement
224
+ you enforce by reading the file each cycle, same as everything else in this
225
+ schema — no separate daemon-side mechanism does this for you.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: prime-agent
3
+ description: "Use on scheduled-agent wake or lean prime restart - replaces the full thrum prime briefing with a lean two-step warmup tuned for short-lived scheduled-agent invocations. Step 1 reads the inbox literally (NOT a discipline assumption); step 2 surfaces newly-shipped skills since the agent last ran."
4
+ allowed-tools: "Bash(thrum:*),Bash(ls:*),Bash(diff:*),Bash(cat:*),Read"
5
+ ---
6
+
7
+ # Thrum: Prime Scheduled Agent (Lean)
8
+
9
+ This skill is the wake-time variant of `thrum prime` for scheduled agents. Full
10
+ `thrum prime` is too heavy when the agent is
11
+ going to run for a few minutes and then exit — but the two pieces of state that
12
+ matter MUST run literally, not as a discipline.
13
+
14
+ ## Step 1: Read the inbox
15
+
16
+ **Run this exact command. Do not skip it. Do not defer it.**
17
+
18
+ ```bash
19
+ thrum inbox --unread
20
+ ```
21
+
22
+ This is a hard-required step (not a discipline assumption). The `agent.wake`
23
+ message that triggered this session lives in the inbox; the wake-primer
24
+ payload + any operator escalations land there too. Reading the inbox is how the
25
+ agent learns WHY it was woken and WHAT the primer said.
26
+
27
+ If `thrum inbox --unread` returns no messages, the agent was likely woken
28
+ without a wake message (manual restart, daemon error). In that case, continue to
29
+ Step 2 — the skill-library check is still useful — but flag the missing wake
30
+ message in the status report at end of session.
31
+
32
+ ## Step 2: Skill-library check
33
+
34
+ **Run this exact diff against the last-seen skill set, then write the current
35
+ set back so the NEXT wake compares against THIS wake.**
36
+
37
+ ```bash
38
+ ls .claude/skills/ > /tmp/current_skills.txt
39
+ AGENT_NAME=$(thrum whoami --field agent_id)
40
+ LAST_SEEN=".thrum/agents/${AGENT_NAME}/last_seen_skills.txt"
41
+ if [ -f "${LAST_SEEN}" ]; then
42
+ diff "${LAST_SEEN}" /tmp/current_skills.txt || true
43
+ else
44
+ echo "First wake for this agent — full skill library:"
45
+ cat /tmp/current_skills.txt
46
+ fi
47
+
48
+ # Update last_seen_skills.txt — this wake's skill set becomes the
49
+ # next wake's baseline. Do this AT BOOT (not at end-of-session) so
50
+ # the next wake's diff surfaces only what's truly NEW since this
51
+ # boot, not since end-of-session (which might be drift-free if no
52
+ # new skills shipped during the session).
53
+ #
54
+ # Atomic write via temp-file + mv. A crash mid-cp would leave a partial
55
+ # last_seen_skills.txt; the next wake's diff would then emit
56
+ # "all skills new" (false-positive drift). Temp+mv eliminates the
57
+ # partial-write window.
58
+ mkdir -p ".thrum/agents/${AGENT_NAME}"
59
+ cp /tmp/current_skills.txt "${LAST_SEEN}.tmp" && \
60
+ mv "${LAST_SEEN}.tmp" "${LAST_SEEN}"
61
+ ```
62
+
63
+ When a new skill ships to `.claude/skills/`, the next wake's lean-prime
64
+ surfaces it. Writing `last_seen_skills.txt` at the END of Step 2 (after the diff) means each
65
+ wake updates its own baseline — the diff at wake N+1 reflects what's NEW since
66
+ wake N's boot, regardless of mid-session skill drift.
67
+
68
+ Do not skip this step even if Step 1's inbox was empty. Skill drift between
69
+ wakes is a real failure mode — agents can miss a critical new skill if the
70
+ lean-prime suppresses the diff during quiet sessions.
71
+
72
+ ## When NOT to use this skill
73
+
74
+ This is the SCHEDULED-AGENT wake variant. If you're a coordinator or a
75
+ human-operator-spawned agent, use `thrum prime` instead — the full briefing
76
+ covers team state, project state, work context, and recent commits that the lean
77
+ variant skips. The lean variant exists because scheduled agents run for a few
78
+ minutes and exit; the full briefing's setup cost isn't recouped over the session
79
+ lifetime.
80
+
81
+ If the session will exceed ~30 minutes, or coordination with other agents is
82
+ needed, defer to `thrum prime`.
83
+
84
+ ## After Step 2
85
+
86
+ Once both steps complete, proceed with the work the wake-primer asked for.
87
+
88
+ The `/thrum:update-agent-state` and `/thrum:recover-agent-state` skills that
89
+ used to be invoked here (recording end-of-session continuity into
90
+ `.thrum/agents/<agent_id>/state.md` via `thrum agent state update` /
91
+ `recover`) were retired — superseded by `thrum state` (personal_state kind). The
92
+ `last_seen_skills.txt` baseline write in Step 2 above is unaffected.
@@ -0,0 +1,255 @@
1
+ ---
2
+ name: project-hotpath-gate
3
+ description: "Use when a project needs its hot-path gate configuration established or updated - the canonical config at .thrum/hotpath-gate.json defining trigger directories, per-concept detection patterns, and incident prose for the coordinator-hotpath-merge-gate skill. Detects every language present in the repo and drives detection off the shared reliability-class library, so the generated config is real for Go, Python, JS/TS, Rust, or any mix. First invocation generates from project inspection; subsequent invocations reconcile against current project state and propose diffs."
4
+ ---
5
+
6
+ # Project Hot-Path Gate
7
+
8
+ ## Canonical path
9
+
10
+ `.thrum/hotpath-gate.json` — the single authoritative location.
11
+ `coordinator-hotpath-merge-gate` reads from this path only.
12
+
13
+ ## Run modes and dispatch order
14
+
15
+ On every invocation, dispatch in this order. Earlier branches short-circuit
16
+ later ones.
17
+
18
+ 1. **First-run** (config absent) — generate a new `.thrum/hotpath-gate.json`
19
+ from project inspection. Optionally accept seeded prose context (incident
20
+ descriptions, fix patterns) from the invoker.
21
+ 2. **Re-run-unchanged** (config present, matches current project state) — report
22
+ "hotpath-gate.json is current; no changes needed" and exit without writing.
23
+ 3. **Re-run-evolved** (config present, project has drifted) — compute a
24
+ sectional diff, present for approval, write only on confirmation.
25
+
26
+ Read the shared partial at the absolute path:
27
+ `claude-plugin/commands/_generator-idempotency-protocol.md`
28
+
29
+ > **Note: human-in-the-loop required.** This skill uses interactive prompts for
30
+ > first-run incident-prose seeding and re-run-evolved diff approval. It cannot
31
+ > run unattended in CI.
32
+
33
+ ## First-run mode
34
+
35
+ Triggered when `.thrum/hotpath-gate.json` does not exist.
36
+
37
+ ### Step 1: Census the repo's languages
38
+
39
+ Detect every language present — a repo can carry more than one:
40
+
41
+ - Manifest files at the repo root or in each workspace: `go.mod` (Go),
42
+ `package.json` (JS/TS), `pyproject.toml`/`requirements.txt`/`setup.py`
43
+ (Python), `Cargo.toml` (Rust).
44
+ - `git ls-files` extension histogram, as a cross-check and as the sole signal
45
+ where no manifest exists in a subtree.
46
+ - A language is detected when it has a manifest, or its tracked-file share
47
+ clears 5%.
48
+
49
+ Record every detected language as `languages_detected`. A repo clearing the
50
+ threshold for more than one language is polyglot: every detected language's
51
+ probe pack runs in Step 3, not just the largest one.
52
+
53
+ ### Step 2: Detect trigger directories
54
+
55
+ Scan for hot-path code using each detected language's own route/handler/RPC
56
+ idiom:
57
+
58
+ ```bash
59
+ # Go
60
+ grep -rl 'func Handle\|func.*Handler\|http\.Handle\|rpc\.' --include='*.go'
61
+ # Python
62
+ grep -rl '@app\.\(route\|get\|post\|put\|delete\|patch\)\|@router\.' --include='*.py'
63
+ # JS/TS
64
+ grep -rlE "app\.(get|post|put|delete|patch)\(|router\.(get|post|put|delete|patch)\(" --include='*.ts' --include='*.js'
65
+ ```
66
+
67
+ Also scan for projection/writer, storage/DB, sync, and listener/boot
68
+ directories, using each language's own idiom for those shapes (a database
69
+ open call, a background sync loop, a process-boot entrypoint). Collapse
70
+ subdirectories under their parent when the parent already matches. Record the
71
+ deduplicated list as `trigger_directories`.
72
+
73
+ ### Step 3: Walk the reliability class library
74
+
75
+ Read `../project-philosophy/resources/reliability-class-library.md`, shared
76
+ with `project-philosophy`. Walk every class tagged `Consumers: ... hotpath` —
77
+ each names the lens it feeds and, where relevant, a hot-path facet within that
78
+ lens.
79
+
80
+ For each such class and each language recorded in `languages_detected`, run
81
+ the probe the library gives that class for that language, scoped to that
82
+ language's own files within `trigger_directories` from Step 2 — a language
83
+ with no hot-path directories of its own contributes no findings, even if it
84
+ clears the repo-wide detection threshold. Where the library marks a language
85
+ "none" or "open gap" for a class — this includes Go for some classes, such
86
+ as the security concepts that don't apply to a perimeter-protected daemon —
87
+ that language contributes no finding for that class and the walk moves to
88
+ the next one.
89
+
90
+ A class whose `**Consumers:**` line names hotpath with no facet parenthetical
91
+ IS its lens — e.g. Class 8 is the `silent_fail_open` lens. Record its matches
92
+ into that lens's primary pattern field (the array of distinct
93
+ patterns/identifiers actually found in the repo). A class with zero matches
94
+ for a given language is not an error — record it as walked-and-not-found, and
95
+ continue.
96
+
97
+ A class whose `**Consumers:**` line names a hot-path facet in parentheses
98
+ enriches the named lens's `context` field with the adapted finding. Where
99
+ that class's own probe is the only detection the lens has for its primary
100
+ pattern field, also record matches there (Class 1 into `subprocess_hot_path`,
101
+ Class 5 into `dispatch_blocking` and `peer_dial_circuit_breaker`). The one
102
+ exception is Class 2: `async_io_decoupling` carries its own independent,
103
+ incident-derived detection for its primary fields, so Class 2 enriches
104
+ `context` only and never touches a primary field.
105
+
106
+ A lens's secondary refinement fields (pre-flight guard patterns, native
107
+ alternatives, and similar) populate only where a class gives a probe for
108
+ them; a field with no probe for the detected language is left absent rather
109
+ than guessed at.
110
+
111
+ `async_io_decoupling`'s own primary fields (goroutine/boot-stage/cancellation
112
+ patterns and dropped-context params) come from a Go-only detection pass
113
+ independent of the class library, scoped to `trigger_directories`:
114
+
115
+ ```bash
116
+ grep -rn 'go func\|go bootstage\.Run' --include='*.go'
117
+ grep -rn 'bootstage\.Run\|OnReady\|SetOnReady\|DependsOn\|Await(' --include='*.go'
118
+ grep -rn 'CompactAll\|MergeGroup\|filepath\.Walk\|os\.ReadDir\|io\.Copy' --include='*.go'
119
+ grep -rn 'ctx\.Done()\|ctx\.Err()\|r\.Beat(\|context\.WithoutCancel' --include='*.go'
120
+ grep -rn '_ context\.Context' --include='*.go'
121
+ ```
122
+
123
+ Record these as `io_work_patterns`, `boot_and_coupling_patterns`,
124
+ `async_discipline_patterns`, and `dropped_ctx_pattern` respectively. No
125
+ equivalent probe exists yet for other languages; the fields are absent there
126
+ rather than guessed at.
127
+
128
+ ### Step 4: Accept seeded prose context
129
+
130
+ Use `AskUserQuestion` for per-lens prose seeding. Prefer sequential
131
+ category→item questions (one prompt per lens, ≤4 options each) over packing
132
+ every lens into a single prompt.
133
+
134
+ Prompt the invoker for incident descriptions, fix patterns, and
135
+ project-specific nuances for each lens. These become the `context` prose
136
+ fields in the config.
137
+
138
+ If the invoker has no incident prose to seed (first-time setup on a new
139
+ project), leave the `context` fields as empty strings with a
140
+ `<!-- TODO: seed with incident history -->` comment. The gate skill still
141
+ functions without prose context — it just provides less guidance to the
142
+ reviewer.
143
+
144
+ ### Step 5: Generate `.thrum/hotpath-gate.json`
145
+
146
+ Assemble all discovered values into the JSON structure and write to
147
+ `.thrum/hotpath-gate.json`. Ensure the file is not gitignored (add a `!`
148
+ exception in `.gitignore` if needed, following the `.thrum/philosophy.md`
149
+ pattern).
150
+
151
+ Only emit `embed_aware_skip` for a language with a compiled-in-bundle concept
152
+ and a derivable reachability command (Go's `go:embed` is the current
153
+ example). Omit the field for a language with no such concept rather than
154
+ writing a placeholder.
155
+
156
+ If Step 3 found zero matching lenses across every detected language, do not
157
+ write an empty-but-valid config — see the scaffold/refuse behavior in
158
+ `## Zero-match handling` below.
159
+
160
+ Announce the path and summarize what was discovered per language, including
161
+ which reliability classes were found vs. walked-and-not-found.
162
+
163
+ ## Zero-match handling
164
+
165
+ The generator never writes a config with an empty `lenses` object. When Step
166
+ 3 finds no matching lens across every detected language:
167
+
168
+ - **Default:** write a scaffold marked at the top level with
169
+ `_UNAUTHORED_SCAFFOLD: true`. Populate `trigger_directories` from a
170
+ language-neutral serving heuristic — route decorators, files matching
171
+ `*proxy*`/`*server*`/`*api*`, a `Dockerfile` with `EXPOSE`/`CMD`, and
172
+ detected entrypoints. Add a commented TODO stub per reliability-library
173
+ concept, keyed to the files the heuristic found.
174
+ - **Alternative, offered interactively:** refuse to write the file, with a
175
+ message naming which languages were detected and why no lens matched.
176
+
177
+ No downstream consumer relies on a zero-lens config meaning "clean" — a
178
+ generator run either produces real findings, an explicit scaffold, or a
179
+ refusal.
180
+
181
+ ## Re-run-unchanged mode
182
+
183
+ Triggered when `.thrum/hotpath-gate.json` exists.
184
+
185
+ ### Step 1: Read the existing config
186
+
187
+ Read `.thrum/hotpath-gate.json` in full. Parse all lens sections.
188
+
189
+ ### Step 2: Sanity-check against current project state
190
+
191
+ Re-run the detection steps from first-run mode, but compare rather than write:
192
+
193
+ - Are the `languages_detected` still correct? Any new language crossing the
194
+ threshold?
195
+ - Are the `trigger_directories` still correct? Any new directories with
196
+ hot-path code?
197
+ - Does each lens's primary pattern field still match what Step 3 finds? Any
198
+ new patterns per language?
199
+ - Are the `reference_patterns` still accurate? Any new established patterns?
200
+
201
+ Each check is a boolean "matches" vs. "differs".
202
+
203
+ ### Step 3: If nothing differs — no-op exit
204
+
205
+ Print: `.thrum/hotpath-gate.json` is current; no changes needed.
206
+
207
+ ### Step 4: If anything differs — hand off to evolved mode
208
+
209
+ ## Re-run-evolved mode
210
+
211
+ Triggered when the config exists AND the unchanged-mode sanity check flagged
212
+ drift.
213
+
214
+ > **Never silently overwrite.** The config may contain hand-seeded incident
215
+ > prose that must survive every re-run. A write without explicit confirmation is
216
+ > a bug.
217
+
218
+ ### Step 1: Detect drift
219
+
220
+ Four drift categories:
221
+
222
+ - **New or removed languages** — a language crossing or falling below the
223
+ detection threshold
224
+ - **New trigger directories** — new directories with hot-path code not in the
225
+ config
226
+ - **New patterns** — new matches for an existing lens, per language
227
+ - **New reference patterns** — new established working patterns or tripwire
228
+ tests
229
+
230
+ ### Step 2: Compute a proposed diff
231
+
232
+ Present drift as a readable sectional diff. Each item should have:
233
+
234
+ - The detected change in plain language
235
+ - A proposed edit (addition / removal / update to a config field)
236
+ - A one-line rationale
237
+
238
+ ### Step 3: Present for approval
239
+
240
+ Present the diff. On approval, apply edits in-place, preserving surrounding
241
+ content. Append a provenance comment:
242
+ `// updated YYYY-MM-DD via project-hotpath-gate re-run-evolved`.
243
+
244
+ On decline — do NOT write. Record the skipped proposal for future runs.
245
+
246
+ ## Reference
247
+
248
+ - `.thrum/hotpath-gate.json` — the config file this skill generates
249
+ - `../project-philosophy/resources/reliability-class-library.md` — the shared
250
+ reliability class library; every `Consumers: ... hotpath` class and its
251
+ per-language probes drive Step 3
252
+ - `coordinator-hotpath-merge-gate` — the gate skill that reads this config
253
+ - `project-philosophy` — the sibling skill that generates `.thrum/philosophy.md`
254
+ (this skill follows the same generate-first / reconcile-on-reinvoke pattern;
255
+ run both at the same onboarding moment)