@lumoai/cli 1.57.0 → 1.59.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 (46) hide show
  1. package/assets/skill/SKILL.md +52 -130
  2. package/assets/skill/references/artifacts-figma.md +4 -3
  3. package/assets/skill/references/confirmation.md +131 -0
  4. package/assets/skill/references/criteria.md +60 -20
  5. package/assets/skill/references/doc-editing.md +11 -9
  6. package/assets/skill/references/docs.md +4 -3
  7. package/assets/skill/references/ideas.md +82 -0
  8. package/assets/skill/references/initiatives.md +28 -0
  9. package/assets/skill/references/memory.md +4 -2
  10. package/assets/skill/references/milestones.md +3 -2
  11. package/assets/skill/references/outcome.md +1 -14
  12. package/assets/skill/references/plan-runs.md +32 -0
  13. package/assets/skill/references/sessions.md +7 -5
  14. package/assets/skill/references/sprints.md +18 -17
  15. package/assets/skill/references/task-context.md +1 -1
  16. package/assets/skill/references/task-deps.md +4 -3
  17. package/assets/skill/references/tasks.md +34 -2
  18. package/assets/skill/references/verify.md +46 -68
  19. package/assets/skill/references/worktree.md +13 -7
  20. package/dist/cli/src/commands/doc-delete.js +35 -23
  21. package/dist/cli/src/commands/doc-rebuild-source.js +16 -4
  22. package/dist/cli/src/commands/memory-rm.js +68 -10
  23. package/dist/cli/src/commands/milestone-delete.js +13 -10
  24. package/dist/cli/src/commands/outcome.js +0 -77
  25. package/dist/cli/src/commands/session-attach.js +8 -2
  26. package/dist/cli/src/commands/sprint-close.js +29 -9
  27. package/dist/cli/src/commands/sprint-delete.js +13 -10
  28. package/dist/cli/src/commands/sprint-show.js +3 -9
  29. package/dist/cli/src/commands/task-artifact-rm.js +58 -28
  30. package/dist/cli/src/commands/task-criteria-list.js +1 -4
  31. package/dist/cli/src/commands/task-criteria-set.js +3 -12
  32. package/dist/cli/src/commands/task-deps.js +20 -6
  33. package/dist/cli/src/commands/task-status.js +165 -110
  34. package/dist/cli/src/commands/task-update.js +129 -0
  35. package/dist/cli/src/commands/verify.js +22 -13
  36. package/dist/cli/src/commands/worktree-rm.js +35 -7
  37. package/dist/cli/src/index.js +47 -47
  38. package/dist/cli/src/lib/blocked-error.js +178 -0
  39. package/dist/cli/src/lib/confirmation.js +89 -0
  40. package/dist/cli/src/lib/hook-runner.js +23 -11
  41. package/dist/shared/src/referent-kind.js +31 -1
  42. package/dist/shared/src/security-scan.js +29 -0
  43. package/package.json +1 -1
  44. package/assets/skill/references/fidelity.md +0 -32
  45. package/dist/cli/src/commands/fidelity.js +0 -108
  46. package/dist/cli/src/commands/verdict.js +0 -189
@@ -27,12 +27,14 @@ The command catalog below is a **map**: it lists every command grouped by domain
27
27
  | `task context`, retrieval (`slack/web/figma context`, `comments list`, `pr show`) | [references/task-context.md](references/task-context.md) |
28
28
  | `task create/update/list/show/comment`, `next` | [references/tasks.md](references/tasks.md) |
29
29
  | `task deps add/list/confirm/dismiss/rm` (dependency edges) | [references/task-deps.md](references/task-deps.md) |
30
+ | `idea*` — capture pool, lifecycle, evidence, comments | [references/ideas.md](references/ideas.md) |
31
+ | `plan`, `plan status` — the idea→plan converter run | [references/plan-runs.md](references/plan-runs.md) |
32
+ | `initiative create/list` (`LUM-INIT-<n>`) | [references/initiatives.md](references/initiatives.md) |
30
33
  | `task artifact*`, `task figma*` | [references/artifacts-figma.md](references/artifacts-figma.md) |
31
34
  | `task criteria set/list`, drafting the acceptance contract | [references/criteria.md](references/criteria.md) |
32
35
  | `verify`, `task status` — machine verification loop, claim-done flow, self-check/resume | [references/verify.md](references/verify.md) |
33
36
  | `cost` — per-operation (per-tool) token cost read-out; `task lineage` Top-5 | [references/task-context.md](references/task-context.md) |
34
- | `outcome show/record/rate` — post-hoc outcome well (falsifier oracle, rejection-only) | [references/outcome.md](references/outcome.md) |
35
- | `fidelity show` — per-delivery grounding + independence change-pattern read-out | [references/fidelity.md](references/fidelity.md) |
37
+ | `outcome show/rate` — post-hoc outcome well (falsifier oracle, rejection-only) | [references/outcome.md](references/outcome.md) |
36
38
  | `priority [text]`, `priority list` — team priority declaration (append-only) | [references/priority.md](references/priority.md) |
37
39
  | `project list`, `milestone*` | [references/milestones.md](references/milestones.md) |
38
40
  | `doc create/update/list/move/bind/share/import` (CRUD) | [references/docs.md](references/docs.md) |
@@ -41,17 +43,18 @@ The command catalog below is a **map**: it lists every command grouped by domain
41
43
  | `task/project memory`, `memory promote/rm` | [references/memory.md](references/memory.md) |
42
44
  | `session attach/status`, git-suggest on start, Layer-2 review | [references/sessions.md](references/sessions.md) |
43
45
  | `worktree add/rm/list` (local dev tooling) | [references/worktree.md](references/worktree.md) |
46
+ | `--confirm` / exit code 4 — the confirmation envelope every gated mutation speaks | [references/confirmation.md](references/confirmation.md) |
44
47
 
45
48
  ## Command catalog
46
49
 
47
- **Onboarding / auth / update** — see [onboarding.md](references/onboarding.md)
50
+ ### Onboarding / auth / update — see [onboarding.md](references/onboarding.md)
48
51
 
49
52
  - `lumo setup [--user|--project] [--force] [--agent <token>]` — install skill files + hooks
50
53
  - `lumo auth login` / `lumo auth logout` — paste an API key / clear credentials
51
54
  - `lumo whoami` — show current identity (email, workspace, key)
52
55
  - `lumo update` — upgrade the CLI to the latest npm release
53
56
 
54
- **Task context & retrieval** — see [task-context.md](references/task-context.md)
57
+ ### Task context & retrieval — see [task-context.md](references/task-context.md)
55
58
 
56
59
  - `lumo task context <id>` — load task background (memory, source cards, PR review todos, prior sessions)
57
60
  - `lumo task slack show <id> <contextId>` — full stored Slack thread
