@lumoai/cli 1.56.0 → 1.57.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.
@@ -158,7 +158,7 @@ The command catalog below is a **map**: it lists every command grouped by domain
158
158
 
159
159
  **Verification (machine acceptance loop)** — see [verify.md](references/verify.md)
160
160
 
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). Round cap 3: all-pass → task to IN_REVIEW (agent stops); round-3 fail → escalate to human. **Run this before claiming a task is done.**
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
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
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.**
164
164
  - `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.
@@ -302,7 +302,7 @@ Don't guess these:
302
302
  2. `lumo task context LUM-42` — load background; review unresolved items, PR-review todos, the description
303
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)
304
304
  4. Do the work
305
- 5. **Before claiming done: `lumo verify`** — the machine half of the acceptance loop. Fix failures and re-run (round cap 3). All-pass → task moves to IN_REVIEW and you stop. See [verify.md](references/verify.md)
305
+ 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
306
 
307
307
  **Resuming / after a send-back** — `lumo task status` **before** re-reading code or planning. It tells you where the loop stands (round, what passed, what's unmet and why, any REVIEW_ADDED criteria from review) so you don't redo finished work or miss why it bounced. A send-back means **fix in place / amend the contract — do not spin off a new task.** See [verify.md](references/verify.md)
308
308
 
@@ -219,4 +219,4 @@ lumo task criteria list LUM-42
219
219
 
220
220
  ## After the contract: the verification loop
221
221
 
222
- The contract is judged by `lumo verify` — run it before claiming the task is done. See [verify.md](verify.md) for the loop (round cap 3, IN_REVIEW on all-pass, escalation on a round-3 fail).
222
+ The contract is judged by `lumo verify` — run it before claiming the task is done. See [verify.md](verify.md) for the loop (a workspace-configurable round cap — `verificationMaxRounds`, default 3, shown as `Round N/M` in the output — IN_REVIEW on all-pass, escalation on a last-round fail).
@@ -3,8 +3,11 @@
3
3
  `lumo verify` is the machine half of the acceptance system: it executes every **MACHINE** criterion's checkpointer in the local
4
4
  repo, POSTs one structured PASS/FAIL verdict per criterion, and prints what to
5
5
  do next. Execution is on the client; adjudication is server-side — round
6
- numbering, the **3-round cap**, escalation, and the **IN_REVIEW** transition all
7
- happen on the server.
6
+ numbering, the **round cap**, escalation, and the **IN_REVIEW** transition all
7
+ happen on the server. The cap is **not a universal constant** — it is the
8
+ workspace-level `verificationMaxRounds` (default 3); the effective value for
9
+ _this_ workspace is the `M` in the `Round N/M` line that `lumo verify` and
10
+ `lumo task status` print.
8
11
 
9
12
  ## The claim-done rule
10
13
 
@@ -39,11 +42,11 @@ still verifies — the claim degrades to the synthesized run-summary fallback.)
39
42
  TaskActivity event.
40
43
  4. Prints the round outcome:
41
44
 
42
- | Round outcome | Effect | What to do |
43
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
44
- | **All PASS** | Task transitions to **IN_REVIEW** (existing state machine + TASK_IN_REVIEW notification); the `--note` self-report is frozen as the task's claim (source `AGENT`) | **Stop here.** Human adjudication + any HUMAN criteria take over; **never set DONE yourself** |
45
- | **Any FAIL** | Task status untouched; unmet criteria printed as next actions (statement, checkpointer, failure tail) | Fix and re-run |
46
- | **Round 3 still failing** | Loop escalates: a human is notified (AGENT_VERIFY, requires action); further `lumo verify` rounds are rejected with **409** | **Stop retrying**; fix only what the human directs |
45
+ | Round outcome | Effect | What to do |
46
+ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
47
+ | **All PASS** | Task transitions to **IN_REVIEW** (existing state machine + TASK_IN_REVIEW notification); the `--note` self-report is frozen as the task's claim (source `AGENT`) | **Stop here.** Human adjudication + any HUMAN criteria take over; **never set DONE yourself** |
48
+ | **Any FAIL** | Task status untouched; unmet criteria printed as next actions (statement, checkpointer, failure tail) | Fix and re-run |
49
+ | **Last round still failing** (round = the cap `M`) | Loop escalates: a human is notified (AGENT_VERIFY, requires action); further `lumo verify` rounds are rejected with **409** | **Stop retrying**; fix only what the human directs |
47
50
 
48
51
  Exit code 0 = all passed (or nothing to run); 1 = failures, escalation, or
49
52
  errors.
@@ -74,9 +77,10 @@ errors.
74
77
 
75
78
  ## Round discipline
76
79
 
77
- Rounds are a hard budget of 3, not a retry loop. Between rounds, actually fix
78
- the failures — re-running without changes burns a round and (at round 3) pages a
79
- human. **A FAIL round never changes task status; only an all-pass round moves it
80
+ Rounds are a hard budget, not a retry loop and the size of that budget is a
81
+ workspace setting (`verificationMaxRounds`, default 3), so check `Round N/M`
82
+ instead of assuming three. Between rounds, actually fix the failures
83
+ re-running without changes burns a round and (on the last one) pages a human. **A FAIL round never changes task status; only an all-pass round moves it
80
84
  (to IN_REVIEW, never further).**
81
85
 
82
86
  ## Review-time drift habits (gap findings)
@@ -120,7 +124,7 @@ what's unmet and why (the exact failure tails), and how many rounds are left.
120
124
 
121
125
  ### What it prints
122
126
 
123
- - **Header** — task identifier/title/status + `verification round N/3` (round 0 = never verified) + an escalation warning when the machine loop is exhausted.
127
+ - **Header** — task identifier/title/status + `verification round N/M` (`M` = the workspace's `verificationMaxRounds`; round 0 = never verified) + an escalation warning when the machine loop is exhausted.
124
128
  - **Claim vs verification** — 规律 2 声称vs核验: the headline contrast, printed right after the header (whenever the contract exists), so the report shows **both** columns instead of only the verification one. Two sides:
125
129
  - **Claim** — what the agent _says_ it did: an **unverified self-report** (`agent self-report · estimated, not verification`), estimate-tier provenance (估, not 测). Sourced by layering (LUM-597, preference `AGENT > RUN_SUMMARY > DIGEST > null`): if you supplied a `lumo verify --note`, that **own self-report** is the claim (`↳ source: agent self-report (verify --note)`) — the汇报者's voice, what faithfulness judges. Absent a self-report (old CLI / non-verify path) it degrades to the summarizer paraphrase, labelled honestly (`↳ source: synthesized run summary (no self-report)`). **Fail-closed**: when only a raw STOP turn digest exists it prints `generating — the formal run summary is still being synthesized` and **withholds the raw digest**; with no material at all it prints `not generated yet — …`, never a fabricated claim.
126
130
  - **Verification** — what was actually _confirmed_ (measured): the machine-verification rollup `N machine-verified / M human override (of T MACHINE criteria)` over the active MACHINE criteria (relocated here from its old standalone line under `Criteria`), plus `X of Y criteria met by their latest verdict`. **Fail-closed**: before any round runs it prints `no verification has run yet — the claim is unconfirmed` rather than implying a pass.
@@ -179,8 +183,9 @@ fields don't. Pin on `version` when scripting against it.
179
183
  - **`openCrossings` is `null` when the crossings check failed** — distinct from `[]` (a successful read with zero open crossings). Script consumers must treat `null` as "unknown / could not confirm", **not** "safe".
180
184
 
181
185
  `status` reads; `verify` judges. Running status never starts a round, never
182
- escalates, and never changes task state — loop rules (cap 3, IN_REVIEW on
183
- all-pass, human-only DONE) live entirely in `lumo verify` and the server.
186
+ escalates, and never changes task state — loop rules (the workspace round cap,
187
+ IN_REVIEW on all-pass, human-only DONE) live entirely in `lumo verify` and the
188
+ server.
184
189
 
185
190
  ## lumo verdict — the three verdict channels
186
191
 
@@ -1,10 +1,24 @@
1
1
  # Worktrees
2
2
 
3
- Local dev tooling that scaffolds parallel git worktrees. Unlike every other
4
- `lumo` command, these do **not** touch the Lumo server they shell out to git
5
- and the local filesystem. **Run `add` and `rm` from the main checkout**, not from
6
- inside a worktree (the node_modules symlink source and `.worktrees/` both live
7
- there); `list` is read-only and runnable from anywhere.
3
+ Local dev tooling that scaffolds parallel git worktrees. These are almost
4
+ entirely local they shell out to git and the local filesystem; the **only**
5
+ server call is a single `GET /api/teams` to expand a **bare number** to your
6
+ default team prefix (see "Accepted identifier forms" below), and even that is
7
+ skipped when you pass an explicit `<TEAM>-N`. **Run `add` and `rm` from the main
8
+ checkout**, not from inside a worktree (the node_modules symlink source and
9
+ `.worktrees/` both live there); `list` is read-only and runnable from anywhere.
10
+
11
+ ### Accepted identifier forms
12
+
13
+ The `<id>` argument to `add`/`rm` is a task id with **any team prefix**, not
14
+ just `LUM-` (the `<LUM-N>` in the examples below is just a placeholder):
15
+
16
+ - `LUM-267`, `SPEC-12`, `spec-12` — an explicit `<TEAM>-<n>` (1–10 letters,
17
+ case-insensitive; the prefix is upper-cased). Resolved locally, no network.
18
+ - `267` — a **bare number**: expanded to the workspace's **default team**
19
+ prefix via `GET /api/teams` (or the sole team in a single-team workspace).
20
+ In a multi-team workspace with no default, this errors and asks for an
21
+ explicit prefix; the same happens when you're offline / not logged in.
8
22
 
9
23
  ## `lumo worktree add <LUM-N> [slug]`
10
24
 
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
39
39
  const child_process_1 = require("child_process");
40
40
  const next_steps_1 = require("../lib/next-steps");
41
41
  const worktree_1 = require("../lib/worktree");
42
+ const worktree_ref_1 = require("../lib/worktree-ref");
42
43
  function printGuidance(dir, branch) {
43
44
  console.log('');
44
45
  console.log(`✓ Worktree ready: ${dir}`);
@@ -59,9 +60,16 @@ function printGuidance(dir, branch) {
59
60
  console.log(' cli/package.json haste collision silently runs the wrong tests.');
60
61
  }
61
62
  async function worktreeAdd(rawId, slug, opts) {
62
- const taskId = (0, worktree_1.normalizeTaskId)(rawId);
63
+ let taskId;
64
+ try {
65
+ taskId = await (0, worktree_ref_1.resolveWorktreeTaskId)(rawId);
66
+ }
67
+ catch (err) {
68
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
69
+ return 1;
70
+ }
63
71
  if (!taskId) {
64
- console.error(`Error: "${rawId}" is not a task id (expected LUM-N or N)`);
72
+ console.error(`Error: "${rawId}" is not a task id (expected <TEAM>-N like LUM-42 or SPEC-12, or a bare number)`);
65
73
  return 1;
66
74
  }
67
75
  if (!(0, worktree_1.isMainCheckout)()) {
@@ -46,7 +46,7 @@ function nodeModulesLinked(dir) {
46
46
  return false;
47
47
  }
48
48
  }
49
- /** Pull a LUM-N from a worktree dir basename or its branch, else '—'. */
49
+ /** Pull a `<TEAM>-N` id from a worktree dir basename or its branch, else '—'. */
50
50
  function taskIdOf(dirBase, branch) {
51
51
  return ((0, git_task_1.matchTaskIdentifier)(dirBase) ??
52
52
  (branch ? (0, git_task_1.matchTaskIdentifier)(branch) : null) ??
@@ -2,10 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.worktreeRm = worktreeRm;
4
4
  const worktree_1 = require("../lib/worktree");
5
+ const worktree_ref_1 = require("../lib/worktree-ref");
5
6
  async function worktreeRm(rawId, opts) {
6
- const taskId = (0, worktree_1.normalizeTaskId)(rawId);
7
+ let taskId;
8
+ try {
9
+ taskId = await (0, worktree_ref_1.resolveWorktreeTaskId)(rawId);
10
+ }
11
+ catch (err) {
12
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
13
+ return 1;
14
+ }
7
15
  if (!taskId) {
8
- console.error(`Error: "${rawId}" is not a task id (expected LUM-N or N)`);
16
+ console.error(`Error: "${rawId}" is not a task id (expected <TEAM>-N like LUM-42 or SPEC-12, or a bare number)`);
9
17
  return 1;
10
18
  }
11
19
  if (!(0, worktree_1.isMainCheckout)()) {
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveWorktreeTaskId = resolveWorktreeTaskId;
4
+ const config_1 = require("./config");
5
+ const api_1 = require("./api");
6
+ const worktree_1 = require("./worktree");
7
+ /**
8
+ * Resolve a user-supplied worktree ref to a canonical `<TEAM>-<n>` id.
9
+ *
10
+ * - Explicit prefix (`SPEC-12`, `lum-267`) → normalized locally, NO network.
11
+ * - Bare number (`267`) → completed with the workspace's DEFAULT team prefix
12
+ * (GET /api/teams: the `isDefault` team, or the sole team in a single-team
13
+ * workspace). Worktree tooling is otherwise purely local; the API is touched
14
+ * only to disambiguate a prefix-less number.
15
+ *
16
+ * Returns null when `raw` is not a task-id shape at all (caller prints the
17
+ * "expected <TEAM>-N" usage error). Throws Error with a user-facing message
18
+ * when a bare number needs the default team but it can't be determined
19
+ * (not logged in / offline / ambiguous multi-team without a default) — every
20
+ * message points the user at passing an explicit prefix instead.
21
+ */
22
+ async function resolveWorktreeTaskId(raw) {
23
+ const explicit = (0, worktree_1.normalizeTaskId)(raw);
24
+ if (explicit)
25
+ return explicit;
26
+ const bare = raw.trim().match(/^(\d+)$/);
27
+ if (!bare)
28
+ return null;
29
+ const n = bare[1];
30
+ const creds = (0, config_1.readCredentials)();
31
+ if (!creds) {
32
+ throw new Error(`a bare number needs your default team prefix — run \`lumo auth login\`, or pass an explicit id like LUM-${n}`);
33
+ }
34
+ const base = (0, api_1.trimTrailingSlash)((0, api_1.resolveAuthedApiUrl)(creds.apiUrl));
35
+ let teams;
36
+ try {
37
+ const res = await fetch(`${base}/api/teams`, {
38
+ headers: { Authorization: `Bearer ${creds.token}` },
39
+ });
40
+ if (!res.ok)
41
+ throw new Error(`HTTP ${res.status}`);
42
+ ({ teams } = (await res.json()));
43
+ }
44
+ catch (err) {
45
+ const msg = err instanceof Error ? err.message : String(err);
46
+ throw new Error(`could not resolve the default team for bare id "${n}" (${msg}) — pass an explicit id like LUM-${n}`);
47
+ }
48
+ const def = teams.find(t => t.isDefault) ?? (teams.length === 1 ? teams[0] : undefined);
49
+ if (!def) {
50
+ throw new Error(`workspace has multiple teams and no default — pass an explicit id like SPEC-${n} instead of a bare number`);
51
+ }
52
+ return `${def.identifier.toUpperCase()}-${n}`;
53
+ }
@@ -46,19 +46,21 @@ exports.getGitCommonDir = getGitCommonDir;
46
46
  exports.isMainCheckout = isMainCheckout;
