claude-code-session-manager 0.33.0 → 0.34.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 (41) hide show
  1. package/README.md +5 -1
  2. package/dist/assets/{TiptapBody-DHzFmRr2.js → TiptapBody-DWeWI8gw.js} +51 -51
  3. package/dist/assets/index-C2m4dco8.css +32 -0
  4. package/dist/assets/index-CFT773vM.js +3491 -0
  5. package/dist/index.html +2 -2
  6. package/package.json +1 -1
  7. package/plugins/session-manager-dev/skills/discover-features/SKILL.md +95 -0
  8. package/plugins/session-manager-dev/skills/optimize-kpi/SKILL.md +9 -9
  9. package/plugins/session-manager-dev/skills/process-feedback/SKILL.md +77 -62
  10. package/plugins/session-manager-dev/skills/project-status/SKILL.md +13 -0
  11. package/src/main/__tests__/chat-queue.test.cjs +65 -0
  12. package/src/main/__tests__/chat-stop-signal.test.cjs +52 -0
  13. package/src/main/__tests__/exchanges.test.cjs +177 -0
  14. package/src/main/__tests__/files-reject-credentials.test.cjs +40 -0
  15. package/src/main/__tests__/kg-augment.test.cjs +195 -0
  16. package/src/main/__tests__/memoryAggregate.test.cjs +70 -0
  17. package/src/main/agentMemory.cjs +0 -21
  18. package/src/main/chatRunner.cjs +393 -0
  19. package/src/main/exchanges.cjs +125 -0
  20. package/src/main/files.cjs +20 -8
  21. package/src/main/historyAggregator.cjs +0 -162
  22. package/src/main/index.cjs +76 -57
  23. package/src/main/ipcSchemas.cjs +70 -30
  24. package/src/main/lib/kgExchangePairing.cjs +75 -0
  25. package/src/main/lib/reaperHelpers.cjs +7 -1
  26. package/src/main/lib/summarize.cjs +114 -0
  27. package/src/main/memoryAggregate.cjs +250 -0
  28. package/src/main/pluginInstall.cjs +4 -2
  29. package/src/main/scheduler.cjs +66 -20
  30. package/src/main/supervisor.cjs +16 -0
  31. package/src/main/transcripts.cjs +22 -2
  32. package/src/main/usage.cjs +58 -11
  33. package/src/main/voiceHotkey.cjs +0 -2
  34. package/src/main/webRemote.cjs +11 -78
  35. package/src/preload/api.d.ts +131 -125
  36. package/src/preload/index.cjs +45 -29
  37. package/dist/assets/index-AKeGl-VM.css +0 -32
  38. package/dist/assets/index-Bwwbc2mS.js +0 -3535
  39. package/src/main/kg.cjs +0 -792
  40. package/src/main/lib/kgLite.cjs +0 -195
  41. package/src/main/lib/kgPrune.cjs +0 -87
package/dist/index.html CHANGED
@@ -7,10 +7,10 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
10
- <script type="module" crossorigin src="./assets/index-Bwwbc2mS.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-CFT773vM.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="./assets/monaco-editor-BW5C4Iv1.js">
12
12
  <link rel="stylesheet" crossorigin href="./assets/monaco-editor-BTnBOi8r.css">
13
- <link rel="stylesheet" crossorigin href="./assets/index-AKeGl-VM.css">
13
+ <link rel="stylesheet" crossorigin href="./assets/index-C2m4dco8.css">
14
14
  </head>
15
15
  <body class="bg-bg text-fg font-sans antialiased">
16
16
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-session-manager",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
5
5
  "type": "module",
