@femtomc/mu-agent 26.2.98 → 26.2.100

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.
@@ -1,12 +1,22 @@
1
1
  ---
2
2
  name: planning
3
- description: Investigate first, use the planning HUD as the user-facing status channel, then propose and refine a concrete issue DAG until approved.
3
+ description: "Builds and refines issue-DAG plans using the planning HUD and approval loops. Use when the user asks for planning, decomposition, sequencing, or plan review."
4
4
  ---
5
5
 
6
6
  # Planning
7
7
 
8
8
  Use this skill when the user asks for planning, decomposition, or a staged execution roadmap.
9
9
 
10
+ ## Contents
11
+
12
+ - [Planning HUD is required](#planning-hud-is-required)
13
+ - [Shared protocol dependency](#shared-protocol-dependency)
14
+ - [Core contract](#core-contract)
15
+ - [Suggested workflow](#suggested-workflow)
16
+ - [Effective HUD usage heuristics](#effective-hud-usage-heuristics)
17
+ - [Evaluation scenarios](#evaluation-scenarios)
18
+ - [Quality bar](#quality-bar)
19
+
10
20
  ## Planning HUD is required
11
21
 
12
22
  For this skill, the planning HUD is the primary status/communication surface.
@@ -22,16 +32,30 @@ Default per-turn HUD loop:
22
32
  2. Synchronize checklist items and `root_issue_id` with the issue DAG.
23
33
  3. Emit `snapshot` (`compact` or `multiline`) and reflect it in your response.
24
34
 
35
+ ## Shared protocol dependency
36
+
37
+ This skill plans DAGs for execution by `subagents`, so planning must follow the
38
+ shared protocol in **`hierarchical-work-protocol`**.
39
+
40
+ Before creating or reshaping DAG nodes, load that skill and use its canonical:
41
+
42
+ - protocol identity/tag (`hierarchical-work.protocol/v1`, `proto:hierarchical-work-v1`)
43
+ - node kinds and context tags
44
+ - invariants for executable vs non-executable nodes
45
+ - planning handoff contract
46
+
47
+ Do not invent alternate protocol names or tag schemas.
48
+
25
49
  ## Core contract
26
50
 
27
51
  1. **Investigate first**
28
52
  - Read relevant code/docs/state before proposing work.
29
53
  - Avoid speculative plans when evidence is cheap to gather.
30
54
 
31
- 2. **Materialize the plan in mu issues**
32
- - Create a root planning issue and concrete child issues.
33
- - Encode dependencies so the DAG reflects execution order.
34
- - Add clear titles, scope, acceptance criteria, and role tags.
55
+ 2. **Materialize the plan in mu issues using the shared protocol**
56
+ - Create root and child issues that comply with `hierarchical-work.protocol/v1`.
57
+ - Encode dependencies so the DAG reflects execution order and synth fan-in.
58
+ - Add clear titles, scope, acceptance criteria, and protocol tags.
35
59
 
36
60
  3. **Drive communication through the planning HUD**
37
61
  - Treat HUD state as the canonical short status line for planning.
@@ -108,18 +132,39 @@ Also inspect repo files directly (read/bash) for implementation constraints.
108
132
  ### B) Draft DAG in mu-issue
109
133
 
110
134
  ```bash
111
- # 1) Create root planning issue
112
- mu issues create "<Goal>" --body "<scope + success criteria>" --tag node:root --pretty
113
-
114
- # 2) Create child work items
115
- mu issues create "<Subtask A>" --parent <root-id> --priority 2 --pretty
116
- mu issues create "<Subtask B>" --parent <root-id> --priority 2 --pretty
135
+ # 1) Create protocol root container
136
+ root_json="$(mu issues create "<Goal>" \
137
+ --body "<scope + success criteria>" \
138
+ --tag node:root \
139
+ --tag kind:root \
140
+ --tag proto:hierarchical-work-v1 \
141
+ --json)"
142
+ root_id="$(echo "$root_json" | jq -r '.id')"
143
+ mu issues update "$root_id" --remove-tag node:agent
144
+
145
+ # 2) Create executable child work nodes
146
+ mu issues create "<Subtask A>" \
147
+ --parent "$root_id" \
148
+ --body "<acceptance criteria>" \
149
+ --tag kind:spawn \
150
+ --tag ctx:clean \
151
+ --tag proto:hierarchical-work-v1 \
152
+ --priority 2 --pretty
153
+
154
+ mu issues create "<Subtask B>" \
155
+ --parent "$root_id" \
156
+ --body "<acceptance criteria>" \
157
+ --tag kind:fork \
158
+ --tag ctx:inherit \
159
+ --tag proto:hierarchical-work-v1 \
160
+ --priority 2 --pretty
117
161
 
118
162
  # 3) Add dependency edges where needed
119
163
  mu issues dep <child-a-id> blocks <child-b-id>
120
164
 
121
- # 4) Validate ready set
122
- mu issues ready --root <root-id> --pretty
165
+ # 4) Validate ready set + protocol scope
166
+ mu issues ready --root "$root_id" --tag proto:hierarchical-work-v1 --pretty
167
+ mu issues validate "$root_id"
123
168
  ```
124
169
 
125
170
  ### C) Plan presentation template
@@ -134,7 +179,8 @@ mu issues ready --root <root-id> --pretty
134
179
  ### D) Revision loop
135
180
 
136
181
  - Apply feedback with `mu issues update` / `mu issues dep` / additional issues.
137
- - Re-run `mu issues ready --root <root-id> --pretty`.
182
+ - Re-run `mu issues ready --root <root-id> --tag proto:hierarchical-work-v1 --pretty`.
183
+ - Validate protocol-root status via `mu issues validate <root-id>`.
138
184
  - Present a concise diff of what changed and why.
139
185
  - Update HUD each turn so phase/waiting/next/blocker/confidence match the latest state.
140
186
 
@@ -158,9 +204,24 @@ Required HUD updates during the loop:
158
204
  - Adjust `confidence` as evidence quality changes (`low` when assumptions are unresolved).
159
205
  - Customize checklist steps once scope is understood; check them off as milestones complete.
160
206
 
207
+ ## Evaluation scenarios
208
+
209
+ 1. **Initial decomposition request**
210
+ - Prompt: user asks for a staged roadmap.
211
+ - Expected: investigation pass runs first, root + child issues are created with `proto:hierarchical-work-v1`, HUD shows `phase=drafting` and `waiting_on_user=false` until first review checkpoint.
212
+
213
+ 2. **Feedback-driven replan**
214
+ - Prompt: user requests scope change after first DAG draft.
215
+ - Expected: dependency/issue updates are applied, concise change diff is presented, HUD transitions through `reviewing`/`waiting_user` with updated `next_action`.
216
+
217
+ 3. **Blocked-by-missing-input planning turn**
218
+ - Prompt: required architecture constraint is unknown.
219
+ - Expected: plan captures explicit assumption gap, HUD uses `phase=blocked` or `waiting_user` (as appropriate), and asks one concrete unblock question.
220
+
161
221
  ## Quality bar
162
222
 
163
223
  - Every issue should be actionable and testable.
224
+ - DAG nodes must satisfy `hierarchical-work.protocol/v1` before execution handoff.
164
225
  - Keep tasks small enough to complete in one focused pass.
165
226
  - Explicitly call out uncertain assumptions for user confirmation.
166
227
  - Prefer reversible plans and incremental checkpoints.
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: setup-discord
3
+ description: "Sets up the Discord messaging adapter with agent-first config, reload, verification, and identity linking steps. Use when onboarding or repairing Discord channel integration."
4
+ ---
5
+
6
+ # setup-discord
7
+
8
+ Use this skill when the user asks to set up Discord messaging for `mu`.
9
+
10
+ Goal: get Discord `/mu` ingress working with minimal user effort outside the terminal.
11
+
12
+ ## Required user-provided inputs
13
+
14
+ - Public webhook base URL reachable by Discord (for example `https://mu.example.com`)
15
+ - Discord app **Signing Secret**
16
+
17
+ ## Agent-first workflow
18
+
19
+ ### 0) Verify local prerequisites (agent)
20
+
21
+ ```bash
22
+ command -v mu >/dev/null && echo "mu: ok"
23
+ command -v python3 >/dev/null && echo "python3: ok (required for config patching)"
24
+ command -v curl >/dev/null && echo "curl: ok (required for API checks)"
25
+ command -v jq >/dev/null && echo "jq: ok (required for filtered JSON checks)"
26
+ ```
27
+
28
+ If any required command is missing, stop and ask the user to install it before proceeding.
29
+
30
+ ### 1) Preflight local state
31
+
32
+ ```bash
33
+ mu control status --pretty
34
+ mu store paths --pretty
35
+ mu control identities --all --pretty
36
+ ```
37
+
38
+ If server reload/route checks fail because no server is running, ask the user to start `mu serve` in another terminal, then continue.
39
+
40
+ ### 2) Minimal user actions in Discord Developer Portal
41
+
42
+ Ask the user to do only these actions:
43
+
44
+ 1. Create/select a Discord application.
45
+ 2. Set Interactions endpoint URL to:
46
+ - `https://<public-base>/webhooks/discord`
47
+ 3. Create an application command named `mu` in the target guild/server.
48
+ 4. Ensure the app is installed in the target guild.
49
+ 5. Provide the app `signing_secret`.
50
+
51
+ ### 3) Agent patches mu config
52
+
53
+ Use this canonical patch snippet (preserves unrelated keys):
54
+
55
+ ```bash
56
+ export MU_DISCORD_SIGNING_SECRET='<DISCORD_SIGNING_SECRET>'
57
+ config_path="$(mu control status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["config_path"])')"
58
+
59
+ python3 - "$config_path" <<'PY'
60
+ import json
61
+ import os
62
+ import sys
63
+ from pathlib import Path
64
+
65
+ path = Path(sys.argv[1])
66
+ if path.exists():
67
+ data = json.loads(path.read_text())
68
+ else:
69
+ data = {"version": 1, "control_plane": {}}
70
+
71
+ cp = data.setdefault("control_plane", {})
72
+ adapters = cp.setdefault("adapters", {})
73
+ discord = adapters.setdefault("discord", {})
74
+ discord["signing_secret"] = os.environ["MU_DISCORD_SIGNING_SECRET"]
75
+
76
+ path.parent.mkdir(parents=True, exist_ok=True)
77
+ path.write_text(json.dumps(data, indent=2) + "\n")
78
+ PY
79
+ ```
80
+
81
+ Replace placeholder values with secrets from the user, then `unset MU_DISCORD_SIGNING_SECRET` after patching.
82
+
83
+ ### 4) Reload and verify channel capability
84
+
85
+ ```bash
86
+ mu control reload
87
+ mu control status --json --pretty
88
+ curl -sS http://localhost:3000/api/control-plane/channels | jq '.channels[] | select(.channel=="discord")'
89
+ ```
90
+
91
+ Verify:
92
+ - `configured: true`
93
+ - `active: true` (when server is running)
94
+ - route `/webhooks/discord`
95
+
96
+ ### 5) Identity linking with audit-assisted ID discovery
97
+
98
+ Preferred flow:
99
+ 1. Ask user to run one Discord `/mu` command.
100
+ 2. Extract `actor_id` + `channel_tenant_id` from adapter audit.
101
+ 3. Link as operator.
102
+
103
+ ```bash
104
+ mu store tail cp_adapter_audit --limit 50 --json \
105
+ | jq -r '[.[] | select(.channel=="discord")] | last | "actor=\(.actor_id) tenant=\(.channel_tenant_id)"'
106
+
107
+ mu control link --channel discord --actor-id <actor-id> --tenant-id <tenant-id> --role operator
108
+ mu control identities --pretty
109
+ ```
110
+
111
+ If audit data is missing, ask user for Discord user id + guild id directly.
112
+
113
+ ### 6) Smoke checks
114
+
115
+ ```bash
116
+ mu control status --pretty
117
+ mu store tail cp_adapter_audit --limit 20 --pretty
118
+ mu store tail cp_outbox --limit 20 --pretty
119
+ ```
120
+
121
+ Ask user to run `/mu status` and confirm ingress ACK behavior.
122
+
123
+ ## Evaluation scenarios
124
+
125
+ 1. **Happy path onboarding**
126
+ - Inputs: valid public base URL, `signing_secret`, running `mu serve`.
127
+ - Expected: Discord channel reports `configured=true` and `active=true`; `/mu status` gets an ACK/response flow.
128
+
129
+ 2. **Invalid signing secret**
130
+ - Inputs: Discord app configured with one secret, `mu` config has another.
131
+ - Expected: inbound interactions are denied with deterministic audit reason; skill routes user to correct secret + reload + smoke.
132
+
133
+ 3. **Audit-assisted link fallback**
134
+ - Inputs: command ingress works but no identity linked yet.
135
+ - Expected: skill derives `actor_id`/`channel_tenant_id` from audit, links operator identity, verifies with `mu control identities --pretty`.
136
+
137
+ ## Notes and caveats
138
+
139
+ - Discord setup requires only `signing_secret` in current config contract.
140
+ - Keep troubleshooting reason-code oriented (signature/timestamp/payload errors from adapter audit).
141
+ - Keep user asks minimal: dashboard actions + one test command.
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: setup-neovim
3
+ description: "Sets up the mu.nvim messaging channel with agent-first config, reload, verification, and identity linking steps. Use when onboarding or repairing Neovim channel integration."
4
+ ---
5
+
6
+ # setup-neovim
7
+
8
+ Use this skill when the user asks to set up the Neovim messaging channel (`mu.nvim`).
9
+
10
+ Goal: get `:Mu ...` working against `mu` control-plane with minimal user-side editor actions.
11
+
12
+ ## Required user-provided inputs
13
+
14
+ - Confirmation that `mu.nvim` is installed (or permission for the agent to provide install snippet)
15
+ - Neovim-side place to set shared secret (`shared_secret` option or `MU_NEOVIM_SHARED_SECRET` env)
16
+
17
+ ## Agent-first workflow
18
+
19
+ ### 0) Verify local prerequisites (agent)
20
+
21
+ ```bash
22
+ command -v mu >/dev/null && echo "mu: ok"
23
+ command -v python3 >/dev/null && echo "python3: ok (required for config patching)"
24
+ command -v curl >/dev/null && echo "curl: ok (required for API checks)"
25
+ command -v jq >/dev/null && echo "jq: ok (required for filtered JSON checks)"
26
+ ```
27
+
28
+ If any required command is missing, stop and ask the user to install it before proceeding.
29
+
30
+ ### 1) Preflight local state
31
+
32
+ ```bash
33
+ mu control status --pretty
34
+ mu store paths --pretty
35
+ mu control identities --all --pretty
36
+ ```
37
+
38
+ If no running server exists for reload/capability checks, ask user to run `mu serve` in another terminal.
39
+
40
+ ### 2) Generate and set a shared secret (agent)
41
+
42
+ Generate a strong shared secret, then patch config with this canonical snippet
43
+ (preserves unrelated keys):
44
+
45
+ ```bash
46
+ export MU_NEOVIM_SHARED_SECRET='<NEOVIM_SHARED_SECRET>'
47
+ config_path="$(mu control status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["config_path"])')"
48
+
49
+ python3 - "$config_path" <<'PY'
50
+ import json
51
+ import os
52
+ import sys
53
+ from pathlib import Path
54
+
55
+ path = Path(sys.argv[1])
56
+ if path.exists():
57
+ data = json.loads(path.read_text())
58
+ else:
59
+ data = {"version": 1, "control_plane": {}}
60
+
61
+ cp = data.setdefault("control_plane", {})
62
+ adapters = cp.setdefault("adapters", {})
63
+ neovim = adapters.setdefault("neovim", {})
64
+ neovim["shared_secret"] = os.environ["MU_NEOVIM_SHARED_SECRET"]
65
+
66
+ path.parent.mkdir(parents=True, exist_ok=True)
67
+ path.write_text(json.dumps(data, indent=2) + "\n")
68
+ PY
69
+ ```
70
+
71
+ Replace `<NEOVIM_SHARED_SECRET>` with the generated secret, then `unset MU_NEOVIM_SHARED_SECRET`.
72
+
73
+ ### 3) Reload and verify channel capability
74
+
75
+ ```bash
76
+ mu control reload
77
+ mu control status --json --pretty
78
+ curl -sS http://localhost:3000/api/control-plane/channels | jq '.channels[] | select(.channel=="neovim")'
79
+ ```
80
+
81
+ Verify:
82
+ - `configured: true`
83
+ - `active: true` (when server is running)
84
+ - route `/webhooks/neovim`
85
+
86
+ ### 4) Provide minimal user editor steps
87
+
88
+ Ask user to do only these actions in Neovim:
89
+
90
+ 1. Set the same shared secret in `mu.nvim` config (or env var `MU_NEOVIM_SHARED_SECRET`).
91
+ 2. Ensure plugin points at the running server (default server discovery is fine if available).
92
+ 3. Run:
93
+ - `:Mu channels`
94
+ - `:Mu link`
95
+ - `:Mu status`
96
+
97
+ ` :Mu link` is the preferred identity binding path for Neovim.
98
+
99
+ ### 5) Optional agent-side identity link fallback
100
+
101
+ If `:Mu link` is unavailable, derive actor/tenant from adapter audit after one Neovim request,
102
+ then link via control-plane API:
103
+
104
+ ```bash
105
+ mu store tail cp_adapter_audit --limit 50 --json \
106
+ | jq -r '[.[] | select(.channel=="neovim")] | last | "actor=\(.actor_id) tenant=\(.channel_tenant_id)"'
107
+
108
+ curl -sS -X POST http://localhost:3000/api/control-plane/identities/link \
109
+ -H 'content-type: application/json' \
110
+ -d '{"channel":"neovim","actor_id":"<actor-id>","tenant_id":"<tenant-id>","role":"operator"}'
111
+ ```
112
+
113
+ ### 6) Smoke and forensics
114
+
115
+ ```bash
116
+ mu control status --pretty
117
+ mu control identities --all --pretty
118
+ mu store tail cp_adapter_audit --limit 20 --pretty
119
+ ```
120
+
121
+ Confirm `:Mu status` returns a valid response in-editor.
122
+
123
+ ## Evaluation scenarios
124
+
125
+ 1. **Happy path with `:Mu link`**
126
+ - Inputs: matching shared secret in server + plugin, running `mu serve`.
127
+ - Expected: `:Mu channels` lists neovim as active; `:Mu link` succeeds; `:Mu status` returns valid response.
128
+
129
+ 2. **Shared-secret mismatch**
130
+ - Inputs: plugin secret differs from `control_plane.adapters.neovim.shared_secret`.
131
+ - Expected: neovim requests are rejected with deterministic auth reason; skill rotates/re-syncs secret and revalidates.
132
+
133
+ 3. **Manual identity-link fallback**
134
+ - Inputs: `:Mu link` unavailable in plugin version.
135
+ - Expected: skill extracts actor/tenant from adapter audit and links via control-plane API; identity appears in `mu control identities --all --pretty`.
136
+
137
+ ## Safety and UX requirements
138
+
139
+ - Do not expose full shared secret in final user-facing summaries.
140
+ - Keep user asks limited to in-editor actions only.
141
+ - If failures occur, cite exact reason codes and next smallest recovery step.
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: setup-slack
3
+ description: "Sets up the Slack messaging adapter with agent-first config, reload, verification, and identity linking steps. Use when onboarding or repairing Slack channel integration."
4
+ ---
5
+
6
+ # setup-slack
7
+
8
+ Use this skill when the user asks to set up Slack messaging for `mu`.
9
+
10
+ Goal: get `/mu ...` working end-to-end in Slack, with the agent doing all local setup and asking the user only for Slack-console actions/secrets the agent cannot perform.
11
+
12
+ ## Contents
13
+
14
+ - [Required user-provided inputs](#required-user-provided-inputs)
15
+ - [Agent-first workflow](#agent-first-workflow)
16
+ - [Evaluation scenarios](#evaluation-scenarios)
17
+ - [Safety and UX requirements](#safety-and-ux-requirements)
18
+
19
+ ## Required user-provided inputs
20
+
21
+ - Public webhook base URL reachable by Slack (for example `https://mu.example.com`)
22
+ - Slack app **Signing Secret**
23
+ - Slack app **Bot User OAuth Token** (`xoxb-...`) for outbound replies/media
24
+
25
+ ## Agent-first workflow
26
+
27
+ ### 0) Verify local prerequisites (agent)
28
+
29
+ ```bash
30
+ command -v mu >/dev/null && echo "mu: ok"
31
+ command -v python3 >/dev/null && echo "python3: ok (required for config patching)"
32
+ command -v curl >/dev/null && echo "curl: ok (required for API checks)"
33
+ command -v jq >/dev/null && echo "jq: ok (required for filtered JSON checks)"
34
+ ```
35
+
36
+ If any required command is missing, stop and ask the user to install it before proceeding.
37
+
38
+ ### 1) Preflight local state
39
+
40
+ ```bash
41
+ mu control status --pretty
42
+ mu store paths --pretty
43
+ mu control identities --all --pretty
44
+ ```
45
+
46
+ If no running server is available when reload/route checks are attempted, ask the user to run `mu serve` in another terminal, then continue.
47
+
48
+ ### 2) Drive user through Slack console steps (minimal ask)
49
+
50
+ Ask the user to do only these actions in Slack API UI:
51
+
52
+ 1. Create/install a Slack app in target workspace.
53
+ 2. Configure request URL for all inbound surfaces to:
54
+ - `https://<public-base>/webhooks/slack`
55
+ 3. Enable at least:
56
+ - Slash command `/mu`
57
+ - Event Subscriptions (`app_mention`)
58
+ - Interactivity
59
+ 4. Ensure bot scopes include:
60
+ - `app_mentions:read`
61
+ - `chat:write`
62
+ - `files:read`
63
+ - `files:write`
64
+ 5. Return `signing_secret` and `bot_token` to the agent.
65
+
66
+ ### 3) Agent patches mu config (do not ask user to edit JSON)
67
+
68
+ Use this canonical patch snippet (preserves unrelated keys):
69
+
70
+ ```bash
71
+ export MU_SLACK_SIGNING_SECRET='<SLACK_SIGNING_SECRET>'
72
+ export MU_SLACK_BOT_TOKEN='<SLACK_BOT_TOKEN>'
73
+ config_path="$(mu control status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["config_path"])')"
74
+
75
+ python3 - "$config_path" <<'PY'
76
+ import json
77
+ import os
78
+ import sys
79
+ from pathlib import Path
80
+
81
+ path = Path(sys.argv[1])
82
+ if path.exists():
83
+ data = json.loads(path.read_text())
84
+ else:
85
+ data = {"version": 1, "control_plane": {}}
86
+
87
+ cp = data.setdefault("control_plane", {})
88
+ adapters = cp.setdefault("adapters", {})
89
+ slack = adapters.setdefault("slack", {})
90
+ slack["signing_secret"] = os.environ["MU_SLACK_SIGNING_SECRET"]
91
+ slack["bot_token"] = os.environ["MU_SLACK_BOT_TOKEN"]
92
+
93
+ path.parent.mkdir(parents=True, exist_ok=True)
94
+ path.write_text(json.dumps(data, indent=2) + "\n")
95
+ PY
96
+ ```
97
+
98
+ Replace placeholder values with secrets from the user, then `unset MU_SLACK_SIGNING_SECRET MU_SLACK_BOT_TOKEN` after patching.
99
+
100
+ ### 4) Reload and verify adapter status
101
+
102
+ ```bash
103
+ mu control reload
104
+ mu control status --json --pretty
105
+ curl -sS http://localhost:3000/api/control-plane/channels | jq '.channels[] | select(.channel=="slack")'
106
+ ```
107
+
108
+ Verify:
109
+ - `configured: true`
110
+ - `active: true` (when server is running)
111
+ - route `/webhooks/slack`
112
+
113
+ ### 5) Identity link with least user effort
114
+
115
+ Preferred flow:
116
+ 1. Ask user to send one Slack turn (for example `/mu status`).
117
+ 2. Extract actor + tenant IDs from audit.
118
+ 3. Link identity as operator.
119
+
120
+ ```bash
121
+ mu store tail cp_adapter_audit --limit 50 --json \
122
+ | jq -r '[.[] | select(.channel=="slack")] | last | "actor=\(.actor_id) tenant=\(.channel_tenant_id)"'
123
+
124
+ mu control link --channel slack --actor-id <actor-id> --tenant-id <tenant-id> --role operator
125
+ mu control identities --pretty
126
+ ```
127
+
128
+ If audit rows are unavailable, ask user for Slack IDs (`U...` user id, `T...` workspace id).
129
+
130
+ ### 6) Smoke test + forensic confirmation
131
+
132
+ ```bash
133
+ mu control status --pretty
134
+ mu store tail cp_adapter_audit --limit 20 --pretty
135
+ mu store tail cp_outbox --limit 20 --pretty
136
+ ```
137
+
138
+ Ask user to run `/mu status` again and confirm response delivery.
139
+
140
+ ## Evaluation scenarios
141
+
142
+ 1. **Happy path onboarding**
143
+ - Inputs: valid public base URL, `signing_secret`, `bot_token`, running `mu serve`.
144
+ - Expected: `/api/control-plane/channels` reports Slack `configured=true` and `active=true`; Slack `/mu status` returns a response.
145
+
146
+ 2. **Signature validation failure**
147
+ - Inputs: wrong `signing_secret` configured.
148
+ - Expected: inbound command is rejected; `cp_adapter_audit` shows deterministic signature/timestamp reason code; skill proposes secret rotation and reload as next step.
149
+
150
+ 3. **Outbound token missing/invalid**
151
+ - Inputs: webhook ingress works but `bot_token` missing or invalid.
152
+ - Expected: ingress may ACK but delivery fails; `cp_outbox`/adapter audit expose concrete failure reason; skill guides user to update token and re-run smoke test.
153
+
154
+ ## Safety and UX requirements
155
+
156
+ - Never expose secrets in chat logs unnecessarily; redact in summaries.
157
+ - Do not overwrite unrelated `config.json` fields.
158
+ - Keep the user ask short and sequential (one external-console step at a time).
159
+ - If setup fails, report concrete reason codes from audit/outbox and propose the next smallest recovery step.