@turri-dev/cli 0.1.8-next.b03162b → 0.1.8-next.bfcb24e
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/dist/extension.js +6 -1
- package/dist/ledger-tools.d.ts +10 -0
- package/dist/ledger-tools.js +64 -0
- package/dist/regenerate/ledger-view.d.ts +19 -0
- package/dist/regenerate/ledger-view.js +63 -4
- package/dist/regenerate/ledger.d.ts +1 -0
- package/dist/regenerate/test-scripts.js +6 -4
- package/docs/development.md +1 -1
- package/docs/using.md +4 -3
- package/package.json +1 -1
package/dist/extension.js
CHANGED
|
@@ -46,6 +46,7 @@ import { defaultBuildDir } from "./regenerate/paths.js";
|
|
|
46
46
|
import { describeTurnIncident, describeUsage, onModelEvent } from "./regenerate/activity.js";
|
|
47
47
|
import { ledgerWanted, startLedger } from "./regenerate/ledger.js";
|
|
48
48
|
import { describePastPulse, describeRuns, earlierPulses, ledgerProjects } from "./regenerate/ledger-view.js";
|
|
49
|
+
import { ledgerTool } from "./ledger-tools.js";
|
|
49
50
|
import { describeSpecConflict } from "./regenerate/stop-report.js";
|
|
50
51
|
import { inheritProviders, inheritThinkingLevel } from "./pi.js";
|
|
51
52
|
import { beginCapture, captureConsole, describeCaptured, dropNoise } from "./quiet.js";
|
|
@@ -1489,9 +1490,13 @@ function registerJobTools(pi, jobs, cwd) {
|
|
|
1489
1490
|
return { content: [{ type: "text", text: `${pastPulseNote}\n\n${past}` }], details: { status: "earlier" } };
|
|
1490
1491
|
return { content: [{ type: "text", text: `No pulse ${params.job_id} has run in this session, and the ledger has none by that id.` }], details: { status: "unknown" } };
|
|
1491
1492
|
}
|
|
1492
|
-
|
|
1493
|
+
// What the ledger recorded of it besides: its rounds, test runs, healers and commits, as facts.
|
|
1494
|
+
const facts = describePastPulse(ledgerProjects(cwd), job.id);
|
|
1495
|
+
const text = facts !== undefined ? `${whyFailed(job)}\n\nWhat the ledger recorded of it:\n${facts}` : whyFailed(job);
|
|
1496
|
+
return { content: [{ type: "text", text }], details: { status: job.status } };
|
|
1493
1497
|
},
|
|
1494
1498
|
});
|
|
1499
|
+
pi.registerTool(ledgerTool({ cwd }));
|
|
1495
1500
|
pi.registerTool({
|
|
1496
1501
|
name: "pulses",
|
|
1497
1502
|
label: "Pulses",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
type AnyTool = ToolDefinition<any, any, any>;
|
|
3
|
+
/**
|
|
4
|
+
* ledger: what turri did, as the ledger recorded it - in this session and every one before it. One pulse, by its id,
|
|
5
|
+
* as facts; the last runs, a line a pulse; or the events of some kinds, counted and listed. Reads only.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ledgerTool(options: {
|
|
8
|
+
cwd: string;
|
|
9
|
+
}): AnyTool;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { describeEvents, describePastPulse, describeRuns, ledgerProjects, runViews, sinceOf } from "./regenerate/ledger-view.js";
|
|
4
|
+
/** How many runs `ledger` lists when it is asked for none. */
|
|
5
|
+
const RUNS_SHOWN = 3;
|
|
6
|
+
/**
|
|
7
|
+
* ledger: what turri did, as the ledger recorded it - in this session and every one before it. One pulse, by its id,
|
|
8
|
+
* as facts; the last runs, a line a pulse; or the events of some kinds, counted and listed. Reads only.
|
|
9
|
+
*/
|
|
10
|
+
export function ledgerTool(options) {
|
|
11
|
+
return defineTool({
|
|
12
|
+
name: "ledger",
|
|
13
|
+
label: "Ledger",
|
|
14
|
+
description: "What turri did here, as its ledger recorded it - facts, not transcripts - in this session and every earlier one. " +
|
|
15
|
+
"With pulse: that pulse, this session's or an earlier one's - how it ended, its rounds and test runs (how many failed " +
|
|
16
|
+
"the same way), the judge's and the reviews' verdicts, the healers (the debugger, the diagnosis) and whether they " +
|
|
17
|
+
"failed, the model provider's trouble, what turri refused and stopped, what it committed or rolled back, its last " +
|
|
18
|
+
"stages and its log. With kinds (and module, since): how many events of those kinds there were, and the most recent, " +
|
|
19
|
+
"a line each - healer.failed, provider.error, script.run, gate.round, judge.verdict, guard.refused, history.failed, " +
|
|
20
|
+
"job.finished, ...; a group by its first part, healer for every healer event. Otherwise: the last runs, a line a pulse - " +
|
|
21
|
+
"how each ended, how long, what it cost. Starts nothing and calls no model.",
|
|
22
|
+
promptSnippet: "ledger: what turri did - a pulse as facts, the last runs, or events counted by kind - this session and earlier ones",
|
|
23
|
+
promptGuidelines: [
|
|
24
|
+
"Use ledger for what turri recorded of the work - a pulse's rounds, test runs, verdicts, healers and commits, this session's or an earlier one's; how often something happened; what earlier runs did and cost - rather than reading .turri/ledger with the shell. pulses is what is running and planned now; a pulse's whole transcript is its log, which ledger names.",
|
|
25
|
+
],
|
|
26
|
+
parameters: Type.Object({
|
|
27
|
+
pulse: Type.Optional(Type.String({ description: "A pulse's id, or the start of it: that pulse, as the ledger has it" })),
|
|
28
|
+
kinds: Type.Optional(Type.Array(Type.String(), { description: "Event kinds to count and list - healer.failed, or healer for the whole group" })),
|
|
29
|
+
module: Type.Optional(Type.String({ description: "With kinds: only the events of this module" })),
|
|
30
|
+
since: Type.Optional(Type.String({ description: "With kinds: only events since then - 24h, 7d, 30m, or a date and time" })),
|
|
31
|
+
runs: Type.Optional(Type.Number({ description: `Without pulse or kinds: how many of the last runs to list (default ${RUNS_SHOWN})` })),
|
|
32
|
+
limit: Type.Optional(Type.Number({ description: "With kinds: how many of the most recent events to list (default 20)" })),
|
|
33
|
+
}),
|
|
34
|
+
async execute(_id, params) {
|
|
35
|
+
const text = (body, details = {}) => ({ content: [{ type: "text", text: body }], details });
|
|
36
|
+
const projects = ledgerProjects(options.cwd);
|
|
37
|
+
if (projects.length === 0)
|
|
38
|
+
return text("The ledger is empty here: nothing has run in this folder, or in a project under it, since it began.", { found: false });
|
|
39
|
+
if (params.pulse !== undefined) {
|
|
40
|
+
const described = describePastPulse(projects, params.pulse.trim());
|
|
41
|
+
return described !== undefined ? text(described, { found: true }) : text(`The ledger has no pulse ${params.pulse}.`, { found: false });
|
|
42
|
+
}
|
|
43
|
+
if (params.kinds !== undefined && params.kinds.length > 0) {
|
|
44
|
+
let since;
|
|
45
|
+
if (params.since !== undefined) {
|
|
46
|
+
since = sinceOf(params.since);
|
|
47
|
+
if (since === undefined)
|
|
48
|
+
return text(`since takes 24h, 7d, 30m or a date and time, not ${JSON.stringify(params.since)}.`, { found: false });
|
|
49
|
+
}
|
|
50
|
+
return text(describeEvents(projects, {
|
|
51
|
+
kinds: params.kinds,
|
|
52
|
+
...(params.module !== undefined ? { module: params.module } : {}),
|
|
53
|
+
...(since !== undefined ? { since } : {}),
|
|
54
|
+
...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
55
|
+
}), { found: true });
|
|
56
|
+
}
|
|
57
|
+
const count = params.runs !== undefined && Number.isFinite(params.runs) ? Math.max(1, Math.floor(params.runs)) : RUNS_SHOWN;
|
|
58
|
+
const runs = projects.flatMap((project) => runViews(project)).sort((a, b) => (b.started?.ts ?? "").localeCompare(a.started?.ts ?? ""));
|
|
59
|
+
const shown = runs.slice(0, count);
|
|
60
|
+
const more = runs.length - shown.length;
|
|
61
|
+
return text(`${describeRuns(shown)}${more > 0 ? `\n\n${more} earlier run${more === 1 ? "" : "s"} beyond these: ask with runs for more.` : ""}`, { found: true, runs: shown.length });
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -70,4 +70,23 @@ export declare function earlierPulses(projects: string[], known: Set<string>, ru
|
|
|
70
70
|
};
|
|
71
71
|
/** A pulse of the ledger's, as the lists show a job: finished as it ended, its time and cost - and nothing to run. */
|
|
72
72
|
export declare function asJob(view: PulseView): Job;
|
|
73
|
+
/** What to count in the ledger: kinds - whole, or a group by what comes before the dot - a module, since when. */
|
|
74
|
+
export interface EventQuery {
|
|
75
|
+
/** `healer.failed`, or `healer` for every healer event. */
|
|
76
|
+
kinds?: string[];
|
|
77
|
+
module?: string;
|
|
78
|
+
/** Only events at or after this time. */
|
|
79
|
+
since?: Date;
|
|
80
|
+
/** How many of the most recent to list, a line each. */
|
|
81
|
+
limit?: number;
|
|
82
|
+
}
|
|
83
|
+
/** One event in a line: when, its pulse and module, its kind, its own fields - the agent's role when it has one. */
|
|
84
|
+
export declare function eventLine(entry: LedgerEntry): string;
|
|
85
|
+
/**
|
|
86
|
+
* The events of these projects' ledgers that `query` asks for: how many of each kind, and the most recent, a line each -
|
|
87
|
+
* or, when no kind asked for is in the ledger, the kinds it has.
|
|
88
|
+
*/
|
|
89
|
+
export declare function describeEvents(projects: string[], query?: EventQuery): string;
|
|
90
|
+
/** `24h`, `7d`, `30m`, or a date and time: the moment it means, or undefined when it means none. */
|
|
91
|
+
export declare function sinceOf(text: string, now?: number): Date | undefined;
|
|
73
92
|
export {};
|
|
@@ -148,18 +148,25 @@ export function describePulseView(view) {
|
|
|
148
148
|
lines.push(` tried again (${retry.attempt} of ${retry.of}) after: ${retry.failure}`);
|
|
149
149
|
if (view.rounds.size > 0)
|
|
150
150
|
lines.push(` rounds: ${[...view.rounds].map(([gate, { round, of }]) => `${gate} ${round} of ${of}`).join("; ")}`);
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
// can_it_fail's runs, on code changed to break it, apart: one failing is a test that caught the bug.
|
|
152
|
+
const mutations = view.scripts.filter((run) => run.mutated === true);
|
|
153
|
+
const tests = view.scripts.filter((run) => run.mutated !== true);
|
|
154
|
+
if (tests.length > 0) {
|
|
155
|
+
const failing = tests.filter((run) => run.exit !== 0);
|
|
153
156
|
const signatures = new Map();
|
|
154
157
|
for (const run of failing)
|
|
155
158
|
if (run.signature !== undefined)
|
|
156
159
|
signatures.set(run.signature, (signatures.get(run.signature) ?? 0) + 1);
|
|
157
160
|
const most = Math.max(0, ...signatures.values());
|
|
158
|
-
const timedOut =
|
|
159
|
-
lines.push(` test runs: ${
|
|
161
|
+
const timedOut = tests.filter((run) => run.timedOut).length;
|
|
162
|
+
lines.push(` test runs: ${tests.length}, ${failing.length} failing` +
|
|
160
163
|
(most > 1 ? ` - ${most} of them the same way` : "") +
|
|
161
164
|
(timedOut > 0 ? `, ${timedOut} stopped at the time limit` : ""));
|
|
162
165
|
}
|
|
166
|
+
if (mutations.length > 0) {
|
|
167
|
+
const caught = mutations.filter((run) => run.exit !== 0 && !run.timedOut).length;
|
|
168
|
+
lines.push(` can_it_fail: ${mutations.length} run${mutations.length === 1 ? "" : "s"} on code broken on purpose, ${caught} caught by the tests`);
|
|
169
|
+
}
|
|
163
170
|
if (view.judge.size > 0)
|
|
164
171
|
lines.push(` the judge blamed: ${[...view.judge].map(([culprit, times]) => `${culprit} ${times}x`).join(", ")}`);
|
|
165
172
|
if (view.reviews.length > 0)
|
|
@@ -284,3 +291,55 @@ export function asJob(view) {
|
|
|
284
291
|
earlier: { run: view.run, ...(view.runMode !== undefined ? { runMode: view.runMode } : {}), lines: describePulseView(view).split("\n").slice(1).map((line) => line.replace(/^ /, "")) },
|
|
285
292
|
};
|
|
286
293
|
}
|
|
294
|
+
/** The envelope's fields, said apart from an event's own. */
|
|
295
|
+
const ENVELOPE = new Set(["v", "ts", "seq", "run", "job", "module", "agent", "kind"]);
|
|
296
|
+
/** One event in a line: when, its pulse and module, its kind, its own fields - the agent's role when it has one. */
|
|
297
|
+
export function eventLine(entry) {
|
|
298
|
+
const fields = Object.entries(entry)
|
|
299
|
+
.filter(([key]) => !ENVELOPE.has(key))
|
|
300
|
+
.map(([key, value]) => {
|
|
301
|
+
const text = typeof value === "string" ? JSON.stringify(value.length > 160 ? `${value.slice(0, 159)}…` : value) : JSON.stringify(value);
|
|
302
|
+
return `${key}=${text}`;
|
|
303
|
+
});
|
|
304
|
+
const where = [entry.job, entry.module, entry.agent?.role ?? entry.agent?.label].filter((part) => part !== undefined);
|
|
305
|
+
return [entry.ts.slice(0, 19).replace("T", " "), ...where, entry.kind, ...fields].join(" · ");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The events of these projects' ledgers that `query` asks for: how many of each kind, and the most recent, a line each -
|
|
309
|
+
* or, when no kind asked for is in the ledger, the kinds it has.
|
|
310
|
+
*/
|
|
311
|
+
export function describeEvents(projects, query = {}) {
|
|
312
|
+
const all = projects.flatMap((project) => readLedger(project, query.since !== undefined ? { since: query.since } : {}));
|
|
313
|
+
const inModule = query.module !== undefined ? all.filter((entry) => entry.module === query.module) : all;
|
|
314
|
+
const wanted = query.kinds?.map((kind) => kind.trim()).filter((kind) => kind !== "");
|
|
315
|
+
const matches = (kind) => wanted === undefined || wanted.length === 0 || wanted.some((asked) => kind === asked || kind.startsWith(`${asked}.`));
|
|
316
|
+
const found = inModule.filter((entry) => matches(entry.kind));
|
|
317
|
+
const scope = [query.module !== undefined ? `in ${query.module}` : "", query.since !== undefined ? `since ${query.since.toISOString().slice(0, 16).replace("T", " ")}` : ""].filter(Boolean).join(", ");
|
|
318
|
+
if (found.length === 0) {
|
|
319
|
+
const kinds = [...new Set(inModule.map((entry) => entry.kind))].sort();
|
|
320
|
+
return `No ${wanted !== undefined && wanted.length > 0 ? wanted.join(", ") : ""} events${scope !== "" ? ` ${scope}` : ""} in the ledger.${kinds.length > 0 ? ` It has: ${kinds.join(", ")}.` : ""}`;
|
|
321
|
+
}
|
|
322
|
+
const counts = new Map();
|
|
323
|
+
for (const entry of found)
|
|
324
|
+
counts.set(entry.kind, (counts.get(entry.kind) ?? 0) + 1);
|
|
325
|
+
const pulses = new Set(found.map((entry) => entry.job).filter((job) => job !== undefined)).size;
|
|
326
|
+
const runs = new Set(found.map((entry) => entry.run)).size;
|
|
327
|
+
const limit = Math.max(1, query.limit ?? 20);
|
|
328
|
+
const recent = found.slice(-limit);
|
|
329
|
+
return [
|
|
330
|
+
`${found.length} event${found.length === 1 ? "" : "s"}${scope !== "" ? ` ${scope}` : ""}, in ${runs} run${runs === 1 ? "" : "s"} and ${pulses} pulse${pulses === 1 ? "" : "s"}: ${[...counts].sort((a, b) => b[1] - a[1]).map(([kind, times]) => `${kind} ${times}`).join(", ")}`,
|
|
331
|
+
"",
|
|
332
|
+
recent.length === found.length ? "Each, oldest first:" : `The last ${recent.length}, oldest first:`,
|
|
333
|
+
...recent.map(eventLine),
|
|
334
|
+
].join("\n");
|
|
335
|
+
}
|
|
336
|
+
/** `24h`, `7d`, `30m`, or a date and time: the moment it means, or undefined when it means none. */
|
|
337
|
+
export function sinceOf(text, now = Date.now()) {
|
|
338
|
+
const relative = /^(\d+)\s*([mhd])$/i.exec(text.trim());
|
|
339
|
+
if (relative !== null) {
|
|
340
|
+
const unit = { m: 60_000, h: 3_600_000, d: 86_400_000 }[relative[2].toLowerCase()];
|
|
341
|
+
return new Date(now - Number(relative[1]) * unit);
|
|
342
|
+
}
|
|
343
|
+
const date = new Date(text.trim());
|
|
344
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
345
|
+
}
|
|
@@ -378,6 +378,8 @@ async function runScript(cwd, script, args, logDir, options = {}) {
|
|
|
378
378
|
ms: run.elapsedMs,
|
|
379
379
|
timedOut: run.timedOut === true,
|
|
380
380
|
inPlace: options.named === undefined,
|
|
381
|
+
// On code changed for the run - can_it_fail's bug: failing is what it is for.
|
|
382
|
+
...(options.mutated === true ? { mutated: true } : {}),
|
|
381
383
|
...(run.exitCode !== 0 ? { signature: signatureHash(failureSignature(run)) } : {}),
|
|
382
384
|
});
|
|
383
385
|
return run;
|
|
@@ -417,10 +419,10 @@ function quote(path) {
|
|
|
417
419
|
return `'${path.replace(/'/g, "'\\''")}'`;
|
|
418
420
|
}
|
|
419
421
|
/** A testing script run on a fresh copy of the code: the project's script copied fresh into the staging folder, beside it. */
|
|
420
|
-
async function runOnCopy(stagingRoot, script, args, logDir, limitMs) {
|
|
422
|
+
async function runOnCopy(stagingRoot, script, args, logDir, limitMs, mutated) {
|
|
421
423
|
const copy = join(stagingRoot, basename(script));
|
|
422
424
|
writeFileSync(copy, readFileSync(script));
|
|
423
|
-
return runScript(stagingRoot, script, args, logDir, { named: copy, ...(limitMs !== undefined ? { limitMs } : {}) });
|
|
425
|
+
return runScript(stagingRoot, script, args, logDir, { named: copy, ...(limitMs !== undefined ? { limitMs } : {}), ...(mutated === true ? { mutated } : {}) });
|
|
424
426
|
}
|
|
425
427
|
/**
|
|
426
428
|
* The harness's run of the unit testing script on the code as it is now - and run_unit_tests'.
|
|
@@ -486,7 +488,7 @@ limitMs) {
|
|
|
486
488
|
return run;
|
|
487
489
|
};
|
|
488
490
|
if (project.inPlace) {
|
|
489
|
-
const inPlace = () => runScript(project.root, script, [resolve(buildDir)], buildDir, limitMs !== undefined ? { limitMs } : {});
|
|
491
|
+
const inPlace = () => runScript(project.root, script, [resolve(buildDir)], buildDir, { ...(limitMs !== undefined ? { limitMs } : {}), ...(change !== undefined ? { mutated: true } : {}) });
|
|
490
492
|
const started = Date.now();
|
|
491
493
|
if (change === undefined)
|
|
492
494
|
return passed(await inPlace(), started);
|
|
@@ -501,7 +503,7 @@ limitMs) {
|
|
|
501
503
|
if (change !== undefined)
|
|
502
504
|
writeFileSync(join(staging.build, change.file), change.text);
|
|
503
505
|
const started = Date.now();
|
|
504
|
-
return passed(await runOnCopy(staging.root, script, [staging.build], buildDir, limitMs), started);
|
|
506
|
+
return passed(await runOnCopy(staging.root, script, [staging.build], buildDir, limitMs, change !== undefined), started);
|
|
505
507
|
}
|
|
506
508
|
finally {
|
|
507
509
|
rmSync(staging.root, { recursive: true, force: true });
|
package/docs/development.md
CHANGED
|
@@ -57,7 +57,7 @@ The workflow signs in to npm with npm's trusted publishing (OIDC), not a token:
|
|
|
57
57
|
| `src/screen-padding.ts` | the blank row above and below the whole screen and the two columns either side, in both of pi's ways of drawing it |
|
|
58
58
|
| `src/regenerate/spec-map.ts`, `src/regenerate/spec-map-view.ts` | regen: each item's mark, from the records, the `.plain` files and the work loop's plan (`mapPlan`), and the map drawn |
|
|
59
59
|
| `src/regenerate/debug-log.ts` | each render agent's whole conversation, always streamed to `.turri/debug/` |
|
|
60
|
-
| `src/regenerate/ledger.ts`, `src/regenerate/ledger-view.ts`, `src/ledger-cli.ts` | the ledger: what turri did, as typed facts, in `.turri/ledger/` - `record()`, read back for `pulses`, `diagnose` and `turri ledger` - see [the ledger](using.md#the-ledger) |
|
|
60
|
+
| `src/regenerate/ledger.ts`, `src/regenerate/ledger-view.ts`, `src/ledger-tools.ts`, `src/ledger-cli.ts` | the ledger: what turri did, as typed facts, in `.turri/ledger/` - `record()`, read back for the `ledger` tool, `pulses`, `diagnose` and `turri ledger` - see [the ledger](using.md#the-ledger) |
|
|
61
61
|
| `genome/` | what turri knows and can do: `genome/skills/`, the skill library, and `genome/rules/`, the \*\*\*plain authoring rules |
|
|
62
62
|
|
|
63
63
|
## Known edges
|
package/docs/using.md
CHANGED
|
@@ -36,7 +36,8 @@ While a skill runs, a line above the input shows its phases, every one by name -
|
|
|
36
36
|
| `engine` | show or choose the model the code is regenerated on - see [below](#choosing-a-model) |
|
|
37
37
|
| `pulses` | the pulses - running, planned and finished - or one of them |
|
|
38
38
|
| `halt` | halt a pulse, a module's planned pulses, or what `activate` started |
|
|
39
|
-
| `diagnose` | why a pulse did not land, from its transcript: how it ended, the stages that went wrong, the last failing test run, the last review, the tool calls that failed |
|
|
39
|
+
| `diagnose` | why a pulse did not land, from its transcript: how it ended, the stages that went wrong, the last failing test run, the last review, the tool calls that failed - and what the [ledger](#the-ledger) recorded of it |
|
|
40
|
+
| `ledger` | what turri did, from the [ledger](#the-ledger), this session's and every earlier one's: a pulse as facts, by its id - its rounds, test runs, verdicts, healers, refusals and commits; events of some kinds counted and listed, for a module or since a time - `healer.failed`, or `healer` for the group; or the last runs, a line a pulse |
|
|
40
41
|
| `lineage` | a module's lineage: what each regeneration and conformance run changed in it, and what it was regenerated from |
|
|
41
42
|
| `cultivate` | run a gated skill, its steps and review gates enforced |
|
|
42
43
|
| `release`, `activate` | try what is regenerated: release it to `dist/<module>` and activate it on this machine, installing what it needs - see [deploy](deploy.md) |
|
|
@@ -182,7 +183,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
|
|
|
182
183
|
| pulses | `job.queued`, `job.started`, every `job.stage`, `job.retried`, `job.finished` - its status, whether it landed, how long it ran, what it cost, how it ended |
|
|
183
184
|
| the work loop | `loop.halted`, `loop.stopped` (a pulse the spec moved on from), `planner.failed`, `planner.fellBack` |
|
|
184
185
|
| the model | `model.call` (time and tokens), `model.turn`, `model.compaction`, `provider.retry`, `provider.stall`, `provider.error`, `turn.stopped` (a turn turri ended itself, and why) |
|
|
185
|
-
| tools and tests | `tool.call` (the tool, how long, whether it failed - never its arguments or result), `script.run` (the testing script, its exit, how long, whether the time limit stopped it, the failure's fingerprint) |
|
|
186
|
+
| tools and tests | `tool.call` (the tool, how long, whether it failed - never its arguments or result), `script.run` (the testing script, its exit, how long, whether the time limit stopped it, the failure's fingerprint, and `mutated` for a `can_it_fail` run on code broken on purpose) |
|
|
186
187
|
| the gates | `gate.round`, `review.verdict`, `arbiter.ruling`, `judge.verdict` |
|
|
187
188
|
| what turri refused | `guard.refused` (a repeated call, a call past its budget, a tool the turn does not have), `command.refused`, `audit.putBack` |
|
|
188
189
|
| the healers | `healer.started`, `healer.ended` with what it found, `healer.failed` and why - the debugger, the diagnosis of a failed pulse |
|
|
@@ -191,7 +192,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
|
|
|
191
192
|
|
|
192
193
|
Never in it: prompts, replies, thinking, tool arguments and results, file contents. What an event does carry - a stage, an error's first line - is masked as the logs are.
|
|
193
194
|
|
|
194
|
-
`turri ledger` prints the last runs, a line a pulse; `turri ledger <pulse>` one pulse - its rounds, its test runs and how many failed the same way, the verdicts, the healers, what turri refused, what its history recorded, its log; `--json` the events themselves, for `jq`. In the chat, `pulses` lists the last three earlier runs under this session's pulses,
|
|
195
|
+
`turri ledger` prints the last runs, a line a pulse; `turri ledger <pulse>` one pulse - its rounds, its test runs and how many failed the same way, the verdicts, the healers, what turri refused, what its history recorded, its log; `--json` the events themselves, for `jq`. In the chat, `ledger` is the way in - a pulse of this session or an earlier one, the events of some kinds counted (how often the debugger failed this week: `kinds: [healer.failed], since: 7d`), the last runs - and `pulses` lists the last three earlier runs under this session's pulses, `pulses` and `diagnose` tell one of them by its id, and `diagnose` adds the ledger's facts to a pulse of this session; the side panel's pulses list has them as **earlier sessions** (see [the side panel](#in-a-conversation)).
|
|
195
196
|
|
|
196
197
|
It is for looking up, never for deciding: what to build comes from the records alone (see [what the code is built from](regenerating.md#what-the-code-is-built-from)), so deleting `.turri/ledger/` loses nothing but what can be looked up. When a run first writes to the project, the files of runs that have ended are gzipped and the oldest pruned past `ledger-keep:` megabytes - 100 unset, `0` for none at all; a run still going is never touched. `.turri` ignores itself, so the ledger is never committed, and nothing of it is sent anywhere. A ledger that cannot be written is said once and given up on, never failing the work.
|
|
197
198
|
|