@@ -61,142 +64,77 @@ The command catalog below is a **map**: it lists every command grouped by domain
61
64
  - `lumo task pr show <id> <number>` — synced PR metadata (v1)
62
65
  - `lumo task lineage <id>` — read-only causal-trail audit: fragments that fed the task + each one's disclosure tag/outcome, the run's token/loop cost, a disclosure-funnel summary, and a Top-5 "operations by token cost"; `--signal` appends workspace-level usage signal-health (see [task-context.md](references/task-context.md))
63
66
 
64
- **Tasks** — see [tasks.md](references/tasks.md)
67
+ ### Tasks — see [tasks.md](references/tasks.md)
65
68
 
66
69
  - `lumo task create <title> [flags]` — create a task. **Mid-task** (your session is bound to an in-flight task) it requires `--rework-of <id>` (redirects you to fix the existing task — creates nothing) or `--new-scope` (genuinely new, out-of-scope work). On a send-back, fix in place / amend the contract instead of spinning off a new task — see [verify.md](references/verify.md) and [criteria.md](references/criteria.md).
67
- - `lumo task update <id> [flags]` — patch status/title/priority/assignee/milestone/sprint/tags; `--claimable`/`--not-claimable` set the agent-claimable marker (LUM-646)
70
+ - `lumo task update <id> [flags]` — patch status/title/priority/assignee/milestone/sprint/tags; `--claimable`/`--not-claimable` set the agent-claimable marker (LUM-646). `--status done` without `--confirm` exits 4 with a confirmation envelope (see [confirmation.md](references/confirmation.md))
68
71
  - `lumo task list [flags]` — list tasks assigned to you
69
72
  - `lumo next [--count N] [--claimable]` — recommend the next task to work on (read-only). `--claimable` restricts to the autonomy boundary: only tasks that are claimable ∩ unblocked (F3 topology) ∩ within milestone budget (F2); old server degrades gracefully to the standard ranking (LUM-646)
70
73
  - `lumo task show <id>` — print one task's detail
71
74
  - `lumo task comment <id> <body>` — leave a comment
72
75
 