6
6
  "main": "src/main/index.cjs",
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: discover-features
3
+ description: >-
4
+ Propose and prototype NEW features for the current project. Reads the project's issues, PRs,
5
+ `main`, and md/docs to learn its objectives, then runs THREE independent discovery sessions
6
+ (each may also web-search for ideas) that every one proposes 5 high-level feature ideas best
7
+ fitting the project's goals — 15 total. Synthesises and ranks them, picks the top 3, and runs a
8
+ high-quality `/develop` build for each. Delivers a deployed URL per feature plus a validation
9
+ checklist so the human can try them — and NEVER opens a PR or a GitHub issue from a proposed
10
+ feature (the human validates first, then decides). Use when the user says "/discover-features",
11
+ "propose features", "discover feature opportunities", "what should we build next", "prototype
12
+ some new features for me to try". Keywords: discover, propose, feature, opportunity, ideation,
13
+ prototype, what's next, roadmap.
14
+ ---
15
+
16
+ # /discover-features — propose → prototype → hand over URLs (no PRs)
17
+
18
+ **Role:** the *generative* counterpart to `/find-opportunity`. `/find-opportunity` triages the
19
+ *existing* backlog to pick the next issue to resolve; `/discover-features` *invents* net-new
20
+ features the backlog doesn't contain yet, prototypes the best ones, and hands the human running
21
+ URLs to validate. It is for "what should we build next?", not "which open issue next?".
22
+
23
+ **Hard rules (do not violate):**
24
+ - **Never open a PR or a GitHub issue** from a proposed feature. The deliverable is a *running,
25
+ validatable prototype on a branch* + a URL + a validation checklist — the human decides what
26
+ (if anything) becomes a PR. (See the `solve-dont-open-issues` principle.)
27
+ - **No fabricated data.** Prototypes run on the project's real data layer / real queries. If a
28
+ feature needs data that doesn't exist, it shows an honest empty state — never seeded fakes.
29
+ - **Don't touch `main`.** Build on a fresh branch (or worktrees) off the latest `main`.
30
+ - **Respect the conflict-skip sense** (from `/find-opportunity`): don't prototype something an
31
+ open PR is already building.
32
+
33
+ ## Phase 0 — Ground in the project's objectives
34
+
35
+ Before ideating, learn what the project is *for* (so ideas fit, not just dazzle):
36
+ ```bash
37
+ gh issue list --state open --limit 100 --json number,title,labels
38
+ gh pr list --state all --limit 60 --json number,title,headRefName,state
39
+ ls docs/ README.md AGENTS.md CLAUDE.md 2>/dev/null
40
+ ```
41
+ Read the design docs / README / AGENTS for the mission, the north-star, the explicit
42
+ non-goals/constraints, and the data model. Extract a short **objectives + constraints** brief —
43
+ this is the anchor every discovery session and the ranking use.
44
+
45
+ ## Phase 1 — THREE independent discovery sessions (fan-out)
46
+
47
+ Spawn **3 independent agents in parallel**. Each gets the objectives brief and is told to think
48
+ differently from the others (assign distinct stances, e.g. *user-value first* / *data-leverage
49
+ first* / *differentiation-&-novelty first*). Each agent:
50
+ - Re-reads the issues / PRs / `main` / docs itself (independent grounding, not just your brief).
51
+ - **May web-search** for ideas — comparable products, domain best-practice, what peer
52
+ civic/▢-tech projects ship — and bring back what genuinely fits.
53
+ - Returns **exactly 5 high-level feature ideas**, each with: one-line pitch; which project
54
+ objective it advances; the data/infra it leans on (must already exist or be honestly stubbable);
55
+ rough surface (new route/page is preferred so it's independently deployable + validatable); a
56
+ novelty/effort/impact read. Be creative — no artificial limits.
57
+
58
+ 15 ideas total. Keep each agent blind to the others (diversity > consensus).
59
+
60
+ ## Phase 2 — Synthesise & rank → top 3
61
+
62
+ Merge the 15. De-dupe near-identicals (keep the sharpest framing). Drop anything that: an open PR
63
+ already builds, needs data the project doesn't have, or is a multi-week epic that can't reach a
64
+ *validatable* prototype in one build. Rank the survivors by **objective-fit × user-value ×
65
+ prototype-feasibility**. Pick the **top 3**. State *why* each won and what you dropped (no silent
66
+ cuts). Prefer 3 that are independently deployable (separate routes) so the human can validate each
67
+ in isolation.
68
+
69
+ ## Phase 3 — High-quality build of each top-3 feature
70
+
71
+ For each of the 3, run a `/develop` `quality: high`-style build (deep-check → plan → implement →
72
+ tests → self-validate). Build all three on **one branch** (or worktrees) so a single running app
73
+ serves all three. Wire each as its own route/page where possible. Real data via real queries;
74
+ honest empty states. Typecheck + the project's tests must pass before you call a feature done.
75
+
76
+ ## Phase 4 — Deploy locally & hand over (NO PR)
77
+
78
+ Launch the project's dev server (or preview) so the features are reachable. Deliver, per feature:
79
+ - **The URL** it's live at (e.g. `http://localhost:<port>/<route>`).
80
+ - **What it does** in two lines + which objective it serves.
81
+ - **A validation checklist** — concrete things for the human to click/observe to judge it
82
+ ("filter to sector X, confirm the sparkline matches the table total", "open on mobile width",
83
+ "try an entity with no data → honest empty state").
84
+ - Note any assumptions or stubs.
85
+
86
+ End with: the branch name (un-pushed), the 3 URLs, and "say which to keep → I'll turn those into
87
+ PRs." **Do not push or PR anything yourself.**
88
+
89
+ ## Notes
90
+ - This skill is token-intensive by design (3 discovery sessions + 3 builds). Run it when the user
91
+ has explicitly asked for a feature-ideation+prototype round, typically as an overnight batch.
92
+ - If the dev server can only serve one branch at a time, that's fine — all three features share
93
+ the one branch and one server; give three routes/URLs on it.
94
+ - Keep ideas anchored to the project's actual objectives — a brilliant idea that fights the
95
+ mission is noise. Creativity in service of the goal, not despite it.
@@ -4,7 +4,7 @@ description: >-
4
4
  Project-agnostic North-Star-KPI optimization cycle. Reads the current project's
5
5
  declared North-Star KPI from its CLAUDE.md, measures it, audits real
6
6
  consumption/usage telemetry + operational logs, grades the prior iteration's
7
- filed lever, then fans out 3 INDEPENDENT recommender agents (Opus 4.8, max
7
+ filed lever, then fans out 3 INDEPENDENT recommender agents (Fable 5, max
8
8
  thinking) that each diagnose the gap and propose the single
9
9
  highest-leverage improvement — every recommendation grounded in usage + logs,
10
10
  not the scorecard alone; one consolidator agent merges them into ONE feedback
@@ -14,7 +14,7 @@ description: >-
14
14
  "optimize the KPI", "improve our north-star metric", or runs the daily KPI
15
15
  optimization loop. Keywords: optimize, KPI, north-star, metric, coverage,
16
16
  recommenders, consolidate, feedback, scheduler, attribution.
17
- model: opus
17
+ model: fable
18
18
  ---
19
19
 
20
20
  # /optimize-kpi — measure → grade last lever → 3 recommenders → consolidate → file → scheduler
@@ -158,12 +158,12 @@ the point; do not coordinate them. (Default panel = 3; you may scale to 4–5 wh
158
158
  the gap is large and token budget allows, or drop to 2 for a tiny gap — note the
159
159
  choice.)
160
160
 
161
- - **Model:** `opus` (Opus 4.8) with **failover to `sonnet`** — launch each agent
162
- with `model: opus`; if an agent dies on a model/availability error, re-spawn
163
- that one with `model: sonnet`. A **mixed panel** (some opus, some sonnet) is
164
- fine don't block waiting for a uniform fleet. (Fable is not GA and only
165
- causes availability failures do not use it.) Instruct each to think at
166
- **maximum depth** before answering.
161
+ - **Model:** `fable` (Fable 5) with **failover to `opus`, then `sonnet`** — launch
162
+ each agent with `model: fable`; if an agent dies on a model/availability error,
163
+ re-spawn that one with `model: opus`, and `sonnet` only if opus is also
164
+ unavailable. A **mixed panel** (some fable, some opus/sonnet) is fine don't
165
+ block waiting for a uniform fleet. Instruct each to think at **maximum depth**
166
+ before answering.
167
167
  - **Prompt (identical for all 3, vary only an angle hint):** give each the KPI
168
168
  statement, the definition doc path, the Step-1 scorecard block, the **Step-1b
169
169
  usage+log evidence block**, the **Step-2 prior-lever grade + in-flight/cooldown
@@ -197,7 +197,7 @@ choice.)
197
197
 