47
47
  exports.getRepoRoot = getRepoRoot;
48
48
  /**
49
- * Normalize a user-supplied task reference to canonical `LUM-<n>` form.
50
- * Accepts `LUM-267`, `lum-267`, and bare `267`. Returns null for anything
51
- * that is not a recognizable task id.
49
+ * Normalize a user-supplied task reference to canonical `<TEAM>-<n>` form.
50
+ * Accepts any team prefix (`LUM-267`, `SPEC-12`, `spec-12`), not just LUM
51
+ * the prefix is a team's configured identifier (1–10 letters), matching the
52
+ * single-sourced shape in shared/src/task-identifier.ts (LUM-419). The prefix
53
+ * is upper-cased; the number is preserved.
54
+ *
55
+ * A bare number (`267`) returns null here: without team context the prefix
56
+ * cannot be inferred locally. Team-aware bare-number resolution (via the
57
+ * workspace's default team) lives in `resolveWorktreeTaskId` (worktree-ref.ts).
52
58
  */
53
59
  function normalizeTaskId(input) {
54
- const trimmed = input.trim();
55
- const prefixed = trimmed.match(/^lum-?(\d+)$/i);
56
- if (prefixed)
57
- return `LUM-${prefixed[1]}`;
58
- const bare = trimmed.match(/^(\d+)$/);
59
- if (bare)
60
- return `LUM-${bare[1]}`;
61
- return null;
60
+ const m = input.trim().match(/^([A-Za-z]{1,10})-(\d+)$/);
61
+ if (!m)
62
+ return null;
63
+ return `${m[1].toUpperCase()}-${m[2]}`;
62
64
  }
63
65
  /**
64
66
  * Turn an arbitrary human slug into a branch/dir-safe segment: lowercase,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumoai/cli",
3
- "version": "1.56.0",
3
+ "version": "1.57.0",
4
4
  "description": "Lumo CLI — manage tasks and sessions from the terminal",
5
5
  "license": "MIT",
6
6
  "author": "cli@uselumo.ai",