worktrust 0.8.8 → 0.8.9
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/README.md +1 -0
- package/package.json +1 -1
- package/preserve-lines.mjs +9 -2
- package/stretch-evidence.mjs +18 -4
- package/worktrust.mjs +1 -1
package/README.md
CHANGED
|
@@ -173,6 +173,7 @@ nothing it does not:
|
|
|
173
173
|
| `unconfirmed` | checks the stretch ran whose outcome no reader could see (Codex's code mode prints no exit code; a background terminal), per kind: counted, never as a pass (0.8.5) |
|
|
174
174
|
| `autonomy`, `authorship` | the permission mode the AI app ran in, on one scale (ask, edits, plan, auto, full), how often it changed and the turns in plan mode; of the stretch's commits, how many name an AI as co-author (the trailer is read here, its names never leave) (0.8.7) |
|
|
175
175
|
| `framing`, `quality`, `risk`, `tool_mix`, `reads`, `context_files` | how the work was framed (your turns before the agent's first action, a plan first or not), checked (a check passing after the last change, the failures before a check passed, the change looked at before delivery; security scans count as checks), risked (destructive commands proposed, refused, run), which kinds of tools were used, files read again unchanged, and which of the AI app's own context files (instructions, skills, agents, commands, settings, MCP) the commits changed; kinds and counts only (0.8.8) |
|
|
176
|
+
| `quality.change_blocks`, `checked_blocks`, `rework_cycles`, `stage` | how many change blocks (edits with no check between them) a check followed, how often a failed check was followed by another change before it ran again (rework), and the furthest step the stretch reached here: attempted, verified, committed, pushed, pr, deployed; counts and one word only (0.8.9) |
|
|
176
177
|
| `collector_version` | the CLI version that measured it |
|
|
177
178
|
| `seq`, `prev`, `hash` | the line's place, the hash of the line before it, and its own hash |
|
|
178
179
|
|
package/package.json
CHANGED
package/preserve-lines.mjs
CHANGED
|
@@ -105,7 +105,12 @@ export function archiveLine(entry, behaviour = null, { ids = {}, collector, salt
|
|
|
105
105
|
// PRACTICE (0.8.8): framing, verification quality, risk, tool mix, re-reads, context files; whole counts and flags only.
|
|
106
106
|
const bool = (value) => value === true || value === false;
|
|
107
107
|
if (entry.framing && whole(entry.framing.turns_before_action) && bool(entry.framing.planned_first)) line.framing = { turns_before_action: entry.framing.turns_before_action, planned_first: entry.framing.planned_first };
|
|
108
|
-
if (entry.quality && whole(entry.quality.fix_cycles_max) && (bool(entry.quality.checked_after_change) || entry.quality.checked_after_change === null) && (bool(entry.quality.inspected_first) || entry.quality.inspected_first === null))
|
|
108
|
+
if (entry.quality && whole(entry.quality.fix_cycles_max) && (bool(entry.quality.checked_after_change) || entry.quality.checked_after_change === null) && (bool(entry.quality.inspected_first) || entry.quality.inspected_first === null)) {
|
|
109
|
+
line.quality = { checked_after_change: entry.quality.checked_after_change, fix_cycles_max: entry.quality.fix_cycles_max, inspected_first: entry.quality.inspected_first };
|
|
110
|
+
// 0.8.9: change blocks, how many a check followed, rework cycles.
|
|
111
|
+
if (whole(entry.quality.change_blocks) && whole(entry.quality.checked_blocks) && whole(entry.quality.rework_cycles) && entry.quality.checked_blocks <= entry.quality.change_blocks) Object.assign(line.quality, { change_blocks: entry.quality.change_blocks, checked_blocks: entry.quality.checked_blocks, rework_cycles: entry.quality.rework_cycles });
|
|
112
|
+
}
|
|
113
|
+
if (STAGES.includes(entry.stage)) line.stage = entry.stage;
|
|
109
114
|
const risk = pick(entry.risk, ["proposed", "refused", "run"]);
|
|
110
115
|
if (risk && risk.proposed > 0 && risk.refused + risk.run <= risk.proposed) line.risk = risk;
|
|
111
116
|
const keyed = (record, keys) => { const kept = Object.entries(record && typeof record === "object" ? record : {}).filter(([key, n]) => keys.includes(key) && whole(n) && n > 0).sort(); return kept.length ? Object.fromEntries(kept) : null; };
|
|
@@ -162,7 +167,9 @@ export function archiveEntries(dir, ownKey) {
|
|
|
162
167
|
* a line of their own that names the stretch it adds to (`supplements`) and carries only what no earlier line of that
|
|
163
168
|
* stretch carries, in the same chain, under the same day's root. Null when there is nothing new.
|
|
164
169
|
*/
|
|
165
|
-
|
|
170
|
+
/** The furthest step a stretch reached on the computer (0.8.9). */
|
|
171
|
+
const STAGES = ["attempted", "verified", "committed", "pushed", "pr", "deployed"];
|
|
172
|
+
export const DERIVED_KEYS = ["signals", "analyzer_version", "verification", "unconfirmed", "delivery", "recovery", "delegation", "context", "routing", "steering", "tools", "complexity", "oversight", "planning", "changes", "autonomy", "authorship", "framing", "quality", "risk", "tool_mix", "reads", "context_files", "stage", "interrupts", "steers", "utc_offset"];
|
|
166
173
|
export function supplementFor(line, earlier) {
|
|
167
174
|
const missing = DERIVED_KEYS.filter((key) => line[key] !== undefined && !earlier.some((old) => old[key] !== undefined));
|
|
168
175
|
if (missing.length === 0) return null;
|
package/stretch-evidence.mjs
CHANGED
|
@@ -268,6 +268,10 @@ function autonomyOf(messages) {
|
|
|
268
268
|
* (plan mode, a plan approved, a to-do list) came before it.
|
|
269
269
|
* quality: whether a check passed AFTER the stretch's last change; the most failures one check family took before it
|
|
270
270
|
* passed; whether the change was looked at (git diff, show, status) before the first delivery step.
|
|
271
|
+
* 0.8.9: change BLOCKS (edits with no check between them) and how many a check followed before the next one;
|
|
272
|
+
* REWORK cycles (a check failed, then a change before that check ran again: change, fail, change).
|
|
273
|
+
* stage: the furthest step the stretch reached on this computer: attempted (it changed something), verified (a check
|
|
274
|
+
* passed), committed, pushed, pr, deployed (0.8.9). Merged and confirmed are the server's, from GitHub.
|
|
271
275
|
* risk: destructive commands proposed, refused (by the person or a guard) and run.
|
|
272
276
|
* tool_mix: calls per kind of tool (search, read, edit, shell, agent, web, data, mcp, plan).
|
|
273
277
|
* reads: files read again unchanged (the same read twice), the cost of context that did not hold.
|
|
@@ -282,6 +286,8 @@ function practiceOf(messages, { isHumanTurn }) {
|
|
|
282
286
|
let turns = 0, acted = false, planned = false, turnsBefore = 0, plannedFirst = false;
|
|
283
287
|
let lastChange = -1, passedAfter = false, delivered = false, inspectedFirst = false;
|
|
284
288
|
const failuresBefore = new Map(); let cyclesMax = 0;
|
|
289
|
+
let blocks = 0, checkedBlocks = 0, inBlock = false, rework = 0, passedAny = false;
|
|
290
|
+
const pendingFail = new Set(), reached = new Set();
|
|
285
291
|
const risk = { proposed: 0, refused: 0, run: 0 }, mix = {}, reads = new Map();
|
|
286
292
|
let index = 0;
|
|
287
293
|
for (const message of messages) {
|
|
@@ -294,15 +300,21 @@ function practiceOf(messages, { isHumanTurn }) {
|
|
|
294
300
|
mix[toolKind(tool)] = (mix[toolKind(tool)] ?? 0) + 1;
|
|
295
301
|
if (PLAN_TOOLS.has(tool) && tool !== "AskUserQuestion") planned = true;
|
|
296
302
|
if (!acted && ACTION_TOOLS.has(tool)) { acted = true; turnsBefore = turns; plannedFirst = planned; }
|
|
297
|
-
if (EDIT_TOOLS.has(tool)) {
|
|
303
|
+
if (EDIT_TOOLS.has(tool)) {
|
|
304
|
+
lastChange = index; passedAfter = false;
|
|
305
|
+
if (!inBlock) { blocks += 1; inBlock = true; }
|
|
306
|
+
rework += pendingFail.size; pendingFail.clear();
|
|
307
|
+
}
|
|
298
308
|
if (tool === "Read") reads.set(call.digest, (reads.get(call.digest) ?? 0) + 1);
|
|
299
309
|
const checks = call.kinds.filter((kind) => CHECK_KINDS.includes(kind));
|
|
310
|
+
if (checks.length > 0 && inBlock) { checkedBlocks += 1; inBlock = false; }
|
|
300
311
|
for (const kind of checks) {
|
|
301
|
-
if (result?.failed === true) failuresBefore.set(kind, (failuresBefore.get(kind) ?? 0) + 1);
|
|
312
|
+
if (result?.failed === true) { failuresBefore.set(kind, (failuresBefore.get(kind) ?? 0) + 1); pendingFail.add(kind); }
|
|
313
|
+
if (result?.failed === false) { passedAny = true; pendingFail.delete(kind); }
|
|
302
314
|
if (result?.failed === false) { cyclesMax = Math.max(cyclesMax, failuresBefore.get(kind) ?? 0); failuresBefore.set(kind, 0); if (index > lastChange) passedAfter = true; }
|
|
303
315
|
}
|
|
304
316
|
if (call.kinds.includes("inspect") && !delivered) inspectedFirst = true;
|
|
305
|
-
if (call.kinds.some((kind) => DELIVERY_KINDS.includes(kind)) && result?.failed === false) delivered = true;
|
|
317
|
+
if (call.kinds.some((kind) => DELIVERY_KINDS.includes(kind)) && result?.failed === false) { delivered = true; for (const kind of call.kinds) if (DELIVERY_KINDS.includes(kind)) reached.add(kind); }
|
|
306
318
|
if (call.kinds.includes("destructive")) {
|
|
307
319
|
risk.proposed += 1;
|
|
308
320
|
if (result?.refusal) risk.refused += 1; else if (result?.failed === false) risk.run += 1;
|
|
@@ -311,9 +323,11 @@ function practiceOf(messages, { isHumanTurn }) {
|
|
|
311
323
|
}
|
|
312
324
|
if (index === 0) return null;
|
|
313
325
|
const repeated = [...reads.values()].reduce((sum, n) => sum + Math.max(0, n - 1), 0);
|
|
326
|
+
const stage = ["deploy", "pr", "push", "commit"].find((kind) => reached.has(kind)) ?? (lastChange > 0 ? (passedAny ? "verified" : "attempted") : null);
|
|
314
327
|
return {
|
|
315
328
|
framing: { turns_before_action: acted ? turnsBefore : turns, planned_first: acted ? plannedFirst : planned },
|
|
316
|
-
quality: { checked_after_change: lastChange > 0 ? passedAfter : null, fix_cycles_max: cyclesMax, inspected_first: delivered ? inspectedFirst : null },
|
|
329
|
+
quality: { checked_after_change: lastChange > 0 ? passedAfter : null, fix_cycles_max: cyclesMax, inspected_first: delivered ? inspectedFirst : null, change_blocks: blocks, checked_blocks: checkedBlocks, rework_cycles: rework },
|
|
330
|
+
...(stage ? { stage: { deploy: "deployed", pr: "pr", push: "pushed", commit: "committed" }[stage] ?? stage } : {}),
|
|
317
331
|
...(risk.proposed > 0 ? { risk } : {}),
|
|
318
332
|
tool_mix: mix,
|
|
319
333
|
reads: { repeated },
|
package/worktrust.mjs
CHANGED
|
@@ -73,7 +73,7 @@ const command = args.find((arg, at) => !arg.startsWith("--") && !(at > 0 && VALU
|
|
|
73
73
|
const flag = (name) => { const at = args.indexOf(`--${name}`); return at >= 0 ? args[at + 1] : undefined; };
|
|
74
74
|
const has = (name) => args.includes(`--${name}`);
|
|
75
75
|
/** This CLI's version, said to the door so the app can tell which computer runs an old one (check-cli-package holds it equal to package.json). */
|
|
76
|
-
const CLI_VERSION = "0.8.
|
|
76
|
+
const CLI_VERSION = "0.8.9";
|
|
77
77
|
const ORIGIN = (flag("origin") ?? process.env.WORKTRUST_ORIGIN ?? "https://app.worktrust.io").replace(/\/$/, "");
|
|
78
78
|
const MCP = flag("url") ?? process.env.WORKTRUST_MCP_URL ?? `${ORIGIN}/api/mcp`;
|
|
79
79
|
const HOME_DIR = join(homedir(), ".worktrust");
|