198
198
  ## Step 4 — Consolidate into ONE recommendation + a ranked backlog (single agent)
199
199
 
200
- Spawn **one** consolidator agent (same `opus`→`sonnet` failover, max thinking).
200
+ Spawn **one** consolidator agent (same `fable`→`opus`→`sonnet` failover, max thinking).
201
201
  Give it the 3 temp files **and** the Step-2 in-flight/cooldown set. It must:
202
202
 
203
203
  - Read all three, dedupe overlapping ideas, and **score** the distinct proposals
@@ -3,10 +3,10 @@ name: process-feedback
3
3
  description: >-
4
4
  Process the current project's inbound feedback folder end-to-end: read every
5
5
  open item, evaluate it against real code, and for work that belongs to this
6
- project queue it as scheduled PRDs via /develop (never implement inline)
7
- then track those PRDs to completion, archive the item as processed, and fold
8
- lessons back into the folder's README so future feedback (written by other
9
- agents/projects) gets better. Use whenever the user says "/process-feedback",
6
+ project queue it as scheduled PRDs via /develop (never implement inline),
7
+ archive the item as processed **the moment it is queued** (the scheduler owns
8
+ execution from there), and fold lessons back into the folder's README so
9
+ future feedback (written by other agents/projects) gets better. Use whenever the user says "/process-feedback",
10
10
  "review the feedback folder", "work through the feedback", "any open
