truecourse 0.7.0-next.0 → 0.7.0-next.2
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 +23 -0
- package/cli.mjs +359 -144
- package/package.json +1 -1
- package/public/assets/index-B4R8yF4j.js +869 -0
- package/public/assets/index-D9ShQqXg.css +1 -0
- package/public/index.html +2 -2
- package/server.mjs +349 -156
- package/public/assets/index-Bgp-7NT8.css +0 -1
- package/public/assets/index-DFIvau_M.js +0 -868
package/cli.mjs
CHANGED
|
@@ -105218,7 +105218,7 @@ function parseBlockedOnCapabilities(reason) {
|
|
|
105218
105218
|
return [];
|
|
105219
105219
|
return m[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
105220
105220
|
}
|
|
105221
|
-
var GuardWrittenScenarioSchema, GuardCoverageGapKindSchema, GuardCoverageGapSchema, GuardBirthFindingSchema, GuardGenerateErrorSchema, GuardExtractionFailureSchema, GuardOrphanedSectionSchema, GuardRecipeReportSchema, GuardGenerateUsageSchema, GuardGenerateReportSchema;
|
|
105221
|
+
var GuardWrittenScenarioSchema, GuardCoverageGapKindSchema, GuardCoverageGapSchema, GuardBirthFindingSchema, GuardGenerateErrorSchema, GuardEntryPreflightSchema, GuardExtractionFailureSchema, GuardOrphanedSectionSchema, GuardRecipeReportSchema, GuardGenerateUsageSchema, GuardGenerateReportSchema;
|
|
105222
105222
|
var init_report = __esm({
|
|
105223
105223
|
"packages/shared/dist/guard/report.js"() {
|
|
105224
105224
|
"use strict";
|
|
@@ -105271,6 +105271,14 @@ var init_report = __esm({
|
|
|
105271
105271
|
anchor: external_exports.string(),
|
|
105272
105272
|
message: external_exports.string()
|
|
105273
105273
|
}).strict();
|
|
105274
|
+
GuardEntryPreflightSchema = external_exports.object({
|
|
105275
|
+
/** Display form of the entry argv, e.g. `node tools/cli/dist/index.js`. */
|
|
105276
|
+
entry: external_exports.string(),
|
|
105277
|
+
/** The recipe build command, for the rebuild hint. */
|
|
105278
|
+
buildCommand: external_exports.string(),
|
|
105279
|
+
/** The full, UNTRUNCATED startup output the dead entry produced. */
|
|
105280
|
+
stderr: external_exports.string()
|
|
105281
|
+
}).strict();
|
|
105274
105282
|
GuardExtractionFailureSchema = external_exports.object({
|
|
105275
105283
|
doc: external_exports.string(),
|
|
105276
105284
|
reason: external_exports.string()
|
|
@@ -105316,7 +105324,13 @@ var init_report = __esm({
|
|
|
105316
105324
|
*/
|
|
105317
105325
|
birthPassed: external_exports.number().int().nonnegative().optional(),
|
|
105318
105326
|
manifestPath: external_exports.string().optional(),
|
|
105319
|
-
usage: GuardGenerateUsageSchema.optional()
|
|
105327
|
+
usage: GuardGenerateUsageSchema.optional(),
|
|
105328
|
+
/**
|
|
105329
|
+
* Present ONLY when the built entry failed to start — the whole birth phase was
|
|
105330
|
+
* short-circuited, so every changed section stayed unsettled. Optional so older
|
|
105331
|
+
* reports (written before this field existed) keep parsing.
|
|
105332
|
+
*/
|
|
105333
|
+
entryPreflight: GuardEntryPreflightSchema.optional()
|
|
105320
105334
|
}).strict();
|
|
105321
105335
|
}
|
|
105322
105336
|
});
|
|
@@ -170270,7 +170284,7 @@ function readToolVersion() {
|
|
|
170270
170284
|
if (cachedVersion)
|
|
170271
170285
|
return cachedVersion;
|
|
170272
170286
|
if (true) {
|
|
170273
|
-
cachedVersion = "0.7.0-next.
|
|
170287
|
+
cachedVersion = "0.7.0-next.2";
|
|
170274
170288
|
return cachedVersion;
|
|
170275
170289
|
}
|
|
170276
170290
|
try {
|
|
@@ -196272,8 +196286,14 @@ var CorpusDocSchema = external_exports.object({
|
|
|
196272
196286
|
var OverlapSectionSchema = external_exports.object({
|
|
196273
196287
|
/** The doc this section lives in, by ref (one of the overlap's two docs). */
|
|
196274
196288
|
doc: DocRefSchema,
|
|
196275
|
-
/**
|
|
196276
|
-
|
|
196289
|
+
/**
|
|
196290
|
+
* The heading text of the conflicting section (verbatim from the doc), or
|
|
196291
|
+
* `null` when the conflicting passage sits in the doc's PREAMBLE — the block
|
|
196292
|
+
* before its first heading (README badges/tagline, intro line). A plain string
|
|
196293
|
+
* from older corpora still parses; `null` is the preamble marker the viewer
|
|
196294
|
+
* bands as the pre-first-heading block.
|
|
196295
|
+
*/
|
|
196296
|
+
heading: external_exports.string().nullable()
|
|
196277
196297
|
});
|
|
196278
196298
|
var OverlapSchema = external_exports.object({
|
|
196279
196299
|
/** The two docs that overlap, by ref. */
|
|
@@ -197210,7 +197230,9 @@ NOT a disagreement (do NOT flag):
|
|
|
197210
197230
|
|
|
197211
197231
|
Bias: when there is a PLAUSIBLE contradiction a human should check, flag it. When the docs are clearly complementary or agree, do not.
|
|
197212
197232
|
|
|
197213
|
-
When you flag an overlap, also identify the SECTIONS that conflict: the nearest markdown heading (the \`#\`/\`##\`/\`###\` title, verbatim, WITHOUT the leading #s) above the conflicting text in EACH doc. List one entry per side; omit a side only
|
|
197233
|
+
When you flag an overlap, also identify the SECTIONS that conflict: the nearest markdown heading (the \`#\`/\`##\`/\`###\` title, verbatim, WITHOUT the leading #s) above the conflicting text in EACH doc. List one entry per side; omit a side only when it genuinely has no conflicting passage.
|
|
197234
|
+
|
|
197235
|
+
PREAMBLE: when a doc's conflicting passage sits ABOVE its first \`#\` heading \u2014 the preamble block (a README's badges, tagline, or the intro line before any heading) \u2014 there is no heading to name. Set that side's \`heading\` to the JSON literal \`null\` (not the string "null", not ""). Use \`null\` ONLY for that pre-first-heading block; whenever the passage is under a heading, name the heading verbatim.
|
|
197214
197236
|
|
|
197215
197237
|
In the NOTE, refer to each doc by its FILENAME (the basename shown in the header, e.g. \`users.md\`) \u2014 NEVER "doc A" / "doc B", which mean nothing to the reader.
|
|
197216
197238
|
|
|
@@ -197220,6 +197242,12 @@ Output ONLY a JSON object, no prose, no code fences:
|
|
|
197220
197242
|
"note": "users.md uses auth0_id; identity.md uses auth0_sub for the same user column",
|
|
197221
197243
|
"sections": [ { "side": "A", "heading": "User model" }, { "side": "B", "heading": "Identity" } ] }
|
|
197222
197244
|
|
|
197245
|
+
Preamble example \u2014 doc A's claim is a README badge/tagline above any heading, so its side uses null:
|
|
197246
|
+
|
|
197247
|
+
{ "overlap": true,
|
|
197248
|
+
"note": "README.md lists C# as a supported language in the preamble; plan.md's Tech Stack omits it",
|
|
197249
|
+
"sections": [ { "side": "A", "heading": null }, { "side": "B", "heading": "Tech Stack" } ] }
|
|
197250
|
+
|
|
197223
197251
|
Use { "overlap": false, "note": "", "sections": [] } when they are complementary or agree. The note is shown to the user \u2014 name the specific thing that differs.`;
|
|
197224
197252
|
var OVERLAP_PREVIEW_LINES = 120;
|
|
197225
197253
|
function docBody2(doc) {
|
|
@@ -197271,12 +197299,12 @@ function buildOverlapUserPrompt(areaId, a, b) {
|
|
|
197271
197299
|
var LlmOverlapSchema = external_exports.object({
|
|
197272
197300
|
overlap: external_exports.boolean(),
|
|
197273
197301
|
note: external_exports.string().default(""),
|
|
197274
|
-
sections: external_exports.array(external_exports.object({ side: external_exports.enum(["A", "B"]), heading: external_exports.string() })).default([])
|
|
197302
|
+
sections: external_exports.array(external_exports.object({ side: external_exports.enum(["A", "B"]), heading: external_exports.string().nullable() })).default([])
|
|
197275
197303
|
});
|
|
197276
197304
|
var OverlapVerdictSchema = external_exports.object({
|
|
197277
197305
|
overlap: external_exports.boolean(),
|
|
197278
197306
|
note: external_exports.string().default(""),
|
|
197279
|
-
sections: external_exports.array(external_exports.object({ doc: external_exports.string(), heading: external_exports.string() })).default([])
|
|
197307
|
+
sections: external_exports.array(external_exports.object({ doc: external_exports.string(), heading: external_exports.string().nullable() })).default([])
|
|
197280
197308
|
});
|
|
197281
197309
|
function spawnOverlapRunner(opts = {}) {
|
|
197282
197310
|
const transport = opts.transport ?? cliTransport({ bin: opts.bin });
|
|
@@ -197466,35 +197494,49 @@ function prefilterDocs(docs, manualIncludes = []) {
|
|
|
197466
197494
|
skipped: [...reasons].map(([path79, reason]) => ({ path: path79, reason }))
|
|
197467
197495
|
};
|
|
197468
197496
|
}
|
|
197497
|
+
async function planRelevanceWork(repoRoot5, docs, manualIncludes = []) {
|
|
197498
|
+
const manualSet = new Set(manualIncludes);
|
|
197499
|
+
const { toClassify, skipped } = prefilterDocs(docs, manualIncludes);
|
|
197500
|
+
const needsCall = [];
|
|
197501
|
+
const known = /* @__PURE__ */ new Map();
|
|
197502
|
+
await Promise.all(toClassify.map(async (doc) => {
|
|
197503
|
+
if (manualSet.has(doc.path)) {
|
|
197504
|
+
known.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
|
|
197505
|
+
return;
|
|
197506
|
+
}
|
|
197507
|
+
const cached = await readCache4(repoRoot5, computeCacheKey5(doc));
|
|
197508
|
+
if (cached)
|
|
197509
|
+
known.set(doc.path, cached);
|
|
197510
|
+
else
|
|
197511
|
+
needsCall.push(doc);
|
|
197512
|
+
}));
|
|
197513
|
+
return { toClassify, prefilterSkipped: skipped, needsCall, known };
|
|
197514
|
+
}
|
|
197469
197515
|
async function filterByRelevance(repoRoot5, docs, opts = {}) {
|
|
197470
197516
|
if (opts.enabled === false || docs.length === 0) {
|
|
197471
197517
|
return { included: docs, skipped: [] };
|
|
197472
197518
|
}
|
|
197473
|
-
const manualSet = new Set(opts.manualIncludes ?? []);
|
|
197474
197519
|
const runner = opts.runner ?? spawnRelevanceRunner({ transport: opts.transport, model: opts.model, fallbackModel: opts.fallbackModel });
|
|
197475
197520
|
const concurrency = opts.concurrency ?? defaultConcurrency();
|
|
197476
|
-
const
|
|
197477
|
-
const prefilterReason = new Map(prefilterSkipped.map((s) => [s.path, s.reason]));
|
|
197521
|
+
const plan = await planRelevanceWork(repoRoot5, docs, opts.manualIncludes ?? []);
|
|
197522
|
+
const prefilterReason = new Map(plan.prefilterSkipped.map((s) => [s.path, s.reason]));
|
|
197478
197523
|
const total = docs.length;
|
|
197479
197524
|
let done = 0;
|
|
197480
197525
|
const markDone = () => opts.onProgress?.(++done, total);
|
|
197481
197526
|
opts.onProgress?.(0, total);
|
|
197482
|
-
|
|
197527
|
+
const resolvedUpfront = prefilterReason.size + plan.known.size;
|
|
197528
|
+
for (let i = 0; i < resolvedUpfront; i++)
|
|
197483
197529
|
markDone();
|
|
197484
|
-
const verdicts =
|
|
197530
|
+
const verdicts = new Map(plan.known);
|
|
197531
|
+
const pending = plan.needsCall;
|
|
197485
197532
|
let cursor = 0;
|
|
197486
197533
|
let active11 = 0;
|
|
197487
197534
|
await new Promise((resolve12) => {
|
|
197535
|
+
if (pending.length === 0)
|
|
197536
|
+
return resolve12();
|
|
197488
197537
|
const launch = () => {
|
|
197489
|
-
while (active11 < concurrency && cursor <
|
|
197490
|
-
const doc =
|
|
197491
|
-
if (manualSet.has(doc.path)) {
|
|
197492
|
-
verdicts.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
|
|
197493
|
-
markDone();
|
|
197494
|
-
if (cursor >= toClassify.length && active11 === 0)
|
|
197495
|
-
resolve12();
|
|
197496
|
-
continue;
|
|
197497
|
-
}
|
|
197538
|
+
while (active11 < concurrency && cursor < pending.length) {
|
|
197539
|
+
const doc = pending[cursor++];
|
|
197498
197540
|
active11++;
|
|
197499
197541
|
classifyOne(repoRoot5, doc, runner).then((verdict) => {
|
|
197500
197542
|
verdicts.set(doc.path, verdict);
|
|
@@ -197507,14 +197549,12 @@ async function filterByRelevance(repoRoot5, docs, opts = {}) {
|
|
|
197507
197549
|
}).finally(() => {
|
|
197508
197550
|
markDone();
|
|
197509
197551
|
active11--;
|
|
197510
|
-
if (cursor >=
|
|
197552
|
+
if (cursor >= pending.length && active11 === 0)
|
|
197511
197553
|
resolve12();
|
|
197512
197554
|
else
|
|
197513
197555
|
launch();
|
|
197514
197556
|
});
|
|
197515
197557
|
}
|
|
197516
|
-
if (cursor >= toClassify.length && active11 === 0)
|
|
197517
|
-
resolve12();
|
|
197518
197558
|
};
|
|
197519
197559
|
launch();
|
|
197520
197560
|
});
|
|
@@ -197628,7 +197668,7 @@ async function classifyOne(repoRoot5, doc, runner) {
|
|
|
197628
197668
|
await writeCache4(repoRoot5, cacheKey, verdict);
|
|
197629
197669
|
return verdict;
|
|
197630
197670
|
}
|
|
197631
|
-
var RELEVANCE_SYSTEM_PROMPT = `You are a documentation relevance classifier.
|
|
197671
|
+
var RELEVANCE_SYSTEM_PROMPT = `You are a documentation relevance classifier. Judge mainly by CONTENT \u2014 does this doc state durable, intended behavior or decisions about THE SYSTEM IN THIS REPOSITORY (its endpoints, data, auth, events, invariants, business rules, architecture)? The doc's PATH (given below) is evidence too, never an automatic verdict: weigh path and content together.
|
|
197632
197672
|
|
|
197633
197673
|
${OUTPUT_ONLY_GUARDRAIL}
|
|
197634
197674
|
|
|
@@ -197641,22 +197681,23 @@ INCLUDE (spec-source material):
|
|
|
197641
197681
|
SKIP (not spec-source material):
|
|
197642
197682
|
- Pure status / TODO checklists, kanban boards, release notes / changelog drafts
|
|
197643
197683
|
- Docs about a THIRD-PARTY / external system (vendor API research, integration notes) \u2014 that is someone else's contract, not ours, and cannot be verified against this codebase
|
|
197684
|
+
- TEST-DATA specs: a doc under a test / fixture / sample / example tree (PATH segments like tests/, fixtures/, __fixtures__/, examples/, sample-*) that describes a FICTIONAL or sample product used as test data for THIS repo's own tooling \u2014 that product is not this repository's system, so its "spec" is not ours. (The path is evidence, not proof: if the doc plainly describes THIS repository's real product or engineering, keep it.)
|
|
197644
197685
|
- SUPERSEDED docs \u2014 an older version of a newer doc covering the same subject
|
|
197645
197686
|
- Process / meta docs not about product behavior: contribution / onboarding guides, code-style guides, deployment runbooks (keep a deployment doc ONLY if it states our runtime contracts)
|
|
197646
197687
|
- Exploratory scratch with no committed decisions (brain dumps, open-questions-only notes)
|
|
197647
197688
|
- AI-agent instructions / prompt templates; personal engineering journals
|
|
197648
197689
|
|
|
197649
|
-
Distinguish "states a decision about our system" (INCLUDE) from "tracks status / describes an external
|
|
197690
|
+
Distinguish "states a decision about our system" (INCLUDE) from "tracks status / describes an external or test-data product / is superseded / is process" (SKIP). The SKIP categories above are explicit \u2014 they are not "doubt." WHEN GENUINELY AMBIGUOUS: include (dropping a real spec doc costs more than keeping noise).
|
|
197650
197691
|
|
|
197651
197692
|
Output ONLY a JSON object:
|
|
197652
197693
|
|
|
197653
197694
|
{ "include": true|false, "reason": "short explanation" }
|
|
197654
197695
|
|
|
197655
|
-
The reason is shown to the user in the dashboard \u2014 be specific ("vendor API research (ServiceTitan)", "superseded by capacity-ml-plan-v3", "deployment runbook, no product contracts") so they can verify the call.`;
|
|
197696
|
+
The reason is shown to the user in the dashboard \u2014 be specific ("vendor API research (ServiceTitan)", "superseded by capacity-ml-plan-v3", "deployment runbook, no product contracts", "test-data spec for a sample product (tests/fixtures/\u2026)") so they can verify the call.`;
|
|
197656
197697
|
function buildRelevanceUserPrompt(doc) {
|
|
197657
197698
|
const preview = doc.preview.split("\n").slice(0, 60).join("\n");
|
|
197658
197699
|
return [
|
|
197659
|
-
`
|
|
197700
|
+
`PATH (repo-relative): ${doc.path}`,
|
|
197660
197701
|
`Detected kind: ${doc.kind}`,
|
|
197661
197702
|
`Size: ${doc.size} bytes`,
|
|
197662
197703
|
"",
|
|
@@ -197710,9 +197751,6 @@ async function readCache4(scope, cacheKey) {
|
|
|
197710
197751
|
async function writeCache4(scope, cacheKey, verdict) {
|
|
197711
197752
|
await setCacheEntry(scope, CACHE_NAME5, cacheKey, verdict);
|
|
197712
197753
|
}
|
|
197713
|
-
async function readRelevanceCache(repoRoot5, doc) {
|
|
197714
|
-
return readCache4(repoRoot5, computeCacheKey5(doc));
|
|
197715
|
-
}
|
|
197716
197754
|
|
|
197717
197755
|
// packages/spec-consolidator/dist/curate.js
|
|
197718
197756
|
async function curate(repoRoot5, opts = {}) {
|
|
@@ -201820,6 +201858,9 @@ var STAGE_DEFAULTS = {
|
|
|
201820
201858
|
// Authoring an executable scenario faithful to a spec claim is the hard,
|
|
201821
201859
|
// load-bearing call (a weak scenario is false confidence) — opus.
|
|
201822
201860
|
"guard.generate": "opus",
|
|
201861
|
+
// The one evidence-retry per birth-failed claim — the same authoring task, so
|
|
201862
|
+
// the same tier; a distinct stage so retry spend is attributed to the birth line.
|
|
201863
|
+
"guard.retry": "opus",
|
|
201823
201864
|
// Proposing a build/entry recipe is a modest structured task — sonnet.
|
|
201824
201865
|
"guard.recipe": "sonnet",
|
|
201825
201866
|
"rules.violationGen": "opus"
|
|
@@ -205110,6 +205151,129 @@ function listSandboxFiles(dir) {
|
|
|
205110
205151
|
return out.sort();
|
|
205111
205152
|
}
|
|
205112
205153
|
|
|
205154
|
+
// packages/guard-runner/dist/executor.js
|
|
205155
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
205156
|
+
var DEFAULT_STEP_TIMEOUT_MS = 3e4;
|
|
205157
|
+
function executeStep(opts) {
|
|
205158
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
205159
|
+
const [command, ...args] = opts.argv;
|
|
205160
|
+
const start = Date.now();
|
|
205161
|
+
return new Promise((resolve12) => {
|
|
205162
|
+
const child = spawn9(command, args, {
|
|
205163
|
+
cwd: opts.cwd,
|
|
205164
|
+
env: opts.env,
|
|
205165
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
205166
|
+
});
|
|
205167
|
+
let stdout = "";
|
|
205168
|
+
let stderr = "";
|
|
205169
|
+
let timedOut = false;
|
|
205170
|
+
let settled = false;
|
|
205171
|
+
const timer = setTimeout(() => {
|
|
205172
|
+
timedOut = true;
|
|
205173
|
+
child.kill("SIGKILL");
|
|
205174
|
+
}, timeoutMs);
|
|
205175
|
+
const finish = (capture) => {
|
|
205176
|
+
if (settled)
|
|
205177
|
+
return;
|
|
205178
|
+
settled = true;
|
|
205179
|
+
clearTimeout(timer);
|
|
205180
|
+
resolve12(capture);
|
|
205181
|
+
};
|
|
205182
|
+
child.stdout.on("data", (chunk3) => {
|
|
205183
|
+
stdout += chunk3.toString("utf-8");
|
|
205184
|
+
});
|
|
205185
|
+
child.stderr.on("data", (chunk3) => {
|
|
205186
|
+
stderr += chunk3.toString("utf-8");
|
|
205187
|
+
});
|
|
205188
|
+
child.on("error", (err) => {
|
|
205189
|
+
finish({
|
|
205190
|
+
exitCode: null,
|
|
205191
|
+
signal: null,
|
|
205192
|
+
stdout,
|
|
205193
|
+
stderr,
|
|
205194
|
+
timedOut,
|
|
205195
|
+
spawnError: err.message,
|
|
205196
|
+
durationMs: Date.now() - start
|
|
205197
|
+
});
|
|
205198
|
+
});
|
|
205199
|
+
child.on("close", (code, signal) => {
|
|
205200
|
+
finish({
|
|
205201
|
+
exitCode: code,
|
|
205202
|
+
signal,
|
|
205203
|
+
stdout,
|
|
205204
|
+
stderr,
|
|
205205
|
+
timedOut,
|
|
205206
|
+
durationMs: Date.now() - start
|
|
205207
|
+
});
|
|
205208
|
+
});
|
|
205209
|
+
if (opts.stdin !== void 0)
|
|
205210
|
+
child.stdin.write(opts.stdin);
|
|
205211
|
+
child.stdin.end();
|
|
205212
|
+
});
|
|
205213
|
+
}
|
|
205214
|
+
|
|
205215
|
+
// packages/guard-runner/dist/preflight.js
|
|
205216
|
+
var ENTRY_PROBE_ARGVS = [[], ["--help"]];
|
|
205217
|
+
var ENTRY_PREFLIGHT_TIMEOUT_MS = 2e4;
|
|
205218
|
+
var defaultEntryProbeExecutor = async (fullArgv, recipeEnv, timeoutMs) => {
|
|
205219
|
+
const sandbox = createSandbox({ recipeEnv });
|
|
205220
|
+
try {
|
|
205221
|
+
return await executeStep({ argv: fullArgv, cwd: sandbox.cwd, env: sandbox.env, timeoutMs });
|
|
205222
|
+
} finally {
|
|
205223
|
+
sandbox.cleanup();
|
|
205224
|
+
}
|
|
205225
|
+
};
|
|
205226
|
+
async function preflightEntry(opts) {
|
|
205227
|
+
const exec3 = opts.exec ?? defaultEntryProbeExecutor;
|
|
205228
|
+
const timeoutMs = opts.timeoutMs ?? ENTRY_PREFLIGHT_TIMEOUT_MS;
|
|
205229
|
+
const entry = opts.displayEntry.join(" ");
|
|
205230
|
+
const probes = await Promise.all(ENTRY_PROBE_ARGVS.map(async (argv) => ({
|
|
205231
|
+
argv: [...argv],
|
|
205232
|
+
capture: await exec3([...opts.resolvedEntry, ...argv], opts.recipeEnv, timeoutMs)
|
|
205233
|
+
})));
|
|
205234
|
+
const ok = entryStarts(probes);
|
|
205235
|
+
return { ok, entry, stderr: ok ? "" : startupOutput(probes[0].capture, timeoutMs), probes };
|
|
205236
|
+
}
|
|
205237
|
+
function entryStarts(probes) {
|
|
205238
|
+
if (probes.some((p2) => startedCleanly(p2.capture)))
|
|
205239
|
+
return true;
|
|
205240
|
+
const shapes = probes.map((p2) => failureShape(p2.capture));
|
|
205241
|
+
return shapes.some((s) => s !== shapes[0]);
|
|
205242
|
+
}
|
|
205243
|
+
function startedCleanly(c2) {
|
|
205244
|
+
return !c2.spawnError && !c2.timedOut && c2.signal === null && c2.exitCode === 0;
|
|
205245
|
+
}
|
|
205246
|
+
function failureShape(c2) {
|
|
205247
|
+
return JSON.stringify({
|
|
205248
|
+
spawnError: c2.spawnError ?? null,
|
|
205249
|
+
signal: c2.signal ?? null,
|
|
205250
|
+
timedOut: c2.timedOut,
|
|
205251
|
+
exitCode: c2.exitCode,
|
|
205252
|
+
stdout: c2.stdout,
|
|
205253
|
+
stderr: c2.stderr
|
|
205254
|
+
});
|
|
205255
|
+
}
|
|
205256
|
+
function startupOutput(c2, timeoutMs) {
|
|
205257
|
+
const parts = [];
|
|
205258
|
+
const primary = c2.stderr || c2.stdout;
|
|
205259
|
+
if (primary)
|
|
205260
|
+
parts.push(primary.trimEnd());
|
|
205261
|
+
if (c2.spawnError)
|
|
205262
|
+
parts.push(`failed to spawn: ${c2.spawnError}`);
|
|
205263
|
+
if (c2.timedOut)
|
|
205264
|
+
parts.push(`(the entry produced no output within ${timeoutMs}ms and was killed)`);
|
|
205265
|
+
if (parts.length === 0) {
|
|
205266
|
+
parts.push(c2.exitCode !== null ? `(the entry exited ${c2.exitCode} with no output)` : `(the entry was killed by ${c2.signal})`);
|
|
205267
|
+
}
|
|
205268
|
+
return parts.join("\n");
|
|
205269
|
+
}
|
|
205270
|
+
function entryPreflightHeadline(entry, buildCommand) {
|
|
205271
|
+
return `The recipe entry \`${entry}\` failed to start \u2014 it crashes before it runs, so every scenario would fail identically. Rebuild it with \`${buildCommand}\` (its build output is likely stale or incomplete), then retry.`;
|
|
205272
|
+
}
|
|
205273
|
+
function formatEntryPreflightError(opts) {
|
|
205274
|
+
return [entryPreflightHeadline(opts.entry, opts.buildCommand), "", "Startup output:", opts.stderr].join("\n");
|
|
205275
|
+
}
|
|
205276
|
+
|
|
205113
205277
|
// packages/guard-runner/dist/capabilities/git.js
|
|
205114
205278
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
205115
205279
|
import fs51 from "node:fs";
|
|
@@ -205190,67 +205354,6 @@ function applyCapabilities(setup, ctx) {
|
|
|
205190
205354
|
}
|
|
205191
205355
|
}
|
|
205192
205356
|
|
|
205193
|
-
// packages/guard-runner/dist/executor.js
|
|
205194
|
-
import { spawn as spawn9 } from "node:child_process";
|
|
205195
|
-
var DEFAULT_STEP_TIMEOUT_MS = 3e4;
|
|
205196
|
-
function executeStep(opts) {
|
|
205197
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
205198
|
-
const [command, ...args] = opts.argv;
|
|
205199
|
-
const start = Date.now();
|
|
205200
|
-
return new Promise((resolve12) => {
|
|
205201
|
-
const child = spawn9(command, args, {
|
|
205202
|
-
cwd: opts.cwd,
|
|
205203
|
-
env: opts.env,
|
|
205204
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
205205
|
-
});
|
|
205206
|
-
let stdout = "";
|
|
205207
|
-
let stderr = "";
|
|
205208
|
-
let timedOut = false;
|
|
205209
|
-
let settled = false;
|
|
205210
|
-
const timer = setTimeout(() => {
|
|
205211
|
-
timedOut = true;
|
|
205212
|
-
child.kill("SIGKILL");
|
|
205213
|
-
}, timeoutMs);
|
|
205214
|
-
const finish = (capture) => {
|
|
205215
|
-
if (settled)
|
|
205216
|
-
return;
|
|
205217
|
-
settled = true;
|
|
205218
|
-
clearTimeout(timer);
|
|
205219
|
-
resolve12(capture);
|
|
205220
|
-
};
|
|
205221
|
-
child.stdout.on("data", (chunk3) => {
|
|
205222
|
-
stdout += chunk3.toString("utf-8");
|
|
205223
|
-
});
|
|
205224
|
-
child.stderr.on("data", (chunk3) => {
|
|
205225
|
-
stderr += chunk3.toString("utf-8");
|
|
205226
|
-
});
|
|
205227
|
-
child.on("error", (err) => {
|
|
205228
|
-
finish({
|
|
205229
|
-
exitCode: null,
|
|
205230
|
-
signal: null,
|
|
205231
|
-
stdout,
|
|
205232
|
-
stderr,
|
|
205233
|
-
timedOut,
|
|
205234
|
-
spawnError: err.message,
|
|
205235
|
-
durationMs: Date.now() - start
|
|
205236
|
-
});
|
|
205237
|
-
});
|
|
205238
|
-
child.on("close", (code, signal) => {
|
|
205239
|
-
finish({
|
|
205240
|
-
exitCode: code,
|
|
205241
|
-
signal,
|
|
205242
|
-
stdout,
|
|
205243
|
-
stderr,
|
|
205244
|
-
timedOut,
|
|
205245
|
-
durationMs: Date.now() - start
|
|
205246
|
-
});
|
|
205247
|
-
});
|
|
205248
|
-
if (opts.stdin !== void 0)
|
|
205249
|
-
child.stdin.write(opts.stdin);
|
|
205250
|
-
child.stdin.end();
|
|
205251
|
-
});
|
|
205252
|
-
}
|
|
205253
|
-
|
|
205254
205357
|
// packages/guard-runner/dist/normalizers.js
|
|
205255
205358
|
var CANONICAL_ORDER = [
|
|
205256
205359
|
"abs-paths",
|
|
@@ -205881,16 +205984,27 @@ async function runGuard(opts) {
|
|
|
205881
205984
|
}));
|
|
205882
205985
|
const executable = planned.filter((p2) => p2.resolution.kind === "match" || p2.resolution.kind === "remap");
|
|
205883
205986
|
const nonExecutable = planned.filter((p2) => p2.resolution.kind === "stale" || p2.resolution.kind === "orphaned");
|
|
205884
|
-
|
|
205987
|
+
const buildsOwnEntry = !opts.skipBuild && executable.length > 0;
|
|
205988
|
+
if (buildsOwnEntry) {
|
|
205885
205989
|
opts.onPhase?.("build");
|
|
205886
205990
|
const build8 = await runBuild(repoRoot5, loaded.recipe.build, loaded.recipe.env);
|
|
205887
205991
|
if (!build8.ok)
|
|
205888
205992
|
return { status: "build-failed", build: build8, loadErrors };
|
|
205889
205993
|
}
|
|
205994
|
+
const resolvedEntry = resolveEntry(repoRoot5, loaded.recipe.entry);
|
|
205995
|
+
if (buildsOwnEntry) {
|
|
205996
|
+
const preflight = await preflightEntry({
|
|
205997
|
+
resolvedEntry,
|
|
205998
|
+
displayEntry: loaded.recipe.entry,
|
|
205999
|
+
recipeEnv: loaded.recipe.env
|
|
206000
|
+
});
|
|
206001
|
+
if (!preflight.ok) {
|
|
206002
|
+
return { status: "entry-preflight-failed", preflight, buildCommand: loaded.recipe.build, loadErrors };
|
|
206003
|
+
}
|
|
206004
|
+
}
|
|
205890
206005
|
opts.onPhase?.("run", selected.length);
|
|
205891
206006
|
const runId = buildRunId();
|
|
205892
206007
|
const ranAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
205893
|
-
const resolvedEntry = resolveEntry(repoRoot5, loaded.recipe.entry);
|
|
205894
206008
|
const stepTimeoutMs = opts.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
205895
206009
|
const concurrency = opts.concurrency ?? defaultRunConcurrency();
|
|
205896
206010
|
const results = [];
|
|
@@ -206435,11 +206549,11 @@ function spawnGenerateRunner2(opts = {}) {
|
|
|
206435
206549
|
return async (ctx) => {
|
|
206436
206550
|
const refs = ctx.claims.map((c2) => c2.ref).join(",");
|
|
206437
206551
|
const isRetry = ctx.claims.some((c2) => c2.retry);
|
|
206438
|
-
const
|
|
206552
|
+
const stage = isRetry ? "guard.retry" : "guard.generate";
|
|
206439
206553
|
const raw = await transport({
|
|
206440
|
-
id:
|
|
206441
|
-
stage
|
|
206442
|
-
model: opts.model,
|
|
206554
|
+
id: `${stage}:${ctx.doc}:${refs}${ctx.correction ? ":correction" : ""}`,
|
|
206555
|
+
stage,
|
|
206556
|
+
model: isRetry ? opts.retryModel ?? opts.model : opts.model,
|
|
206443
206557
|
fallbackModel: opts.fallbackModel,
|
|
206444
206558
|
system: GENERATE_SYSTEM_PROMPT,
|
|
206445
206559
|
user: buildAuthorUserPrompt(ctx),
|
|
@@ -206751,12 +206865,15 @@ async function captureProbes(opts) {
|
|
|
206751
206865
|
const cached = await getCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4);
|
|
206752
206866
|
if (cached) {
|
|
206753
206867
|
const parsed = ProbeTranscriptSchema.safeParse(cached);
|
|
206754
|
-
if (parsed.success)
|
|
206868
|
+
if (parsed.success) {
|
|
206869
|
+
opts.onProbeCaptured?.();
|
|
206755
206870
|
return parsed.data;
|
|
206871
|
+
}
|
|
206756
206872
|
}
|
|
206757
206873
|
const capture = await exec3([...opts.resolvedEntry, ...argv], opts.recipeEnv);
|
|
206758
206874
|
const transcript = toTranscript(argv, [...opts.displayEntry, ...argv], capture);
|
|
206759
206875
|
await setCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4, transcript);
|
|
206876
|
+
opts.onProbeCaptured?.();
|
|
206760
206877
|
return transcript;
|
|
206761
206878
|
}));
|
|
206762
206879
|
}
|
|
@@ -207021,6 +207138,7 @@ function deleteScenarioFiles(repoRoot5, ids) {
|
|
|
207021
207138
|
|
|
207022
207139
|
// packages/guard-generator/dist/generate.js
|
|
207023
207140
|
var GENERATE_CACHE_NAME = "guard/generate";
|
|
207141
|
+
var ENTRY_PREFLIGHT_ANCHOR = "(entry preflight)";
|
|
207024
207142
|
function defaultConcurrency3() {
|
|
207025
207143
|
const env = process.env.TRUECOURSE_MAX_CONCURRENCY;
|
|
207026
207144
|
if (env) {
|
|
@@ -207090,7 +207208,12 @@ async function generateGuards(options) {
|
|
|
207090
207208
|
const limit = pLimit(Math.max(1, options.concurrency ?? defaultConcurrency3()));
|
|
207091
207209
|
const batchSize = Math.max(1, options.batchSize ?? defaultGenerateBatch2());
|
|
207092
207210
|
const extractRunner = options.extractRunner ?? spawnExtractRunner({ transport: options.transport, model: options.models?.extract, fallbackModel: options.models?.fallback });
|
|
207093
|
-
const generateRunner = options.generateRunner ?? spawnGenerateRunner2({
|
|
207211
|
+
const generateRunner = options.generateRunner ?? spawnGenerateRunner2({
|
|
207212
|
+
transport: options.transport,
|
|
207213
|
+
model: options.models?.generate,
|
|
207214
|
+
retryModel: options.models?.retry,
|
|
207215
|
+
fallbackModel: options.models?.fallback
|
|
207216
|
+
});
|
|
207094
207217
|
const coverageGaps = [];
|
|
207095
207218
|
const errors = [];
|
|
207096
207219
|
const extractionFailures = [];
|
|
@@ -207161,11 +207284,15 @@ async function generateGuards(options) {
|
|
|
207161
207284
|
return buildPromise;
|
|
207162
207285
|
};
|
|
207163
207286
|
let resolvedEntryMemo = null;
|
|
207287
|
+
let groundPlanned = 0;
|
|
207288
|
+
let groundCaptured = 0;
|
|
207164
207289
|
const groundClaims = async (claimTexts) => {
|
|
207165
207290
|
const probes = deriveProbes(claimTexts, recipe.entry);
|
|
207166
207291
|
const build8 = await buildPromise;
|
|
207167
|
-
if (!build8.ok)
|
|
207292
|
+
if (!build8.ok || probes.length === 0)
|
|
207168
207293
|
return [];
|
|
207294
|
+
groundPlanned += probes.length;
|
|
207295
|
+
options.onGroundProgress?.(groundCaptured, groundPlanned);
|
|
207169
207296
|
resolvedEntryMemo ??= resolveEntry(repoRoot5, recipe.entry);
|
|
207170
207297
|
return captureProbes({
|
|
207171
207298
|
repoRoot: repoRoot5,
|
|
@@ -207173,9 +207300,21 @@ async function generateGuards(options) {
|
|
|
207173
207300
|
resolvedEntry: resolvedEntryMemo,
|
|
207174
207301
|
displayEntry: recipe.entry,
|
|
207175
207302
|
recipeFingerprint,
|
|
207176
|
-
recipeEnv: recipe.env
|
|
207303
|
+
recipeEnv: recipe.env,
|
|
207304
|
+
onProbeCaptured: () => options.onGroundProgress?.(++groundCaptured, groundPlanned)
|
|
207177
207305
|
});
|
|
207178
207306
|
};
|
|
207307
|
+
let entryPreflightMemo = null;
|
|
207308
|
+
const preflightEntryOnce = () => {
|
|
207309
|
+
entryPreflightMemo ??= (async () => {
|
|
207310
|
+
const build8 = await buildPromise;
|
|
207311
|
+
if (!build8.ok)
|
|
207312
|
+
return null;
|
|
207313
|
+
resolvedEntryMemo ??= resolveEntry(repoRoot5, recipe.entry);
|
|
207314
|
+
return preflightEntry({ resolvedEntry: resolvedEntryMemo, displayEntry: recipe.entry, recipeEnv: recipe.env });
|
|
207315
|
+
})();
|
|
207316
|
+
return entryPreflightMemo;
|
|
207317
|
+
};
|
|
207179
207318
|
const priorSections = readPriorManifest(repoRoot5);
|
|
207180
207319
|
const manifestByKey = new Map(priorSections.map((e) => [`${e.doc}\0${e.anchor}`, e]));
|
|
207181
207320
|
const usedIds = existingScenarioIds(repoRoot5);
|
|
@@ -207199,9 +207338,11 @@ async function generateGuards(options) {
|
|
|
207199
207338
|
});
|
|
207200
207339
|
settledKeys.add(k);
|
|
207201
207340
|
writeWorkingManifest();
|
|
207341
|
+
options.onSectionSettled?.(settledKeys.size, plan.work.length);
|
|
207202
207342
|
};
|
|
207203
207343
|
const written = [];
|
|
207204
207344
|
const birthFindings = [];
|
|
207345
|
+
let entryPreflightFailure = null;
|
|
207205
207346
|
let birthTotal = 0;
|
|
207206
207347
|
let birthSettled = 0;
|
|
207207
207348
|
let birthPassed = 0;
|
|
@@ -207248,6 +207389,20 @@ async function generateGuards(options) {
|
|
|
207248
207389
|
return;
|
|
207249
207390
|
settleChain = settleChain.then(() => settleCliSection(sectionByKey.get(k), refsBySection.get(k)));
|
|
207250
207391
|
};
|
|
207392
|
+
const deadEntry = async () => {
|
|
207393
|
+
const preflight = await preflightEntryOnce();
|
|
207394
|
+
if (!preflight || preflight.ok)
|
|
207395
|
+
return false;
|
|
207396
|
+
if (!entryPreflightFailure) {
|
|
207397
|
+
entryPreflightFailure = { entry: preflight.entry, buildCommand: recipe.build, stderr: preflight.stderr };
|
|
207398
|
+
errors.push({
|
|
207399
|
+
doc: preflight.entry,
|
|
207400
|
+
anchor: ENTRY_PREFLIGHT_ANCHOR,
|
|
207401
|
+
message: formatEntryPreflightError(entryPreflightFailure)
|
|
207402
|
+
});
|
|
207403
|
+
}
|
|
207404
|
+
return true;
|
|
207405
|
+
};
|
|
207251
207406
|
async function settleCliSection(section, refs) {
|
|
207252
207407
|
const k = key2(section);
|
|
207253
207408
|
for (const id of priorIdsOf(k))
|
|
@@ -207282,6 +207437,8 @@ async function generateGuards(options) {
|
|
|
207282
207437
|
const message = `build failed (\`${build8.command}\`)${build8.timedOut ? " \u2014 timed out" : ""}`;
|
|
207283
207438
|
for (const c2 of round1)
|
|
207284
207439
|
localErrors.push(errorFrom({ candidate: c2, result: { failure: { actual: message } } }));
|
|
207440
|
+
} else if (await deadEntry()) {
|
|
207441
|
+
return;
|
|
207285
207442
|
} else {
|
|
207286
207443
|
birthTotal += round1.length;
|
|
207287
207444
|
const r1 = await birthValidate(repoRoot5, round1, { skipBuild: true, onPhase: options.onBirthPhase, onScenarioSettled: bumpBirth });
|
|
@@ -207358,6 +207515,8 @@ async function generateGuards(options) {
|
|
|
207358
207515
|
const authorTotal = authTasks.length;
|
|
207359
207516
|
let authorDone = 0;
|
|
207360
207517
|
const bumpAuthor = (n) => options.onAuthorProgress?.(authorDone += n, authorTotal);
|
|
207518
|
+
if (authorTotal > 0)
|
|
207519
|
+
options.onAuthorProgress?.(authorDone, authorTotal);
|
|
207361
207520
|
const missTasks = [];
|
|
207362
207521
|
for (const t2 of authTasks) {
|
|
207363
207522
|
const cached = await readAuthorCache(repoRoot5, t2.claim, t2.section, recipeFingerprint);
|
|
@@ -207429,7 +207588,8 @@ async function generateGuards(options) {
|
|
|
207429
207588
|
extractionFailures,
|
|
207430
207589
|
orphaned,
|
|
207431
207590
|
birthPassed,
|
|
207432
|
-
manifestPath: manifestPath2(repoRoot5)
|
|
207591
|
+
manifestPath: manifestPath2(repoRoot5),
|
|
207592
|
+
...entryPreflightFailure ? { entryPreflight: entryPreflightFailure } : {}
|
|
207433
207593
|
};
|
|
207434
207594
|
}
|
|
207435
207595
|
var key2 = (s) => `${s.doc}\0${s.anchor}`;
|
|
@@ -207818,12 +207978,14 @@ function previewChars() {
|
|
|
207818
207978
|
return 60 * 50;
|
|
207819
207979
|
}
|
|
207820
207980
|
async function estimateScanTokens(repoRoot5, prices) {
|
|
207981
|
+
const decisions = readCorpusDecisions(repoRoot5);
|
|
207982
|
+
const manualIncludes = decisions.manualIncludes ?? [];
|
|
207983
|
+
const manualExcludes = new Set(decisions.manualExcludes ?? []);
|
|
207821
207984
|
const docs = discoverDocs(repoRoot5);
|
|
207822
|
-
const
|
|
207823
|
-
const nClassify = toClassify.length;
|
|
207824
|
-
const
|
|
207825
|
-
const
|
|
207826
|
-
const cachedKeptDocs = toClassify.filter((_2, i) => relevance[i]?.include === true);
|
|
207985
|
+
const plan = await planRelevanceWork(repoRoot5, docs, manualIncludes);
|
|
207986
|
+
const nClassify = plan.toClassify.length;
|
|
207987
|
+
const relevanceMissDocs = plan.needsCall;
|
|
207988
|
+
const cachedKeptDocs = plan.toClassify.filter((d3) => plan.known.get(d3.path)?.include === true && !manualExcludes.has(d3.path));
|
|
207827
207989
|
const cachedKeptTagged = await Promise.all(cachedKeptDocs.map((d3) => isAreaTagCached(repoRoot5, d3)));
|
|
207828
207990
|
const cachedKeptTagMisses = cachedKeptTagged.filter((cached) => !cached).length;
|
|
207829
207991
|
const estChangedKept = Math.round(relevanceMissDocs.length * KEEP_RATE);
|
|
@@ -210089,12 +210251,18 @@ async function runSpecDocsSkipped(opts = {}) {
|
|
|
210089
210251
|
"Force-include a skipped doc with `spec docs include <path>`; force-exclude a kept doc with `spec docs exclude <path>`."
|
|
210090
210252
|
);
|
|
210091
210253
|
}
|
|
210092
|
-
async function runSpecDocsInclude(
|
|
210254
|
+
async function runSpecDocsInclude(docPaths, opts = {}) {
|
|
210093
210255
|
const root2 = repoRoot3(opts);
|
|
210094
|
-
|
|
210095
|
-
|
|
210256
|
+
const paths = docPaths.filter(Boolean);
|
|
210257
|
+
if (paths.length === 0) return fail3("Missing doc path");
|
|
210258
|
+
for (const docPath of paths) {
|
|
210259
|
+
await addManualInclude(root2, docPath);
|
|
210260
|
+
O2.step(`Force-included ${docPath}`);
|
|
210261
|
+
}
|
|
210096
210262
|
await reScan2(root2);
|
|
210097
|
-
gt(
|
|
210263
|
+
gt(
|
|
210264
|
+
paths.length === 1 ? `Force-include ${paths[0]} \u2014 re-scanned. Review \`truecourse spec conflicts list\`.` : `Force-included ${paths.length} docs \u2014 re-scanned once. Review \`truecourse spec conflicts list\`.`
|
|
210265
|
+
);
|
|
210098
210266
|
}
|
|
210099
210267
|
async function runSpecDocsUninclude(docPath, opts = {}) {
|
|
210100
210268
|
const root2 = repoRoot3(opts);
|
|
@@ -210103,12 +210271,18 @@ async function runSpecDocsUninclude(docPath, opts = {}) {
|
|
|
210103
210271
|
await reScan2(root2);
|
|
210104
210272
|
gt(`Removed force-include for ${docPath} \u2014 re-scanned.`);
|
|
210105
210273
|
}
|
|
210106
|
-
async function runSpecDocsExclude(
|
|
210274
|
+
async function runSpecDocsExclude(docPaths, opts = {}) {
|
|
210107
210275
|
const root2 = repoRoot3(opts);
|
|
210108
|
-
|
|
210109
|
-
|
|
210276
|
+
const paths = docPaths.filter(Boolean);
|
|
210277
|
+
if (paths.length === 0) return fail3("Missing doc path");
|
|
210278
|
+
for (const docPath of paths) {
|
|
210279
|
+
await addManualExclude(root2, docPath);
|
|
210280
|
+
O2.step(`Force-excluded ${docPath}`);
|
|
210281
|
+
}
|
|
210110
210282
|
await reScan2(root2);
|
|
210111
|
-
gt(
|
|
210283
|
+
gt(
|
|
210284
|
+
paths.length === 1 ? `Force-exclude ${paths[0]} \u2014 re-scanned. Review \`truecourse spec conflicts list\`.` : `Force-excluded ${paths.length} docs \u2014 re-scanned once. Review \`truecourse spec conflicts list\`.`
|
|
210285
|
+
);
|
|
210112
210286
|
}
|
|
210113
210287
|
async function runSpecDocsUnexclude(docPath, opts = {}) {
|
|
210114
210288
|
const root2 = repoRoot3(opts);
|
|
@@ -210213,12 +210387,14 @@ var GUARD_GENERATE_STEPS = [
|
|
|
210213
210387
|
var GUARD_STEP_STAGES = {
|
|
210214
210388
|
index: ["guard.recipe"],
|
|
210215
210389
|
extract: ["guard.extract"],
|
|
210216
|
-
author: ["guard.generate"]
|
|
210390
|
+
author: ["guard.generate"],
|
|
210391
|
+
validate: ["guard.retry"]
|
|
210217
210392
|
};
|
|
210218
210393
|
function resolveGuardModels(repoRoot5) {
|
|
210219
210394
|
return {
|
|
210220
210395
|
extract: resolveModel("guard.extract", void 0, repoRoot5),
|
|
210221
210396
|
generate: resolveModel("guard.generate", void 0, repoRoot5),
|
|
210397
|
+
retry: resolveModel("guard.retry", void 0, repoRoot5),
|
|
210222
210398
|
recipe: resolveModel("guard.recipe", void 0, repoRoot5),
|
|
210223
210399
|
fallback: resolveFallbackModel(repoRoot5) ?? void 0
|
|
210224
210400
|
};
|
|
@@ -210260,10 +210436,20 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210260
210436
|
cur = ni;
|
|
210261
210437
|
};
|
|
210262
210438
|
const withUsage = (key4, base2) => `${base2}${stageUsageTag(GUARD_STEP_STAGES[key4] ?? [], repoRoot5)}`;
|
|
210439
|
+
let authorDone = 0;
|
|
210440
|
+
let authorTotal = 0;
|
|
210441
|
+
let authorFinished = false;
|
|
210442
|
+
let groundCaptured = 0;
|
|
210443
|
+
let groundPlanned = 0;
|
|
210444
|
+
const authorDetail = () => {
|
|
210445
|
+
const claims = `${authorDone}/${authorTotal} claim${authorTotal === 1 ? "" : "s"}`;
|
|
210446
|
+
const base2 = groundPlanned > 0 ? `grounding probes ${groundCaptured}/${groundPlanned} \xB7 authoring ${claims}` : claims;
|
|
210447
|
+
return withUsage("author", base2);
|
|
210448
|
+
};
|
|
210263
210449
|
let building = false;
|
|
210264
|
-
let birthSeen = false;
|
|
210265
210450
|
let birthDone = 0;
|
|
210266
|
-
let
|
|
210451
|
+
let sectionsDone = 0;
|
|
210452
|
+
let sectionsTotal = 0;
|
|
210267
210453
|
let retrySeen = false;
|
|
210268
210454
|
let retryDone = 0;
|
|
210269
210455
|
let retryTotal = 0;
|
|
@@ -210274,10 +210460,10 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210274
210460
|
tracker?.start("validate");
|
|
210275
210461
|
validateStarted = true;
|
|
210276
210462
|
}
|
|
210277
|
-
const parts = [building ? "building\u2026" : `birth ${birthDone}
|
|
210463
|
+
const parts = [`sections ${sectionsDone}/${sectionsTotal}`, building ? "building\u2026" : `birth ${birthDone}`];
|
|
210278
210464
|
if (retrySeen)
|
|
210279
|
-
parts.push(`retrying
|
|
210280
|
-
tracker?.detail("validate", parts.join(" \xB7 "));
|
|
210465
|
+
parts.push(`retrying ${retryDone}/${retryTotal}`);
|
|
210466
|
+
tracker?.detail("validate", withUsage("validate", parts.join(" \xB7 ")));
|
|
210281
210467
|
};
|
|
210282
210468
|
tracker?.start("index");
|
|
210283
210469
|
try {
|
|
@@ -210289,6 +210475,7 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210289
210475
|
generateRunner: options.generateRunner,
|
|
210290
210476
|
recipeRunner: options.recipeRunner,
|
|
210291
210477
|
onPlan: (total, work) => {
|
|
210478
|
+
sectionsTotal = work;
|
|
210292
210479
|
tracker?.done("index", withUsage("index", `${work} of ${total} section${total === 1 ? "" : "s"} changed`));
|
|
210293
210480
|
cur = STEPS.indexOf("extract");
|
|
210294
210481
|
tracker?.start("extract", `0 views`);
|
|
@@ -210305,27 +210492,30 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210305
210492
|
},
|
|
210306
210493
|
onAuthorProgress: (done, total) => {
|
|
210307
210494
|
advanceTo("author");
|
|
210308
|
-
|
|
210309
|
-
|
|
210310
|
-
|
|
210311
|
-
|
|
210312
|
-
tracker?.
|
|
210313
|
-
},
|
|
210314
|
-
onBirthPhase: (phase, total) => {
|
|
210315
|
-
if (phase === "build") {
|
|
210316
|
-
building = true;
|
|
210495
|
+
authorDone = done;
|
|
210496
|
+
authorTotal = total;
|
|
210497
|
+
if (done >= total) {
|
|
210498
|
+
authorFinished = true;
|
|
210499
|
+
tracker?.done("author", withUsage("author", `${done}/${total} claim${total === 1 ? "" : "s"}`));
|
|
210317
210500
|
} else {
|
|
210318
|
-
|
|
210319
|
-
if (!birthSeen && total !== void 0)
|
|
210320
|
-
birthTotal = total;
|
|
210501
|
+
tracker?.detail("author", authorDetail());
|
|
210321
210502
|
}
|
|
210503
|
+
},
|
|
210504
|
+
onGroundProgress: (captured, planned) => {
|
|
210505
|
+
groundCaptured = captured;
|
|
210506
|
+
groundPlanned = planned;
|
|
210507
|
+
if (authorFinished)
|
|
210508
|
+
return;
|
|
210509
|
+
advanceTo("author");
|
|
210510
|
+
tracker?.detail("author", authorDetail());
|
|
210511
|
+
},
|
|
210512
|
+
onBirthPhase: (phase) => {
|
|
210513
|
+
building = phase === "build";
|
|
210322
210514
|
renderValidate();
|
|
210323
210515
|
},
|
|
210324
|
-
onBirthProgress: (done
|
|
210516
|
+
onBirthProgress: (done) => {
|
|
210325
210517
|
building = false;
|
|
210326
|
-
birthSeen = true;
|
|
210327
210518
|
birthDone = done;
|
|
210328
|
-
birthTotal = total;
|
|
210329
210519
|
renderValidate();
|
|
210330
210520
|
},
|
|
210331
210521
|
onRetryProgress: (done, total) => {
|
|
@@ -210333,6 +210523,12 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210333
210523
|
retryDone = done;
|
|
210334
210524
|
retryTotal = total;
|
|
210335
210525
|
renderValidate();
|
|
210526
|
+
},
|
|
210527
|
+
onSectionSettled: (settled, total) => {
|
|
210528
|
+
sectionsDone = settled;
|
|
210529
|
+
sectionsTotal = total;
|
|
210530
|
+
if (validateStarted)
|
|
210531
|
+
renderValidate();
|
|
210336
210532
|
}
|
|
210337
210533
|
});
|
|
210338
210534
|
for (let i = cur; i < STEPS.length; i++)
|
|
@@ -210356,7 +210552,7 @@ async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
|
210356
210552
|
}
|
|
210357
210553
|
}
|
|
210358
210554
|
}
|
|
210359
|
-
var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate"];
|
|
210555
|
+
var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate", "guard.retry"];
|
|
210360
210556
|
function sumGuardUsage() {
|
|
210361
210557
|
const usage = getStageUsage();
|
|
210362
210558
|
let calls = 0;
|
|
@@ -210404,6 +210600,8 @@ async function guardRunInProcess(repoRoot5, options = {}) {
|
|
|
210404
210600
|
tracker?.done("run", `${n} scenario${n === 1 ? "" : "s"}`);
|
|
210405
210601
|
} else if (result.status === "build-failed") {
|
|
210406
210602
|
tracker?.error("build", `Build failed (\`${result.build.command}\`)${result.build.timedOut ? " \u2014 timed out" : ""}`);
|
|
210603
|
+
} else if (result.status === "entry-preflight-failed") {
|
|
210604
|
+
tracker?.error("build", `Entry failed to start: \`${result.preflight.entry}\` (rebuild via \`${result.buildCommand}\`)`);
|
|
210407
210605
|
}
|
|
210408
210606
|
return result;
|
|
210409
210607
|
}
|
|
@@ -210467,6 +210665,15 @@ async function runGuardRun(opts = {}) {
|
|
|
210467
210665
|
process.exit(1);
|
|
210468
210666
|
return;
|
|
210469
210667
|
}
|
|
210668
|
+
case "entry-preflight-failed": {
|
|
210669
|
+
O2.error(`The recipe entry \`${result.preflight.entry}\` failed to start \u2014 every scenario would fail identically.`);
|
|
210670
|
+
for (const line of result.preflight.stderr.trimEnd().split("\n")) console.log(` ${line}`);
|
|
210671
|
+
O2.step(`Rebuild it with \`${result.buildCommand}\` (its build output is likely stale or incomplete), then re-run \`truecourse guard run\`.`);
|
|
210672
|
+
printLoadErrors(result.loadErrors);
|
|
210673
|
+
gt("Aborted \u2014 the entry could not start; no scenarios ran.");
|
|
210674
|
+
process.exit(1);
|
|
210675
|
+
return;
|
|
210676
|
+
}
|
|
210470
210677
|
case "ok":
|
|
210471
210678
|
break;
|
|
210472
210679
|
}
|
|
@@ -210549,6 +210756,13 @@ async function runGuardGenerate(opts = {}) {
|
|
|
210549
210756
|
gt("Add or fix `.truecourse/scenarios/recipe.json` and retry.");
|
|
210550
210757
|
process.exit(1);
|
|
210551
210758
|
}
|
|
210759
|
+
if (guard.entryPreflight) {
|
|
210760
|
+
O2.error(`The recipe entry \`${guard.entryPreflight.entry}\` failed to start \u2014 every scenario would fail identically, so nothing was validated.`);
|
|
210761
|
+
for (const line of guard.entryPreflight.stderr.trimEnd().split("\n")) console.log(` ${line}`);
|
|
210762
|
+
O2.step(`Rebuild it with \`${guard.entryPreflight.buildCommand}\` (its build output is likely stale or incomplete), then re-run \`truecourse guard generate\`.`);
|
|
210763
|
+
gt("Aborted \u2014 the entry could not start; no scenarios were validated.");
|
|
210764
|
+
process.exit(1);
|
|
210765
|
+
}
|
|
210552
210766
|
if (guard.recipe?.status === "discovered") {
|
|
210553
210767
|
O2.step(`recipe wrote ${guard.recipe.wrotePath} \u2014 review and commit it`);
|
|
210554
210768
|
}
|
|
@@ -210772,6 +210986,7 @@ var STAGE_LABEL = {
|
|
|
210772
210986
|
"contract.gapJudge": "Contract \xB7 gap judge",
|
|
210773
210987
|
"guard.extract": "Guard \xB7 claim extract",
|
|
210774
210988
|
"guard.generate": "Guard \xB7 scenario gen",
|
|
210989
|
+
"guard.retry": "Guard \xB7 scenario retry",
|
|
210775
210990
|
"guard.recipe": "Guard \xB7 recipe discover",
|
|
210776
210991
|
"rules.violationGen": "Rules \xB7 violation gen"
|
|
210777
210992
|
};
|
|
@@ -211080,7 +211295,7 @@ async function runHooksRun() {
|
|
|
211080
211295
|
|
|
211081
211296
|
// tools/cli/src/index.ts
|
|
211082
211297
|
var program2 = new Command();
|
|
211083
|
-
program2.name("truecourse").version("0.7.0-next.
|
|
211298
|
+
program2.name("truecourse").version("0.7.0-next.2").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
211084
211299
|
var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").option("--reconfigure", "Re-prompt for console vs background service mode").option("--service", "Run as a background service (skips mode prompt)").option("--console", "Run in this terminal (skips mode prompt)").action(async (options) => {
|
|
211085
211300
|
if (options.service && options.console) {
|
|
211086
211301
|
console.error("error: --service and --console are mutually exclusive");
|
|
@@ -211193,14 +211408,14 @@ docsCmd.command("list").description("List the kept (corpus) docs with their area
|
|
|
211193
211408
|
docsCmd.command("skipped").description("List docs the relevance filter excluded from extraction").action(async () => {
|
|
211194
211409
|
await runSpecDocsSkipped();
|
|
211195
211410
|
});
|
|
211196
|
-
docsCmd.command("include <path
|
|
211197
|
-
await runSpecDocsInclude(
|
|
211411
|
+
docsCmd.command("include <path...>").description("Force-include one or more skipped docs and re-scan once").action(async (docPaths) => {
|
|
211412
|
+
await runSpecDocsInclude(docPaths);
|
|
211198
211413
|
});
|
|
211199
211414
|
docsCmd.command("uninclude <path>").description("Remove a force-include override").action(async (docPath) => {
|
|
211200
211415
|
await runSpecDocsUninclude(docPath);
|
|
211201
211416
|
});
|
|
211202
|
-
docsCmd.command("exclude <path
|
|
211203
|
-
await runSpecDocsExclude(
|
|
211417
|
+
docsCmd.command("exclude <path...>").description("Force-exclude one or more kept docs from the corpus and re-scan once").action(async (docPaths) => {
|
|
211418
|
+
await runSpecDocsExclude(docPaths);
|
|
211204
211419
|
});
|
|
211205
211420
|
docsCmd.command("unexclude <path>").description("Remove a force-exclude override").action(async (docPath) => {
|
|
211206
211421
|
await runSpecDocsUnexclude(docPath);
|