73
- **Ideas (team-level capture pool)**
74
-
75
- - `lumo idea "<statement>" [--context <text>]` — capture a team-level idea into the pool in <10s. Provenance is grabbed opportunistically and never blocks the capture: the current `CLAUDE_CODE_SESSION_ID` (→ sourceSessionId) and the session's bound task (→ sourceTaskId, null when nothing is bound). Prints an **I-prefixed** id (`✓ 想法 LUM-I42 已入池`) the `I` keeps ideas from colliding with task ids (`LUM-42`) in the same team namespace. Ideas are team-scoped (no project); the workspace's default team owns them. The unprocessed (CAPTURED) pool is what the Phase-2 transformer consumes.
76
- - **When to suggest**: the user has a stray idea/thought/improvement they want to park without derailing the current task ("记一下这个想法", "capture this idea", "add to the backlog of ideas"). Prefer this over `task create` for un-triaged sparks — an idea is above projects and isn't yet actionable work.
77
- - **Example**:
78
- ```bash
79
- lumo idea "Add a quick-capture keyboard shortcut" --context "raised in standup"
80
- ```
81
- - `lumo idea list` — print the team idea pool **newest-first**, one line per idea: the `LUM-I<n>` id, status (`CAPTURED` | `DEVELOPING` | `PLANNED` | `DROPPED`) and statement. Reads `GET /api/ideas`, which returns the whole pool (every status), not just the unprocessed slice. Empty pool prints `No ideas.`. The same pool is browsable/searchable in web at `/workspace/<slug>/ideas` (sidebar → Ideas), where a PLANNED idea links to the plan run that materialised it and the status filter hides DROPPED by default.
82
- - **When to suggest**: the user wants to see what's parked in the idea pool / "看看想法池" / "list the ideas" / triage before running `lumo plan`.
83
- - **Example**:
84
- ```bash
85
- lumo idea list
86
- ```
87
- - `lumo idea update <id> --status <captured|developing|planned|dropped>` — move an idea through its lifecycle (LUM-679). `<id>` is the `LUM-I<n>` id (a bare number also resolves); `--status` is case-insensitive. The four states are `CAPTURED` (pooled) → `DEVELOPING` (in triage / pre-work) → `PLANNED` (consumed by an initiative/plan), with `DROPPED` the terminal discard. Allowed transitions: `CAPTURED↔DEVELOPING`, `DEVELOPING→PLANNED`, and **any non-terminal → DROPPED**; nothing transitions out of `DROPPED`, and `CAPTURED→PLANNED` is not a manual move (only the converter's materialisation lands an idea in PLANNED). An illegal move is rejected with a clear error (400); an unknown id → 404.
88
- - **When to suggest**: the user wants to triage the pool by hand — start pre-work on an idea ("开始预研这个想法" → `developing`), or discard one ("废弃/丢掉这个想法" `dropped`). The status control also lives on the Idea detail page in web.
89
- - **Example**:
90
- ```bash
91
- lumo idea update LUM-I42 --status developing
92
- lumo idea update LUM-I42 --status dropped
93
- ```
94
- - `lumo idea slack add/show/rm <idea> ...`, `lumo idea web add/show/rm <idea> ...`, `lumo idea figma add/list/rm/refresh/context <idea> ...` (LUM-681) — attach & inspect evidence (Slack threads, web links, Figma design links) on an idea, mirroring the `task slack/web/figma` source-card commands but hitting `/api/ideas/:id/...`. `<idea>` is the `LUM-I<n>` id. `show`/`context` are Tier-2 retrieval (stored snapshot / fetched body / cached design metadata, no live re-fetch) and stamp the disclosure funnel like their task-side counterparts; `figma refresh` re-fetches metadata for every link on the idea.
95
- - **When to suggest**: the user wants to attach supporting evidence (a Slack discussion, a spec doc link, a Figma mock) to an idea before it goes through `lumo plan` — the converter carries this evidence forward into the resulting task/initiative. Prefer attaching to the idea over waiting until after conversion.
96
- - **Example**:
97
- ```bash
98
- lumo idea web add LUM-I42 https://example.com/spec
99
- lumo idea web show LUM-I42 wl_1
100
- lumo idea web rm LUM-I42 wl_1
101
- lumo idea slack add LUM-I42 https://myteam.slack.com/archives/C1/p123
102
- lumo idea slack show LUM-I42 ctx_1
103
- lumo idea slack rm LUM-I42 ctx_1
104
- lumo idea figma add LUM-I42 https://www.figma.com/file/abc/Mock
105
- lumo idea figma list LUM-I42
106
- lumo idea figma context LUM-I42 cfl_1
107
- lumo idea figma refresh LUM-I42
108
- lumo idea figma rm LUM-I42 cfl_1
109
- ```
110
-
111
- - `lumo idea comment <id> <body>` — post a comment on an idea (LUM-680). `<id>` is the `LUM-I<n>` id (a bare number also resolves) or a cuid; the body is plain text — quote it to pass spaces/newlines. Ideas reuse the task Comment model via a polymorphic `ideaId`; the thread is flat (no replies). Posting also records a `COMMENT_ADDED` entry on the idea's activity timeline. Comments/activity also render on the Idea detail slide-over in web.
112
- - `lumo idea comments list <id> [--full]` — print an idea's comment thread (oldest first), capped to the output budget like `task comments list`; `--full` prints every comment. Empty thread → `(no comments)`.
113
- - **When to suggest**: the user wants to discuss or annotate a parked idea ("给这个想法加条评论", "comment on the idea"), or review an idea's discussion/history ("看看这个想法的评论/活动"). For the activity timeline (status changes, statement edits, materialisation) there is no CLI reader — it's shown on the Idea detail slide-over in web.
114
- - **Example**:
115
- ```bash
116
- lumo idea comment LUM-I42 "Let's scope this for next sprint"
117
- lumo idea comments list LUM-I42
118
- ```
119
-
120
- **Plan runs (design-thinking 转换器)**
121
-
122
- - `lumo plan [--abandon-active]` — start a converter run that turns the team's captured idea pool + current priority into an executable plan through a 3-gate recoverable state machine (聚类→对齐→成计划). Freezes the input snapshot at start and enforces **one active run per team**: if a run is already active it refuses and points to `lumo plan status`; `--abandon-active` abandons the prior run first, then starts fresh. On success it prints (and opens) the **gate-A** web deep-link (`/workspace/<slug>/plan/<runId>`). The CLI only triggers — **all editing and gate confirmation happen in web**.
123
- - `lumo plan status` — print the active run's current stage (working segment or the open `*_READY` gate) and the deep-link to act on its next gate; with no active run, prompts to run `lumo plan`.
124
- - **When to suggest**: the user wants to turn the parked idea pool into a plan / "跑一下转换器" / "start a planning run" / "把想法变成计划"; or asks where an in-flight plan run stands ("plan 到哪一步了", "plan status"). The gate work itself is web-only — the CLI hands off the deep-link.
125
- - **Example** — check where the active run stands:
126
- ```bash
127
- lumo plan status
128
- ```
129
-
130
- **Initiatives (team-level, LUM-INIT-<n>)**
131
-
132
- - `lumo initiative create "<goal>" [--assumption <text>]` — create a team-level Initiative **directly, no plan run required** (`planRunId=null`, status `ACTIVE`). Prints the `LUM-INIT-<n>` id; `--assumption` records the bet (赌注旁注) behind it. A blank goal is rejected (400). Converter-materialised initiatives (from `lumo plan`) live in the same list.
133
- - **When to suggest**: the user wants to stand up a strategic initiative directly — "建一个 initiative" / "create an initiative" / "开一个举措" — without running the converter.
134
- - **Example**:
135
- ```bash
136
- lumo initiative create "Ship the initiatives browse surface" --assumption "teams curate initiatives directly"
137
- ```
138
- - `lumo initiative list` — print the team's initiatives newest-first, one line per initiative: the `LUM-INIT-<n>` id, status (`ACTIVE` | `DONE` | `DROPPED`) and goal. Browsable in web at `/workspace/<slug>/initiatives` (sidebar → Initiatives), where each links to its lineage (consumed ideas + attached tasks).
139
- - **When to suggest**: the user wants to see the team's initiatives / "看看 initiatives" / "list the initiatives".
140
- - **Example**:
141
- ```bash
142
- lumo initiative list
143
- ```
144
-
145
- **Task dependencies** — see [task-deps.md](references/task-deps.md)
76
+ ### Ideas (team-level capture pool) — see [ideas.md](references/ideas.md)
77
+
78
+ - `lumo idea "<statement>" [--context <text>]` — capture an un-triaged spark into the team pool (<10s); prints an **I-prefixed** id (`LUM-I42`) that cannot collide with task ids
79
+ - `lumo idea list` the whole pool newest-first (every status); also browsable at `/workspace/<slug>/ideas`
80
+ - `lumo idea update <id> --status <captured|developing|planned|dropped>` — manual lifecycle move; `CAPTURED→PLANNED` is converter-only
81
+ - `lumo idea slack|web|figma …` — attach & inspect evidence on an idea, mirroring the task-side source cards
82
+ - `lumo idea comment <id> <body>` · `lumo idea comments list <id> [--full]` flat comment thread on an idea
83
+
84
+ ### Plan runs (design-thinking 转换器) see [plan-runs.md](references/plan-runs.md)
85
+
86
+ - `lumo plan [--abandon-active]` — start a converter run (聚类→对齐→成计划) over the captured pool + current priority; one active run per team; prints the gate-A web deep-link
87
+ - `lumo plan status` — the active run's stage plus the deep-link to its next gate. Gate work is **web-only**
88
+
89
+ ### Initiatives (team-level, `LUM-INIT-<n>`) — see [initiatives.md](references/initiatives.md)
90
+
91
+ - `lumo initiative create "<goal>" [--assumption <text>]` create directly, no plan run required
92
+ - `lumo initiative list` — team initiatives newest-first; lineage browsable in web
93
+
94
+ ### Task dependencies see [task-deps.md](references/task-deps.md)
146
95
 
147
96
  - `lumo task deps list <id>` — list dependency edges both directions, grouped CONFIRMED / SUGGESTED / DISMISSED (each row: short edge id + other task + detected evidence)
148
97
  - `lumo task deps add <id> --blocked-by <LUM-N>` — declare a manual hard dependency (CONFIRMED)
149
98
  - `lumo task deps confirm <id> <edge> [--reverse]` — confirm a detected candidate (`--reverse` flips direction)
150
99
  - `lumo task deps dismiss <id> <edge>` — dismiss a candidate (never re-suggested)
151
- - `lumo task deps rm <id> <edge> --yes` — delete an edge (refuses without `--yes`)
100
+ - `lumo task deps rm <id> <edge> --confirm` — delete an edge (without `--confirm`: exit 4 + confirmation envelope; `--yes` is a legacy alias)
152
101
  - `<edge>` = short edge-id prefix (≥6 chars) or the other task's identifier; ambiguous/unknown selectors list candidates and exit 1. See [tasks.md](references/tasks.md)
153
102
 
154
- **Acceptance criteria (contract)** — see [criteria.md](references/criteria.md)
103
+ ### Acceptance criteria (contract) — see [criteria.md](references/criteria.md)
155
104
 
156
105
  - `lumo task criteria set <task> --file <criteria.json> [--human] [--cause <tag>]` — submit the whole contract (full-group replace). Default = agent draft (AGENT_DRAFT, editable until DONE); `--human` = a HUMAN_EDIT revision transcribed from the conversation; `--cause` annotates why the contract drifted. See [criteria.md](references/criteria.md)
157
106
  - `lumo task criteria list <task>` — print the contract (id, MACHINE/HUMAN, provenance source@round, checkpointer)
158
107
 
159
- **Verification (machine acceptance loop)** — see [verify.md](references/verify.md)
108
+ ### Verification (machine acceptance loop) — see [verify.md](references/verify.md)
160
109
 
161
- - `lumo verify [task] --note "<self-report>" [--timeout <seconds>]` — run every MACHINE criterion's checkpointer locally and report a structured PASS/FAIL verdict per criterion. **`--note` is required** when a round will be posted: a one-line self-report ("我改了 X,因为 Y 可验收") frozen as your task's claim (source AGENT) the moment the round passes into IN_REVIEW, then checked against the diff for faithfulness. No note → no round posted (none burned). The round cap is the workspace's `verificationMaxRounds` (default 3) — read the actual budget off the `Round N/M` line the command prints, never assume a number. All-pass → task to IN_REVIEW (agent stops); a fail on the last round → escalate to human. **Run this before claiming a task is done.**
162
- - `lumo task status [task] [--json]` — read-only acceptance self-check (no LLM): the contract with each criterion's latest verdict, verification history/round, last failure reasons, `nextActions` (the unmet criteria), and any OPEN boundary crossings (fails closed — `null`/`⚠` means "could not confirm", not safe). **Run it first when resuming a task or after a round was rejected.**
163
- - `lumo verdict [task] --pass | --fail` — acceptance verdicts. `--pass` deep-links to the human verdict bar (records nothing). `--fail --reason <enum> …` records an AGENT send-back → IN_PROGRESS. **An unresolved send-back blocks the DONE transition with 409.**
110
+ - `lumo verify [task] [--note "<self-report>"] [--timeout <seconds>]` — run every MACHINE criterion's checkpointer locally and report a structured PASS/FAIL verdict per criterion. `--note` is an optional one-line self-report ("我改了 X,因为 Y 可验收"), frozen as the task's claim (source AGENT) when the round passes into IN_REVIEW. The round cap is the workspace's `verificationMaxRounds` (default 3) — read the actual budget off the `Round N/M` line the command prints, never assume a number. All-pass → task to IN_REVIEW (agent stops); a fail on the last round → escalate to human. **Run this before claiming a task is done.**
111
+ - `lumo task status [task] [--full] [--json]` — read-only acceptance self-check (no LLM): the contract with each criterion's latest verdict, the current round, last failure reasons, `nextActions` (the unmet criteria, plus any undispositioned PR **security findings** as `SECURITY_FINDING` entries — `blocks DONE` vs `advisory`), a per-linked-PR **scan status line** (stage states incl. the L3 `hunt` layer, external-finding count, partial flag, external failure reason — LUM-735/739), and any OPEN boundary crossings; `--full` adds the dashboard sections (verification rollup, history, cost, struggle trail, trend) (fails closed — `null`/`⚠` means "could not confirm", not safe). **Run it first when resuming a task or after a round was rejected.**
164
112
  - `lumo crossing explain <id> --note "<text>"` — append an agent self-explanation ("申辩") to a boundary crossing; append-only, **never clears the crossing or unblocks Done** (disposition stays web + human-only). A review aid, not a self-clear.
165
113
 
166
- **Cost (per-operation token read-out)** — see [task-context.md](references/task-context.md)
114
+ ### Cost (per-operation token read-out) — see [task-context.md](references/task-context.md)
167
115
 
168
116
  - `lumo cost [--task <id>|--session <id>|--since <date>] [--by tool|model|member|session] [--json]` — per-operation token cost read-out, attributing each model step's token delta to the tool(s) it ran. Scope is mutually exclusive (default = workspace last-30-days). For the per-task Top-5 inline, see `lumo task lineage`.
169
117
 
170
- **Priority (team-level declaration)** — see [priority.md](references/priority.md)
118
+ ### Priority (team-level declaration) — see [priority.md](references/priority.md)
171
119
 
172
120
  - `lumo priority [text]` — declare the team's current priority (a 1-3 sentence plain-text statement). With `text`, records it directly; with no argument, prompts interactively. **Append-only**: a new declaration supersedes the old by a later `effectiveFrom` — nothing is ever overwritten or deleted.
173
121
  - `lumo priority list` — print the append history newest-first (the current declaration is marked `*`).
174
122
 
175
- **Criteria audit**workspace-level referent-kind analytics
123
+ ### Criteria audit — see [criteria.md](references/criteria.md)
176
124
 
177
125
  - `lumo criteria audit` — workspace-level read-out: count of acceptance criteria by effective referent kind (EXTERNAL_FACT / AGENT_CONSTRUCTED_STATE / PENDING_OUTCOME / UNVERIFIED_ASSERTION / UNCLASSIFIED) + the self-confirming-green ratio
178
126
 
179
- **Outcome well (post-hoc oracle)** — see [outcome.md](references/outcome.md)
127
+ ### Outcome well (post-hoc oracle) — see [outcome.md](references/outcome.md)
180
128
 
181
129
  - `lumo outcome show <task>` — read the post-hoc outcome well: the **falsifier** verdict (`REJECTED` | `INCONCLUSIVE` — never a "pass") plus its backing rejection signals. `INCONCLUSIVE` = no rejection on record, **not** "satisfied".
182
- - `lumo outcome record <task> --note "<what reality did>" [--kind <kind>]` — record a human-observed post-hoc **rejection** of a delivery (revert / rollback / CI regression / downstream redirect / bypass). Append-only; **there is no "mark satisfied" counterpart** — the well only asserts rejection. `--note` is required (the observed referent).
183
130
  - `lumo outcome rate [--min N] [--json]` — workspace **trust × post-hoc-fate** join (LUM-603, LUM-606): per delivery-time **verification-friction** bucket (`no-verification` / `single-round` / `multi-round-or-sendback` — two raw counts frozen at delivery, replacing the saturating forecast-confidence axis), the post-hoc REJECTED rate from the well. Honest by construction — thin buckets read `insufficient` (no fabricated rate), the single-round-vs-multi comparison stays `inconclusive` until CIs separate, and the rate is a **lower bound** (no signal = INCONCLUSIVE, never satisfied). Bucket names are value-neutral — direction is decided by the correlation, never welded into the label. Under-sampled today by design — the skeleton produces real strata once the well fills.
184
131
 
185
- **Fidelity read-out (mechanical change-pattern)** — see [fidelity.md](references/fidelity.md)
186
-
187
- - `lumo fidelity show <task> [--json]` — per delivery snapshot (LUM-609), a **composition table, not a single score**: (1) the **grounding** of the frozen contract — grounded (`EXTERNAL_FACT`, a machine-checkable upper bound) / self-confirming (`AGENT_CONSTRUCTED_STATE` + `UNVERIFIED_ASSERTION`) / inconclusive (`PENDING_OUTCOME` + `UNCLASSIFIED`, surfaced not swallowed) + the full effective-kind tally; (2) the **independence** signal — criteria `ADDED`/`UPDATED` at round>0 (after work started) that the delivery rode on = backward-inference suspects. Purely mechanical (timing + op + grounding) — **no direction verdict**. A no-snapshot task is reported explicitly (not a pass). Zero new storage — reads the snapshot + `CRITERION_CHANGED` trail.
188
-
189
- ```bash
190
- lumo fidelity show LUM-42
191
- lumo fidelity show LUM-42 --json
192
- ```
193
-
194
- **Artifacts & Figma** — see [artifacts-figma.md](references/artifacts-figma.md)
132
+ ### Artifacts & Figma — see [artifacts-figma.md](references/artifacts-figma.md)
195
133
 
196
134
  - `lumo task artifact add/update/list/show/rm` — record spec/plan products on a task
197
135
  - `lumo task figma add/list/rm/refresh` — attach & manage Figma designs
198
136
 
199
- **Projects & milestones** — see [milestones.md](references/milestones.md)
137
+ ### Projects & milestones — see [milestones.md](references/milestones.md)
200
138
 
201
139
  - `lumo project list` — list projects (slugs feed `--project`)
202
140
  - `lumo milestone list/create/show/update/delete` — milestone CRUD (`show` includes a Sprint-coverage section)
@@ -208,7 +146,7 @@ lumo fidelity show LUM-42 --json
208
146
  - `lumo milestone reorder/move` — manual ordering
209
147
  - `lumo milestone criteria set/list/verdict` — milestone-level **exit criteria** (LUM-643): the close contract, drafted & adjudicated (裁定) by the steward (LUM-640) or a human. Unresolved criteria (pending or NOT_MET) block `--status completed` **and** the LUM-624 auto-rollup with 409/fail-closed; human override is web-only (`?force=1`, stamped as a persistent trail). `verdict --met` on `EXTERNAL_FACT` requires `--evidence` (422); `PENDING_OUTCOME` can never be MET
210
148
 
211
- **Documents** — CRUD/sharing → [docs.md](references/docs.md); **editing live docs** (raw/section/patch/append/diff/rebuild) → [doc-editing.md](references/doc-editing.md)
149
+ ### Documents — CRUD/sharing → [docs.md](references/docs.md); **editing live docs** (raw/section/patch/append/diff/rebuild) → [doc-editing.md](references/doc-editing.md)
212
150
 
213
151
  - `lumo doc create/update/show/list/delete` — document CRUD; `doc update` takes `--if-revision <n>` (mismatch → 409, re-read and retry) and is structure-guarded (drops to tables/rows/headings → 422 unless `--allow-shrink`)
214
152
  - `lumo doc show <doc> --raw` / `--section "<heading>"` — print the byte-identical markdown source (the only legal edit base; never the lossy HTML→md render), whole or one heading-addressed slice
@@ -219,55 +157,36 @@ lumo fidelity show LUM-42 --json
219
157
  - `lumo doc move/bind/unbind/share/unshare/share-list` — reparent · task linkage · member sharing
220
158
  - `lumo doc import-gdoc` / `lumo doc sync` — Google Doc import & re-sync
221
159
 
222
- **Sprints** — see [sprints.md](references/sprints.md)
160
+ ### Sprints — see [sprints.md](references/sprints.md)
223
161
 
224
162
  - `lumo sprint list/create/show/update/delete` — sprint CRUD (`show` includes Progress / Health / Blockers)
225
163
  - `lumo sprint start/close` — status transitions (no `--status` flag)
226
164
  - `lumo sprint add/remove <id> <task>` — bind/unbind a task
227
165
  - `lumo sprint summary [--retry]` — AI retro
228
166
 
229
- **Memory** — see [memory.md](references/memory.md)
167
+ ### Memory — see [memory.md](references/memory.md)
230
168
 
231
169
  - `lumo task memory add/list` · `lumo project memory add/list` — record/curate Memory (TASK vs PROJECT)
232
170
  - `lumo memory show <id>` — show one memory's full card (category + content) by id (progressive disclosure from a one-line index entry)
233
- - `lumo memory promote <id>` / `lumo memory rm <id> --yes` — TASK→PROJECT / delete
171
+ - `lumo memory promote <id>` / `lumo memory rm <id> --confirm` — TASK→PROJECT / delete (exit 4 + envelope without `--confirm`)
234
172
  - `lumo memory sync [--dry-run] [--clean]` — downsync team memory into the local Claude Code store (`team/<id>.md` + a managed MEMORY.md block); only touches files it owns
235
173
  - `lumo memory push [--dry-run]` — upsync locally-authored memories from `<memory-dir>/outbox/*.json` to the team
236
174
  - `lumo memory fold [project-ref] --dry-run` — read-only PREVIEW of the autonomous topic-fold pass (folding runs via a daily cron; no manual apply, no `unfold`). See [memory.md](references/memory.md)
237
175
 
238
- **Sessions** — see [sessions.md](references/sessions.md)
176
+ ### Sessions — see [sessions.md](references/sessions.md)
239
177
 
240
178
  - `lumo session attach <id>` — bind this session to a task (then run `task context`). **Lifetime lock**: re-attaching to the same task is a no-op; attaching to a _different_ task is refused with 409 — start a new Claude Code session instead. No `--force`, no `session detach`.
241
- - `lumo session attach --steward <milestone>` — bind this session to a **milestone** as a **STEWARD (governance) session** (LUM-640): plan & accept only. Governance passes (task create/update, criteria set incl. `--human`, deps confirm/dismiss, `verdict --fail` with session provenance, milestone add/remove, milestone triage (LUM-646 claimable), all reads); implementation is gated (`lumo verify` → 409 **without burning a round**; moving a task to in_progress assigned to yourself → 409); commits/PRs produced anyway are recorded as milestone-scoped boundary crossings (fail-closed; disposition stays web+human-only, no CLI clear path). Same lifetime lock: same milestone idempotent, any other attach (a task, or a different milestone) → 409; multiple stewards per milestone are fine. See [sessions.md](references/sessions.md)
179
+ - `lumo session attach --steward <milestone>` — bind this session to a **milestone** as a **STEWARD (governance) session** (LUM-640): plan & accept only. Governance passes (task create/update, criteria set incl. `--human`, deps confirm/dismiss, milestone add/remove, milestone triage (LUM-646 claimable), all reads); implementation is gated (`lumo verify` → 409 **without burning a round**; moving a task to in_progress assigned to yourself → 409); commits/PRs produced anyway are recorded as milestone-scoped boundary crossings (fail-closed; disposition stays web+human-only, no CLI clear path). Same lifetime lock: same milestone idempotent, any other attach (a task, or a different milestone) → 409; multiple stewards per milestone are fine. See [sessions.md](references/sessions.md)
242
180
  - `lumo session status` — show current binding (a task, or the milestone + STEWARD role)
243
181
  - End-of-session housekeeping is fully automatic (no command). On DONE the server runs three best-effort silent passes: Layer-1 memory curation, fragment-usage audit, and blocked-tag automation. See [sessions.md](references/sessions.md)
244
182
  - Git-suggest at session start (suggests `session attach`, never auto-binds) + Layer-2 project-memory review — see the reference
245
183
 
246
- **Worktrees (local dev tooling)** — see [worktree.md](references/worktree.md)
184
+ ### Worktrees (local dev tooling) — see [worktree.md](references/worktree.md)
247
185
 
248
186
  - `lumo worktree add <LUM-N> [slug]` — scaffold `.worktrees/<LUM-N>` + node_modules symlink off origin/main; run from the main checkout
249
- - `lumo worktree rm <LUM-N> --yes` — remove a worktree (keeps the branch unless `--delete-branch`)
187
+ - `lumo worktree rm <LUM-N> --confirm` — remove a worktree (exit 4 + envelope without `--confirm`; dirty tree also needs `--force`; keeps the branch unless `--delete-branch`)
250
188
  - `lumo worktree list` — list `.worktrees/` worktrees (task id, branch, dirty, node_modules link)
251
189
 
252
- ## Criteria audit
253
-
254
- ```bash
255
- lumo criteria audit
256
- ```
257
-
258
- `lumo criteria audit` — workspace-level acceptance-criteria analytics. Calls `GET /api/criteria/audit` and prints two things:
259
-
260
- 1. **Declared-kind distribution** — count of criteria by effective referent kind:
261
- - `EXTERNAL_FACT` — declared as externally observable (git SHA, CI status, DB row) and the checkpointer invokes real external tooling
262
- - `AGENT_CONSTRUCTED_STATE` — passes by checking output the agent itself produced
263
- - `PENDING_OUTCOME` — deferred to post-hoc falsifier
264
- - `UNVERIFIED_ASSERTION` — declared `EXTERNAL_FACT` but the checkpointer does **not** actually invoke external tooling (git/CI/HTTP); auto-downgraded. As of LUM-605 this is an **enforced gate, not just a label**: `lumo task criteria set` warns on it, and `lumo verify` blocks a PASS on it with **422 (no round burned)** until it's re-grounded or honestly re-declared `AGENT_CONSTRUCTED_STATE` (see references/criteria.md "referentKind")
265
- - `UNCLASSIFIED` — legacy rows predating the `referentKind` field (null in DB)
266
-
267
- 2. **Self-confirming-green headline ratio** — `(AGENT_CONSTRUCTED_STATE + UNVERIFIED_ASSERTION) / classified`. Criteria in these two buckets pass by confirming only the agent's own work, not an independent external state.
268
-
269
- **When to suggest**: when the user wants to audit how much of the workspace's "green" criteria are self-confirming vs externally anchored; when diagnosing whether acceptance criteria have real external hooks or are circular.
270
-
271
190
  ## Next-step hints (the trailing `Next:` block)
272
191
 
273
192
  `task create`, `task update`, `session attach`, `task context` and `next` end with a short `Next:` block naming what to do now — attach an unbound session, draft a missing contract, run `lumo verify`, read `task status` after a rejected round, explain an open boundary crossing, or stop and wait when the task is IN_REVIEW.
@@ -282,6 +201,7 @@ The block never replaces the acceptance-contract section: on `session attach` /
282
201
 
283
202
  Don't guess these:
284
203
 
204
+ - No `lumo verdict`, `lumo fidelity`, or `lumo outcome record` (removed in LUM-733): send-backs and passes are human-only in the web verdict bar; outcome signals are derived automatically (git revert feed); the fidelity read-out is an internal API (`GET /api/tasks/<id>/fidelity`), not a CLI command
285
205
  - No `lumo session start` — binding is `lumo session attach <LUM-N>`
286
206
  - No `lumo session detach` — the session↔task binding is a lifetime lock; to work on a different task, start a new Claude Code session
287
207
  - No steward "role switch" or un-steward — `session attach --steward` is the same lifetime lock on a milestone; a steward session never converts to an implementation session (and there is no CLI path to clear its boundary crossings)
@@ -292,7 +212,8 @@ Don't guess these:
292
212
  - No `--content` on `task/project memory add` — memory is structured fields (`--category` + per-category flags), see [memory.md](references/memory.md)
293
213
  - No global `--verbose` flag on any command
294
214
  - `lumo task comments list` (plural) **reads** the thread; `lumo task comment` (singular) **writes** one
295
- - Status updates: `lumo task update LUM-N --status done` is one direct call — do **not** walk `in_progress → in_review → done` step by step (see [tasks.md](references/tasks.md) for the transition matrix; under the verify flow you shouldn't be setting `in_review`/`done` yourself at all)
215
+ - Status updates: `lumo task update LUM-N --status done` is one direct call — do **not** walk `in_progress → in_review → done` step by step (see [tasks.md](references/tasks.md) for the transition matrix; under the verify flow you shouldn't be setting `in_review`/`done` yourself at all). `--status done` has no interactive prompt and no `--yes`: without `--confirm` it exits 4 with a confirmation envelope you relay to the user (see [confirmation.md](references/confirmation.md))
216
+ - No interactive yes/no prompts anywhere, and no `--yes` on `task update` — every gated mutation exits 4 with a `confirmation_required` envelope instead; `--yes` survives only as a legacy alias of `--confirm` on the delete/rm/close commands
296
217
 
297
218
  ## Core workflow
298
219
 
@@ -300,7 +221,7 @@ Don't guess these:
300
221
 
301
222
  1. `lumo session attach LUM-42` — bind this session (the binding is a lifetime lock; one session = one task)
302
223
  2. `lumo task context LUM-42` — load background; review unresolved items, PR-review todos, the description
303
- 3. **If the task has no acceptance criteria** (context shows the draft reminder, not a contract): draft outcome-level criteria sized to the task (3–7 for a typical multi-file task; 1–2 for a micro task) and submit them with `lumo task criteria set` **before writing the first line of code**see [criteria.md](references/criteria.md)
224
+ 3. **If the task has no acceptance criteria** (context shows the draft reminder, not a contract): draft outcome-level criteria sized to the task (3–7 for a typical multi-file task; 1–2 for a micro task) and submit them with `lumo task criteria set` **before writing the first line of code**. When a `### Reference class (outside view)` block follows the reminder, draft against it it lists how similar delivered tasks actually failed; treat it as material to reason from, never as a checklist to cover. See [criteria.md](references/criteria.md)
304
225
  4. Do the work
305
226
  5. **Before claiming done: `lumo verify`** — the machine half of the acceptance loop. Fix failures and re-run (the cap is workspace-configurable — see the `Round N/M` line in the output). All-pass → task moves to IN_REVIEW and you stop. See [verify.md](references/verify.md)
306
227
 
@@ -314,4 +235,5 @@ Don't guess these:
314
235
  - **Evidence before "done".** Never claim done from reading code — run `lumo verify`. While a PR is open, the task is IN_REVIEW, not DONE.
315
236
  - **Never set DONE yourself after a verify loop** — that adjudication is human-only. An unresolved send-back blocks the DONE transition with 409.
316
237
  - **Status updates are one direct call** (`task update --status done`), not a step-by-step walk through `in_progress → in_review → done`.
238
+ - **Exit code 4 = the user must confirm — you may not.** A gated mutation (`--status done`, `sprint/milestone/doc delete`, `memory rm`, `task deps/artifact rm`, `sprint close --move-all/--backlog-all`, `worktree rm`, `doc rebuild-source` on an existing source) run without `--confirm` prints a `confirmation_required` envelope on stdout and does nothing. Show every line of its `changes` to the user, wait for their explicit approval, then run its `confirmCommand` verbatim. **Never add `--confirm` (or `--yes` / `--force`) yourself** — the flag means "the user has confirmed", and adding it on your own initiative is the exact failure the protocol exists to stop. **Exit code 5 = blocked by a human-only gate** (`DONE_BLOCKED`: a send-back, an undispositioned boundary crossing, a blocking security finding): relay its `blockers` and `remediation` to the user; nothing on the CLI clears it — no retry, no flag, no rewording a failed criterion. Details: [confirmation.md](references/confirmation.md)
317
239
  - **Read the reference before composing a command** whose flags/edge-cases matter — don't run from memory.
@@ -46,12 +46,13 @@ Prints one artifact's key:value header (id, kind, title, agent, source, order, t
46
46
  lumo task artifact show LUM-42 cma_xxx
47
47
  ```
48
48
 
49
- #### `lumo task artifact rm <task> <artifact-id> --yes`
49
+ #### `lumo task artifact rm <task> <artifact-id> --confirm`
50
50
 
51
- Deletes an artifact from a task. Irreversible `--yes` is required and there is no interactive prompt (agent-friendly). On success prints `Removed <artifact-id> from <task>`. A 404 (task or artifact missing in this workspace) prints the server message and exits 1.
51
+ Deletes an artifact from a task. Irreversible. Without `--confirm` the CLI fetches the artifact and exits **4** with a confirmation envelope naming it (`Will delete artifact cma_xxx "API spec" (SPEC) from LUM-42` · `Artifact deletion is irreversible`) — no DELETE is sent; relay `changes` to the user and re-run `confirmCommand` on approval ([confirmation.md](confirmation.md)). `--yes` is a legacy alias. On success prints `Removed <artifact-id> from <task>`. A 404 (task or artifact missing in this workspace) prints the server message and exits 1.
52
52
 
53
53
  ```bash
54
- lumo task artifact rm LUM-42 cma_xxx --yes
54
+ lumo task artifact rm LUM-42 cma_xxx # exit 4 + envelope
55
+ lumo task artifact rm LUM-42 cma_xxx --confirm # after the user approved
55
56
  ```
56
57
 
57
58
  When to suggest: after running a spec/plan workflow in Claude Code, offer to record the product(s) with `task artifact add` (one call per artifact) — always pass `--source` with the framework you used. Use `task artifact list` to see what's already recorded, `task artifact show` to inspect a single artifact's content, `task artifact update` to fix a wrong kind/title/source without re-uploading, and `task artifact rm` to drop one that's wrong or stale.
@@ -0,0 +1,131 @@
1
+ # Confirmation protocol (`--confirm`, exit code 4)
2
+
3
+ Some `lumo` commands change or destroy state that a human should sign off on first — deleting a sprint, milestone, document, memory, artifact or dependency edge; closing a sprint with unfinished tasks; removing a worktree; re-deriving a doc's markdown source; and moving a task to **DONE**. Since LUM-755 they all speak one protocol instead of prompting or hard-refusing:
4
+
5
+ 1. You run the command **without `--confirm`**.
6
+ 2. The CLI computes, from real server/local state, what it would do — and **does not do it**. It prints a **confirmation envelope on stdout** and exits **4**.
7
+ 3. You show the envelope's `changes` to the user, verbatim.
8
+ 4. The user approves or rejects.
9
+ 5. Only on approval, you re-run the envelope's `confirmCommand` — the same invocation with `--confirm` appended.
10
+
11
+ There is no interactive yes/no prompt on a TTY either: a human and an agent walk the same steps.
12
+
13
+ ## The envelope
14
+
15
+ When stdout is not a TTY (the agent path) the envelope is JSON:
16
+
17
+ ```json
18
+ {
19
+ "status": "confirmation_required",
20
+ "command": "sprint delete",
21
+ "changes": [
22
+ "Will delete sprint #3 \"Sprint 3\"",
23
+ "12 tasks under it keep their data; only sprintId is cleared"
24
+ ],
25
+ "confirmCommand": "lumo sprint delete 3 --confirm"
26
+ }
27
+ ```
28
+
29
+ | Field | Meaning |
30
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | `status` | Always `confirmation_required`. |
32
+ | `command` | The command path, e.g. `task update`, `worktree rm`. |
33
+ | `changes` | Human-readable lines describing exactly what would happen, computed from the current state (titles, counts, linked PRs, dirty files…). Never empty. |
34
+ | `confirmCommand` | Your original argv (flags and quoting preserved) plus `--confirm` — and, when a second gate applies, that gate's flag too (`--force --confirm`). |
35
+
36
+ On a TTY the same fields are rendered as text: a header line, one `-` bullet per change, then `Re-run with --confirm once the user has approved:` and the command on its own line.
37
+
38
+ Nothing goes to stderr; the envelope is the command's result, not an error. **Exit 4 is the only signal you need** — do not grep the text to decide whether a command "wants confirmation".
39
+
40
+ ## Rules for an agent
41
+
42
+ - **Exit 4 → stop and relay.** Print every line of `changes` to the user. Do not paraphrase away a `⚠` line.
43
+ - **Wait for an explicit approval.** A user who asked you to "delete the sprint" earlier has not yet seen what the envelope says will happen; the envelope is what they approve.
44
+ - **Run `confirmCommand` unchanged.** Do not rebuild the command by hand, do not drop flags the envelope carried (`--force`, `--team`, `--project`…).
45
+ - **Never add `--confirm` (or `--yes`, or `--force`) on your own initiative.** `--confirm` means "the user has confirmed". Passing it without that approval is the one thing this protocol exists to prevent, and it is visible in the session trail.
46
+ - A rejected envelope is not an error to retry — report the user's decision and move on.
47
+
48
+ ## Flags
49
+
50
+ - `--confirm` — the user approved. Present on every command in the table below.
51
+ - `--yes` — legacy alias of `--confirm` on the commands that took it before LUM-755. Prefer `--confirm` in new invocations. `task update` never had `--yes` and does not gain it.
52
+ - `--force` — a **second gate** that stays separate from `--confirm`: `worktree rm` uses it to acknowledge discarding uncommitted changes; `doc rebuild-source` uses it to acknowledge replacing an existing source. Missing `--force` also yields an envelope (exit 4) whose `confirmCommand` carries `--force --confirm`.
53
+
54
+ ## Commands that use the protocol
55
+
56
+ | Command | `changes` describes | Notes |
57
+ | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `task update <id> --status done` | `Will move LUM-N "title" to DONE` · `Status: <current> → DONE` · one line per linked PR (`#926 merged title url`) or `Pull requests: none linked` · a trailing `⚠ N linked pull request(s) not merged: …` when any PR is open/closed/draft | Only `--status done` is gated; other statuses PATCH directly. The server's own DONE gates (unresolved send-back, open boundary crossing → 409) still apply after `--confirm`. See [tasks.md](tasks.md). |
59
+ | `task deps rm <id> <edge>` | the resolved edge (short id, direction, other task, status, source) | [task-deps.md](task-deps.md) |
60
+ | `task artifact rm <task> <artifact-id>` | artifact id, title, kind; "irreversible" | [artifacts-figma.md](artifacts-figma.md) |
61
+ | `memory rm <memoryId>` | memory id, `[CATEGORY]`, a one-line content summary | [memory.md](memory.md) |
62
+ | `doc delete <doc>` | doc id + title; nested documents are deleted with it | [docs.md](docs.md) |
63
+ | `sprint delete <ref>` | sprint number + name; task count (tasks keep their data) | [sprints.md](sprints.md) |
64
+ | `sprint close <ref> --move-all` / `--backlog-all` | sprint; every unfinished task and where it goes | Without a mode flag and with unfinished tasks the command is a plain exit-1 refusal (pick a mode). [sprints.md](sprints.md) |
65
+ | `milestone delete <ref>` | milestone name; task count (tasks keep their data) | [milestones.md](milestones.md) |
66
+ | `worktree rm <LUM-N>` | path + branch; `⚠ … uncommitted changes … discarded` when dirty; whether the branch is kept or deleted | Dirty tree → `confirmCommand` carries `--force --confirm`. [worktree.md](worktree.md) |
67
+ | `doc rebuild-source <doc>` (when a source already exists) | the server's 409 reason; what `--force` would replace | `confirmCommand` carries `--force --confirm`. [doc-editing.md](doc-editing.md) |
68
+
69
+ ## When the gate is human-only: `DONE_BLOCKED`, exit code 5
70
+
71
+ Not every refusal is something the user can approve. The server's DONE gates — an **unresolved send-back**, an **undispositioned boundary crossing**, a **blocking security finding** — are verdicts on the agent's own work, and by design there is no CLI path to clear them (the interested party may not adjudicate itself). They are therefore **not** part of the confirmation protocol: no `--confirm`, no `--force`, nothing you can pass.
72
+
73
+ What the CLI does instead: when `task update <id> --status done --confirm` is refused by one of these gates, it re-reads the task's acceptance status and open crossings, prints a structured error on **stdout** and exits **5**:
74
+
75
+ ```json
76
+ {
77
+ "status": "blocked",
78
+ "code": "DONE_BLOCKED",
79
+ "command": "task update",
80
+ "task": "LUM-48",
81
+ "message": "Cannot move this task to DONE — 1 undispositioned boundary-crossing (forbidden action taken during this task): 1 TEST_FILE_DELETED. …",
82
+ "blockers": [
83
+ {
84
+ "kind": "BOUNDARY_CROSSING",
85
+ "id": "cmtw…",
86
+ "severity": "MEDIUM",
87
+ "category": "TEST_FILE_DELETED",
88
+ "detail": "deleted __tests__/cli/old.test.ts"
89
+ }
90
+ ],
91
+ "unconfirmed": [],
92
+ "remediation": [
93
+ "BOUNDARY_CROSSING: leave a rationale with `lumo crossing explain cmtw… --note \"…\"` (append-only; it does not clear anything). A human dispositions each crossing (false positive / confirmed) at https://…/my-tasks/LUM-48#boundary-crossings"
94
+ ],
95
+ "dispositionUrl": "https://…/my-tasks/LUM-48#boundary-crossings"
96
+ }
97
+ ```
98
+
99
+ | Field | Meaning |
100
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `message` | The server's 409 text, verbatim. |
102
+ | `blockers` | One entry per item holding the gate: `SEND_BACK` (criterionId, statement, failedAtRound, rejectionReason), `BOUNDARY_CROSSING` (id, severity, category, detail), `SECURITY_FINDING` (findingId, severity, ruleId, filePath, line, prNumber, statement). Built from the same read models as `lumo task status`. |
103
+ | `unconfirmed` | Enrichment reads that failed (`boundary-crossings read failed: HTTP 500`). Non-empty means the blocker list may be incomplete — it never silently shrinks. |
104
+ | `remediation` | One line per blocker kind present, naming only human-side or append-only paths: re-run `lumo verify`, `lumo crossing explain`, fix-and-push, the web disposition panel. |
105
+
106
+ **Exit 4 vs exit 5:** 4 means "the user must approve, then you can proceed"; 5 means "a human must adjudicate elsewhere — nothing on the CLI moves this". On exit 5, relay `blockers` and `remediation` to the user. Do not retry, do not look for a flag, and do not reword or delete a failed criterion to get past a send-back (that is tampering and is audited). A 409 that is not a DONE gate (e.g. a permission refusal) keeps the plain exit-1 text on stderr.
107
+
108
+ ## Example exchange
109
+
110
+ ```bash
111
+ lumo task update LUM-48 --status done
112
+ # exit 4, stdout:
113
+ # {
114
+ # "status": "confirmation_required",
115
+ # "command": "task update",
116
+ # "changes": [
117
+ # "Will move LUM-48 \"Panel refactor\" to DONE",
118
+ # "Status: IN_REVIEW → DONE",
119
+ # "#926 merged refactor: panel https://github.com/o/r/pull/926",
120
+ # "#930 open feat: wip https://github.com/o/r/pull/930",
121
+ # "⚠ 1 linked pull request not merged: #930 open"
122
+ # ],
123
+ # "confirmCommand": "lumo task update LUM-48 --status done --confirm"
124
+ # }
125
+ ```
126
+
127
+ Agent → user: "Moving LUM-48 to DONE would change IN_REVIEW → DONE. PR #926 is merged, but **#930 is still open**. Confirm?" — and only after a yes:
128
+
129
+ ```bash
130
+ lumo task update LUM-48 --status done --confirm
131
+ ```