@femtomc/mu-agent 26.2.99 → 26.2.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/operator.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { HudDocSchema, normalizeHudDocs } from "@femtomc/mu-core";
1
2
  import { appendJsonl, getStorePaths, readJsonl } from "@femtomc/mu-core/node";
2
3
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
4
  import { dirname, join } from "node:path";
@@ -6,6 +7,7 @@ import { CommandContextResolver } from "./command_context.js";
6
7
  import { createMuSession } from "./session_factory.js";
7
8
  import { DEFAULT_OPERATOR_SYSTEM_PROMPT } from "./default_prompts.js";
8
9
  const OPERATOR_RESPONSE_MAX_CHARS = 12_000;
10
+ const OPERATOR_TURN_HUD_DOCS_MAX = 16;
9
11
  const SAFE_RESPONSE_RE = new RegExp(`^[\\s\\S]{1,${OPERATOR_RESPONSE_MAX_CHARS}}$`);
10
12
  const OPERATOR_TIMEOUT_MIN_MS = 1_000;
11
13
  const OPERATOR_TIMEOUT_HARD_CAP_MS = 10 * 60 * 1_000;
@@ -42,9 +44,18 @@ export const OperatorApprovedCommandSchema = z.discriminatedUnion("kind", [
42
44
  thinking: z.string().trim().min(1),
43
45
  }),
44
46
  ]);
47
+ const OperatorTurnHudDocsSchema = z.array(HudDocSchema).max(OPERATOR_TURN_HUD_DOCS_MAX).optional();
45
48
  export const OperatorBackendTurnResultSchema = z.discriminatedUnion("kind", [
46
- z.object({ kind: z.literal("respond"), message: z.string().trim().min(1).max(OPERATOR_RESPONSE_MAX_CHARS) }),
47
- z.object({ kind: z.literal("command"), command: OperatorApprovedCommandSchema }),
49
+ z.object({
50
+ kind: z.literal("respond"),
51
+ message: z.string().trim().min(1).max(OPERATOR_RESPONSE_MAX_CHARS),
52
+ hud_docs: OperatorTurnHudDocsSchema,
53
+ }),
54
+ z.object({
55
+ kind: z.literal("command"),
56
+ command: OperatorApprovedCommandSchema,
57
+ hud_docs: OperatorTurnHudDocsSchema,
58
+ }),
48
59
  ]);
