tldr-experts 0.3.0 → 0.3.1
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 +994 -0
- package/README.md +26 -7
- package/dist/hooks/answer-capture.js +20 -11
- package/dist/hooks/budget-gate.js +28 -8
- package/dist/hooks/chunk-0z27twdk.js +159 -0
- package/dist/hooks/{chunk-0bt6yb2q.js → chunk-39zh2e44.js} +10 -1
- package/dist/hooks/{chunk-1zwcxd3f.js → chunk-3g61yg59.js} +1 -1
- package/dist/hooks/chunk-3t91gvpp.js +193 -0
- package/dist/hooks/{chunk-kw4tffzf.js → chunk-458wgg9j.js} +30 -24
- package/dist/hooks/{chunk-j234zf0t.js → chunk-9kkm6q0t.js} +4 -165
- package/dist/hooks/{chunk-t8tdv11p.js → chunk-afamdvyn.js} +1 -1
- package/dist/hooks/{chunk-a8p2rc94.js → chunk-s5qsb4k6.js} +1 -1
- package/dist/hooks/{chunk-g395gk7e.js → chunk-sznsenee.js} +1 -1
- package/dist/hooks/{chunk-y0jdr3et.js → chunk-xpxe94qe.js} +23 -10
- package/dist/hooks/{chunk-azctppjh.js → chunk-ybacnpxd.js} +4 -1
- package/dist/hooks/{chunk-x98qs959.js → chunk-ytvmc5ns.js} +19 -22
- package/dist/hooks/chunk-zdxgragg.js +1655 -0
- package/dist/hooks/{chunk-p274ckxv.js → chunk-ztczwtj0.js} +6 -1
- package/dist/hooks/claim-sources.js +9 -9
- package/dist/hooks/dod-gate.js +12 -37
- package/dist/hooks/no-reask.js +9 -9
- package/dist/hooks/session-start.js +176 -422
- package/dist/hooks/statusline.js +14 -11
- package/dist/tldrx.js +12029 -6330
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/tldrx/SKILL.md +16 -3
- package/stages/build/stage.md +5 -0
- package/templates/budget.yml +5 -0
- package/dist/hooks/chunk-ae6bkfs5.js +0 -0
- package/dist/hooks/chunk-sdjnnmzz.js +0 -497
- package/dist/hooks/chunk-t56k6146.js +0 -14
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
hasSrcMarker,
|
|
3
3
|
parseSrcToken,
|
|
4
4
|
resolveSrc
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-sznsenee.js";
|
|
6
6
|
|
|
7
7
|
// src/core/text/handoff.ts
|
|
8
8
|
var HANDOFF_SECTIONS = ["Findings", "Decisions", "Unknowns", "Evidence ledger"];
|
|
@@ -64,56 +64,55 @@ function parseHandoff(text) {
|
|
|
64
64
|
function isHandoff(text) {
|
|
65
65
|
return missingSections(parseHandoff(text)).length === 0;
|
|
66
66
|
}
|
|
67
|
-
function missingSections(handoff) {
|
|
67
|
+
function missingSections(handoff, required = HANDOFF_SECTIONS) {
|
|
68
68
|
const present = handoff.headings;
|
|
69
69
|
const missing = [];
|
|
70
70
|
let cursor = 0;
|
|
71
|
-
for (const
|
|
72
|
-
const at = present.indexOf(
|
|
71
|
+
for (const name of required) {
|
|
72
|
+
const at = present.indexOf(name, cursor);
|
|
73
73
|
if (at === -1)
|
|
74
|
-
missing.push(
|
|
74
|
+
missing.push(name);
|
|
75
75
|
else
|
|
76
76
|
cursor = at + 1;
|
|
77
77
|
}
|
|
78
78
|
return missing;
|
|
79
79
|
}
|
|
80
80
|
var MAX_BULLETS = 200;
|
|
81
|
-
function
|
|
82
|
-
const handoff = parseHandoff(text);
|
|
83
|
-
const missing = missingSections(handoff);
|
|
81
|
+
function validateSections(handoff, required, ctx, lineOffset = 0) {
|
|
84
82
|
const emptySections = [];
|
|
85
83
|
const unsourced = [];
|
|
86
84
|
const malformed = [];
|
|
87
85
|
const unresolved = [];
|
|
88
86
|
const unverified = [];
|
|
89
87
|
let bulletCount = 0;
|
|
90
|
-
const
|
|
88
|
+
const wanted = new Set(required);
|
|
91
89
|
for (const section of handoff.sections) {
|
|
92
|
-
if (!
|
|
90
|
+
if (!wanted.has(section.name))
|
|
93
91
|
continue;
|
|
94
92
|
if (section.bullets.length === 0) {
|
|
95
|
-
emptySections.push({ name: section.name, line: section.headingLine });
|
|
93
|
+
emptySections.push({ name: section.name, line: section.headingLine + lineOffset });
|
|
96
94
|
}
|
|
97
95
|
for (const bullet of section.bullets) {
|
|
98
96
|
bulletCount++;
|
|
97
|
+
const line = bullet.line + lineOffset;
|
|
99
98
|
if (bullet.token === null) {
|
|
100
99
|
if (hasSrcMarker(bullet.text)) {
|
|
101
100
|
malformed.push({
|
|
102
|
-
line
|
|
101
|
+
line,
|
|
103
102
|
message: "malformed citation — the `[src: …]` token must be the last thing on the line " + "(closing quotes, brackets and a final `.` are allowed after it, words are not)"
|
|
104
103
|
});
|
|
105
104
|
} else {
|
|
106
|
-
unsourced.push(
|
|
105
|
+
unsourced.push(line);
|
|
107
106
|
}
|
|
108
107
|
continue;
|
|
109
108
|
}
|
|
110
109
|
for (const error of bullet.token.errors) {
|
|
111
|
-
unresolved.push({ line
|
|
110
|
+
unresolved.push({ line, message: `[src: ${error.raw}] — ${error.message}` });
|
|
112
111
|
}
|
|
113
112
|
const claim = bullet.text.replace(bullet.token.raw, " ").trim();
|
|
114
113
|
for (const ref of bullet.token.refs) {
|
|
115
114
|
const resolution = resolveSrc(ref, ctx, section.name, claim);
|
|
116
|
-
const issue = { line
|
|
115
|
+
const issue = { line, message: `[src: ${ref.raw}] — ${resolution.message ?? "unresolvable"}` };
|
|
117
116
|
if (resolution.outcome === "refused")
|
|
118
117
|
unresolved.push(issue);
|
|
119
118
|
else if (resolution.outcome === "unverified")
|
|
@@ -121,19 +120,26 @@ function validateHandoff(text, ctx) {
|
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
return { emptySections, unsourced, malformed, unresolved, unverified, bulletCount };
|
|
124
|
+
}
|
|
125
|
+
function validateHandoff(text, ctx) {
|
|
126
|
+
const handoff = parseHandoff(text);
|
|
127
|
+
const missing = missingSections(handoff);
|
|
128
|
+
const report = validateSections(handoff, HANDOFF_SECTIONS, ctx);
|
|
129
|
+
const unresolved = [...report.unresolved];
|
|
130
|
+
if (report.bulletCount > MAX_BULLETS) {
|
|
131
|
+
unresolved.push({ line: 0, message: `${report.bulletCount} bullets exceeds the ${MAX_BULLETS} cap` });
|
|
126
132
|
}
|
|
127
133
|
return {
|
|
128
|
-
ok: missing.length === 0 && emptySections.length === 0 && unsourced.length === 0 && malformed.length === 0 && unresolved.length === 0,
|
|
134
|
+
ok: missing.length === 0 && report.emptySections.length === 0 && report.unsourced.length === 0 && report.malformed.length === 0 && unresolved.length === 0,
|
|
129
135
|
missingSections: missing,
|
|
130
|
-
emptySections,
|
|
131
|
-
unsourced,
|
|
132
|
-
malformed,
|
|
136
|
+
emptySections: report.emptySections,
|
|
137
|
+
unsourced: report.unsourced,
|
|
138
|
+
malformed: report.malformed,
|
|
133
139
|
unresolved,
|
|
134
|
-
unverified,
|
|
135
|
-
bulletCount
|
|
140
|
+
unverified: report.unverified,
|
|
141
|
+
bulletCount: report.bulletCount
|
|
136
142
|
};
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
export { isHandoff, validateHandoff };
|
|
145
|
+
export { parseHandoff, isHandoff, validateHandoff };
|
|
@@ -1,170 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseSrcToken
|
|
3
|
-
} from "./chunk-g395gk7e.js";
|
|
4
1
|
import {
|
|
5
2
|
PROJECT_FRAMEWORK_DIR,
|
|
6
3
|
PROJECT_WORK_DIR
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
|
|
9
|
-
// src/core/text/questions.ts
|
|
10
|
-
var REQUIRED_METADATA_KEYS = ["id", "status", "area", "asked_by", "asked_at"];
|
|
11
|
-
var HEADING_RE = /^##\s+(Q\d{1,6})\s+·\s+(.+?)\s*$/;
|
|
12
|
-
var METADATA_RE = /^<!--\s*(.*?)\s*-->$/;
|
|
13
|
-
var WHY_RE = /^Why asked:\s*(.*)$/;
|
|
14
|
-
var OPTION_RE = /^-\s+([A-E])\)\s*(.*)$/;
|
|
15
|
-
var ANSWER_RE = /^\[Answer\]:[ \t]*(\S.*)$/;
|
|
16
|
-
var ANSWER_SLOT_RE = /^\[Answer\]:/;
|
|
17
|
-
var FOOTER_KEYS = ["answered_by", "answered_at", "fact"];
|
|
18
|
-
function parseQuestions(text) {
|
|
19
|
-
const trailingNewline = text.endsWith(`
|
|
20
|
-
`);
|
|
21
|
-
const body = trailingNewline ? text.slice(0, -1) : text;
|
|
22
|
-
const lines = body === "" ? [] : body.split(`
|
|
23
|
-
`);
|
|
24
|
-
const preamble = [];
|
|
25
|
-
const blocks = [];
|
|
26
|
-
let pending = null;
|
|
27
|
-
const close = () => {
|
|
28
|
-
if (pending === null)
|
|
29
|
-
return;
|
|
30
|
-
blocks.push(buildBlock(pending.start + 1, pending.lines));
|
|
31
|
-
pending = null;
|
|
32
|
-
};
|
|
33
|
-
for (let i = 0;i < lines.length; i++) {
|
|
34
|
-
const line = lines[i] ?? "";
|
|
35
|
-
if (HEADING_RE.test(line)) {
|
|
36
|
-
close();
|
|
37
|
-
pending = { start: i, lines: [line] };
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (pending === null)
|
|
41
|
-
preamble.push(line);
|
|
42
|
-
else
|
|
43
|
-
pending.lines.push(line);
|
|
44
|
-
}
|
|
45
|
-
close();
|
|
46
|
-
return { preamble, blocks, trailingNewline };
|
|
47
|
-
}
|
|
48
|
-
function buildBlock(startLine, lines) {
|
|
49
|
-
const heading = HEADING_RE.exec(lines[0] ?? "");
|
|
50
|
-
const id = heading?.[1] ?? "";
|
|
51
|
-
const title = heading?.[2] ?? "";
|
|
52
|
-
let metadata = null;
|
|
53
|
-
let metadataIndex = -1;
|
|
54
|
-
let whyAsked = null;
|
|
55
|
-
let whySrc = null;
|
|
56
|
-
const options = [];
|
|
57
|
-
let answer = "";
|
|
58
|
-
let answerIndex = -1;
|
|
59
|
-
let footer = null;
|
|
60
|
-
for (let i = 1;i < lines.length; i++) {
|
|
61
|
-
const line = lines[i] ?? "";
|
|
62
|
-
const comment = METADATA_RE.exec(line);
|
|
63
|
-
if (comment !== null && comment[1] !== undefined) {
|
|
64
|
-
const pairs = parsePipeComment(comment[1]);
|
|
65
|
-
if (metadata === null && pairs.some(([k]) => k === "id")) {
|
|
66
|
-
metadata = toMetadata(pairs);
|
|
67
|
-
metadataIndex = i;
|
|
68
|
-
} else if (footer === null && pairs.some(([k]) => k === "answered_by")) {
|
|
69
|
-
footer = toFooter(pairs);
|
|
70
|
-
}
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
const why = WHY_RE.exec(line);
|
|
74
|
-
if (why !== null && whyAsked === null) {
|
|
75
|
-
whyAsked = why[1] ?? "";
|
|
76
|
-
whySrc = parseSrcToken(line);
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const option = OPTION_RE.exec(line);
|
|
80
|
-
if (option !== null && option[1] !== undefined && option[2] !== undefined) {
|
|
81
|
-
options.push({ letter: option[1], text: option[2] });
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (ANSWER_SLOT_RE.test(line) && answerIndex === -1) {
|
|
85
|
-
answerIndex = i;
|
|
86
|
-
const captured = ANSWER_RE.exec(line);
|
|
87
|
-
answer = (captured?.[1] ?? "").trim();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
id,
|
|
92
|
-
title,
|
|
93
|
-
metadata,
|
|
94
|
-
metadataIndex,
|
|
95
|
-
whyAsked,
|
|
96
|
-
whySrc,
|
|
97
|
-
options,
|
|
98
|
-
answer,
|
|
99
|
-
answerIndex,
|
|
100
|
-
footer,
|
|
101
|
-
startLine,
|
|
102
|
-
lines: [...lines]
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
function parsePipeComment(inner) {
|
|
106
|
-
const pairs = [];
|
|
107
|
-
for (const part of inner.split("|")) {
|
|
108
|
-
const colon = part.indexOf(":");
|
|
109
|
-
if (colon === -1)
|
|
110
|
-
continue;
|
|
111
|
-
pairs.push([part.slice(0, colon).trim(), part.slice(colon + 1).trim()]);
|
|
112
|
-
}
|
|
113
|
-
return pairs;
|
|
114
|
-
}
|
|
115
|
-
function toMetadata(pairs) {
|
|
116
|
-
const map = new Map(pairs);
|
|
117
|
-
const extra = pairs.filter(([k]) => !REQUIRED_METADATA_KEYS.includes(k));
|
|
118
|
-
return {
|
|
119
|
-
id: map.get("id") ?? "",
|
|
120
|
-
status: map.get("status") ?? "",
|
|
121
|
-
area: map.get("area") ?? "",
|
|
122
|
-
asked_by: map.get("asked_by") ?? "",
|
|
123
|
-
asked_at: map.get("asked_at") ?? "",
|
|
124
|
-
extra
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function toFooter(pairs) {
|
|
128
|
-
const map = new Map(pairs);
|
|
129
|
-
return {
|
|
130
|
-
answered_by: map.get("answered_by") ?? "",
|
|
131
|
-
answered_at: map.get("answered_at") ?? "",
|
|
132
|
-
fact: map.get("fact") ?? ""
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function serializeQuestions(doc) {
|
|
136
|
-
const out = [...doc.preamble];
|
|
137
|
-
for (const block of doc.blocks)
|
|
138
|
-
out.push(...block.lines);
|
|
139
|
-
const text = out.join(`
|
|
140
|
-
`);
|
|
141
|
-
return doc.trailingNewline ? `${text}
|
|
142
|
-
` : text;
|
|
143
|
-
}
|
|
144
|
-
function detectAnswered(blocks) {
|
|
145
|
-
return blocks.filter((b) => b.metadata?.status === "open" && b.answer !== "");
|
|
146
|
-
}
|
|
147
|
-
function openBlocks(blocks) {
|
|
148
|
-
return blocks.filter((b) => b.metadata?.status === "open");
|
|
149
|
-
}
|
|
150
|
-
function recordAnswer(block, footer) {
|
|
151
|
-
const lines = [...block.lines];
|
|
152
|
-
if (block.metadataIndex >= 0) {
|
|
153
|
-
const line = lines[block.metadataIndex] ?? "";
|
|
154
|
-
lines[block.metadataIndex] = line.replace(/(\|\s*status:\s*)open(\s*\|)/, "$1answered$2");
|
|
155
|
-
}
|
|
156
|
-
const footerLine = `<!-- ${FOOTER_KEYS.map((k) => `${k}: ${footer[k]}`).join(" | ")} -->`;
|
|
157
|
-
const insertAt = block.answerIndex >= 0 ? block.answerIndex + 1 : lines.length;
|
|
158
|
-
lines.splice(insertAt, 0, footerLine);
|
|
159
|
-
const metadata = block.metadata === null ? null : { ...block.metadata, status: "answered" };
|
|
160
|
-
return { ...block, lines, metadata, footer };
|
|
161
|
-
}
|
|
162
|
-
function replaceBlock(doc, block) {
|
|
163
|
-
return {
|
|
164
|
-
...doc,
|
|
165
|
-
blocks: doc.blocks.map((b) => b.id === block.id ? block : b)
|
|
166
|
-
};
|
|
167
|
-
}
|
|
4
|
+
} from "./chunk-ztczwtj0.js";
|
|
168
5
|
|
|
169
6
|
// src/core/lock/workspaceLock.ts
|
|
170
7
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeSync, closeSync } from "node:fs";
|
|
@@ -315,6 +152,8 @@ function emitFact(fact, indent = " ") {
|
|
|
315
152
|
`${inner}supersedes: ${yamlScalar(fact.supersedes)}`,
|
|
316
153
|
`${inner}superseded_by: ${yamlScalar(fact.superseded_by)}`
|
|
317
154
|
];
|
|
155
|
+
if (fact.truncated === true)
|
|
156
|
+
lines.push(`${inner}truncated: true`);
|
|
318
157
|
if (fact.retired === null) {
|
|
319
158
|
lines.push(`${inner}retired: null`);
|
|
320
159
|
} else {
|
|
@@ -336,4 +175,4 @@ function emitFactsYaml(file, header) {
|
|
|
336
175
|
`;
|
|
337
176
|
}
|
|
338
177
|
|
|
339
|
-
export { readLock, isAlive, workspaceRootOfRunDir, workspaceRootOfFactsPath, withWorkspaceLock, yamlScalar, emitFactsYaml
|
|
178
|
+
export { readLock, isAlive, workspaceRootOfRunDir, workspaceRootOfFactsPath, withWorkspaceLock, yamlScalar, emitFactsYaml };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_ECONOMY,
|
|
2
3
|
asRunBudget,
|
|
3
4
|
asRunFile,
|
|
4
5
|
cursorStage,
|
|
5
6
|
derivePhaseStatus,
|
|
6
7
|
deriveRunStatus,
|
|
7
8
|
flatten,
|
|
9
|
+
isAttendedByHost,
|
|
8
10
|
isFinished,
|
|
9
11
|
isTerminal,
|
|
10
12
|
nowRfc3339,
|
|
@@ -12,26 +14,26 @@ import {
|
|
|
12
14
|
stageAt,
|
|
13
15
|
validateRunBudget,
|
|
14
16
|
validateRunFile
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-zdxgragg.js";
|
|
16
18
|
import {
|
|
17
19
|
EventLog
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-ybacnpxd.js";
|
|
19
21
|
import {
|
|
20
22
|
isAlive,
|
|
21
|
-
openBlocks,
|
|
22
|
-
parseQuestions,
|
|
23
23
|
readLock,
|
|
24
24
|
withWorkspaceLock,
|
|
25
25
|
workspaceRootOfRunDir,
|
|
26
26
|
yamlScalar
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-9kkm6q0t.js";
|
|
28
28
|
import {
|
|
29
|
-
noteDeprecations
|
|
30
|
-
|
|
29
|
+
noteDeprecations,
|
|
30
|
+
openBlocks,
|
|
31
|
+
parseQuestions
|
|
32
|
+
} from "./chunk-3t91gvpp.js";
|
|
31
33
|
import {
|
|
32
34
|
listRunDirs,
|
|
33
35
|
parseYaml
|
|
34
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ztczwtj0.js";
|
|
35
37
|
|
|
36
38
|
// src/core/statusline/runSnapshot.ts
|
|
37
39
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -52,8 +54,12 @@ function gatesPolicy(policy) {
|
|
|
52
54
|
const entries = Object.entries(policy).map(([id, value]) => `${yamlScalar(id)}: ${yamlScalar(value)}`);
|
|
53
55
|
return `{${entries.join(", ")}}`;
|
|
54
56
|
}
|
|
57
|
+
function gateEvidence(e) {
|
|
58
|
+
return `{path: ${yamlScalar(e.path)}, role: ${yamlScalar(e.role)}, verdict: ${yamlScalar(e.verdict)}, ` + `sampled: ${String(e.sampled)}, of: ${String(e.of)}, resolved: ${String(e.resolved)}, ` + `refuted: ${String(e.refuted)}, outside_surface: ${String(e.outside_surface)}}`;
|
|
59
|
+
}
|
|
55
60
|
function gate(g) {
|
|
56
|
-
|
|
61
|
+
const evidence = g.evidence === undefined ? "" : `, evidence: ${gateEvidence(g.evidence)}`;
|
|
62
|
+
return `{type: ${yamlScalar(g.type)}, status: ${yamlScalar(g.status)}, by: ${yamlScalar(g.by)}, ` + `at: ${yamlScalar(g.at)}, note: ${yamlScalar(g.note)}${evidence}}`;
|
|
57
63
|
}
|
|
58
64
|
function task(t, indent) {
|
|
59
65
|
const inner = `${indent} `;
|
|
@@ -120,6 +126,9 @@ function emitRunYaml(run) {
|
|
|
120
126
|
if (run.cancelled !== undefined) {
|
|
121
127
|
lines.push(`cancelled: {by: ${yamlScalar(run.cancelled.by)}, at: ${yamlScalar(run.cancelled.at)}, ` + `note: ${yamlScalar(run.cancelled.note)}}`);
|
|
122
128
|
}
|
|
129
|
+
if (run.attended_by !== undefined) {
|
|
130
|
+
lines.push(`attended_by: ${yamlScalar(run.attended_by)}`);
|
|
131
|
+
}
|
|
123
132
|
if (run.gates_policy !== undefined && Object.keys(run.gates_policy).length > 0) {
|
|
124
133
|
lines.push(`gates_policy: ${gatesPolicy(run.gates_policy)}`);
|
|
125
134
|
}
|
|
@@ -149,10 +158,12 @@ function emitBudgetYaml(budget) {
|
|
|
149
158
|
`per_agent_max_usd: ${money(budget.per_agent_max_usd)}`,
|
|
150
159
|
`warn_at_pct: ${budget.warn_at_pct}`,
|
|
151
160
|
`on_exceed: ${yamlScalar(budget.on_exceed)}`,
|
|
161
|
+
...budget.economy === DEFAULT_ECONOMY ? [] : [`economy: ${yamlScalar(budget.economy)}`],
|
|
152
162
|
"phases:"
|
|
153
163
|
];
|
|
154
164
|
for (const phase of budget.phases) {
|
|
155
|
-
|
|
165
|
+
const economy = phase.economy === null ? "" : `, economy: ${yamlScalar(phase.economy)}`;
|
|
166
|
+
lines.push(` - {id: ${yamlScalar(phase.id)}, ceiling_usd: ${money(phase.ceiling_usd)}, ` + `spent_usd: ${money(phase.spent_usd)}${economy}}`);
|
|
156
167
|
}
|
|
157
168
|
return `${lines.join(`
|
|
158
169
|
`)}
|
|
@@ -418,6 +429,7 @@ function fromStore(root) {
|
|
|
418
429
|
openCount: open.length,
|
|
419
430
|
autoGates: stages.filter((s) => s.gate.status === "approved" && s.gate.by === AUTO).length,
|
|
420
431
|
staleStages: stages.filter((s) => s.stale === true).length,
|
|
432
|
+
attendedByHost: isAttendedByHost(run),
|
|
421
433
|
source: "run-store"
|
|
422
434
|
};
|
|
423
435
|
}
|
|
@@ -446,6 +458,7 @@ function fromTolerantRead(root) {
|
|
|
446
458
|
openCount: open.length,
|
|
447
459
|
autoGates: 0,
|
|
448
460
|
staleStages: 0,
|
|
461
|
+
attendedByHost: false,
|
|
449
462
|
source: "tolerant"
|
|
450
463
|
};
|
|
451
464
|
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
requireNumber,
|
|
6
6
|
requireString,
|
|
7
7
|
result
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-39zh2e44.js";
|
|
9
9
|
|
|
10
10
|
// src/core/events/EventLog.ts
|
|
11
11
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -17,6 +17,7 @@ var EVENT_TYPES = [
|
|
|
17
17
|
"run.closed",
|
|
18
18
|
"run.unlocked",
|
|
19
19
|
"run.cancelled",
|
|
20
|
+
"run.attended",
|
|
20
21
|
"phase.started",
|
|
21
22
|
"phase.done",
|
|
22
23
|
"stage.started",
|
|
@@ -33,6 +34,8 @@ var EVENT_TYPES = [
|
|
|
33
34
|
"gate.approved",
|
|
34
35
|
"gate.rejected",
|
|
35
36
|
"gate.revoked",
|
|
37
|
+
"story.reopened",
|
|
38
|
+
"story.base_fastforwarded",
|
|
36
39
|
"check.passed",
|
|
37
40
|
"check.failed",
|
|
38
41
|
"budget.warned",
|
|
@@ -2,10 +2,17 @@ import {
|
|
|
2
2
|
emitFactsYaml,
|
|
3
3
|
withWorkspaceLock,
|
|
4
4
|
workspaceRootOfFactsPath
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-9kkm6q0t.js";
|
|
6
6
|
import {
|
|
7
|
+
FACT_CONFIDENCES,
|
|
8
|
+
FACT_KINDS,
|
|
9
|
+
MAX_FACTS,
|
|
10
|
+
MAX_FACT_CHARS,
|
|
11
|
+
factNumber,
|
|
12
|
+
formatFactId,
|
|
13
|
+
isRetired,
|
|
7
14
|
noteDeprecations
|
|
8
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3t91gvpp.js";
|
|
9
16
|
import {
|
|
10
17
|
asDocument,
|
|
11
18
|
isRecord,
|
|
@@ -15,30 +22,15 @@ import {
|
|
|
15
22
|
requireString,
|
|
16
23
|
requireVersion,
|
|
17
24
|
result
|
|
18
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-39zh2e44.js";
|
|
19
26
|
import {
|
|
20
27
|
parseYaml
|
|
21
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ztczwtj0.js";
|
|
22
29
|
|
|
23
30
|
// src/core/facts/FactsStore.ts
|
|
24
31
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
25
32
|
import { dirname } from "node:path";
|
|
26
33
|
|
|
27
|
-
// src/core/facts/Fact.ts
|
|
28
|
-
var FACT_KINDS = ["answer", "observed", "derived"];
|
|
29
|
-
var FACT_CONFIDENCES = ["measured", "inferred", "stated"];
|
|
30
|
-
var MAX_FACTS = 5000;
|
|
31
|
-
var MAX_FACT_CHARS = 300;
|
|
32
|
-
function isRetired(fact) {
|
|
33
|
-
return fact.retired !== null && fact.retired.at !== null;
|
|
34
|
-
}
|
|
35
|
-
function factNumber(id) {
|
|
36
|
-
return Number.parseInt(id.slice(1), 10);
|
|
37
|
-
}
|
|
38
|
-
function formatFactId(n) {
|
|
39
|
-
return `F${String(n).padStart(3, "0")}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
34
|
// src/core/facts/validateFactsFile.ts
|
|
43
35
|
var ID_RE = /^F\d{3,6}$/;
|
|
44
36
|
var Q_RE = /^Q\d{1,6}$/;
|
|
@@ -94,6 +86,9 @@ function validateFactsFile(input) {
|
|
|
94
86
|
} else if (row.source !== undefined) {
|
|
95
87
|
issues.push({ path: `${path}.source`, message: "expected a mapping" });
|
|
96
88
|
}
|
|
89
|
+
if (row.truncated !== undefined && typeof row.truncated !== "boolean") {
|
|
90
|
+
issues.push({ path: `${path}.truncated`, message: "expected true, false or absent" });
|
|
91
|
+
}
|
|
97
92
|
const superseded = typeof row.superseded_by === "string";
|
|
98
93
|
const retired = isRecord(row.retired) && row.retired.at !== null && row.retired.at !== undefined;
|
|
99
94
|
if (superseded && retired) {
|
|
@@ -214,9 +209,10 @@ class FactsStore {
|
|
|
214
209
|
return formatFactId(highest + 1);
|
|
215
210
|
}
|
|
216
211
|
append(input) {
|
|
212
|
+
const cut = input.fact.length > MAX_FACT_CHARS;
|
|
217
213
|
const fact = {
|
|
218
214
|
id: this.nextId(),
|
|
219
|
-
fact:
|
|
215
|
+
fact: cut ? `${input.fact.slice(0, MAX_FACT_CHARS - 1)}…` : input.fact,
|
|
220
216
|
area: input.area,
|
|
221
217
|
repos: [...input.repos],
|
|
222
218
|
kind: input.kind,
|
|
@@ -224,7 +220,8 @@ class FactsStore {
|
|
|
224
220
|
source: { ...input.source },
|
|
225
221
|
supersedes: input.supersedes ?? null,
|
|
226
222
|
superseded_by: null,
|
|
227
|
-
retired: input.retired ?? null
|
|
223
|
+
retired: input.retired ?? null,
|
|
224
|
+
...cut || input.truncated === true ? { truncated: true } : {}
|
|
228
225
|
};
|
|
229
226
|
this.rows.push(fact);
|
|
230
227
|
return fact;
|
|
@@ -299,4 +296,4 @@ function writeFactsAtomic(path, text) {
|
|
|
299
296
|
}
|
|
300
297
|
}
|
|
301
298
|
|
|
302
|
-
export {
|
|
299
|
+
export { FactsStore };
|