toolroll 0.9.14 → 0.9.16
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/CHANGELOG.md +34 -0
- package/dist/assignment-brief.d.ts +10 -0
- package/dist/assignment-brief.js +49 -26
- package/dist/assignment-ui.d.ts +1 -0
- package/dist/assignment-ui.js +6 -4
- package/dist/assignment.d.ts +34 -1
- package/dist/assignment.js +86 -7
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +38 -38
- package/dist/browser-crew.d.ts +2 -1
- package/dist/browser-crew.js +3 -1
- package/dist/browser-workspace.d.ts +93 -18
- package/dist/chat-actions.d.ts +1 -1
- package/dist/chat-actions.js +6 -4
- package/dist/chat-ask.d.ts +28 -0
- package/dist/chat-ask.js +77 -0
- package/dist/chat-channel.js +6 -1
- package/dist/chat-controls.d.ts +8 -0
- package/dist/chat-controls.js +3 -0
- package/dist/chat-delivery-state.d.ts +7 -2
- package/dist/chat-delivery-state.js +15 -2
- package/dist/chat-delivery.js +20 -3
- package/dist/chat-quiet.js +4 -2
- package/dist/chat-voice.d.ts +6 -9
- package/dist/chat-voice.js +11 -5
- package/dist/demo.js +82 -2
- package/dist/disclosure.d.ts +6 -0
- package/dist/disclosure.js +13 -0
- package/dist/discord-chat.js +10 -0
- package/dist/lead-commitments.d.ts +84 -0
- package/dist/lead-commitments.js +208 -0
- package/dist/lead-context.d.ts +32 -3
- package/dist/lead-context.js +133 -19
- package/dist/lead-follow.js +3 -0
- package/dist/lead-identity.d.ts +23 -0
- package/dist/lead-identity.js +33 -0
- package/dist/lead-status.js +2 -2
- package/dist/lead-voice.d.ts +12 -9
- package/dist/lead-voice.js +25 -16
- package/dist/ledger-view.js +3 -2
- package/dist/mate-cli.d.ts +1 -1
- package/dist/mate-cli.js +8 -2
- package/dist/mate-contract.d.ts +1 -1
- package/dist/mate-contract.js +11 -4
- package/dist/mate-progress.js +5 -0
- package/dist/mate-tools.d.ts +18 -1
- package/dist/mate-tools.js +250 -6
- package/dist/mate.d.ts +9 -0
- package/dist/mate.js +34 -38
- package/dist/needs-you.d.ts +48 -0
- package/dist/needs-you.js +93 -1
- package/dist/operate.js +2 -1
- package/dist/project-delete.js +3 -1
- package/dist/result-acts.d.ts +63 -5
- package/dist/result-acts.js +53 -3
- package/dist/result-review.d.ts +4 -3
- package/dist/result-review.js +7 -3
- package/dist/retention.js +6 -0
- package/dist/serve.d.ts +3 -0
- package/dist/serve.js +1220 -694
- package/dist/slack-chat.js +11 -1
- package/dist/store.d.ts +51 -0
- package/dist/store.js +115 -1
- package/dist/surface.js +2 -2
- package/dist/task-status.d.ts +26 -19
- package/dist/task-status.js +59 -21
- package/dist/team-runtime.js +2 -1
- package/dist/teams-chat.js +4 -1
- package/dist/telegram-mate.d.ts +4 -1
- package/dist/telegram-mate.js +17 -1
- package/dist/telegram-progress.js +3 -2
- package/dist/telegram.js +43 -3
- package/dist/test-temp.js +1 -1
- package/dist/transitions-recipes.d.ts +1 -1
- package/dist/transitions-recipes.js +5 -9
- package/dist/when-html.d.ts +24 -2
- package/dist/when-html.js +98 -11
- package/dist/work-index.d.ts +5 -1
- package/dist/work-index.js +22 -9
- package/dist/work-summary.d.ts +1 -1
- package/dist/workspace-motion.js +3 -3
- package/dist/workspace-ui.d.ts +44 -20
- package/dist/workspace-ui.js +63 -37
- package/package.json +1 -1
package/dist/workspace-ui.d.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* claimed without one.
|
|
18
18
|
*/
|
|
19
19
|
import type { DispatchDiagnosis } from "./dispatch.js";
|
|
20
|
-
import { ACCEPT_NEEDS_REASON } from "./result-acts.js";
|
|
21
|
-
export { ACCEPT_NEEDS_REASON };
|
|
20
|
+
import { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf, type AcceptLabel, type EvidenceProblem } from "./result-acts.js";
|
|
21
|
+
export { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf, type EvidenceProblem };
|
|
22
22
|
import { type ProofVerdict } from "./proof.js";
|
|
23
23
|
import type { ReviewRetryState, TaskState } from "./store.js";
|
|
24
24
|
import type { TaskControlView } from "./task-control.js";
|
|
@@ -113,14 +113,6 @@ export type DisplayStatus = {
|
|
|
113
113
|
tone: StatusTone;
|
|
114
114
|
action: NextAction | null;
|
|
115
115
|
};
|
|
116
|
-
/** What kind of evidence problem a verdict records: a check the machine
|
|
117
|
-
* actually ran and saw fail, evidence that contradicts the sealed record
|
|
118
|
-
* (a mismatched changed-path claim, an altered criterion, a caveat that
|
|
119
|
-
* contradicts a met verdict), or evidence that is simply absent. A generic
|
|
120
|
-
* refuted verdict is NOT a failed test claim — only the verify-command
|
|
121
|
-
* reasons say a check failed. */
|
|
122
|
-
export type EvidenceProblem = "checks-failed" | "mismatched" | "missing" | "none";
|
|
123
|
-
export declare function evidenceProblemOf(verdict: ProofVerdict | null, reasons: readonly string[]): EvidenceProblem;
|
|
124
116
|
/** The exit code of the failed check, when the recorded reason names one. */
|
|
125
117
|
export declare function failedCheckExit(reasons: readonly string[]): number | null;
|
|
126
118
|
/** The observed publication record in words. Each state is a distinct
|
|
@@ -192,10 +184,6 @@ export declare function dispatchActionLabel(dispatch: DispatchDiagnosis | null):
|
|
|
192
184
|
/** A finished result's display status in the shared headline's words: a
|
|
193
185
|
* failed project check is Failed, a result missing its record needs a
|
|
194
186
|
* person, every other saved result is Ready for review. Token and action stay. */
|
|
195
|
-
/** Why a result can't be accepted as it stands, in one plain line, or null
|
|
196
|
-
* when nothing refutes it (a person's acceptance settles it). The result page
|
|
197
|
-
* shows it before Request changes, its one ink act. */
|
|
198
|
-
export declare function cantAcceptYetOf(verdict: ProofVerdict | null, reasons: readonly string[], accepted: boolean): string | null;
|
|
199
187
|
/** The result's headline while its report disagrees with its saved changes. */
|
|
200
188
|
export declare const MISMATCH_HEADLINE = "The report doesn't match the changes";
|
|
201
189
|
/** One way the report disagrees with the saved changes, in plain words: the
|
|
@@ -241,15 +229,15 @@ export declare function lastErrorLineOf(log: string): {
|
|
|
241
229
|
/** The result page's own sentence when the decision is on it. */
|
|
242
230
|
export declare const RESULT_DECISION_SENTENCE = "Review the change, then accept it or ask for changes.";
|
|
243
231
|
/** What the result page's Accept says, from facts it already shows: the
|
|
244
|
-
* recorded checks, the requirements not met, the
|
|
245
|
-
*
|
|
246
|
-
* checks passed; otherwise "Accept without checks" and one line naming
|
|
232
|
+
* recorded checks, the requirements not met, whether the saved proof reads,
|
|
233
|
+
* and what the button posts. "Accept and finish" only when everything is met
|
|
234
|
+
* and the checks passed; otherwise "Accept without checks" and one line naming
|
|
235
|
+
* what. The person's own checks are the page's to add (acceptWithChecksOf).
|
|
247
236
|
* `effect` says what pressing it does, in the action's own terms. */
|
|
248
237
|
export type AcceptFacts = {
|
|
249
238
|
checks: "passed" | "failed" | "not-run" | "off" | "running" | "unavailable" | null;
|
|
250
239
|
unmet: number;
|
|
251
|
-
|
|
252
|
-
/** `complete`: marks the exact result complete; `accept`: records a person's acceptance only. */
|
|
240
|
+
/** `complete`: accepts and finishes the exact result in one request; `accept`: records a person's acceptance only (nothing here can finish it). */
|
|
253
241
|
action: "complete" | "accept";
|
|
254
242
|
/** `pull-request`: one can be opened from the task after; `off`: publishing isn't set up; `other`: neither is said. Accept itself never publishes. */
|
|
255
243
|
publishing: "pull-request" | "off" | "other";
|
|
@@ -257,7 +245,7 @@ export type AcceptFacts = {
|
|
|
257
245
|
proof: boolean;
|
|
258
246
|
};
|
|
259
247
|
export type AcceptWords = {
|
|
260
|
-
label:
|
|
248
|
+
label: AcceptLabel;
|
|
261
249
|
ready: boolean;
|
|
262
250
|
why: string | null;
|
|
263
251
|
effect: string;
|
|
@@ -287,3 +275,39 @@ export type PrimaryDestination = "chat" | "work" | "projects" | "flows" | "setti
|
|
|
287
275
|
export declare function primaryDestinationOf(active: string): PrimaryDestination;
|
|
288
276
|
/** Learning stays in Settings and a useful, closed result disclosure. */
|
|
289
277
|
export declare function learningHtml(view: import('./project-learning.js').LearningView, csrf: string, canManage: boolean, source?: number): string;
|
|
278
|
+
/** The first signed requirement a finished attempt missed (failed or unanswered; a person's own check is not a miss),
|
|
279
|
+
* with the evidence line behind it in plain words. Null when every requirement held. */
|
|
280
|
+
export declare function missedRequirementOf(matrix: readonly {
|
|
281
|
+
id: string;
|
|
282
|
+
statement: string;
|
|
283
|
+
state: string;
|
|
284
|
+
detail: readonly string[];
|
|
285
|
+
}[]): {
|
|
286
|
+
id: string;
|
|
287
|
+
statement: string;
|
|
288
|
+
evidence: string | null;
|
|
289
|
+
} | null;
|
|
290
|
+
/** One milestone of a live build's plan, as the agent last reported it; its words are null where only the recorded
|
|
291
|
+
* progress was read (a list row reads no plan file). */
|
|
292
|
+
export type BuildStep = {
|
|
293
|
+
description: string | null;
|
|
294
|
+
state: "pending" | "current" | "completed" | "blocked";
|
|
295
|
+
note: string | null;
|
|
296
|
+
};
|
|
297
|
+
/** Where a live build is, in one line: "Step 3 of 6: …", or, when a step is blocked, "Stuck on step 4 of 6: <why>" in
|
|
298
|
+
* its place. `stuck` says which step and why; null while nothing is blocked. */
|
|
299
|
+
export type BuildProgress = {
|
|
300
|
+
step: number;
|
|
301
|
+
total: number;
|
|
302
|
+
line: string;
|
|
303
|
+
stuck: {
|
|
304
|
+
step: number;
|
|
305
|
+
why: string | null;
|
|
306
|
+
line: string;
|
|
307
|
+
} | null;
|
|
308
|
+
};
|
|
309
|
+
/** A live build's progress: the step in progress (else the first one not done) as "Step N of M: <step>"; a blocked
|
|
310
|
+
* step replaces it as "Stuck on step N of M: <why>" (its note, else the step itself). Null with no plan steps. */
|
|
311
|
+
export declare function buildProgressOf(steps: readonly BuildStep[] | null | undefined): BuildProgress | null;
|
|
312
|
+
/** Earlier attempts that stopped before the one now running, in one quiet line. */
|
|
313
|
+
export declare function earlierAttemptsWords(count: number): string;
|
package/dist/workspace-ui.js
CHANGED
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
* observed" name observed publication records, and deployment is never
|
|
17
17
|
* claimed without one.
|
|
18
18
|
*/
|
|
19
|
-
import { ACCEPT_NEEDS_REASON } from "./result-acts.js";
|
|
20
|
-
export { ACCEPT_NEEDS_REASON };
|
|
19
|
+
import { ACCEPT_NEEDS_REASON, FAILED_CHECK, cantAcceptYetOf, evidenceProblemOf } from "./result-acts.js";
|
|
20
|
+
export { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf };
|
|
21
21
|
import { GOAL_ASSESSMENT_PENDING, manualReviewOnly, plainReasonWords } from "./proof.js";
|
|
22
|
-
import { plainReasonOf, stageOfCode, taskStatusOf, workToneOf } from "./task-status.js";
|
|
22
|
+
import { OPEN_RESULT, plainReasonOf, stageOfCode, taskStatusOf, workToneOf } from "./task-status.js";
|
|
23
|
+
import { whenUtc } from "./when-html.js";
|
|
23
24
|
export const WORK_VIEWS = [
|
|
24
25
|
{ key: "all", label: "All", hint: "Every task in view, most urgent first.", empty: "Nothing is in progress. Describe work in chat or add a task, and it appears here." },
|
|
25
26
|
{ key: "needs-you", label: "Needs you", hint: "Tasks waiting on an answer, approval, or your inspection.", empty: "Nothing needs you right now. Queued and running work continues on its own." },
|
|
@@ -67,15 +68,7 @@ export function reviewStatusOf(review) {
|
|
|
67
68
|
// it would unreviewed, marked "not reviewed" by the result itself.
|
|
68
69
|
if (review === null || review.automaticPending !== true || (review.state !== "queued" && review.state !== "running"))
|
|
69
70
|
return null;
|
|
70
|
-
return { token: "reviewing", label: "Reviewing", detail: "An automatic review is reading this result before it reaches you.", tone: "live", action: { label:
|
|
71
|
-
}
|
|
72
|
-
const FAILED_CHECK = /^the repository's approved verification command exited (-?[0-9]+)/;
|
|
73
|
-
export function evidenceProblemOf(verdict, reasons) {
|
|
74
|
-
if (verdict === "verified" || verdict === "attested")
|
|
75
|
-
return "none";
|
|
76
|
-
if (verdict === "refuted")
|
|
77
|
-
return reasons.some(reason => FAILED_CHECK.test(reason)) ? "checks-failed" : "mismatched";
|
|
78
|
-
return "missing";
|
|
71
|
+
return { token: "reviewing", label: "Reviewing", detail: "An automatic review is reading this result before it reaches you.", tone: "live", action: { label: OPEN_RESULT, kind: "open-result" } };
|
|
79
72
|
}
|
|
80
73
|
/** The exit code of the failed check, when the recorded reason names one. */
|
|
81
74
|
export function failedCheckExit(reasons) {
|
|
@@ -193,11 +186,11 @@ function storedResultStatusOf(result, publication) {
|
|
|
193
186
|
// weighed, so it says nothing about whether that check passed.
|
|
194
187
|
detail: withPublication("What the agent reported doesn't match the changes it saved."),
|
|
195
188
|
tone: "problem",
|
|
196
|
-
action: { label:
|
|
189
|
+
action: { label: OPEN_RESULT, kind: "open-review" },
|
|
197
190
|
};
|
|
198
191
|
}
|
|
199
192
|
if (result.verdict === "short" && humanReview) {
|
|
200
|
-
return { token: "verification-needed", label: "Ready to inspect", detail: withPublication("The remaining requirements need a person's inspection."), tone: "attention", action: { label:
|
|
193
|
+
return { token: "verification-needed", label: "Ready to inspect", detail: withPublication("The remaining requirements need a person's inspection."), tone: "attention", action: { label: OPEN_RESULT, kind: "open-review" } };
|
|
201
194
|
}
|
|
202
195
|
if (result.verdict === "short") {
|
|
203
196
|
return {
|
|
@@ -214,7 +207,7 @@ function storedResultStatusOf(result, publication) {
|
|
|
214
207
|
// presence facts here.
|
|
215
208
|
return result.recordComplete === false && result.verdict === null
|
|
216
209
|
? { token: "record-incomplete", label: "No-change result, record incomplete", detail: "The build concluded nothing needed to change, but its handoff or sealed diff is missing.", tone: "problem", action: { label: "Open the record", kind: "open-run" } }
|
|
217
|
-
: { token: "no-change", label: "No changes were needed", detail: withPublication("The build concluded nothing needed to change; its handoff and sealed diff are on record."), tone: "done", action: { label:
|
|
210
|
+
: { token: "no-change", label: "No changes were needed", detail: withPublication("The build concluded nothing needed to change; its handoff and sealed diff are on record."), tone: "done", action: { label: OPEN_RESULT, kind: "open-result" } };
|
|
218
211
|
}
|
|
219
212
|
if (result.verdict === null) {
|
|
220
213
|
return {
|
|
@@ -231,7 +224,7 @@ function storedResultStatusOf(result, publication) {
|
|
|
231
224
|
label: "Result saved — checks reported by the agent",
|
|
232
225
|
detail: withPublication("No independent project check ran; the checks listed are the agent's own report."),
|
|
233
226
|
tone: "neutral",
|
|
234
|
-
action: { label:
|
|
227
|
+
action: { label: OPEN_RESULT, kind: "open-result" },
|
|
235
228
|
};
|
|
236
229
|
}
|
|
237
230
|
if (published !== null && (published.token === "merge-observed" || published.token === "pr-opened" || published.token === "pr-closed")) {
|
|
@@ -240,7 +233,7 @@ function storedResultStatusOf(result, publication) {
|
|
|
240
233
|
label: published.label,
|
|
241
234
|
detail: `The approved check passed against this result. ${published.detail}`,
|
|
242
235
|
tone: published.token === "pr-closed" ? "muted" : "done",
|
|
243
|
-
action: published.token === "merge-observed" ? { label:
|
|
236
|
+
action: published.token === "merge-observed" ? { label: OPEN_RESULT, kind: "open-result" } : { label: "Open the pull request", kind: "open-pr" },
|
|
244
237
|
};
|
|
245
238
|
}
|
|
246
239
|
return {
|
|
@@ -248,7 +241,7 @@ function storedResultStatusOf(result, publication) {
|
|
|
248
241
|
label: "Ready",
|
|
249
242
|
detail: withPublication("The approved check passed against this result."),
|
|
250
243
|
tone: "ready",
|
|
251
|
-
action: { label:
|
|
244
|
+
action: { label: OPEN_RESULT, kind: "open-result" },
|
|
252
245
|
};
|
|
253
246
|
}
|
|
254
247
|
/** The completion receipt's heading: what the record actually supports.
|
|
@@ -288,7 +281,7 @@ export function needsPerson(dispatch) {
|
|
|
288
281
|
/** Navigation labels describe the available help, not a mutation: opening
|
|
289
282
|
* a hold or pause must never promise that the task has already resumed. */
|
|
290
283
|
const DISPATCH_ACTION_LABELS = {
|
|
291
|
-
"open-result":
|
|
284
|
+
"open-result": OPEN_RESULT,
|
|
292
285
|
"retry-task": "Review and retry",
|
|
293
286
|
"place-task": "Choose a project",
|
|
294
287
|
"write-scope": "Define the task",
|
|
@@ -314,17 +307,6 @@ const VERIFIED_RESULT = new Set(["ready-to-review", "pr-opened", "merge-observed
|
|
|
314
307
|
/** A finished result's display status in the shared headline's words: a
|
|
315
308
|
* failed project check is Failed, a result missing its record needs a
|
|
316
309
|
* person, every other saved result is Ready for review. Token and action stay. */
|
|
317
|
-
/** Why a result can't be accepted as it stands, in one plain line, or null
|
|
318
|
-
* when nothing refutes it (a person's acceptance settles it). The result page
|
|
319
|
-
* shows it before Request changes, its one ink act. */
|
|
320
|
-
export function cantAcceptYetOf(verdict, reasons, accepted) {
|
|
321
|
-
if (accepted || verdict !== "refuted")
|
|
322
|
-
return null;
|
|
323
|
-
// A mismatch is the status card's own headline; here only what accepting takes.
|
|
324
|
-
return evidenceProblemOf(verdict, reasons) === "checks-failed"
|
|
325
|
-
? "Can't accept yet: the project's check failed on these changes."
|
|
326
|
-
: ACCEPT_NEEDS_REASON;
|
|
327
|
-
}
|
|
328
310
|
/** The result's headline while its report disagrees with its saved changes. */
|
|
329
311
|
export const MISMATCH_HEADLINE = "The report doesn't match the changes";
|
|
330
312
|
const OVERCLAIMED = /^claimed changed paths? not in the sealed diff: ([\s\S]+)$/;
|
|
@@ -413,15 +395,14 @@ export function acceptWordsOf(facts) {
|
|
|
413
395
|
...(facts.checks === "passed" ? [] : [facts.checks === "failed" ? "checks failed" : facts.checks === "off" ? "checks are off for this project"
|
|
414
396
|
: facts.checks === "running" ? "checks are still running" : facts.checks === "unavailable" ? "saved checks can't be read" : "checks didn't run"]),
|
|
415
397
|
...(facts.unmet > 0 ? [`${plural(facts.unmet, "requirement isn't", "requirements aren't")} met`] : []),
|
|
416
|
-
...(facts.yours > 0 ? [`${plural(facts.yours, "item still needs", "items still need")} your check`] : []),
|
|
417
398
|
];
|
|
418
399
|
const ready = parts.length === 0;
|
|
419
400
|
const joined = parts.length <= 1 ? parts.join("") : `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}`;
|
|
420
|
-
const effect = facts.action === "accept" ? "Records that you accept it.
|
|
421
|
-
: facts.publishing === "pull-request" ? "
|
|
422
|
-
: facts.publishing === "off" ? "
|
|
423
|
-
: "
|
|
424
|
-
return { label: ready ? "Accept" : "Accept without checks", ready, why: ready ? null : `${joined.charAt(0).toUpperCase()}${joined.slice(1)}.`, effect };
|
|
401
|
+
const effect = facts.action === "accept" ? "Records that you accept it. The task stays open."
|
|
402
|
+
: facts.publishing === "pull-request" ? "Finishes the task. No pull request opens; you can open one from the task after."
|
|
403
|
+
: facts.publishing === "off" ? "Finishes the task. The branch stays; publishing isn't set up."
|
|
404
|
+
: "Finishes the task. Nothing is published.";
|
|
405
|
+
return { label: ready ? "Accept and finish" : "Accept without checks", ready, why: ready ? null : `${joined.charAt(0).toUpperCase()}${joined.slice(1)}.`, effect };
|
|
425
406
|
}
|
|
426
407
|
export function resultHeadlineOf(status) {
|
|
427
408
|
const read = RESULT_NEEDS_YOU.has(status.token) ? { stage: "needs-you", need: "other" }
|
|
@@ -549,6 +530,51 @@ export function learningHtml(view, csrf, canManage, source) {
|
|
|
549
530
|
const cards = lessons.map(l => `<article class="card" data-lesson="${l.id}"><p><strong>${e(l.payload.observation)}</strong></p><p>${e(l.payload.action)}</p><p class="meta">${l.payload.kind === 'system' ? 'System suggestion · No change applied' : e(l.status === 'adopted' ? 'Adopted advice' : l.status === 'disabled' ? 'Disabled' : 'Proposed lesson')}</p><details><summary>Source and use</summary><p><a href="/r/${l.source}">Result #${l.source}</a> · <a href="/r/${l.reviewer}">Review #${l.reviewer}</a></p><p>${e(l.payload.paths.join(', '))} · ${e(l.payload.phases.join(', '))} · ${e({ darwin: "macOS", win32: "Windows", linux: "Linux" }[l.payload.platform] ?? l.payload.platform)}</p><ul>${evidence(l.payload.evidence, l.source)}</ul>${l.payload.kind !== 'project' ? '' : `<p class="meta">${l.status === 'proposed' ? 'Save permits advisory reuse when enabled and applicable. It does not prove a remedy works.' : 'Disabling affects future runs. Active snapshots, code and approvals stay unchanged.'}</p>${l.status === 'proposed' ? act('adopt', 'Save lesson', l) : ''}`}</details>${l.payload.kind === 'project' && l.status === 'adopted' ? act('disable', 'Disable lesson', l) : ''}</article>`).join('');
|
|
550
531
|
if (source !== undefined)
|
|
551
532
|
return lessons.length ? `<details class="learning result-learning"><summary>Learned from this task</summary>${cards}<a href="/settings/learning?repo=${encodeURIComponent(view.repo)}">Learning settings</a></details>` : '';
|
|
552
|
-
const history = view.events.map(ev => `<article class="card" data-learning-event="${e(ev.action)}"><p><strong>${e({ assessment: { propose: 'Learning suggested', none: 'No lesson needed', unassessed: 'Learning not assessed', invalid: 'Learning assessment invalid' }[ev.after] ?? 'Learning not assessed', proposal: 'Suggestion recorded', adopt: 'Lesson adopted', disable: 'Lesson disabled', reset: 'Learning reset', enable: 'Reuse enabled', pause: 'Reuse paused', reuse: 'Run context saved', failure: 'Learning issue', capture: 'Capture finished' }[ev.action] ?? ev.action)}</strong>${ev.action === "reuse" ? ` · ${e(ev.after)}` : ""}</p><p class="meta"
|
|
533
|
+
const history = view.events.map(ev => `<article class="card" data-learning-event="${e(ev.action)}"><p><strong>${e({ assessment: { propose: 'Learning suggested', none: 'No lesson needed', unassessed: 'Learning not assessed', invalid: 'Learning assessment invalid' }[ev.after] ?? 'Learning not assessed', proposal: 'Suggestion recorded', adopt: 'Lesson adopted', disable: 'Lesson disabled', reset: 'Learning reset', enable: 'Reuse enabled', pause: 'Reuse paused', reuse: 'Run context saved', failure: 'Learning issue', capture: 'Capture finished' }[ev.action] ?? ev.action)}</strong>${ev.action === "reuse" ? ` · ${e(ev.after)}` : ""}</p><p class="meta">${whenUtc(ev.at)} · ${e(ev.actor)}${ev.run === null ? '' : ` · <a href="/r/${ev.run}">Run #${ev.run}</a>${ev.outcome === null ? "" : ` · ${e(ev.outcome)}`}`}</p><details><summary>Details</summary><p>${e(ev.before)} → ${e(ev.after)}</p><p>${e(ev.reason)}</p>${ev.lesson === null ? '' : `<p>Lesson #${ev.lesson}</p>`}<ul>${evidence(ev.evidence, ev.run)}</ul>${ev.snapshot === null ? "" : `<details><summary>Exact context</summary><pre>${e(ev.snapshot)}</pre></details>`}</details></article>`).join('');
|
|
553
534
|
return `<section class="learning">${view.damaged ? '<p class="problem" role="alert">Some lessons no longer verify and are excluded from reuse.</p>' : ''}<p>${view.enabled ? 'Use adopted lessons: on' : 'Use adopted lessons: off'}</p>${act(view.enabled ? 'pause' : 'enable', view.enabled ? 'Pause reuse' : 'Enable reuse')}${cards || '<p>No lessons yet. Reviews may suggest useful advice here.</p>'}<details><summary>Reset learning</summary><p>Disable all adopted lessons and future reuse. Keep history, code, approvals and active run snapshots.</p>${act('reset', 'Reset learning')}</details><h2>Changes</h2>${history || '<p>No learning changes yet.</p>'}${view.next === null ? '' : `<a class="button-link" href="/settings/learning?repo=${encodeURIComponent(view.repo)}&before=${view.next}">Older changes</a>`}</section>`;
|
|
554
535
|
}
|
|
536
|
+
/** The first signed requirement a finished attempt missed (failed or unanswered; a person's own check is not a miss),
|
|
537
|
+
* with the evidence line behind it in plain words. Null when every requirement held. */
|
|
538
|
+
export function missedRequirementOf(matrix) {
|
|
539
|
+
const missed = matrix.find(row => row.state === "failed") ?? matrix.find(row => row.state === "missing") ?? null;
|
|
540
|
+
if (missed === null)
|
|
541
|
+
return null;
|
|
542
|
+
const detail = missed.detail.find(one => !/is waiting for the final check/.test(one)) ?? missed.detail[0] ?? null;
|
|
543
|
+
return { id: missed.id, statement: missed.statement, evidence: detail === null ? (missed.state === "missing" ? "The agent's report doesn't answer it." : null) : requirementEvidenceWords(missed.id, detail) };
|
|
544
|
+
}
|
|
545
|
+
/** A matrix row's recorded detail as a sentence a person reads: the agent's own note when one admits the miss. */
|
|
546
|
+
function requirementEvidenceWords(id, detail) {
|
|
547
|
+
const caveat = CONTRADICTED_BY_CAVEAT.exec(detail);
|
|
548
|
+
// The note often names its requirement first ("c1: …"); the line already says which requirement it is.
|
|
549
|
+
if (caveat !== null && caveat[1] === id)
|
|
550
|
+
return sentenceOf(`The agent's own note says: ${caveat[2].trim().replace(new RegExp(`^${id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*[:—-]\\s*`), "")}`);
|
|
551
|
+
const lead = [`criterion "${id}"'s `, `criterion "${id}" `].find(prefix => detail.startsWith(prefix));
|
|
552
|
+
return sentenceOf(plainReasonWords(lead === undefined ? detail : detail.slice(lead.length)));
|
|
553
|
+
}
|
|
554
|
+
const stepWords = (text) => {
|
|
555
|
+
const plain = text?.trim().replace(/\s+/g, " ").replace(/[.\s]+$/, "") ?? "";
|
|
556
|
+
return plain === "" ? null : plain;
|
|
557
|
+
};
|
|
558
|
+
/** A live build's progress: the step in progress (else the first one not done) as "Step N of M: <step>"; a blocked
|
|
559
|
+
* step replaces it as "Stuck on step N of M: <why>" (its note, else the step itself). Null with no plan steps. */
|
|
560
|
+
export function buildProgressOf(steps) {
|
|
561
|
+
if (steps == null || steps.length === 0)
|
|
562
|
+
return null;
|
|
563
|
+
const total = steps.length;
|
|
564
|
+
const blocked = steps.findIndex(one => one.state === "blocked");
|
|
565
|
+
if (blocked >= 0) {
|
|
566
|
+
const why = stepWords(steps[blocked].note) ?? stepWords(steps[blocked].description);
|
|
567
|
+
const line = `Stuck on step ${blocked + 1} of ${total}${why === null ? "" : `: ${why}`}.`;
|
|
568
|
+
return { step: blocked + 1, total, line, stuck: { step: blocked + 1, why, line } };
|
|
569
|
+
}
|
|
570
|
+
const current = steps.findIndex(one => one.state === "current");
|
|
571
|
+
const at = current >= 0 ? current : steps.findIndex(one => one.state !== "completed");
|
|
572
|
+
if (at < 0)
|
|
573
|
+
return { step: total, total, line: `All ${total} steps done. Finishing up.`, stuck: null };
|
|
574
|
+
const words = stepWords(steps[at].description);
|
|
575
|
+
return { step: at + 1, total, line: `Step ${at + 1} of ${total}${words === null ? "" : `: ${words}`}.`, stuck: null };
|
|
576
|
+
}
|
|
577
|
+
/** Earlier attempts that stopped before the one now running, in one quiet line. */
|
|
578
|
+
export function earlierAttemptsWords(count) {
|
|
579
|
+
return `${count} earlier attempt${count === 1 ? "" : "s"} stopped`;
|
|
580
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.16",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|