49
60
  function normalizeArg(arg) {
50
61
  return arg.trim();
@@ -244,6 +255,43 @@ function nonEmptyString(value) {
244
255
  const trimmed = value.trim();
245
256
  return trimmed.length > 0 ? trimmed : null;
246
257
  }
258
+ function isHudToolName(toolName) {
259
+ const normalized = toolName.trim().toLowerCase();
260
+ return normalized === "mu_hud";
261
+ }
262
+ function extractHudDocsFromToolResult(result) {
263
+ const rec = asRecord(result);
264
+ if (!rec) {
265
+ return [];
266
+ }
267
+ const details = asRecord(rec.details);
268
+ const candidates = [];
269
+ const topLevelHudDocs = rec.hud_docs;
270
+ if (Array.isArray(topLevelHudDocs)) {
271
+ candidates.push(...topLevelHudDocs);
272
+ }
273
+ if (details) {
274
+ const detailHudDocs = details.hud_docs;
275
+ if (Array.isArray(detailHudDocs)) {
276
+ candidates.push(...detailHudDocs);
277
+ }
278
+ }
279
+ return normalizeHudDocs(candidates, { maxDocs: OPERATOR_TURN_HUD_DOCS_MAX });
280
+ }
281
+ function collectHudDocsFromToolExecutionEvent(event) {
282
+ const rec = asRecord(event);
283
+ if (!rec) {
284
+ return [];
285
+ }
286
+ if (nonEmptyString(rec.type) !== "tool_execution_end") {
287
+ return [];
288
+ }
289
+ const toolName = nonEmptyString(rec.toolName);
290
+ if (!toolName || !isHudToolName(toolName)) {
291
+ return [];
292
+ }
293
+ return extractHudDocsFromToolResult(rec.result);
294
+ }
247
295
  function finiteInt(value) {
248
296
  if (typeof value !== "number" || !Number.isFinite(value)) {
249
297
  return null;
@@ -574,6 +622,7 @@ export class MessagingOperatorRuntime {
574
622
  return {
575
623
  kind: "response",
576
624
  message,
625
+ ...(backendResult.hud_docs && backendResult.hud_docs.length > 0 ? { hud_docs: backendResult.hud_docs } : {}),
577
626
  operatorSessionId: sessionId,
578
627
  operatorTurnId: turnId,
579
628
  };
@@ -594,6 +643,7 @@ export class MessagingOperatorRuntime {
594
643
  return {
595
644
  kind: "command",
596
645
  commandText: approved.commandText,
646
+ ...(backendResult.hud_docs && backendResult.hud_docs.length > 0 ? { hud_docs: backendResult.hud_docs } : {}),
597
647
  operatorSessionId: sessionId,
598
648
  operatorTurnId: turnId,
599
649
  };
@@ -905,6 +955,7 @@ export class PiMessagingOperatorBackend {
905
955
  const session = sessionRecord.session;
906
956
  let assistantText = "";
907
957
  let capturedCommand = null;
958
+ let capturedHudDocs = [];
908
959
  const unsub = session.subscribe((event) => {
909
960
  // Capture assistant text for fallback responses.
910
961
  if (event?.type === "message_end" && event?.message?.role === "assistant") {
@@ -933,6 +984,12 @@ export class PiMessagingOperatorBackend {
933
984
  capturedCommand = parsed.data;
934
985
  }
935
986
  }
987
+ const hudDocs = collectHudDocsFromToolExecutionEvent(event);
988
+ if (hudDocs.length > 0) {
989
+ capturedHudDocs = normalizeHudDocs([...capturedHudDocs, ...hudDocs], {
990
+ maxDocs: OPERATOR_TURN_HUD_DOCS_MAX,
991
+ });
992
+ }
936
993
  });
937
994
  const promptText = buildOperatorPrompt(input);
938
995
  const promptOnce = async () => {
@@ -970,6 +1027,7 @@ export class PiMessagingOperatorBackend {
970
1027
  await session.agent.waitForIdle();
971
1028
  assistantText = "";
972
1029
  capturedCommand = null;
1030
+ capturedHudDocs = [];
973
1031
  await promptOnce();
974
1032
  }
975
1033
  }
@@ -993,6 +1051,9 @@ export class PiMessagingOperatorBackend {
993
1051
  command: capturedCommand,
994
1052
  messagePreview: assistantText,
995
1053
  });
1054
+ if (capturedHudDocs.length > 0) {
1055
+ return { kind: "command", command: capturedCommand, hud_docs: capturedHudDocs };
1056
+ }
996
1057
  return { kind: "command", command: capturedCommand };
997
1058
  }
998
1059
  // Otherwise treat the assistant text as a plain response.
@@ -1009,6 +1070,9 @@ export class PiMessagingOperatorBackend {
1009
1070
  outcome: "respond",
1010
1071
  messagePreview: responseMessage,
1011
1072
  });
1073
+ if (capturedHudDocs.length > 0) {
1074
+ return { kind: "respond", message: responseMessage, hud_docs: capturedHudDocs };
1075
+ }
1012
1076
  return { kind: "respond", message: responseMessage };
1013
1077
  }
1014
1078
  dispose() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-agent",
3
- "version": "26.2.99",
3
+ "version": "26.2.101",
4
4
  "description": "Shared operator runtime for mu assistant sessions and serve extensions.",
5
5
  "keywords": [
6
6
  "mu",
@@ -24,7 +24,7 @@
24
24
  "themes/**"
25
25
  ],
26
26
  "dependencies": {
27
- "@femtomc/mu-core": "26.2.99",
27
+ "@femtomc/mu-core": "26.2.101",
28
28
  "@mariozechner/pi-agent-core": "^0.53.0",
29
29
  "@mariozechner/pi-ai": "^0.53.0",
30
30
  "@mariozechner/pi-coding-agent": "^0.53.0",
@@ -13,6 +13,7 @@ Use this skill when the user asks to schedule, inspect, tune, or debug `mu heart
13
13
  - [Preflight checks](#preflight-checks)
14
14
  - [Heartbeat lifecycle workflow](#heartbeat-lifecycle-workflow)
15
15
  - [Prompt design for bounded ticks](#prompt-design-for-bounded-ticks)
16
+ - [Reusable status-voice snippet](#reusable-status-voice-snippet)
16
17
  - [Diagnostics and recovery](#diagnostics-and-recovery)
17
18
  - [Evaluation scenarios](#evaluation-scenarios)
18
19
 
@@ -107,15 +108,39 @@ Good pattern:
107
108
  - inspect queue/state
108
109
  - do exactly one action
109
110
  - verify
110
- - summarize progress
111
+ - report project-level progress as a titled status note plus a concise narrative paragraph
112
+ - narrative should cover project context, what milestone moved, impact, overall progress, and next step
113
+ - keep low-level queue/worker internals out of default reporting; include them only for blocker/anomaly diagnosis
111
114
  - exit
112
115
 
113
116
  Example bounded prompt:
114
117
 
115
118
  ```text
116
- Review ready issues under root <root-id>. Perform exactly one bounded step:
117
- claim/work one ready issue (or report blocked), verify state, post concise progress,
118
- then exit.
119
+ Review issues under root <root-id>. Perform exactly one bounded orchestration step,
120
+ verify state, then report for a human as:
121
+ - a short title that summarizes status
122
+ - one concise paragraph: project context, what moved this pass, impact,
123
+ where the project stands overall, and what comes next
124
+ Only include queue/worker details if diagnosing a blocker/anomaly.
125
+ Then exit.
126
+ ```
127
+
128
+ ## Reusable status-voice snippet
129
+
130
+ Use this copy/paste block in heartbeat prompts when updates should be written for
131
+ non-operator humans:
132
+
133
+ ```text
134
+ Write the update as a short status note for a human reader.
135
+ - First line: a plain-language title that captures the status.
136
+ - Then one concise paragraph explaining:
137
+ - what this project is trying to achieve,
138
+ - what meaningful milestone moved in this pass,
139
+ - what impact that creates (or what precondition was completed),
140
+ - where the overall project stands,
141
+ - what comes next and why it matters.
142
+ Avoid low-level orchestration internals by default (queue snapshots, worker/session IDs,
143
+ packet mechanics, raw issue-ID lists). Include them only when diagnosing a blocker/anomaly.
119
144
  ```
120
145
 
121
146
  For hierarchical DAG execution, pair this skill with:
@@ -161,7 +186,7 @@ mu store tail cp_outbox --limit 30 --pretty
161
186
 
162
187
  1. **Periodic progress heartbeat**
163
188
  - Setup: heartbeat created with bounded control-loop prompt and `--every-ms 15000`.
164
- - Expected: each wake performs one bounded pass and exits; no unbounded run behavior.
189
+ - Expected: each wake performs one bounded pass, emits a high-level titled narrative status update, and exits; no unbounded run behavior.
165
190
 
166
191
  2. **Event-driven heartbeat mode**
167
192
  - Setup: heartbeat created/updated with `--every-ms 0`.
@@ -164,6 +164,7 @@ mu issues dep <step-a> blocks <step-b>
164
164
  - Scoped authority: mutate only current issue and descendants.
165
165
  - Non-executable containers/questions must not retain `node:agent`.
166
166
  - Forum updates are append-only and resumable (`START`/`RESULT` packets).
167
+ - Orchestrator progress packets are human-facing and objective-linked: use a clear status title plus concise narrative paragraph (project context, milestone moved, impact, overall progress, next step); include low-level queue/worker internals only for blocker/anomaly diagnosis.
167
168
  - Every executable issue closes with explicit outcome.
168
169
  - `mu issues validate <root-id>` must pass before declaring completion.
169
170
 
@@ -192,7 +193,7 @@ Worker/orchestrator passes always choose one primitive at a time:
192
193
  2. Choose one primitive (`ask` | `expand` | `complete` | orchestration primitive)
193
194
  3. Apply
194
195
  4. Verify (`get`, `children`, `ready`, `validate`)
195
- 5. Log concise progress to forum
196
+ 5. Log human-facing progress to forum as a titled narrative update (context -> milestone moved -> impact -> overall progress -> next), using the reusable status-voice style from `heartbeats`
196
197
  6. Exit bounded pass
197
198
 
198
199
  ## Minimal bootstrap template
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: hud
3
+ description: "Defines HUD usage for `mu_hud` and `/mu hud`, including doc schema patterns, deterministic update rules, and rendering-safe conventions."
4
+ ---
5
+
6
+ # hud
7
+
8
+ Use this skill whenever you need to publish, update, or inspect HUD state.
9
+
10
+ This skill is the canonical HUD reference for:
11
+
12
+ - `mu_hud` tool calls (structured HUD state)
13
+ - `/mu hud ...` command usage (inspection/control)
14
+ - `HudDoc` conventions that render well in TUI/Slack/Telegram
15
+
16
+ ## Contents
17
+
18
+ - [Core contract](#core-contract)
19
+ - [HudDoc shape](#huddoc-shape)
20
+ - [Recommended turn loop](#recommended-turn-loop)
21
+ - [Planning and subagents profiles](#planning-and-subagents-profiles)
22
+ - [Determinism and rendering limits](#determinism-and-rendering-limits)
23
+ - [Evaluation scenarios](#evaluation-scenarios)
24
+
25
+ ## Core contract
26
+
27
+ ### Tool (`mu_hud`)
28
+
29
+ Actions:
30
+
31
+ - `status`, `snapshot`
32
+ - `on`, `off`, `toggle`
33
+ - `set`, `update`, `replace`, `remove`, `clear`
34
+
35
+ Key params:
36
+
37
+ - `doc` (for `set`/`update`)
38
+ - `docs` (for `replace`)
39
+ - `hud_id` (for `remove`)
40
+ - `snapshot_format` (`compact` or `multiline`)
41
+
42
+ Notes:
43
+
44
+ - `set` and `update` are both upsert-style single-doc writes.
45
+ - `replace` is whole-inventory replacement.
46
+ - Tool results include normalized `hud_docs` for downstream transport/rendering.
47
+
48
+ ### Command (`/mu hud ...`)
49
+
50
+ Supported subcommands:
51
+
52
+ - `/mu hud status`
53
+ - `/mu hud snapshot [compact|multiline]`
54
+ - `/mu hud on|off|toggle`
55
+ - `/mu hud clear`
56
+ - `/mu hud remove <hud-id>`
57
+
58
+ Use the tool (`mu_hud`) for structured doc writes.
59
+
60
+ ## HudDoc shape
61
+
62
+ HUD docs are validated against `HudDoc` (`@femtomc/mu-core`).
63
+
64
+ Minimum practical fields:
65
+
66
+ - `v: 1`
67
+ - `hud_id: <non-empty>`
68
+ - `title: <non-empty>`
69
+ - `snapshot_compact: <non-empty>`
70
+ - `updated_at_ms: <int>`
71
+
72
+ Common optional fields:
73
+
74
+ - `scope` (for root/session/issue scoping)
75
+ - `chips` (`[{key,label,tone?}]`)
76
+ - `sections`:
77
+ - `kv` (key/value)
78
+ - `checklist` (checkbox-style progress)
79
+ - `activity` (recent lines)
80
+ - `text` (free text)
81
+ - `actions` (`[{id,label,command_text,kind?}]`)
82
+ - `metadata` (machine-readable extras)
83
+
84
+ Example checklist doc:
85
+
86
+ ```json
87
+ {
88
+ "action": "set",
89
+ "doc": {
90
+ "v": 1,
91
+ "hud_id": "planning",
92
+ "title": "Planning HUD",
93
+ "scope": "mu-root-123",
94
+ "chips": [
95
+ { "key": "phase", "label": "phase:drafting", "tone": "accent" },
96
+ { "key": "steps", "label": "steps:2/5", "tone": "dim" }
97
+ ],
98
+ "sections": [
99
+ {
100
+ "kind": "checklist",
101
+ "title": "Checklist",
102
+ "items": [
103
+ { "id": "1", "label": "Investigate", "done": true },
104
+ { "id": "2", "label": "Draft DAG", "done": true },
105
+ { "id": "3", "label": "Review", "done": false }
106
+ ]
107
+ }
108
+ ],
109
+ "actions": [
110
+ { "id": "snapshot", "label": "Snapshot", "command_text": "/mu hud snapshot", "kind": "secondary" }
111
+ ],
112
+ "snapshot_compact": "HUD(plan) · phase=drafting · steps=2/5",
113
+ "updated_at_ms": 1771853115000,
114
+ "metadata": { "phase": "drafting" }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Recommended turn loop
120
+
121
+ 1. Ensure HUD is on:
122
+
123
+ ```json
124
+ {"action":"on"}
125
+ ```
126
+
127
+ 2. Upsert exactly the docs you own (`set`/`update`).
128
+ 3. Emit compact snapshot for user-facing status:
129
+
130
+ ```json
131
+ {"action":"snapshot","snapshot_format":"compact"}
132
+ ```
133
+
134
+ 4. Keep response text and HUD state aligned (no contradictions).
135
+
136
+ ## Planning and subagents profiles
137
+
138
+ Use profile-specific `hud_id` values:
139
+
140
+ - planning profile: `hud_id: "planning"`
141
+ - subagents profile: `hud_id: "subagents"`
142
+
143
+ Treat these as conventions layered on top of this generic contract.
144
+
145
+ ## Determinism and rendering limits
146
+
147
+ - Keep one canonical doc per `hud_id`.
148
+ - Keep `updated_at_ms` monotonic for each `hud_id`.
149
+ - Prefer a small doc set (usually 1–3 docs total) for channel readability.
150
+ - Keep command actions concise; long commands may degrade to text-only fallbacks on some channels.
151
+ - Assume channel renderers cap docs/actions/lines; put critical state in `snapshot_compact` and first section items.
152
+
153
+ If behavior is unclear, inspect implementation/tests before guessing:
154
+
155
+ - `packages/core/src/hud.ts`
156
+ - `packages/agent/src/extensions/hud.ts`
157
+ - `packages/server/src/control_plane.ts`
158
+ - `packages/agent/test/hud_tool.test.ts`
159
+
160
+ ## Evaluation scenarios
161
+
162
+ 1. **Planning review turn**
163
+ - Expected: `planning` doc updates phase/checklist/waiting state, then emits compact snapshot.
164
+
165
+ 2. **Subagents orchestration pass**
166
+ - Expected: `subagents` doc updates queue/activity/chips after each bounded pass.
167
+
168
+ 3. **HUD reset handoff**
169
+ - Expected: after phase completion, HUD is cleared or removed by `hud_id`, and status reflects no stale docs.
@@ -98,7 +98,7 @@ mu session <session-id>
98
98
  mu turn --session-kind operator --session-id <session-id> --body "<follow-up>"
99
99
  ```
100
100
 
101
- In attached terminal operator chat, `/mu` helpers are available (`/mu events`, `/mu plan`, `/mu subagents`, `/mu help`).
101
+ In attached terminal operator chat, `/mu` helpers are available (`/mu events`, `/mu hud ...`, `/mu help`).
102
102
 
103
103
  ## Durable automation handoff
104
104
 
@@ -10,6 +10,7 @@ Use this skill when the user asks for planning, decomposition, or a staged execu
10
10
  ## Contents
11
11
 
12
12
  - [Planning HUD is required](#planning-hud-is-required)
13
+ - [HUD skill dependency](#hud-skill-dependency)
13
14
  - [Shared protocol dependency](#shared-protocol-dependency)
14
15
  - [Core contract](#core-contract)
15
16
  - [Suggested workflow](#suggested-workflow)
@@ -28,10 +29,17 @@ For this skill, the planning HUD is the primary status/communication surface.
28
29
 
29
30
  Default per-turn HUD loop:
30
31
 
31
- 1. Apply an atomic `update` with current `phase`, `waiting_on_user`, `next_action`, `blocker`, and `confidence`.
32
- 2. Synchronize checklist items and `root_issue_id` with the issue DAG.
32
+ 1. Emit a fresh `planning` HUD doc (`mu_hud` action `set` or `update`) with current `phase`, `waiting_on_user`, `next_action`, `blocker`, and `confidence` in sections/metadata.
33
+ 2. Keep checklist progress and root issue linkage synchronized with the live issue DAG.
33
34
  3. Emit `snapshot` (`compact` or `multiline`) and reflect it in your response.
34
35
 
36
+ ## HUD skill dependency
37
+
38
+ Before emitting or mutating planning HUD state, load **`hud`** and follow its canonical contract.
39
+
40
+ - Treat `hud` as source-of-truth for generic `mu_hud` actions, `HudDoc` shape, and rendering constraints.
41
+ - This planning skill defines planning-specific conventions only (for example `hud_id: "planning"`, planning phases, checklist semantics).
42
+
35
43
  ## Shared protocol dependency
36
44
 
37
45
  This skill plans DAGs for execution by `subagents`, so planning must follow the
@@ -58,6 +66,7 @@ Do not invent alternate protocol names or tag schemas.
58
66
  - Add clear titles, scope, acceptance criteria, and protocol tags.
59
67
 
60
68
  3. **Drive communication through the planning HUD**
69
+ - Load `hud` and use its canonical `mu_hud`/`HudDoc` contract.
61
70
  - Treat HUD state as the canonical short status line for planning.
62
71
  - Keep `phase`, `waiting_on_user`, `next_action`, `blocker`, and `confidence` current.
63
72
  - Ensure HUD state and your natural-language response never contradict each other.
@@ -72,6 +81,10 @@ Do not invent alternate protocol names or tag schemas.
72
81
  - Update issues/dependencies and re-present deltas.
73
82
  - Do not begin broad execution until the user signals satisfaction.
74
83
 
84
+ 6. **After user approval, ask user about next steps**
85
+ - On user acceptance of the plan, turn the planning HUD off.
86
+ - Read the `subagents` skill and offer to supervise subagents to execute the plan.
87
+
75
88
  ## Suggested workflow
76
89
 
77
90
  ### A) Investigation pass
@@ -86,46 +99,36 @@ mu memory search --query "<topic>" --limit 30
86
99
  Bootstrap HUD immediately (interactive operator session):
87
100
 
88
101
  ```text
89
- /mu plan on
90
- /mu plan phase investigating
91
- /mu plan waiting off
92
- /mu plan confidence medium
93
- /mu plan next "Investigate constraints and gather evidence"
94
- /mu plan snapshot
102
+ /mu hud on
103
+ /mu hud status
104
+ /mu hud snapshot
95
105
  ```
96
106
 
97
107
  Tool contract (preferred when tools are available):
98
108
 
99
- - Tool: `mu_planning_hud`
100
- - Actions:
101
- - state: `status`, `snapshot`, `on`, `off`, `toggle`, `reset`, `phase`, `root`
102
- - checklist: `check`, `uncheck`, `toggle_step`, `set_steps`, `add_step`, `remove_step`, `set_step_label`
103
- - communication: `set_waiting`, `set_next`, `set_blocker`, `set_confidence`
104
- - atomic: `update`
105
- - Key parameters:
106
- - `phase`: `investigating|drafting|reviewing|waiting_user|blocked|executing|approved|done`
107
- - `root_issue_id`: issue ID or `clear`
108
- - `waiting_on_user`: boolean
109
- - `next_action`, `blocker`: string or `clear`
110
- - `confidence`: `low|medium|high`
111
- - `steps`: string[]
112
- - `step_updates`: array of `{index, done?, label?}`
109
+ - Canonical contract: see skill `hud`
110
+ - Tool: `mu_hud`
111
+ - Actions: `status`, `snapshot`, `on`, `off`, `toggle`, `set`, `update`, `replace`, `remove`, `clear`
112
+ - Planning convention: maintain a HUD doc with `hud_id: "planning"`
113
+ - Suggested planning doc structure:
114
+ - `title`: `Planning HUD`
115
+ - chips: `phase:<...>`, `steps:<done>/<total>`, `waiting:<yes|no>`, `conf:<low|medium|high>`
116
+ - sections:
117
+ - `kv` status block (`phase`, `root`, `waiting_on_user`, `confidence`, `next_action`, `blocker`)
118
+ - `checklist` block for plan milestones
119
+ - actions: include useful follow-ups (for example, `snapshot`)
113
120
 
114
121
  Example tool calls:
115
- - Atomic status update for an investigation turn:
116
- - `{"action":"update","phase":"investigating","waiting_on_user":false,"next_action":"Draft root issue and child DAG","blocker":"clear","confidence":"medium"}`
117
- - Atomic handoff when waiting for approval:
118
- - `{"action":"update","phase":"waiting_user","waiting_on_user":true,"next_action":"Confirm scope change","blocker":"Need approval","confidence":"low"}`
119
- - Clear communication fields after user reply:
120
- - `{"action":"update","waiting_on_user":false,"blocker":"clear","next_action":"Incorporate feedback and re-draft DAG"}`
121
- - Customize checklist:
122
- - `{"action":"set_steps","steps":["Investigate","Draft DAG","Review with user","Finalize"]}`
122
+ - Turn HUD on:
123
+ - `{"action":"on"}`
124
+ - Set/replace planning doc after investigation pass:
125
+ - `{"action":"set","doc":{"v":1,"hud_id":"planning","title":"Planning HUD","scope":"mu-root-123","chips":[{"key":"phase","label":"phase:investigating","tone":"dim"},{"key":"steps","label":"steps:1/4","tone":"accent"},{"key":"waiting","label":"waiting:no","tone":"dim"},{"key":"confidence","label":"conf:medium","tone":"accent"}],"sections":[{"kind":"kv","title":"Status","items":[{"key":"phase","label":"phase","value":"investigating"},{"key":"root","label":"root","value":"mu-root-123"},{"key":"waiting","label":"waiting_on_user","value":"no"},{"key":"confidence","label":"confidence","value":"medium"},{"key":"next","label":"next_action","value":"Draft root DAG"},{"key":"blocker","label":"blocker","value":"(none)"}]},{"kind":"checklist","title":"Checklist","items":[{"id":"1","label":"Investigate relevant code/docs/state","done":true},{"id":"2","label":"Create root + child issue DAG","done":false},{"id":"3","label":"Present plan + tradeoffs","done":false},{"id":"4","label":"Refine until approved","done":false}]}],"actions":[{"id":"snapshot","label":"Snapshot","command_text":"/mu hud snapshot","kind":"secondary"}],"snapshot_compact":"HUD(plan) · phase=investigating · steps=1/4 · waiting=no · conf=medium","updated_at_ms":1771853115000,"metadata":{"phase":"investigating","waiting_on_user":false,"confidence":"medium"}}}`
123
126
  - Human-facing status line:
124
127
  - `{"action":"snapshot","snapshot_format":"compact"}`
125
128
 
126
129
  If HUD behavior is unclear, inspect implementation/tests before guessing:
127
- - `packages/agent/src/extensions/planning-ui.ts`
128
- - `packages/agent/test/planning_ui_tool.test.ts`
130
+ - `packages/agent/src/extensions/hud.ts`
131
+ - `packages/agent/test/hud_tool.test.ts`
129
132
 
130
133
  Also inspect repo files directly (read/bash) for implementation constraints.
131
134
 
@@ -186,23 +189,17 @@ mu issues validate "$root_id"
186
189
 
187
190
  Required HUD updates during the loop:
188
191
 
189
- ```text
190
- /mu plan root <root-id>
191
- /mu plan phase drafting
192
- /mu plan check 1
193
- /mu plan phase waiting-user
194
- /mu plan waiting on
195
- /mu plan next "Need your approval on tradeoff A/B"
196
- /mu plan snapshot
197
- ```
192
+ - Re-emit the `planning` HUD doc with current `phase`, checklist progress, `waiting_on_user`, `next_action`, and `blocker` after each meaningful planning step.
193
+ - Use `{"action":"snapshot","snapshot_format":"compact"}` for concise user-facing HUD lines.
194
+ - Keep `updated_at_ms` monotonic across updates so latest doc wins deterministically.
198
195
 
199
196
  ## Effective HUD usage heuristics
200
197
 
201
- - Prefer `update` for multi-field changes to avoid inconsistent intermediate state.
202
- - Reserve `waiting_user` for explicit user input/decision waits; use `blocked` for non-user blockers.
198
+ - Keep one canonical planning doc (`hud_id: "planning"`) and refresh it whenever planning state changes.
199
+ - Keep `updated_at_ms` monotonic so deterministic dedupe/ordering always keeps the latest planning state.
200
+ - Use explicit, concise status fields (`phase`, `waiting_on_user`, `next_action`, `blocker`, `confidence`) in sections/metadata.
203
201
  - Keep `next_action` as one concrete action, not a paragraph.
204
- - Adjust `confidence` as evidence quality changes (`low` when assumptions are unresolved).
205
- - Customize checklist steps once scope is understood; check them off as milestones complete.
202
+ - Customize checklist steps once scope is understood; mark them complete as milestones land.
206
203
 
207
204
  ## Evaluation scenarios
208
205