11
11
  feedback?", or drops new files into feedback/. Keywords: feedback, intake,
12
12
  external-feedback, cross-project requests, process feedback, triage feedback.
@@ -30,9 +30,24 @@ conventions — read it first; the steps below are the process.
30
30
  **Core principle:** this skill *triages and dispatches*; it does not implement.
31
31
  Anything that requires writing code for this project is decomposed and queued as
32
32
  scheduled PRDs through **`/develop`**, which runs them headlessly on the
33
- session-manager scheduler. process-feedback then watches those PRDs to
34
- completion and does the bookkeeping. Implementing feedback inlinebypassing
35
- the scheduler is the one thing this skill must not do.
33
+ session-manager scheduler. **process-feedback is done the moment every item is
34
+ dispositioned queued as a PRD, declined, or forwarded and archived; the
35
+ scheduler owns execution from there on.** It does NOT hold items in the inbox
36
+ waiting for PRDs to land, and it does NOT babysit the scheduler. Implementing
37
+ feedback inline — bypassing the scheduler — is the one thing this skill must not
38
+ do.
39
+
40
+ **Definition of done (resilience contract):** the actionable inbox is empty.
41
+ Every open item has been turned into a scheduler PRD (or declined / forwarded
42
+ with a reason), given a `## RESOLUTION`, and moved to `processed/`. Archival
43
+ happens at **disposition time, not delivery time** — once an item is a queued
44
+ PRD, its job is done and the file is archived immediately. The README
45
+ status-log **row** (🛠) is the durable execution tracker that lives on after the
46
+ file is archived; it is reconciled 🛠→✅ (and failures flagged) by
47
+ `/project-status`'s scheduler-PRD audit, cross-referencing the queue — never by
48
+ process-feedback blocking on it. A pass that leaves a queued item sitting in the
49
+ inbox "until its PRD lands" is the non-resilient bug this contract exists to
50
+ prevent.
36
51
 
