@lumoai/cli 1.58.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.
- package/assets/skill/SKILL.md +13 -16
- package/assets/skill/references/artifacts-figma.md +4 -3
- package/assets/skill/references/confirmation.md +131 -0
- package/assets/skill/references/criteria.md +12 -21
- package/assets/skill/references/doc-editing.md +11 -9
- package/assets/skill/references/docs.md +4 -3
- package/assets/skill/references/memory.md +4 -2
- package/assets/skill/references/milestones.md +3 -2
- package/assets/skill/references/outcome.md +1 -14
- package/assets/skill/references/plan-runs.md +5 -0
- package/assets/skill/references/sessions.md +3 -3
- package/assets/skill/references/sprints.md +18 -17
- package/assets/skill/references/task-deps.md +4 -3
- package/assets/skill/references/tasks.md +34 -2
- package/assets/skill/references/verify.md +46 -68
- package/assets/skill/references/worktree.md +13 -7
- package/dist/cli/src/commands/doc-delete.js +35 -23
- package/dist/cli/src/commands/doc-rebuild-source.js +16 -4
- package/dist/cli/src/commands/memory-rm.js +68 -10
- package/dist/cli/src/commands/milestone-delete.js +13 -10
- package/dist/cli/src/commands/outcome.js +0 -77
- package/dist/cli/src/commands/session-attach.js +8 -2
- package/dist/cli/src/commands/sprint-close.js +29 -9
- package/dist/cli/src/commands/sprint-delete.js +13 -10
- package/dist/cli/src/commands/sprint-show.js +3 -9
- package/dist/cli/src/commands/task-artifact-rm.js +58 -28
- package/dist/cli/src/commands/task-criteria-list.js +1 -4
- package/dist/cli/src/commands/task-criteria-set.js +3 -12
- package/dist/cli/src/commands/task-deps.js +20 -6
- package/dist/cli/src/commands/task-status.js +165 -110
- package/dist/cli/src/commands/task-update.js +129 -0
- package/dist/cli/src/commands/verify.js +22 -13
- package/dist/cli/src/commands/worktree-rm.js +35 -7
- package/dist/cli/src/index.js +47 -47
- package/dist/cli/src/lib/blocked-error.js +178 -0
- package/dist/cli/src/lib/confirmation.js +89 -0
- package/dist/cli/src/lib/hook-runner.js +23 -11
- package/dist/shared/src/referent-kind.js +31 -1
- package/dist/shared/src/security-scan.js +29 -0
- package/package.json +1 -1
- package/assets/skill/references/fidelity.md +0 -32
- package/dist/cli/src/commands/fidelity.js +0 -108
- package/dist/cli/src/commands/verdict.js +0 -189
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Fidelity read-outs (mechanical change-pattern)
|
|
2
|
-
|
|
3
|
-
`lumo fidelity` is the fidelity-axis read surface — pure, read-only analytics over what the acceptance loop already records, in the same family as `lumo criteria audit` and `lumo outcome rate`. It adds **no storage and no write path**; it only reads the LUM-609 delivery snapshots and the `CRITERION_CHANGED` audit trail.
|
|
4
|
-
|
|
5
|
-
## `lumo fidelity show <task>`
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
lumo fidelity show LUM-42
|
|
9
|
-
lumo fidelity show LUM-42 --json
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
For **each delivery** of the task (one append-only `TaskCriteriaSnapshot` per fresh IN_REVIEW entry — a reopen→re-deliver appends another), it discloses two compositions and deliberately stops there. There is **no single fidelity score and no good/bad direction verdict** — the read-out is purely mechanical (timing + op + checkpointer grounding; it never reads statement or diff semantics).
|
|
13
|
-
|
|
14
|
-
### 1. Grounding composition
|
|
15
|
-
|
|
16
|
-
Runs the same `effectiveReferentKind` classifier the LUM-605 verify gate uses over the **frozen** contract (delivery-time truth, not the since-edited `latest`), rolled into three buckets plus the full per-kind tally:
|
|
17
|
-
|
|
18
|
-
- **grounded** — `EXTERNAL_FACT` (anchored on a fact the agent cannot author: a git SHA/diff, CI status, a DB row). This count is a **machine-checkable _upper bound_** — the checkpointer heuristic can over-count external grounding, never under-count it.
|
|
19
|
-
- **self-confirming** — `AGENT_CONSTRUCTED_STATE` + `UNVERIFIED_ASSERTION` (passes by checking the agent's own output, incl. a declared-EXTERNAL_FACT checkpointer that doesn't truly ground external).
|
|
20
|
-
- **inconclusive** — `PENDING_OUTCOME` + `UNCLASSIFIED` (not mechanically resolvable here). **Surfaced, never silently swallowed.**
|
|
21
|
-
|
|
22
|
-
### 2. Independence signal
|
|
23
|
-
|
|
24
|
-
A criterion is a **backward-inference suspect** when it is both (a) in this delivery's frozen contract and (b) was `ADDED`/`UPDATED` at **round>0** (after work started) at or before the freeze — i.e. the contract the delivery rode on was bent after the work began rather than fixed up front. round-0 (initial-draft) criteria are clean; `DELETED` ops and edits recorded after the freeze (a later cycle) don't count. Each suspect shows its `op`, `round`, and `causeTag`. The read-out **counts and discloses — it does not judge** whether a given edit was legitimate sharpening or tampering (that direction call is left to a human / a later axis).
|
|
25
|
-
|
|
26
|
-
### Caveat (printed on every report)
|
|
27
|
-
|
|
28
|
-
The time anchor is the **work-start boundary** (`Task.workStartedAt`), so the independence signal detects a contract edited _after work started_ — not specifically _after output was produced_. A task with **no delivery snapshot** (never entered IN_REVIEW, or predates the LUM-609 freeze) is reported explicitly — "nothing to read yet", which is **not** a pass.
|
|
29
|
-
|
|
30
|
-
`--json` emits the full report (`taskId`, `workStartedAt`, `hasSnapshots`, `deliveries[]` with `grounding`/`independence`, `caveat`) for scripting.
|
|
31
|
-
|
|
32
|
-
**When to suggest**: when the user wants to see how externally-grounded a task's delivered acceptance contract actually was, or whether criteria were added/changed after work began (independence). It's a disclosure tool, not a gate — it never blocks DONE and emits no verdict.
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fidelityShow = fidelityShow;
|
|
4
|
-
const config_1 = require("../lib/config");
|
|
5
|
-
const api_1 = require("../lib/api");
|
|
6
|
-
const sanitize_1 = require("../lib/sanitize");
|
|
7
|
-
/**
|
|
8
|
-
* `lumo fidelity show <task>` — the LUM-610 mechanical change-pattern read-out
|
|
9
|
-
* (fidelity axis, second block, on the LUM-609 delivery-snapshot foundation).
|
|
10
|
-
*
|
|
11
|
-
* Per delivery snapshot it discloses two compositions and NOTHING else — no
|
|
12
|
-
* single score, no good/bad verdict (approach (a): purely mechanical):
|
|
13
|
-
* • grounding — how much of the FROZEN contract is externally grounded
|
|
14
|
-
* (EXTERNAL_FACT) vs self-confirming (agent-authored) vs inconclusive.
|
|
15
|
-
* `grounded` is a machine-checkable UPPER BOUND.
|
|
16
|
-
* • independence — criteria ADDED/UPDATED after work started (round>0) that
|
|
17
|
-
* the delivery rode on: backward-inference suspects. Direction left blank.
|
|
18
|
-
*/
|
|
19
|
-
const EFFECTIVE_KINDS = [
|
|
20
|
-
'EXTERNAL_FACT',
|
|
21
|
-
'AGENT_CONSTRUCTED_STATE',
|
|
22
|
-
'UNVERIFIED_ASSERTION',
|
|
23
|
-
'PENDING_OUTCOME',
|
|
24
|
-
'UNCLASSIFIED',
|
|
25
|
-
];
|
|
26
|
-
function authBase() {
|
|
27
|
-
const creds = (0, config_1.readCredentials)();
|
|
28
|
-
if (!creds)
|
|
29
|
-
return { error: 'not logged in. Run `lumo auth login` first.' };
|
|
30
|
-
const base = (0, api_1.trimTrailingSlash)((0, api_1.resolveAuthedApiUrl)(creds.apiUrl));
|
|
31
|
-
const headers = {
|
|
32
|
-
Authorization: `Bearer ${creds.token}`,
|
|
33
|
-
};
|
|
34
|
-
const sessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
35
|
-
if (sessionId)
|
|
36
|
-
headers['X-Lumo-Session-Id'] = sessionId;
|
|
37
|
-
return { base, headers };
|
|
38
|
-
}
|
|
39
|
-
function formatReport(report, taskId) {
|
|
40
|
-
const lines = [];
|
|
41
|
-
lines.push(`Fidelity change-pattern — ${(0, sanitize_1.sanitizeField)(taskId)}`);
|
|
42
|
-
if (!report.hasSnapshots) {
|
|
43
|
-
lines.push(' No delivery snapshot on record (task never entered IN_REVIEW, or ' +
|
|
44
|
-
'predates the LUM-609 freeze). Nothing to read yet — not a pass.');
|
|
45
|
-
lines.push('');
|
|
46
|
-
lines.push(` ${report.caveat}`);
|
|
47
|
-
return lines.join('\n') + '\n';
|
|
48
|
-
}
|
|
49
|
-
report.deliveries.forEach((d, i) => {
|
|
50
|
-
const when = d.frozenAt.slice(0, 19).replace('T', ' ');
|
|
51
|
-
lines.push('');
|
|
52
|
-
lines.push(` Delivery ${i + 1}/${report.deliveries.length} · frozen ${when}Z · ${(0, sanitize_1.sanitizeField)(d.trigger)} · ${d.total} criteria`);
|
|
53
|
-
const g = d.grounding;
|
|
54
|
-
lines.push(` grounding: grounded ${g.grounded} · self-confirming ${g.selfConfirming} · inconclusive ${g.inconclusive} (grounded = machine-checkable upper bound)`);
|
|
55
|
-
const kinds = EFFECTIVE_KINDS.filter(k => g.byEffectiveKind[k] > 0)
|
|
56
|
-
.map(k => `${k} ${g.byEffectiveKind[k]}`)
|
|
57
|
-
.join(', ');
|
|
58
|
-
lines.push(` effective kinds: ${kinds || '(none)'}`);
|
|
59
|
-
const ind = d.independence;
|
|
60
|
-
lines.push(` independence: ${ind.postStartChanges}/${ind.total} criteria ADDED/UPDATED after work started (backward-inference suspects; direction not judged)`);
|
|
61
|
-
for (const s of ind.suspects) {
|
|
62
|
-
const cause = s.causeTag ? ` · ${(0, sanitize_1.sanitizeField)(s.causeTag)}` : '';
|
|
63
|
-
lines.push(` ↳ ${(0, sanitize_1.sanitizeField)(s.criterionId)} ${s.op}@round${s.round}${cause} (${s.at.slice(0, 10)})`);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
lines.push('');
|
|
67
|
-
lines.push(` ${report.caveat}`);
|
|
68
|
-
return lines.join('\n') + '\n';
|
|
69
|
-
}
|
|
70
|
-
async function fidelityShow(taskId, options = {}) {
|
|
71
|
-
if (!taskId || taskId.trim() === '') {
|
|
72
|
-
console.error('Error: a task is required: lumo fidelity show <task>');
|
|
73
|
-
return 1;
|
|
74
|
-
}
|
|
75
|
-
const auth = authBase();
|
|
76
|
-
if ('error' in auth) {
|
|
77
|
-
console.error(`Error: ${auth.error}`);
|
|
78
|
-
return 1;
|
|
79
|
-
}
|
|
80
|
-
let res;
|
|
81
|
-
try {
|
|
82
|
-
res = await fetch(`${auth.base}/api/tasks/${encodeURIComponent(taskId)}/fidelity`, { headers: auth.headers });
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
86
|
-
console.error(`Error: could not reach Lumo API (${msg})`);
|
|
87
|
-
return 1;
|
|
88
|
-
}
|
|
89
|
-
if (res.status === 401) {
|
|
90
|
-
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
91
|
-
return 1;
|
|
92
|
-
}
|
|
93
|
-
if (res.status === 404) {
|
|
94
|
-
console.error(`Error: task ${(0, sanitize_1.sanitizeField)(taskId)} not found.`);
|
|
95
|
-
return 1;
|
|
96
|
-
}
|
|
97
|
-
if (!res.ok) {
|
|
98
|
-
console.error(`Error: could not read the fidelity change-pattern (HTTP ${res.status}).`);
|
|
99
|
-
return 1;
|
|
100
|
-
}
|
|
101
|
-
const report = (await res.json());
|
|
102
|
-
if (options.json) {
|
|
103
|
-
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
process.stdout.write(formatReport(report, taskId));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.collectCriterion = collectCriterion;
|
|
4
|
-
exports.verdict = verdict;
|
|
5
|
-
const config_1 = require("../lib/config");
|
|
6
|
-
const api_1 = require("../lib/api");
|
|
7
|
-
const sanitize_1 = require("../lib/sanitize");
|
|
8
|
-
const browser_1 = require("../lib/browser");
|
|
9
|
-
/**
|
|
10
|
-
* Rejection-reason vocabulary (mirrors the server's VerificationRejectionReason
|
|
11
|
-
* enum). Required on the agent --fail path — the agent pays the structured tax
|
|
12
|
-
* a human is spared (LUM-422 ①).
|
|
13
|
-
*/
|
|
14
|
-
const FAIL_REASONS = [
|
|
15
|
-
'CRITERION_UNMET',
|
|
16
|
-
'EVIDENCE_INSUFFICIENT',
|
|
17
|
-
'CHECK_EXECUTION_ERROR',
|
|
18
|
-
'SCOPE_MISMATCH',
|
|
19
|
-
'OTHER',
|
|
20
|
-
];
|
|
21
|
-
/** Collect repeatable `--criterion <id>` flags into an array (commander idiom). */
|
|
22
|
-
function collectCriterion(value, prev = []) {
|
|
23
|
-
return [...prev, value];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* `lumo verdict [task]` — human + agent acceptance verdicts (LUM-422).
|
|
27
|
-
*
|
|
28
|
-
* Two modes, exactly one required:
|
|
29
|
-
* --pass opens the browser to the task's verdict bar, focused on Pass (a deep
|
|
30
|
-
* link — writes NOTHING; a passing data row is only ever produced by a
|
|
31
|
-
* human's own click, red line).
|
|
32
|
-
* --fail --reason <enum> [--note] [--criterion …] records an AGENT send-back
|
|
33
|
-
* (verdict hard-coded FAIL server-side) and bounces the task to
|
|
34
|
-
* IN_PROGRESS. Bearer-authed.
|
|
35
|
-
*
|
|
36
|
-
* Defaults to the session-bound task; an explicit identifier overrides.
|
|
37
|
-
*/
|
|
38
|
-
async function verdict(identifier, options = {}) {
|
|
39
|
-
const modes = [options.pass && 'pass', options.fail && 'fail'].filter(Boolean);
|
|
40
|
-
if (modes.length === 0) {
|
|
41
|
-
console.error('Error: choose a verdict mode — --pass or --fail.');
|
|
42
|
-
return 1;
|
|
43
|
-
}
|
|
44
|
-
if (modes.length > 1) {
|
|
45
|
-
console.error(`Error: pick exactly one verdict mode (got ${modes.join(', ')}).`);
|
|
46
|
-
return 1;
|
|
47
|
-
}
|
|
48
|
-
const creds = (0, config_1.readCredentials)();
|
|
49
|
-
if (!creds) {
|
|
50
|
-
console.error('Error: not logged in. Run `lumo auth login` first.');
|
|
51
|
-
return 1;
|
|
52
|
-
}
|
|
53
|
-
const base = (0, api_1.trimTrailingSlash)((0, api_1.resolveAuthedApiUrl)(creds.apiUrl));
|
|
54
|
-
const headers = {
|
|
55
|
-
Authorization: `Bearer ${creds.token}`,
|
|
56
|
-
};
|
|
57
|
-
const sessionId = process.env.CLAUDE_CODE_SESSION_ID;
|
|
58
|
-
if (sessionId)
|
|
59
|
-
headers['X-Lumo-Session-Id'] = sessionId;
|
|
60
|
-
// ── Resolve the task: explicit identifier or the session binding ──────────
|
|
61
|
-
let taskId = identifier;
|
|
62
|
-
if (!taskId) {
|
|
63
|
-
if (!sessionId) {
|
|
64
|
-
console.error('Error: no task given and $CLAUDE_CODE_SESSION_ID is not set.\n' +
|
|
65
|
-
'Run `lumo verdict <LUM-N> …` or run inside a session bound via `lumo session attach`.');
|
|
66
|
-
return 1;
|
|
67
|
-
}
|
|
68
|
-
let res;
|
|
69
|
-
try {
|
|
70
|
-
res = await fetch(`${base}/api/sessions/${encodeURIComponent(sessionId)}`, { headers });
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
74
|
-
console.error(`Error: could not reach Lumo API (${msg})`);
|
|
75
|
-
return 1;
|
|
76
|
-
}
|
|
77
|
-
const data = res.ok
|
|
78
|
-
? (await res.json())
|
|
79
|
-
: null;
|
|
80
|
-
if (!data?.taskIdentifier) {
|
|
81
|
-
console.error('Error: this session is not bound to a task. Run `lumo session attach <LUM-N>` first, or pass the task explicitly.');
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
taskId = data.taskIdentifier;
|
|
85
|
-
}
|
|
86
|
-
if (options.fail) {
|
|
87
|
-
return failVerdict(base, headers, taskId, options, creds.workspaceSlug);
|
|
88
|
-
}
|
|
89
|
-
return passDeepLink(base, headers, taskId, creds.workspaceSlug);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* --pass: open the human's verdict bar pre-focused on Pass. The CLI never writes
|
|
93
|
-
* the verdict — it only carries the human to the one click that does (red line:
|
|
94
|
-
* no agent-produced passing row).
|
|
95
|
-
*/
|
|
96
|
-
async function passDeepLink(base, headers, taskId, workspaceSlug) {
|
|
97
|
-
let res;
|
|
98
|
-
try {
|
|
99
|
-
res = await fetch(`${base}/api/tasks/by-identifier/${encodeURIComponent(taskId)}`, { headers });
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
103
|
-
console.error(`Error: could not reach Lumo API (${msg})`);
|
|
104
|
-
return 1;
|
|
105
|
-
}
|
|
106
|
-
if (res.status === 401) {
|
|
107
|
-
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
108
|
-
return 1;
|
|
109
|
-
}
|
|
110
|
-
if (res.status === 404) {
|
|
111
|
-
console.error(`Error: task ${taskId} not found in workspace ${workspaceSlug}`);
|
|
112
|
-
return 1;
|
|
113
|
-
}
|
|
114
|
-
if (!res.ok) {
|
|
115
|
-
console.error(`Error: could not load task (HTTP ${res.status})`);
|
|
116
|
-
return 1;
|
|
117
|
-
}
|
|
118
|
-
const { task } = (await res.json());
|
|
119
|
-
if (!task?.url) {
|
|
120
|
-
console.error('Error: server did not return a task URL to open.');
|
|
121
|
-
return 1;
|
|
122
|
-
}
|
|
123
|
-
const sep = task.url.includes('?') ? '&' : '?';
|
|
124
|
-
const deepLink = `${task.url}${sep}verdict=pass`;
|
|
125
|
-
process.stdout.write(`Opening ${taskId} for a human "Pass" verdict (nothing is recorded until they click):\n` +
|
|
126
|
-
` ${(0, sanitize_1.sanitizeField)(deepLink)}\n`);
|
|
127
|
-
(0, browser_1.openBrowser)(deepLink);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* --fail: record an AGENT send-back. The server hard-codes verdict=FAIL and
|
|
132
|
-
* verifierType=AGENT — there is no passing verdict the CLI can express.
|
|
133
|
-
*/
|
|
134
|
-
async function failVerdict(base, headers, taskId, options, workspaceSlug) {
|
|
135
|
-
if (!options.reason) {
|
|
136
|
-
console.error(`Error: --fail requires --reason <${FAIL_REASONS.join('|')}>.`);
|
|
137
|
-
return 1;
|
|
138
|
-
}
|
|
139
|
-
// Case-insensitive like every other CLI enum flag (LUM-420): fold to the
|
|
140
|
-
// canonical upper-case enum before validating and sending.
|
|
141
|
-
const reason = options.reason.toUpperCase();
|
|
142
|
-
if (!FAIL_REASONS.includes(reason)) {
|
|
143
|
-
console.error(`Error: invalid --reason "${options.reason}". Allowed: ${FAIL_REASONS.join(', ')}.`);
|
|
144
|
-
return 1;
|
|
145
|
-
}
|
|
146
|
-
const body = { rejectionReasonEnum: reason };
|
|
147
|
-
if (options.note)
|
|
148
|
-
body.note = options.note;
|
|
149
|
-
if (options.criterion && options.criterion.length > 0) {
|
|
150
|
-
body.criterionIds = options.criterion;
|
|
151
|
-
}
|
|
152
|
-
let res;
|
|
153
|
-
try {
|
|
154
|
-
res = await fetch(`${base}/api/tasks/${encodeURIComponent(taskId)}/agent-verdict`, {
|
|
155
|
-
method: 'POST',
|
|
156
|
-
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
157
|
-
body: JSON.stringify(body),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
162
|
-
console.error(`Error: could not reach Lumo API (${msg})`);
|
|
163
|
-
return 1;
|
|
164
|
-
}
|
|
165
|
-
if (res.status === 401) {
|
|
166
|
-
console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
|
|
167
|
-
return 1;
|
|
168
|
-
}
|
|
169
|
-
if (res.status === 404) {
|
|
170
|
-
console.error(`Error: task ${taskId} not found in workspace ${workspaceSlug}`);
|
|
171
|
-
return 1;
|
|
172
|
-
}
|
|
173
|
-
if (!res.ok) {
|
|
174
|
-
const errBody = (await res.json().catch(() => null));
|
|
175
|
-
const detail = errBody && typeof errBody.error === 'string'
|
|
176
|
-
? (0, sanitize_1.sanitizeField)(errBody.error)
|
|
177
|
-
: '';
|
|
178
|
-
console.error(`Error: send-back rejected (HTTP ${res.status})${detail ? ` — ${detail}` : ''}`);
|
|
179
|
-
return 1;
|
|
180
|
-
}
|
|
181
|
-
const outcome = (await res.json());
|
|
182
|
-
process.stdout.write(`✗ Sent ${taskId} back (AGENT FAIL, reason ${reason}) — ` +
|
|
183
|
-
`${outcome.criterionIds.length} ${outcome.criterionIds.length === 1 ? 'criterion' : 'criteria'} at round ${outcome.round}; task is now ${outcome.taskStatus}.\n`);
|
|
184
|
-
if (outcome.commentId) {
|
|
185
|
-
process.stdout.write(' A send-back note was posted as a task comment.\n');
|
|
186
|
-
}
|
|
187
|
-
process.stdout.write('Address the unmet criteria (see `lumo task status`), then re-verify.\n');
|
|
188
|
-
return;
|
|
189
|
-
}
|