@lumoai/cli 1.42.0 → 1.43.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.
|
@@ -117,6 +117,15 @@ what's unmet and why (the exact failure tails), and how many rounds are left.
|
|
|
117
117
|
- A pass can carry a **`⚠ pre-edit version`** note (LUM-457): the criterion was changed after that verdict (reworded, or its checkpointer was swapped so the recorded evidence ran a different command). The pass still counts as met (a stale pass does not block DONE — render-only signal), but it vouches for an older version — **re-run `lumo verify` to re-confirm against the current criterion.** This is the habit whenever you edit a MACHINE criterion's checkpointer mid-task: change the check, then re-verify so the green is honest.
|
|
118
118
|
- **History** — one line per recorded round: `rN · timestamp · X PASS / Y FAIL`.
|
|
119
119
|
- **Last round failures** — the most recent round's FAIL verdicts with their rejection reasons (why the last round bounced).
|
|
120
|
+
- **Struggle / rework / outstanding** (LUM-561) — the anti-mum-and-deaf block: **always printed when the contract exists, even on a clean 0-unmet task** so a passing task still shows its scars instead of wiping them to a single PASS count. Lists, when present:
|
|
121
|
+
- **rework rounds** — verify rounds that had a FAIL;
|
|
122
|
+
- **send-backs** — criteria sent back by a human/agent verdict (a MACHINE verify-loop FAIL is not a 打回), with their open/resolved lifecycle, preserved even for since-removed criteria;
|
|
123
|
+
- **leftover follow-ups** — criteria whose latest verdict is `PASS_WITH_FOLLOWUP`;
|
|
124
|
+
- **PR iterations** — when the task has >1 PR (the dominant rework signal when the verify loop ran once but the work churned across many follow-up PRs — e.g. LUM-557: ~10 PRs vs 1 verify round); a single PR is the happy path and is not flagged;
|
|
125
|
+
- **reopens** — backward `IN_REVIEW/DONE → IN_PROGRESS/TODO` transitions (from the `STATUS_CHANGED` log): the task reached review/done and got bounced, a rework that leaves no FAIL verdict.
|
|
126
|
+
|
|
127
|
+
When the trail is genuinely empty it states the **basis** (`None recorded — N rounds run, 0 FAIL, no send-backs, no reopens, no leftover follow-ups`); when nothing has been verified yet it says so (`No verification has run yet — cannot confirm there were no difficulties`) rather than rendering an implicitly-clean slate. Carried in `--json` as `struggleTrail` (incl. `pullRequests` + `reopens`).
|
|
128
|
+
|
|
120
129
|
- **Next actions** — the unmet criteria (latest verdict is not a pass: failed or never verified, HUMAN ones included). This list IS the plan — recomputed from the event log on every read, never maintained separately. Empty + rounds recorded = awaiting human adjudication.
|
|
121
130
|
- **Open boundary crossings** (LUM-448) — a trailing safety block when the task has ≥1 OPEN (undispositioned) forbidden-action crossing: a count, then one line per crossing `• [SEVERITY] CATEGORY — <clipped detail>` (highest-severity first), each followed by a read-only **attribution** line `↳ by model=<m> · agent=<type>[/branch] · session=<8-char prefix>` (LUM-469 — who/what crossed; any dimension that couldn't be resolved server-side prints `unknown`, never a fabricated value), then a pointer to the web acceptance panel. Silent when there are none, so it never overshadows the criteria.
|
|
122
131
|
- **Read-only awareness** — this surfaces crossings detected elsewhere (LUM-426/435/442); there is no CLI path to disposition or clear one. Disposition stays web + human-only (LUM-426/435/422): an agent/CLI bearer cannot clear its own crossing from the terminal.
|
|
@@ -102,10 +102,14 @@ function formatLineageMarkdown(data) {
|
|
|
102
102
|
lines.push(`**Status**: ${data.task.status}`);
|
|
103
103
|
lines.push('');
|
|
104
104
|
if (data.groups.length === 0) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
// LUM-559: an empty report is fail-closed — it means no session ever bound
|
|
106
|
+
// to this task, so neither its cost nor its causal trail could be measured.
|
|
107
|
+
// Say "not measured" explicitly; never let the blank read as "zero cost".
|
|
108
|
+
lines.push('_Cost not measured — no session was ever bound to this task, so its ' +
|
|
109
|
+
'cost and causal trail could not be recorded. This is "not measured", ' +
|
|
110
|
+
'not "zero cost". Cost and lineage are captured once a session binds ' +
|
|
111
|
+
'(`lumo session attach <id>`) and consumes the context; bind before ' +
|
|
112
|
+
'working so future runs are recorded._');
|
|
109
113
|
lines.push('');
|
|
110
114
|
return lines.join('\n');
|
|
111
115
|
}
|
|
@@ -146,6 +150,16 @@ function formatLineageMarkdown(data) {
|
|
|
146
150
|
}
|
|
147
151
|
const summary = outcomeSummary(fragmentOutcomeCounts(g.fragments));
|
|
148
152
|
lines.push(`**Fragments** (${g.fragments.length}${summary ? `: ${summary}` : ''}):`);
|
|
153
|
+
// LUM-559: an edgeless cost group is a session that spent tokens but
|
|
154
|
+
// recorded no fragments (it bound after session-start). Its empty trail is
|
|
155
|
+
// "not captured", NOT "the session used nothing" — say so, and skip the
|
|
156
|
+
// per-fragment usage legend that has nothing to annotate.
|
|
157
|
+
if (g.fragments.length === 0) {
|
|
158
|
+
lines.push('_Causal fragments not captured — this session bound after start, so ' +
|
|
159
|
+
'its cost is recorded but its fragment trail is not._');
|
|
160
|
+
lines.push('');
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
149
163
|
lines.push('_✓ used · · abstained · ✗ unused (manual)_');
|
|
150
164
|
for (const f of g.fragments) {
|
|
151
165
|
const tag = f.disclosure === 'INDEX'
|
|
@@ -134,6 +134,7 @@ function formatTaskStatus(data, extras = {}) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
+
pushStruggleTrail(lines, data);
|
|
137
138
|
lines.push('');
|
|
138
139
|
if (data.nextActions.length === 0) {
|
|
139
140
|
lines.push(data.currentRound > 0
|
|
@@ -165,6 +166,79 @@ function formatTaskStatus(data, extras = {}) {
|
|
|
165
166
|
pushOpenCrossings(lines, extras);
|
|
166
167
|
return lines.join('\n') + '\n';
|
|
167
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Append the honest "Struggle / rework / outstanding" section (LUM-561) — the
|
|
171
|
+
* anti-mum-and-deaf block (kills a silent "Nothing outstanding"). It is ALWAYS
|
|
172
|
+
* rendered when the contract exists, even on a clean 0-unmet task: a passing
|
|
173
|
+
* task that bounced, was sent back, or left a follow-up behind still shows its
|
|
174
|
+
* scars. When the trail is genuinely empty the block states the *basis* for
|
|
175
|
+
* that ("none — N rounds run, 0 FAIL …"), or, when nothing has been verified,
|
|
176
|
+
* that absence cannot be confirmed — never a bare clean slate. Skipped only
|
|
177
|
+
* when the server didn't emit the field (older server).
|
|
178
|
+
*/
|
|
179
|
+
function pushStruggleTrail(lines, data) {
|
|
180
|
+
const trail = data.struggleTrail;
|
|
181
|
+
if (!trail)
|
|
182
|
+
return; // older server: can't fabricate, so don't claim "none".
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push('Struggle / rework / outstanding:');
|
|
185
|
+
// A single PR is the happy path; >1 PR is the iteration signal. Reopens (a
|
|
186
|
+
// bounce after IN_REVIEW/DONE) always count. These two catch the rework that
|
|
187
|
+
// lives in PR cycles / status flips rather than in FAIL verdicts (LUM-561
|
|
188
|
+
// follow-up — without them a 10-PR task like LUM-557 reads as one hiccup).
|
|
189
|
+
const prIterated = trail.pullRequests.length > 1;
|
|
190
|
+
const empty = trail.reworkRounds.length === 0 &&
|
|
191
|
+
trail.sendBacks.length === 0 &&
|
|
192
|
+
trail.followUps.length === 0 &&
|
|
193
|
+
trail.reopens.length === 0 &&
|
|
194
|
+
!prIterated;
|
|
195
|
+
if (empty) {
|
|
196
|
+
if (data.currentRound === 0) {
|
|
197
|
+
// Honest fail-open: nothing was verified, so we cannot claim there were
|
|
198
|
+
// no difficulties — say so rather than render an implicitly-clean slate.
|
|
199
|
+
lines.push(' No verification has run yet — cannot confirm there were no difficulties. Run `lumo verify`.');
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const rounds = `${data.currentRound} verification round${data.currentRound === 1 ? '' : 's'}`;
|
|
203
|
+
lines.push(` None recorded — ${rounds} run, 0 FAIL, no send-backs, no reopens, no leftover follow-ups.`);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (trail.reworkRounds.length > 0) {
|
|
208
|
+
const parts = trail.reworkRounds.map(r => `round ${r.round} (${r.failed} FAIL)`);
|
|
209
|
+
lines.push(` Rework rounds: ${parts.join(', ')}`);
|
|
210
|
+
}
|
|
211
|
+
if (trail.sendBacks.length > 0) {
|
|
212
|
+
lines.push(' Send-backs:');
|
|
213
|
+
for (const s of trail.sendBacks) {
|
|
214
|
+
const lifecycle = s.status === 'resolved'
|
|
215
|
+
? `resolved (sent back r${s.failedAtRound}${s.resolvedAtRound != null ? ` → r${s.resolvedAtRound}` : ''})`
|
|
216
|
+
: `open (sent back r${s.failedAtRound})`;
|
|
217
|
+
lines.push(` • ${(0, sanitize_1.sanitizeField)(s.statement)} — ${lifecycle}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (trail.followUps.length > 0) {
|
|
221
|
+
lines.push(' Follow-ups left behind:');
|
|
222
|
+
for (const f of trail.followUps) {
|
|
223
|
+
lines.push(` • ${(0, sanitize_1.sanitizeField)(f.statement)} — flagged r${f.round}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// PR iteration — the dominant rework signal when the verify loop only ran
|
|
227
|
+
// once but the work churned across many PRs (LUM-557). Only when >1 PR.
|
|
228
|
+
if (prIterated) {
|
|
229
|
+
const PR_CAP = 12;
|
|
230
|
+
const nums = trail.pullRequests.map(p => `#${p.number}`);
|
|
231
|
+
const shown = nums.slice(0, PR_CAP).join(', ');
|
|
232
|
+
const overflow = nums.length > PR_CAP ? `, +${nums.length - PR_CAP} more` : '';
|
|
233
|
+
lines.push(` PR iterations: ${trail.pullRequests.length} PRs (${shown}${overflow})`);
|
|
234
|
+
}
|
|
235
|
+
if (trail.reopens.length > 0) {
|
|
236
|
+
lines.push(` Reopened ${trail.reopens.length}× (bounced back after review/done):`);
|
|
237
|
+
for (const r of trail.reopens) {
|
|
238
|
+
lines.push(` • ${r.from} → ${r.to}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
168
242
|
/**
|
|
169
243
|
* Append the OPEN boundary-crossings safety block (LUM-448) — a count, one line
|
|
170
244
|
* per crossing with its severity + category + clipped detail, and a pointer to
|