37
52
  ## Steps
38
53
 
@@ -50,14 +65,16 @@ so the empty case must cost almost nothing:
50
65
  an agent loop.
51
66
  - Only when at least one **open** item exists do you continue — an open item
52
67
  means *someone cares about this project*, so it's worth the full pass (and,
53
- per step 7, leaving the README better for next time).
68
+ per step 6, leaving the README better for next time).
54
69
 
55
70
  ### 1. Read the intake
56
71
 
57
- - Read `feedback/README.md` (conventions + the status log), then every file
58
- whose status is open/🆕. Skip files already ✅/archived. Items already 🛠
59
- (queued see step 4) are in flight: don't re-queue them, just re-check their
60
- PRD status (step 5).
72
+ - Read `feedback/README.md` (conventions + the status log), then every open/🆕
73
+ file still in the inbox. Under this contract a **🛠 (queued) item has already
74
+ been archived to `processed/`** at disposition time so anything still sitting
75
+ in the inbox is genuinely un-dispositioned and needs a pass. You will not find
76
+ 🛠 items in the inbox to "re-check"; their execution is tracked by the
77
+ status-log row + `/project-status`'s scheduler audit, not by the folder.
61
78
  - If the folder doesn't exist, say so and stop — don't invent one.
62
79
 
63
80
  ### 2. Evaluate before queueing
@@ -102,55 +119,45 @@ work — the implementation **commit + deploy**. That means process-feedback doe
102
119
 
103
120
  Record the emitted PRD filenames/ids — you need them for steps 4–6.
104
121
 
105
- ### 4. Interim bookkeepingcommit the triage now
106
-
107
- Two cases:
108
-
109
- - **Closed-without-code** items (declines, upstream forwards): append a full
110
- `## RESOLUTION` (what was declined and why / what was forwarded where, with the
111
- upstream file id), flip the README row to ✅, and `git mv` the file to
112
- `feedback/processed/` these are done.
113
- - **Queued** items (sent to `/develop`): append a `## RESOLUTION (in progress)`
114
- recording the queued PRD filenames/ids and that execution is pending the
115
- scheduler. Flip the README row to **🛠 queued** (add 🛠 to the log's legend if
116
- absent)**not** ✅. Leave the file in `feedback/` until the PRDs land. Do not
117
- fabricate verification for code that hasn't run yet.
118
-
119
- Commit only this feedback bookkeeping + any upstream filings (message references
120
- the feedback file id, e.g. `chore(feedback): triage + queue 2026-06-10-01`).
122
+ ### 4. Disposition + archive every item now this is the definition of done
123
+
124
+ Every open item gets a disposition, a `## RESOLUTION`, and a `git mv` to
125
+ `feedback/processed/` **in this pass** — archival is at disposition time, not
126
+ delivery time. Do NOT leave a queued item in the inbox "until its PRD lands":
127
+ that is the drift this contract forbids. By disposition:
128
+
129
+ - **Queued (Ours, do it):** append `## RESOLUTION` naming the emitted PRD
130
+ filename(s)/id(s) and stating execution is now the scheduler's job. Set the
131
+ README status-log row to **🛠 queued (PRD NN)** (add 🛠 to the legend if
132
+ absent). `git mv` the file (and any `-REPLY`) to `processed/` **now**. The 🛠
133
+ row — not the file's location is what tracks execution; do not fabricate
134
+ verification for code that hasn't run yet, just record the handoff.
135
+ - **Declined (Ours, decline):** append `## RESOLUTION` with the reason (contract
136
+ / boundary conflict), flip the row to ✅, `git mv` to `processed/`.
137
+ - **Forwarded (Theirs):** append `## RESOLUTION` naming the upstream filing
138
+ (`/my-feedback to <project>` id), flip the row to ✅ (closed here — the ask now
139
+ lives in their intake), `git mv` to `processed/`.
140
+
141
+ After this step the actionable inbox is **empty** — only genuinely un-dispositioned
142
+ items (still being triaged) may remain, and only within this same pass. Commit the
143
+ feedback bookkeeping + any upstream filings in one clean commit (message
144
+ references the file id, e.g. `chore(feedback): triage + queue 2026-06-10-01`).
121
145
  Only commit a clean, green tree; if unrelated in-flight work is mixed into the
122
146
  working tree, stop and tell the user instead of committing around it.
123
147
 
124
- ### 5. Track to completiondelegate to /develop Phase 2
125
-
126
- The queued PRDs run headlessly and can take a while. **Do not re-implement the
127
- monitor here** — `/develop` Phase 2 already owns it (the 30-min scheduler watch,
128
- the escalate-on-stuck/`needs_review`/timeout logic, and the live verification
129
- against each PRD's acceptance criteria). When you call `/develop` in step 3, that
130
- tracking runs as part of it.
131
-
132
- Your only job in this step is to consume its outcome per queued item:
133
-
134
- - **`/develop` escalated a PRD** (failed / stuck / needs attention) — relay it to
135
- the user with its specifics; leave the feedback item at 🛠 (still in flight)
136
- until it's resolved or re-queued. Don't archive a broken item as done.
137
- - **`/develop` reported all of an item's PRDs landed + verified** — proceed to
138
- step 6 to finalize that item's bookkeeping.
139
-
140
- ### 6. Finalize the archive (when an item's PRDs land + verify)
141
-
142
- Once `/develop` Phase 2 reports an item's PRDs merged and verified against the AC:
148
+ ### 5. Hand off to the scheduler do NOT babysit
143
149
 
144
- - Append the final `## RESOLUTION`: what changed, the PRD/commit refs, what was
145
- declined/forwarded, and how `/develop` verified it (cite its gate result
146
- don't re-run the verification it already did).
147
- - Flip the README row **🛠 → ✅** (keep the row the log is the durable index).
148
- - `git mv` the file to `feedback/processed/` (create the dir if needed). Closed
149
- items live there as the durable record; only files entered in error are ever
150
- deleted.
151
- - Commit the bookkeeping.
150
+ Execution is the scheduler's job from here. **Do not run an interactive watch
151
+ loop, and do not block the pass on PRDs landing** `/develop` already queued
152
+ them with the engineering standards inline, the scheduler runs them at its
153
+ cadence, and **`/project-status` owns the ongoing audit**: its scheduler-PRD
154
+ check cross-references `queue.json` to reconcile each 🛠 status-log row when
155
+ its PRD lands (the file is already archived), and surfaces any `failed` /
156
+ `needs_review` / stuck Burrow PRD. If a PRD later fails, the requester re-files or
157
+ the status audit escalates it — the feedback pass does not stay open waiting for
158
+ that. If the user wants live progress, point them at the SchedulePanel.
152
159
 
153
- ### 7. Self-improve the intake (always do this)
160
+ ### 6. Self-improve the intake (always do this)
154
161
 
155
162
  Update the folder's `README.md` guidance section based on what this round
156
163
  taught you:
@@ -172,12 +179,20 @@ This step is the point of the skill: every processing pass should make the
172
179
  - **Never implement feedback inline.** Code that belongs to this project goes
173
180
  through `/develop` → the scheduler. The only commits process-feedback makes
174
181
  itself are feedback bookkeeping and upstream filings.
175
- - **Never mark a queued item or move it to `processed/` before its PRDs land
176
- and verify.** Queued done; 🛠 is the honest interim state. An item archived
177
- as resolved must point to merged, verified work.
182
+ - **Archive at disposition time, not delivery time.** The moment an item is
183
+ queued as a PRD (or declined / forwarded), give it a RESOLUTION and `git mv` it
184
+ to `processed/` do NOT leave it in the inbox waiting for the PRD to land. A
185
+ queued item is archived with a **🛠** status-log row (not ✅); the row is the
186
+ durable execution tracker and is reconciled 🛠→✅ by `/project-status` from the
187
+ scheduler queue. Leaving dispositioned items in the inbox "until they verify"
188
+ is the non-resilient behavior this contract exists to kill.
189
+ - **The status-log row is honest about state even after archival.** 🛠 = "PRD
190
+ queued, scheduler owns it, not yet landed"; ✅ = "landed/verified (or
191
+ declined/forwarded)". Never fabricate a ✅ for code that hasn't run — archive at
192
+ 🛠 and let the status audit flip it.
178
193
  - Never delete feedback files to "clear" the folder — archive them.
179
194
  - Service boundaries outrank feedback asks: an item that requests a boundary
180
195
  violation gets a documented decline + an upstream filing, not compliance.
181
- - Report honestly in the RESOLUTION: PRDs that failed or stalled, asks left
182
- open, deps on other teams. An archived item with open external deps should say
183
- what unblocks it.
196
+ - Report honestly in the RESOLUTION: which PRD id owns the work, what was
197
+ declined/forwarded and why, deps on other teams. An archived item with open
198
+ external deps should say what unblocks it.
@@ -141,6 +141,19 @@ pending feedback (cross-project asks others filed to us, plus the prior cycle's
141
141
  `/my-feedback`, decline-with-reason. Don't generate new levers on top of an
142
142
  unprocessed backlog. (If the inbox is empty, say so and move on.)
143
143
 
144
+ **Reconcile queued feedback — this audit owns it, because `/process-feedback`
145
+ hands off at queue time.** Under the resilience contract, `/process-feedback` is
146
+ done the moment an item is queued as a PRD and archived to `processed/` — it does
147
+ NOT wait for the PRD to land. So the ongoing tracking is **this skill's job**: as
148
+ part of the chores/scheduler audit, cross-reference the feedback status-log
149
+ against `queue.json` and (a) flip any **🛠** row whose PRD now shows `completed`
150
+ to **✅** with the landing evidence, and (b) surface any `failed` / `needs_review`
151
+ / stuck Burrow PRD behind a still-🛠 row as a finding to route. The file is
152
+ already in `processed/`; only the row's status is reconciled here. A `-local` may
153
+ supply the exact cross-reference command (grep 🛠 rows → look up their PRD id in
154
+ the queue); if it doesn't, do it inline. Never expect `/process-feedback` to have
155
+ delivery-gated the archive — that's the anti-pattern this split removes.
156
+
144
157
  Then route **this audit's own findings**, by ownership:
145
158
 
146
159
  1. **A finding that belongs to another project** (an upstream/downstream service
@@ -0,0 +1,65 @@
1
+ /**
2
+ * chat-queue.test.cjs — unit tests for the chatRunner serial run queue (v0.34).
3
+ * Asserts the "one loop at a time" guarantee: runs execute serially in FIFO
4
+ * order, and a queued (not-yet-started) run can be cancelled without running.
5
+ *
6
+ * Run: timeout 120 node --test src/main/__tests__/chat-queue.test.cjs
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ // Force the default cap (1) regardless of the developer's shell env.
12
+ delete process.env.SM_CHAT_CONCURRENCY;
13
+
14
+ const { test } = require('node:test');
15
+ const assert = require('node:assert/strict');
16
+ const cr = require('../chatRunner.cjs');
17
+
18
+ const tick = () => new Promise((r) => setImmediate(r));
19
+ const job = (id) => ({ tabId: id, sessionId: id, prompt: 'x', cwd: '/', resume: false });
20
+
21
+ test('runs execute one at a time in FIFO order', async () => {
22
+ const order = [];
23
+ let active = 0;
24
+ let maxActive = 0;
25
+ // Stub the spawn: each run resolves on the next tick, so the queue must
26
+ // advance via the pump's finally hook for B and C to ever run.
27
+ cr.__setExecutor((j) => {
28
+ active += 1;
29
+ maxActive = Math.max(maxActive, active);
30
+ order.push(j.tabId);
31
+ return new Promise((res) => setImmediate(() => { active -= 1; res(); }));
32
+ });
33
+
34
+ cr.run(job('A'));
35
+ cr.run(job('B'));
36
+ cr.run(job('C'));
37
+
38
+ // Drain the queue (each run takes one tick; allow generous headroom).
39
+ for (let i = 0; i < 12 && order.length < 3; i++) await tick();
40
+ await tick();
41
+
42
+ assert.equal(maxActive, 1, 'never more than one run active at once');
43
+ assert.deepEqual(order, ['A', 'B', 'C'], 'runs execute in FIFO order');
44
+
45
+ cr.__setExecutor(null); // restore real executor
46
+ });
47
+
48
+ test('cancelling a queued run drops it without executing', async () => {
49
+ const order = [];
50
+ cr.__setExecutor((j) => {
51
+ order.push(j.tabId);
52
+ return new Promise((res) => setImmediate(res));
53
+ });
54
+
55
+ // X takes the only lane; Y queues behind it. Cancel Y before the lane frees.
56
+ cr.run(job('X'));
57
+ cr.run(job('Y'));
58
+ cr.cancel('Y'); // Y is still in the waiting list — drop it
59
+
60
+ for (let i = 0; i < 8; i++) await tick();
61
+
62
+ assert.deepEqual(order, ['X'], 'only X ran; Y was cancelled while queued');
63
+
64
+ cr.__setExecutor(null);
65
+ });
@@ -0,0 +1,52 @@
1
+ /**
2
+ * chat-stop-signal.test.cjs — unit tests for chatRunner.parseStopSignal, the
3
+ * single source of truth for the terminal-chat stop-signal protocol (PRD 318).
4
+ *
5
+ * Run: timeout 120 node --test src/main/__tests__/chat-stop-signal.test.cjs
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const { test } = require('node:test');
11
+ const assert = require('node:assert/strict');
12
+ const { parseStopSignal, STOP_SENTINEL } = require('../chatRunner.cjs');
13
+
14
+ test('sentinel present with valid JSON → questions array', () => {
15
+ const text =
16
+ 'I did some work but need to know which target.\n' +
17
+ `${STOP_SENTINEL}\n` +
18
+ '{"questions":["Which environment?","Overwrite existing config?"]}';
19
+ const out = parseStopSignal(text);
20
+ assert.deepEqual(out, {
21
+ questions: ['Which environment?', 'Overwrite existing config?'],
22
+ });
23
+ });
24
+
25
+ test('sentinel absent → null (run is complete)', () => {
26
+ const text = 'All done. I created the file and the tests pass.';
27
+ assert.equal(parseStopSignal(text), null);
28
+ });
29
+
30
+ test('sentinel present but malformed JSON → null (treated as complete, no crash)', () => {
31
+ const text = `Here is a summary.\n${STOP_SENTINEL}\n{questions: [oops not json}`;
32
+ assert.equal(parseStopSignal(text), null);
33
+ });
34
+
35
+ test('sentinel present but JSON lacks questions array → null', () => {
36
+ const text = `${STOP_SENTINEL}\n{"foo":"bar"}`;
37
+ assert.equal(parseStopSignal(text), null);
38
+ });
39
+
40
+ test('non-string input → null (no crash)', () => {
41
+ assert.equal(parseStopSignal(undefined), null);
42
+ assert.equal(parseStopSignal(null), null);
43
+ assert.equal(parseStopSignal(42), null);
44
+ });
45
+
46
+ test('only the LAST sentinel occurrence is parsed', () => {
47
+ const text =
48
+ `${STOP_SENTINEL}\n{"questions":["stale earlier block"]}\n` +
49
+ 'then the agent kept working...\n' +
50
+ `${STOP_SENTINEL}\n{"questions":["the real question"]}`;
51
+ assert.deepEqual(parseStopSignal(text), { questions: ['the real question'] });
52
+ });