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/server.mjs
CHANGED
|
@@ -28079,7 +28079,7 @@ function parseBlockedOnCapabilities(reason) {
|
|
|
28079
28079
|
return [];
|
|
28080
28080
|
return m[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
28081
28081
|
}
|
|
28082
|
-
var GuardWrittenScenarioSchema, GuardCoverageGapKindSchema, GuardCoverageGapSchema, GuardBirthFindingSchema, GuardGenerateErrorSchema, GuardExtractionFailureSchema, GuardOrphanedSectionSchema, GuardRecipeReportSchema, GuardGenerateUsageSchema, GuardGenerateReportSchema;
|
|
28082
|
+
var GuardWrittenScenarioSchema, GuardCoverageGapKindSchema, GuardCoverageGapSchema, GuardBirthFindingSchema, GuardGenerateErrorSchema, GuardEntryPreflightSchema, GuardExtractionFailureSchema, GuardOrphanedSectionSchema, GuardRecipeReportSchema, GuardGenerateUsageSchema, GuardGenerateReportSchema;
|
|
28083
28083
|
var init_report = __esm({
|
|
28084
28084
|
"packages/shared/dist/guard/report.js"() {
|
|
28085
28085
|
"use strict";
|
|
@@ -28132,6 +28132,14 @@ var init_report = __esm({
|
|
|
28132
28132
|
anchor: external_exports.string(),
|
|
28133
28133
|
message: external_exports.string()
|
|
28134
28134
|
}).strict();
|
|
28135
|
+
GuardEntryPreflightSchema = external_exports.object({
|
|
28136
|
+
/** Display form of the entry argv, e.g. `node tools/cli/dist/index.js`. */
|
|
28137
|
+
entry: external_exports.string(),
|
|
28138
|
+
/** The recipe build command, for the rebuild hint. */
|
|
28139
|
+
buildCommand: external_exports.string(),
|
|
28140
|
+
/** The full, UNTRUNCATED startup output the dead entry produced. */
|
|
28141
|
+
stderr: external_exports.string()
|
|
28142
|
+
}).strict();
|
|
28135
28143
|
GuardExtractionFailureSchema = external_exports.object({
|
|
28136
28144
|
doc: external_exports.string(),
|
|
28137
28145
|
reason: external_exports.string()
|
|
@@ -28177,7 +28185,13 @@ var init_report = __esm({
|
|
|
28177
28185
|
*/
|
|
28178
28186
|
birthPassed: external_exports.number().int().nonnegative().optional(),
|
|
28179
28187
|
manifestPath: external_exports.string().optional(),
|
|
28180
|
-
usage: GuardGenerateUsageSchema.optional()
|
|
28188
|
+
usage: GuardGenerateUsageSchema.optional(),
|
|
28189
|
+
/**
|
|
28190
|
+
* Present ONLY when the built entry failed to start — the whole birth phase was
|
|
28191
|
+
* short-circuited, so every changed section stayed unsettled. Optional so older
|
|
28192
|
+
* reports (written before this field existed) keep parsing.
|
|
28193
|
+
*/
|
|
28194
|
+
entryPreflight: GuardEntryPreflightSchema.optional()
|
|
28181
28195
|
}).strict();
|
|
28182
28196
|
}
|
|
28183
28197
|
});
|
|
@@ -194710,6 +194724,129 @@ function listSandboxFiles(dir) {
|
|
|
194710
194724
|
return out.sort();
|
|
194711
194725
|
}
|
|
194712
194726
|
|
|
194727
|
+
// packages/guard-runner/dist/executor.js
|
|
194728
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
194729
|
+
var DEFAULT_STEP_TIMEOUT_MS = 3e4;
|
|
194730
|
+
function executeStep(opts) {
|
|
194731
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
194732
|
+
const [command, ...args] = opts.argv;
|
|
194733
|
+
const start = Date.now();
|
|
194734
|
+
return new Promise((resolve11) => {
|
|
194735
|
+
const child = spawn3(command, args, {
|
|
194736
|
+
cwd: opts.cwd,
|
|
194737
|
+
env: opts.env,
|
|
194738
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
194739
|
+
});
|
|
194740
|
+
let stdout = "";
|
|
194741
|
+
let stderr = "";
|
|
194742
|
+
let timedOut = false;
|
|
194743
|
+
let settled = false;
|
|
194744
|
+
const timer = setTimeout(() => {
|
|
194745
|
+
timedOut = true;
|
|
194746
|
+
child.kill("SIGKILL");
|
|
194747
|
+
}, timeoutMs);
|
|
194748
|
+
const finish = (capture) => {
|
|
194749
|
+
if (settled)
|
|
194750
|
+
return;
|
|
194751
|
+
settled = true;
|
|
194752
|
+
clearTimeout(timer);
|
|
194753
|
+
resolve11(capture);
|
|
194754
|
+
};
|
|
194755
|
+
child.stdout.on("data", (chunk3) => {
|
|
194756
|
+
stdout += chunk3.toString("utf-8");
|
|
194757
|
+
});
|
|
194758
|
+
child.stderr.on("data", (chunk3) => {
|
|
194759
|
+
stderr += chunk3.toString("utf-8");
|
|
194760
|
+
});
|
|
194761
|
+
child.on("error", (err) => {
|
|
194762
|
+
finish({
|
|
194763
|
+
exitCode: null,
|
|
194764
|
+
signal: null,
|
|
194765
|
+
stdout,
|
|
194766
|
+
stderr,
|
|
194767
|
+
timedOut,
|
|
194768
|
+
spawnError: err.message,
|
|
194769
|
+
durationMs: Date.now() - start
|
|
194770
|
+
});
|
|
194771
|
+
});
|
|
194772
|
+
child.on("close", (code, signal) => {
|
|
194773
|
+
finish({
|
|
194774
|
+
exitCode: code,
|
|
194775
|
+
signal,
|
|
194776
|
+
stdout,
|
|
194777
|
+
stderr,
|
|
194778
|
+
timedOut,
|
|
194779
|
+
durationMs: Date.now() - start
|
|
194780
|
+
});
|
|
194781
|
+
});
|
|
194782
|
+
if (opts.stdin !== void 0)
|
|
194783
|
+
child.stdin.write(opts.stdin);
|
|
194784
|
+
child.stdin.end();
|
|
194785
|
+
});
|
|
194786
|
+
}
|
|
194787
|
+
|
|
194788
|
+
// packages/guard-runner/dist/preflight.js
|
|
194789
|
+
var ENTRY_PROBE_ARGVS = [[], ["--help"]];
|
|
194790
|
+
var ENTRY_PREFLIGHT_TIMEOUT_MS = 2e4;
|
|
194791
|
+
var defaultEntryProbeExecutor = async (fullArgv, recipeEnv, timeoutMs) => {
|
|
194792
|
+
const sandbox = createSandbox({ recipeEnv });
|
|
194793
|
+
try {
|
|
194794
|
+
return await executeStep({ argv: fullArgv, cwd: sandbox.cwd, env: sandbox.env, timeoutMs });
|
|
194795
|
+
} finally {
|
|
194796
|
+
sandbox.cleanup();
|
|
194797
|
+
}
|
|
194798
|
+
};
|
|
194799
|
+
async function preflightEntry(opts) {
|
|
194800
|
+
const exec = opts.exec ?? defaultEntryProbeExecutor;
|
|
194801
|
+
const timeoutMs = opts.timeoutMs ?? ENTRY_PREFLIGHT_TIMEOUT_MS;
|
|
194802
|
+
const entry = opts.displayEntry.join(" ");
|
|
194803
|
+
const probes = await Promise.all(ENTRY_PROBE_ARGVS.map(async (argv) => ({
|
|
194804
|
+
argv: [...argv],
|
|
194805
|
+
capture: await exec([...opts.resolvedEntry, ...argv], opts.recipeEnv, timeoutMs)
|
|
194806
|
+
})));
|
|
194807
|
+
const ok = entryStarts(probes);
|
|
194808
|
+
return { ok, entry, stderr: ok ? "" : startupOutput(probes[0].capture, timeoutMs), probes };
|
|
194809
|
+
}
|
|
194810
|
+
function entryStarts(probes) {
|
|
194811
|
+
if (probes.some((p) => startedCleanly(p.capture)))
|
|
194812
|
+
return true;
|
|
194813
|
+
const shapes = probes.map((p) => failureShape(p.capture));
|
|
194814
|
+
return shapes.some((s) => s !== shapes[0]);
|
|
194815
|
+
}
|
|
194816
|
+
function startedCleanly(c) {
|
|
194817
|
+
return !c.spawnError && !c.timedOut && c.signal === null && c.exitCode === 0;
|
|
194818
|
+
}
|
|
194819
|
+
function failureShape(c) {
|
|
194820
|
+
return JSON.stringify({
|
|
194821
|
+
spawnError: c.spawnError ?? null,
|
|
194822
|
+
signal: c.signal ?? null,
|
|
194823
|
+
timedOut: c.timedOut,
|
|
194824
|
+
exitCode: c.exitCode,
|
|
194825
|
+
stdout: c.stdout,
|
|
194826
|
+
stderr: c.stderr
|
|
194827
|
+
});
|
|
194828
|
+
}
|
|
194829
|
+
function startupOutput(c, timeoutMs) {
|
|
194830
|
+
const parts = [];
|
|
194831
|
+
const primary = c.stderr || c.stdout;
|
|
194832
|
+
if (primary)
|
|
194833
|
+
parts.push(primary.trimEnd());
|
|
194834
|
+
if (c.spawnError)
|
|
194835
|
+
parts.push(`failed to spawn: ${c.spawnError}`);
|
|
194836
|
+
if (c.timedOut)
|
|
194837
|
+
parts.push(`(the entry produced no output within ${timeoutMs}ms and was killed)`);
|
|
194838
|
+
if (parts.length === 0) {
|
|
194839
|
+
parts.push(c.exitCode !== null ? `(the entry exited ${c.exitCode} with no output)` : `(the entry was killed by ${c.signal})`);
|
|
194840
|
+
}
|
|
194841
|
+
return parts.join("\n");
|
|
194842
|
+
}
|
|
194843
|
+
function entryPreflightHeadline(entry, buildCommand) {
|
|
194844
|
+
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.`;
|
|
194845
|
+
}
|
|
194846
|
+
function formatEntryPreflightError(opts) {
|
|
194847
|
+
return [entryPreflightHeadline(opts.entry, opts.buildCommand), "", "Startup output:", opts.stderr].join("\n");
|
|
194848
|
+
}
|
|
194849
|
+
|
|
194713
194850
|
// packages/guard-runner/dist/capabilities/git.js
|
|
194714
194851
|
import { spawnSync } from "node:child_process";
|
|
194715
194852
|
import fs15 from "node:fs";
|
|
@@ -194790,67 +194927,6 @@ function applyCapabilities(setup, ctx) {
|
|
|
194790
194927
|
}
|
|
194791
194928
|
}
|
|
194792
194929
|
|
|
194793
|
-
// packages/guard-runner/dist/executor.js
|
|
194794
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
194795
|
-
var DEFAULT_STEP_TIMEOUT_MS = 3e4;
|
|
194796
|
-
function executeStep(opts) {
|
|
194797
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
194798
|
-
const [command, ...args] = opts.argv;
|
|
194799
|
-
const start = Date.now();
|
|
194800
|
-
return new Promise((resolve11) => {
|
|
194801
|
-
const child = spawn3(command, args, {
|
|
194802
|
-
cwd: opts.cwd,
|
|
194803
|
-
env: opts.env,
|
|
194804
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
194805
|
-
});
|
|
194806
|
-
let stdout = "";
|
|
194807
|
-
let stderr = "";
|
|
194808
|
-
let timedOut = false;
|
|
194809
|
-
let settled = false;
|
|
194810
|
-
const timer = setTimeout(() => {
|
|
194811
|
-
timedOut = true;
|
|
194812
|
-
child.kill("SIGKILL");
|
|
194813
|
-
}, timeoutMs);
|
|
194814
|
-
const finish = (capture) => {
|
|
194815
|
-
if (settled)
|
|
194816
|
-
return;
|
|
194817
|
-
settled = true;
|
|
194818
|
-
clearTimeout(timer);
|
|
194819
|
-
resolve11(capture);
|
|
194820
|
-
};
|
|
194821
|
-
child.stdout.on("data", (chunk3) => {
|
|
194822
|
-
stdout += chunk3.toString("utf-8");
|
|
194823
|
-
});
|
|
194824
|
-
child.stderr.on("data", (chunk3) => {
|
|
194825
|
-
stderr += chunk3.toString("utf-8");
|
|
194826
|
-
});
|
|
194827
|
-
child.on("error", (err) => {
|
|
194828
|
-
finish({
|
|
194829
|
-
exitCode: null,
|
|
194830
|
-
signal: null,
|
|
194831
|
-
stdout,
|
|
194832
|
-
stderr,
|
|
194833
|
-
timedOut,
|
|
194834
|
-
spawnError: err.message,
|
|
194835
|
-
durationMs: Date.now() - start
|
|
194836
|
-
});
|
|
194837
|
-
});
|
|
194838
|
-
child.on("close", (code, signal) => {
|
|
194839
|
-
finish({
|
|
194840
|
-
exitCode: code,
|
|
194841
|
-
signal,
|
|
194842
|
-
stdout,
|
|
194843
|
-
stderr,
|
|
194844
|
-
timedOut,
|
|
194845
|
-
durationMs: Date.now() - start
|
|
194846
|
-
});
|
|
194847
|
-
});
|
|
194848
|
-
if (opts.stdin !== void 0)
|
|
194849
|
-
child.stdin.write(opts.stdin);
|
|
194850
|
-
child.stdin.end();
|
|
194851
|
-
});
|
|
194852
|
-
}
|
|
194853
|
-
|
|
194854
194930
|
// packages/guard-runner/dist/normalizers.js
|
|
194855
194931
|
var CANONICAL_ORDER = [
|
|
194856
194932
|
"abs-paths",
|
|
@@ -195481,16 +195557,27 @@ async function runGuard(opts) {
|
|
|
195481
195557
|
}));
|
|
195482
195558
|
const executable = planned.filter((p) => p.resolution.kind === "match" || p.resolution.kind === "remap");
|
|
195483
195559
|
const nonExecutable = planned.filter((p) => p.resolution.kind === "stale" || p.resolution.kind === "orphaned");
|
|
195484
|
-
|
|
195560
|
+
const buildsOwnEntry = !opts.skipBuild && executable.length > 0;
|
|
195561
|
+
if (buildsOwnEntry) {
|
|
195485
195562
|
opts.onPhase?.("build");
|
|
195486
195563
|
const build8 = await runBuild(repoRoot, loaded.recipe.build, loaded.recipe.env);
|
|
195487
195564
|
if (!build8.ok)
|
|
195488
195565
|
return { status: "build-failed", build: build8, loadErrors };
|
|
195489
195566
|
}
|
|
195567
|
+
const resolvedEntry = resolveEntry(repoRoot, loaded.recipe.entry);
|
|
195568
|
+
if (buildsOwnEntry) {
|
|
195569
|
+
const preflight = await preflightEntry({
|
|
195570
|
+
resolvedEntry,
|
|
195571
|
+
displayEntry: loaded.recipe.entry,
|
|
195572
|
+
recipeEnv: loaded.recipe.env
|
|
195573
|
+
});
|
|
195574
|
+
if (!preflight.ok) {
|
|
195575
|
+
return { status: "entry-preflight-failed", preflight, buildCommand: loaded.recipe.build, loadErrors };
|
|
195576
|
+
}
|
|
195577
|
+
}
|
|
195490
195578
|
opts.onPhase?.("run", selected.length);
|
|
195491
195579
|
const runId = buildRunId();
|
|
195492
195580
|
const ranAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
195493
|
-
const resolvedEntry = resolveEntry(repoRoot, loaded.recipe.entry);
|
|
195494
195581
|
const stepTimeoutMs = opts.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
|
|
195495
195582
|
const concurrency = opts.concurrency ?? defaultRunConcurrency();
|
|
195496
195583
|
const results = [];
|
|
@@ -196071,8 +196158,14 @@ var CorpusDocSchema = external_exports.object({
|
|
|
196071
196158
|
var OverlapSectionSchema = external_exports.object({
|
|
196072
196159
|
/** The doc this section lives in, by ref (one of the overlap's two docs). */
|
|
196073
196160
|
doc: DocRefSchema,
|
|
196074
|
-
/**
|
|
196075
|
-
|
|
196161
|
+
/**
|
|
196162
|
+
* The heading text of the conflicting section (verbatim from the doc), or
|
|
196163
|
+
* `null` when the conflicting passage sits in the doc's PREAMBLE — the block
|
|
196164
|
+
* before its first heading (README badges/tagline, intro line). A plain string
|
|
196165
|
+
* from older corpora still parses; `null` is the preamble marker the viewer
|
|
196166
|
+
* bands as the pre-first-heading block.
|
|
196167
|
+
*/
|
|
196168
|
+
heading: external_exports.string().nullable()
|
|
196076
196169
|
});
|
|
196077
196170
|
var OverlapSchema = external_exports.object({
|
|
196078
196171
|
/** The two docs that overlap, by ref. */
|
|
@@ -197009,7 +197102,9 @@ NOT a disagreement (do NOT flag):
|
|
|
197009
197102
|
|
|
197010
197103
|
Bias: when there is a PLAUSIBLE contradiction a human should check, flag it. When the docs are clearly complementary or agree, do not.
|
|
197011
197104
|
|
|
197012
|
-
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
|
|
197105
|
+
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.
|
|
197106
|
+
|
|
197107
|
+
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.
|
|
197013
197108
|
|
|
197014
197109
|
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.
|
|
197015
197110
|
|
|
@@ -197019,6 +197114,12 @@ Output ONLY a JSON object, no prose, no code fences:
|
|
|
197019
197114
|
"note": "users.md uses auth0_id; identity.md uses auth0_sub for the same user column",
|
|
197020
197115
|
"sections": [ { "side": "A", "heading": "User model" }, { "side": "B", "heading": "Identity" } ] }
|
|
197021
197116
|
|
|
197117
|
+
Preamble example \u2014 doc A's claim is a README badge/tagline above any heading, so its side uses null:
|
|
197118
|
+
|
|
197119
|
+
{ "overlap": true,
|
|
197120
|
+
"note": "README.md lists C# as a supported language in the preamble; plan.md's Tech Stack omits it",
|
|
197121
|
+
"sections": [ { "side": "A", "heading": null }, { "side": "B", "heading": "Tech Stack" } ] }
|
|
197122
|
+
|
|
197022
197123
|
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.`;
|
|
197023
197124
|
var OVERLAP_PREVIEW_LINES = 120;
|
|
197024
197125
|
function docBody2(doc) {
|
|
@@ -197070,12 +197171,12 @@ function buildOverlapUserPrompt(areaId, a, b) {
|
|
|
197070
197171
|
var LlmOverlapSchema = external_exports.object({
|
|
197071
197172
|
overlap: external_exports.boolean(),
|
|
197072
197173
|
note: external_exports.string().default(""),
|
|
197073
|
-
sections: external_exports.array(external_exports.object({ side: external_exports.enum(["A", "B"]), heading: external_exports.string() })).default([])
|
|
197174
|
+
sections: external_exports.array(external_exports.object({ side: external_exports.enum(["A", "B"]), heading: external_exports.string().nullable() })).default([])
|
|
197074
197175
|
});
|
|
197075
197176
|
var OverlapVerdictSchema = external_exports.object({
|
|
197076
197177
|
overlap: external_exports.boolean(),
|
|
197077
197178
|
note: external_exports.string().default(""),
|
|
197078
|
-
sections: external_exports.array(external_exports.object({ doc: external_exports.string(), heading: external_exports.string() })).default([])
|
|
197179
|
+
sections: external_exports.array(external_exports.object({ doc: external_exports.string(), heading: external_exports.string().nullable() })).default([])
|
|
197079
197180
|
});
|
|
197080
197181
|
function spawnOverlapRunner(opts = {}) {
|
|
197081
197182
|
const transport = opts.transport ?? cliTransport({ bin: opts.bin });
|
|
@@ -197265,35 +197366,49 @@ function prefilterDocs(docs, manualIncludes = []) {
|
|
|
197265
197366
|
skipped: [...reasons].map(([path73, reason]) => ({ path: path73, reason }))
|
|
197266
197367
|
};
|
|
197267
197368
|
}
|
|
197369
|
+
async function planRelevanceWork(repoRoot, docs, manualIncludes = []) {
|
|
197370
|
+
const manualSet = new Set(manualIncludes);
|
|
197371
|
+
const { toClassify, skipped } = prefilterDocs(docs, manualIncludes);
|
|
197372
|
+
const needsCall = [];
|
|
197373
|
+
const known = /* @__PURE__ */ new Map();
|
|
197374
|
+
await Promise.all(toClassify.map(async (doc) => {
|
|
197375
|
+
if (manualSet.has(doc.path)) {
|
|
197376
|
+
known.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
|
|
197377
|
+
return;
|
|
197378
|
+
}
|
|
197379
|
+
const cached = await readCache4(repoRoot, computeCacheKey5(doc));
|
|
197380
|
+
if (cached)
|
|
197381
|
+
known.set(doc.path, cached);
|
|
197382
|
+
else
|
|
197383
|
+
needsCall.push(doc);
|
|
197384
|
+
}));
|
|
197385
|
+
return { toClassify, prefilterSkipped: skipped, needsCall, known };
|
|
197386
|
+
}
|
|
197268
197387
|
async function filterByRelevance(repoRoot, docs, opts = {}) {
|
|
197269
197388
|
if (opts.enabled === false || docs.length === 0) {
|
|
197270
197389
|
return { included: docs, skipped: [] };
|
|
197271
197390
|
}
|
|
197272
|
-
const manualSet = new Set(opts.manualIncludes ?? []);
|
|
197273
197391
|
const runner2 = opts.runner ?? spawnRelevanceRunner({ transport: opts.transport, model: opts.model, fallbackModel: opts.fallbackModel });
|
|
197274
197392
|
const concurrency = opts.concurrency ?? defaultConcurrency();
|
|
197275
|
-
const
|
|
197276
|
-
const prefilterReason = new Map(prefilterSkipped.map((s) => [s.path, s.reason]));
|
|
197393
|
+
const plan = await planRelevanceWork(repoRoot, docs, opts.manualIncludes ?? []);
|
|
197394
|
+
const prefilterReason = new Map(plan.prefilterSkipped.map((s) => [s.path, s.reason]));
|
|
197277
197395
|
const total = docs.length;
|
|
197278
197396
|
let done = 0;
|
|
197279
197397
|
const markDone = () => opts.onProgress?.(++done, total);
|
|
197280
197398
|
opts.onProgress?.(0, total);
|
|
197281
|
-
|
|
197399
|
+
const resolvedUpfront = prefilterReason.size + plan.known.size;
|
|
197400
|
+
for (let i = 0; i < resolvedUpfront; i++)
|
|
197282
197401
|
markDone();
|
|
197283
|
-
const verdicts =
|
|
197402
|
+
const verdicts = new Map(plan.known);
|
|
197403
|
+
const pending = plan.needsCall;
|
|
197284
197404
|
let cursor = 0;
|
|
197285
197405
|
let active12 = 0;
|
|
197286
197406
|
await new Promise((resolve11) => {
|
|
197407
|
+
if (pending.length === 0)
|
|
197408
|
+
return resolve11();
|
|
197287
197409
|
const launch = () => {
|
|
197288
|
-
while (active12 < concurrency && cursor <
|
|
197289
|
-
const doc =
|
|
197290
|
-
if (manualSet.has(doc.path)) {
|
|
197291
|
-
verdicts.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
|
|
197292
|
-
markDone();
|
|
197293
|
-
if (cursor >= toClassify.length && active12 === 0)
|
|
197294
|
-
resolve11();
|
|
197295
|
-
continue;
|
|
197296
|
-
}
|
|
197410
|
+
while (active12 < concurrency && cursor < pending.length) {
|
|
197411
|
+
const doc = pending[cursor++];
|
|
197297
197412
|
active12++;
|
|
197298
197413
|
classifyOne(repoRoot, doc, runner2).then((verdict) => {
|
|
197299
197414
|
verdicts.set(doc.path, verdict);
|
|
@@ -197306,14 +197421,12 @@ async function filterByRelevance(repoRoot, docs, opts = {}) {
|
|
|
197306
197421
|
}).finally(() => {
|
|
197307
197422
|
markDone();
|
|
197308
197423
|
active12--;
|
|
197309
|
-
if (cursor >=
|
|
197424
|
+
if (cursor >= pending.length && active12 === 0)
|
|
197310
197425
|
resolve11();
|
|
197311
197426
|
else
|
|
197312
197427
|
launch();
|
|
197313
197428
|
});
|
|
197314
197429
|
}
|
|
197315
|
-
if (cursor >= toClassify.length && active12 === 0)
|
|
197316
|
-
resolve11();
|
|
197317
197430
|
};
|
|
197318
197431
|
launch();
|
|
197319
197432
|
});
|
|
@@ -197427,7 +197540,7 @@ async function classifyOne(repoRoot, doc, runner2) {
|
|
|
197427
197540
|
await writeCache4(repoRoot, cacheKey, verdict);
|
|
197428
197541
|
return verdict;
|
|
197429
197542
|
}
|
|
197430
|
-
var RELEVANCE_SYSTEM_PROMPT = `You are a documentation relevance classifier.
|
|
197543
|
+
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.
|
|
197431
197544
|
|
|
197432
197545
|
${OUTPUT_ONLY_GUARDRAIL}
|
|
197433
197546
|
|
|
@@ -197440,22 +197553,23 @@ INCLUDE (spec-source material):
|
|
|
197440
197553
|
SKIP (not spec-source material):
|
|
197441
197554
|
- Pure status / TODO checklists, kanban boards, release notes / changelog drafts
|
|
197442
197555
|
- 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
|
|
197556
|
+
- 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.)
|
|
197443
197557
|
- SUPERSEDED docs \u2014 an older version of a newer doc covering the same subject
|
|
197444
197558
|
- 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)
|
|
197445
197559
|
- Exploratory scratch with no committed decisions (brain dumps, open-questions-only notes)
|
|
197446
197560
|
- AI-agent instructions / prompt templates; personal engineering journals
|
|
197447
197561
|
|
|
197448
|
-
Distinguish "states a decision about our system" (INCLUDE) from "tracks status / describes an external
|
|
197562
|
+
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).
|
|
197449
197563
|
|
|
197450
197564
|
Output ONLY a JSON object:
|
|
197451
197565
|
|
|
197452
197566
|
{ "include": true|false, "reason": "short explanation" }
|
|
197453
197567
|
|
|
197454
|
-
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.`;
|
|
197568
|
+
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.`;
|
|
197455
197569
|
function buildRelevanceUserPrompt(doc) {
|
|
197456
197570
|
const preview = doc.preview.split("\n").slice(0, 60).join("\n");
|
|
197457
197571
|
return [
|
|
197458
|
-
`
|
|
197572
|
+
`PATH (repo-relative): ${doc.path}`,
|
|
197459
197573
|
`Detected kind: ${doc.kind}`,
|
|
197460
197574
|
`Size: ${doc.size} bytes`,
|
|
197461
197575
|
"",
|
|
@@ -197509,9 +197623,6 @@ async function readCache4(scope, cacheKey) {
|
|
|
197509
197623
|
async function writeCache4(scope, cacheKey, verdict) {
|
|
197510
197624
|
await setCacheEntry(scope, CACHE_NAME5, cacheKey, verdict);
|
|
197511
197625
|
}
|
|
197512
|
-
async function readRelevanceCache(repoRoot, doc) {
|
|
197513
|
-
return readCache4(repoRoot, computeCacheKey5(doc));
|
|
197514
|
-
}
|
|
197515
197626
|
|
|
197516
197627
|
// packages/spec-consolidator/dist/curate.js
|
|
197517
197628
|
async function curate(repoRoot, opts = {}) {
|
|
@@ -224308,6 +224419,9 @@ var STAGE_DEFAULTS = {
|
|
|
224308
224419
|
// Authoring an executable scenario faithful to a spec claim is the hard,
|
|
224309
224420
|
// load-bearing call (a weak scenario is false confidence) — opus.
|
|
224310
224421
|
"guard.generate": "opus",
|
|
224422
|
+
// The one evidence-retry per birth-failed claim — the same authoring task, so
|
|
224423
|
+
// the same tier; a distinct stage so retry spend is attributed to the birth line.
|
|
224424
|
+
"guard.retry": "opus",
|
|
224311
224425
|
// Proposing a build/entry recipe is a modest structured task — sonnet.
|
|
224312
224426
|
"guard.recipe": "sonnet",
|
|
224313
224427
|
"rules.violationGen": "opus"
|
|
@@ -225009,11 +225123,11 @@ function spawnGenerateRunner2(opts = {}) {
|
|
|
225009
225123
|
return async (ctx) => {
|
|
225010
225124
|
const refs = ctx.claims.map((c) => c.ref).join(",");
|
|
225011
225125
|
const isRetry = ctx.claims.some((c) => c.retry);
|
|
225012
|
-
const
|
|
225126
|
+
const stage = isRetry ? "guard.retry" : "guard.generate";
|
|
225013
225127
|
const raw = await transport({
|
|
225014
|
-
id:
|
|
225015
|
-
stage
|
|
225016
|
-
model: opts.model,
|
|
225128
|
+
id: `${stage}:${ctx.doc}:${refs}${ctx.correction ? ":correction" : ""}`,
|
|
225129
|
+
stage,
|
|
225130
|
+
model: isRetry ? opts.retryModel ?? opts.model : opts.model,
|
|
225017
225131
|
fallbackModel: opts.fallbackModel,
|
|
225018
225132
|
system: GENERATE_SYSTEM_PROMPT,
|
|
225019
225133
|
user: buildAuthorUserPrompt(ctx),
|
|
@@ -225325,12 +225439,15 @@ async function captureProbes(opts) {
|
|
|
225325
225439
|
const cached = await getCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4);
|
|
225326
225440
|
if (cached) {
|
|
225327
225441
|
const parsed = ProbeTranscriptSchema.safeParse(cached);
|
|
225328
|
-
if (parsed.success)
|
|
225442
|
+
if (parsed.success) {
|
|
225443
|
+
opts.onProbeCaptured?.();
|
|
225329
225444
|
return parsed.data;
|
|
225445
|
+
}
|
|
225330
225446
|
}
|
|
225331
225447
|
const capture = await exec([...opts.resolvedEntry, ...argv], opts.recipeEnv);
|
|
225332
225448
|
const transcript = toTranscript(argv, [...opts.displayEntry, ...argv], capture);
|
|
225333
225449
|
await setCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4, transcript);
|
|
225450
|
+
opts.onProbeCaptured?.();
|
|
225334
225451
|
return transcript;
|
|
225335
225452
|
}));
|
|
225336
225453
|
}
|
|
@@ -225595,6 +225712,7 @@ function deleteScenarioFiles(repoRoot, ids) {
|
|
|
225595
225712
|
|
|
225596
225713
|
// packages/guard-generator/dist/generate.js
|
|
225597
225714
|
var GENERATE_CACHE_NAME = "guard/generate";
|
|
225715
|
+
var ENTRY_PREFLIGHT_ANCHOR = "(entry preflight)";
|
|
225598
225716
|
function defaultConcurrency3() {
|
|
225599
225717
|
const env = process.env.TRUECOURSE_MAX_CONCURRENCY;
|
|
225600
225718
|
if (env) {
|
|
@@ -225664,7 +225782,12 @@ async function generateGuards(options) {
|
|
|
225664
225782
|
const limit = pLimit(Math.max(1, options.concurrency ?? defaultConcurrency3()));
|
|
225665
225783
|
const batchSize = Math.max(1, options.batchSize ?? defaultGenerateBatch2());
|
|
225666
225784
|
const extractRunner = options.extractRunner ?? spawnExtractRunner({ transport: options.transport, model: options.models?.extract, fallbackModel: options.models?.fallback });
|
|
225667
|
-
const generateRunner = options.generateRunner ?? spawnGenerateRunner2({
|
|
225785
|
+
const generateRunner = options.generateRunner ?? spawnGenerateRunner2({
|
|
225786
|
+
transport: options.transport,
|
|
225787
|
+
model: options.models?.generate,
|
|
225788
|
+
retryModel: options.models?.retry,
|
|
225789
|
+
fallbackModel: options.models?.fallback
|
|
225790
|
+
});
|
|
225668
225791
|
const coverageGaps = [];
|
|
225669
225792
|
const errors = [];
|
|
225670
225793
|
const extractionFailures = [];
|
|
@@ -225735,11 +225858,15 @@ async function generateGuards(options) {
|
|
|
225735
225858
|
return buildPromise;
|
|
225736
225859
|
};
|
|
225737
225860
|
let resolvedEntryMemo = null;
|
|
225861
|
+
let groundPlanned = 0;
|
|
225862
|
+
let groundCaptured = 0;
|
|
225738
225863
|
const groundClaims = async (claimTexts) => {
|
|
225739
225864
|
const probes = deriveProbes(claimTexts, recipe.entry);
|
|
225740
225865
|
const build8 = await buildPromise;
|
|
225741
|
-
if (!build8.ok)
|
|
225866
|
+
if (!build8.ok || probes.length === 0)
|
|
225742
225867
|
return [];
|
|
225868
|
+
groundPlanned += probes.length;
|
|
225869
|
+
options.onGroundProgress?.(groundCaptured, groundPlanned);
|
|
225743
225870
|
resolvedEntryMemo ??= resolveEntry(repoRoot, recipe.entry);
|
|
225744
225871
|
return captureProbes({
|
|
225745
225872
|
repoRoot,
|
|
@@ -225747,9 +225874,21 @@ async function generateGuards(options) {
|
|
|
225747
225874
|
resolvedEntry: resolvedEntryMemo,
|
|
225748
225875
|
displayEntry: recipe.entry,
|
|
225749
225876
|
recipeFingerprint,
|
|
225750
|
-
recipeEnv: recipe.env
|
|
225877
|
+
recipeEnv: recipe.env,
|
|
225878
|
+
onProbeCaptured: () => options.onGroundProgress?.(++groundCaptured, groundPlanned)
|
|
225751
225879
|
});
|
|
225752
225880
|
};
|
|
225881
|
+
let entryPreflightMemo = null;
|
|
225882
|
+
const preflightEntryOnce = () => {
|
|
225883
|
+
entryPreflightMemo ??= (async () => {
|
|
225884
|
+
const build8 = await buildPromise;
|
|
225885
|
+
if (!build8.ok)
|
|
225886
|
+
return null;
|
|
225887
|
+
resolvedEntryMemo ??= resolveEntry(repoRoot, recipe.entry);
|
|
225888
|
+
return preflightEntry({ resolvedEntry: resolvedEntryMemo, displayEntry: recipe.entry, recipeEnv: recipe.env });
|
|
225889
|
+
})();
|
|
225890
|
+
return entryPreflightMemo;
|
|
225891
|
+
};
|
|
225753
225892
|
const priorSections = readPriorManifest(repoRoot);
|
|
225754
225893
|
const manifestByKey = new Map(priorSections.map((e) => [`${e.doc}\0${e.anchor}`, e]));
|
|
225755
225894
|
const usedIds = existingScenarioIds(repoRoot);
|
|
@@ -225773,9 +225912,11 @@ async function generateGuards(options) {
|
|
|
225773
225912
|
});
|
|
225774
225913
|
settledKeys.add(k);
|
|
225775
225914
|
writeWorkingManifest();
|
|
225915
|
+
options.onSectionSettled?.(settledKeys.size, plan.work.length);
|
|
225776
225916
|
};
|
|
225777
225917
|
const written = [];
|
|
225778
225918
|
const birthFindings = [];
|
|
225919
|
+
let entryPreflightFailure = null;
|
|
225779
225920
|
let birthTotal = 0;
|
|
225780
225921
|
let birthSettled = 0;
|
|
225781
225922
|
let birthPassed = 0;
|
|
@@ -225822,6 +225963,20 @@ async function generateGuards(options) {
|
|
|
225822
225963
|
return;
|
|
225823
225964
|
settleChain = settleChain.then(() => settleCliSection(sectionByKey.get(k), refsBySection.get(k)));
|
|
225824
225965
|
};
|
|
225966
|
+
const deadEntry = async () => {
|
|
225967
|
+
const preflight = await preflightEntryOnce();
|
|
225968
|
+
if (!preflight || preflight.ok)
|
|
225969
|
+
return false;
|
|
225970
|
+
if (!entryPreflightFailure) {
|
|
225971
|
+
entryPreflightFailure = { entry: preflight.entry, buildCommand: recipe.build, stderr: preflight.stderr };
|
|
225972
|
+
errors.push({
|
|
225973
|
+
doc: preflight.entry,
|
|
225974
|
+
anchor: ENTRY_PREFLIGHT_ANCHOR,
|
|
225975
|
+
message: formatEntryPreflightError(entryPreflightFailure)
|
|
225976
|
+
});
|
|
225977
|
+
}
|
|
225978
|
+
return true;
|
|
225979
|
+
};
|
|
225825
225980
|
async function settleCliSection(section, refs) {
|
|
225826
225981
|
const k = key2(section);
|
|
225827
225982
|
for (const id of priorIdsOf(k))
|
|
@@ -225856,6 +226011,8 @@ async function generateGuards(options) {
|
|
|
225856
226011
|
const message = `build failed (\`${build8.command}\`)${build8.timedOut ? " \u2014 timed out" : ""}`;
|
|
225857
226012
|
for (const c of round1)
|
|
225858
226013
|
localErrors.push(errorFrom({ candidate: c, result: { failure: { actual: message } } }));
|
|
226014
|
+
} else if (await deadEntry()) {
|
|
226015
|
+
return;
|
|
225859
226016
|
} else {
|
|
225860
226017
|
birthTotal += round1.length;
|
|
225861
226018
|
const r1 = await birthValidate(repoRoot, round1, { skipBuild: true, onPhase: options.onBirthPhase, onScenarioSettled: bumpBirth });
|
|
@@ -225932,6 +226089,8 @@ async function generateGuards(options) {
|
|
|
225932
226089
|
const authorTotal = authTasks.length;
|
|
225933
226090
|
let authorDone = 0;
|
|
225934
226091
|
const bumpAuthor = (n) => options.onAuthorProgress?.(authorDone += n, authorTotal);
|
|
226092
|
+
if (authorTotal > 0)
|
|
226093
|
+
options.onAuthorProgress?.(authorDone, authorTotal);
|
|
225935
226094
|
const missTasks = [];
|
|
225936
226095
|
for (const t of authTasks) {
|
|
225937
226096
|
const cached = await readAuthorCache(repoRoot, t.claim, t.section, recipeFingerprint);
|
|
@@ -226003,7 +226162,8 @@ async function generateGuards(options) {
|
|
|
226003
226162
|
extractionFailures,
|
|
226004
226163
|
orphaned,
|
|
226005
226164
|
birthPassed,
|
|
226006
|
-
manifestPath: manifestPath(repoRoot)
|
|
226165
|
+
manifestPath: manifestPath(repoRoot),
|
|
226166
|
+
...entryPreflightFailure ? { entryPreflight: entryPreflightFailure } : {}
|
|
226007
226167
|
};
|
|
226008
226168
|
}
|
|
226009
226169
|
var key2 = (s) => `${s.doc}\0${s.anchor}`;
|
|
@@ -226814,12 +226974,14 @@ function previewChars() {
|
|
|
226814
226974
|
return 60 * 50;
|
|
226815
226975
|
}
|
|
226816
226976
|
async function estimateScanTokens(repoRoot, prices) {
|
|
226977
|
+
const decisions = readCorpusDecisions(repoRoot);
|
|
226978
|
+
const manualIncludes = decisions.manualIncludes ?? [];
|
|
226979
|
+
const manualExcludes = new Set(decisions.manualExcludes ?? []);
|
|
226817
226980
|
const docs = discoverDocs(repoRoot);
|
|
226818
|
-
const
|
|
226819
|
-
const nClassify = toClassify.length;
|
|
226820
|
-
const
|
|
226821
|
-
const
|
|
226822
|
-
const cachedKeptDocs = toClassify.filter((_, i) => relevance[i]?.include === true);
|
|
226981
|
+
const plan = await planRelevanceWork(repoRoot, docs, manualIncludes);
|
|
226982
|
+
const nClassify = plan.toClassify.length;
|
|
226983
|
+
const relevanceMissDocs = plan.needsCall;
|
|
226984
|
+
const cachedKeptDocs = plan.toClassify.filter((d) => plan.known.get(d.path)?.include === true && !manualExcludes.has(d.path));
|
|
226823
226985
|
const cachedKeptTagged = await Promise.all(cachedKeptDocs.map((d) => isAreaTagCached(repoRoot, d)));
|
|
226824
226986
|
const cachedKeptTagMisses = cachedKeptTagged.filter((cached) => !cached).length;
|
|
226825
226987
|
const estChangedKept = Math.round(relevanceMissDocs.length * KEEP_RATE);
|
|
@@ -231237,7 +231399,7 @@ function readToolVersion() {
|
|
|
231237
231399
|
if (cachedVersion)
|
|
231238
231400
|
return cachedVersion;
|
|
231239
231401
|
if (true) {
|
|
231240
|
-
cachedVersion = "0.7.0-next.
|
|
231402
|
+
cachedVersion = "0.7.0-next.2";
|
|
231241
231403
|
return cachedVersion;
|
|
231242
231404
|
}
|
|
231243
231405
|
try {
|
|
@@ -237598,32 +237760,20 @@ router10.delete(
|
|
|
237598
237760
|
}
|
|
237599
237761
|
}
|
|
237600
237762
|
);
|
|
237601
|
-
async function
|
|
237602
|
-
await mutate();
|
|
237603
|
-
const tracker = createSocketSpecTracker(repoId, CURATE_STEPS.map((s) => ({ ...s })));
|
|
237604
|
-
try {
|
|
237605
|
-
await curateInProcess(repoPath, { tracker, source: "dashboard" });
|
|
237606
|
-
emitSpecComplete(repoId, "scan");
|
|
237607
|
-
} catch (e) {
|
|
237608
|
-
emitSpecProgress(repoId, { step: "error", percent: 100, detail: e.message });
|
|
237609
|
-
throw e;
|
|
237610
|
-
}
|
|
237611
|
-
return corpusPayload(repoPath);
|
|
237612
|
-
}
|
|
237613
|
-
async function mutateSpecDecision(repoPath, repoId, res, mutate) {
|
|
237763
|
+
async function mutateSpecDecision(repoPath, res, mutate) {
|
|
237614
237764
|
if (!contractsMaterializeInPlace()) {
|
|
237615
237765
|
await mutate();
|
|
237616
237766
|
await recurateAndRegenIfResolved(repoPath);
|
|
237617
237767
|
res.json(await corpusPayload(repoPath));
|
|
237618
237768
|
return;
|
|
237619
237769
|
}
|
|
237620
|
-
|
|
237621
|
-
|
|
237622
|
-
|
|
237623
|
-
|
|
237624
|
-
|
|
237770
|
+
const decisions = await mutate();
|
|
237771
|
+
res.json({
|
|
237772
|
+
manualIncludes: decisions.manualIncludes ?? [],
|
|
237773
|
+
manualExcludes: decisions.manualExcludes ?? []
|
|
237774
|
+
});
|
|
237625
237775
|
}
|
|
237626
|
-
async function applySpecMutation(req, res, repoPath,
|
|
237776
|
+
async function applySpecMutation(req, res, repoPath, mutate) {
|
|
237627
237777
|
const parsed = parsePrScope(req);
|
|
237628
237778
|
if ("error" in parsed) {
|
|
237629
237779
|
res.status(400).json({ error: parsed.error });
|
|
@@ -237633,7 +237783,7 @@ async function applySpecMutation(req, res, repoPath, repoId, mutate) {
|
|
|
237633
237783
|
await mutateSpecDecisionPr(repoPath, parsed.scope, res, mutate);
|
|
237634
237784
|
return;
|
|
237635
237785
|
}
|
|
237636
|
-
await mutateSpecDecision(repoPath,
|
|
237786
|
+
await mutateSpecDecision(repoPath, res, () => mutate());
|
|
237637
237787
|
}
|
|
237638
237788
|
router10.post(
|
|
237639
237789
|
"/:id/spec/includes",
|
|
@@ -237650,7 +237800,6 @@ router10.post(
|
|
|
237650
237800
|
req,
|
|
237651
237801
|
res,
|
|
237652
237802
|
repo.path,
|
|
237653
|
-
req.params.id,
|
|
237654
237803
|
(opts) => addManualInclude(repo.path, ref, opts)
|
|
237655
237804
|
);
|
|
237656
237805
|
} catch (e) {
|
|
@@ -237673,7 +237822,6 @@ router10.delete(
|
|
|
237673
237822
|
req,
|
|
237674
237823
|
res,
|
|
237675
237824
|
repo.path,
|
|
237676
|
-
req.params.id,
|
|
237677
237825
|
(opts) => removeManualInclude(repo.path, ref, opts)
|
|
237678
237826
|
);
|
|
237679
237827
|
} catch (e) {
|
|
@@ -237696,7 +237844,6 @@ router10.post(
|
|
|
237696
237844
|
req,
|
|
237697
237845
|
res,
|
|
237698
237846
|
repo.path,
|
|
237699
|
-
req.params.id,
|
|
237700
237847
|
(opts) => addManualExclude(repo.path, ref, opts)
|
|
237701
237848
|
);
|
|
237702
237849
|
} catch (e) {
|
|
@@ -237719,7 +237866,6 @@ router10.delete(
|
|
|
237719
237866
|
req,
|
|
237720
237867
|
res,
|
|
237721
237868
|
repo.path,
|
|
237722
|
-
req.params.id,
|
|
237723
237869
|
(opts) => removeManualExclude(repo.path, ref, opts)
|
|
237724
237870
|
);
|
|
237725
237871
|
} catch (e) {
|
|
@@ -237741,6 +237887,8 @@ router10.get(
|
|
|
237741
237887
|
res.json({
|
|
237742
237888
|
contractsStale: false,
|
|
237743
237889
|
verifyStale: false,
|
|
237890
|
+
// EE re-curates on every decision, so decisions never outrun the corpus.
|
|
237891
|
+
decisionsPending: false,
|
|
237744
237892
|
hasCorpus: corpus !== null,
|
|
237745
237893
|
hasGenerated: contractFiles.length > 0,
|
|
237746
237894
|
hasVerified: verify2 !== null
|
|
@@ -237755,6 +237903,7 @@ router10.get(
|
|
|
237755
237903
|
res.json({
|
|
237756
237904
|
contractsStale,
|
|
237757
237905
|
verifyStale,
|
|
237906
|
+
decisionsPending: hasPendingDecisions(repo.path),
|
|
237758
237907
|
hasCorpus: corpusMtime !== null,
|
|
237759
237908
|
hasGenerated: generatedMtime !== null,
|
|
237760
237909
|
hasVerified: verifiedMtime !== null
|
|
@@ -237771,6 +237920,18 @@ function mtimeIfExists(file) {
|
|
|
237771
237920
|
return null;
|
|
237772
237921
|
}
|
|
237773
237922
|
}
|
|
237923
|
+
function hasPendingDecisions(repoPath) {
|
|
237924
|
+
const decisionsMtime = mtimeIfExists(decisionsPath(repoPath));
|
|
237925
|
+
if (decisionsMtime === null) return false;
|
|
237926
|
+
try {
|
|
237927
|
+
const corpus = JSON.parse(fs62.readFileSync(corpusFilePath(repoPath), "utf8"));
|
|
237928
|
+
const generatedAt = Date.parse(corpus.generatedAt ?? "");
|
|
237929
|
+
if (Number.isNaN(generatedAt)) return false;
|
|
237930
|
+
return decisionsMtime > generatedAt;
|
|
237931
|
+
} catch {
|
|
237932
|
+
return false;
|
|
237933
|
+
}
|
|
237934
|
+
}
|
|
237774
237935
|
var spec_default = router10;
|
|
237775
237936
|
|
|
237776
237937
|
// apps/dashboard/server/src/routes/contracts.ts
|
|
@@ -238862,7 +239023,8 @@ var GUARD_GENERATE_STEPS = [
|
|
|
238862
239023
|
var GUARD_STEP_STAGES = {
|
|
238863
239024
|
index: ["guard.recipe"],
|
|
238864
239025
|
extract: ["guard.extract"],
|
|
238865
|
-
author: ["guard.generate"]
|
|
239026
|
+
author: ["guard.generate"],
|
|
239027
|
+
validate: ["guard.retry"]
|
|
238866
239028
|
};
|
|
238867
239029
|
async function estimateGuard(repoRoot) {
|
|
238868
239030
|
return estimateGuardTokens(repoRoot, await getModelPrices());
|
|
@@ -238871,6 +239033,7 @@ function resolveGuardModels(repoRoot) {
|
|
|
238871
239033
|
return {
|
|
238872
239034
|
extract: resolveModel("guard.extract", void 0, repoRoot),
|
|
238873
239035
|
generate: resolveModel("guard.generate", void 0, repoRoot),
|
|
239036
|
+
retry: resolveModel("guard.retry", void 0, repoRoot),
|
|
238874
239037
|
recipe: resolveModel("guard.recipe", void 0, repoRoot),
|
|
238875
239038
|
fallback: resolveFallbackModel(repoRoot) ?? void 0
|
|
238876
239039
|
};
|
|
@@ -238912,10 +239075,20 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
238912
239075
|
cur = ni;
|
|
238913
239076
|
};
|
|
238914
239077
|
const withUsage = (key4, base) => `${base}${stageUsageTag(GUARD_STEP_STAGES[key4] ?? [], repoRoot)}`;
|
|
239078
|
+
let authorDone = 0;
|
|
239079
|
+
let authorTotal = 0;
|
|
239080
|
+
let authorFinished = false;
|
|
239081
|
+
let groundCaptured = 0;
|
|
239082
|
+
let groundPlanned = 0;
|
|
239083
|
+
const authorDetail = () => {
|
|
239084
|
+
const claims = `${authorDone}/${authorTotal} claim${authorTotal === 1 ? "" : "s"}`;
|
|
239085
|
+
const base = groundPlanned > 0 ? `grounding probes ${groundCaptured}/${groundPlanned} \xB7 authoring ${claims}` : claims;
|
|
239086
|
+
return withUsage("author", base);
|
|
239087
|
+
};
|
|
238915
239088
|
let building = false;
|
|
238916
|
-
let birthSeen = false;
|
|
238917
239089
|
let birthDone = 0;
|
|
238918
|
-
let
|
|
239090
|
+
let sectionsDone = 0;
|
|
239091
|
+
let sectionsTotal = 0;
|
|
238919
239092
|
let retrySeen = false;
|
|
238920
239093
|
let retryDone = 0;
|
|
238921
239094
|
let retryTotal = 0;
|
|
@@ -238926,10 +239099,10 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
238926
239099
|
tracker?.start("validate");
|
|
238927
239100
|
validateStarted = true;
|
|
238928
239101
|
}
|
|
238929
|
-
const parts = [building ? "building\u2026" : `birth ${birthDone}
|
|
239102
|
+
const parts = [`sections ${sectionsDone}/${sectionsTotal}`, building ? "building\u2026" : `birth ${birthDone}`];
|
|
238930
239103
|
if (retrySeen)
|
|
238931
|
-
parts.push(`retrying
|
|
238932
|
-
tracker?.detail("validate", parts.join(" \xB7 "));
|
|
239104
|
+
parts.push(`retrying ${retryDone}/${retryTotal}`);
|
|
239105
|
+
tracker?.detail("validate", withUsage("validate", parts.join(" \xB7 ")));
|
|
238933
239106
|
};
|
|
238934
239107
|
tracker?.start("index");
|
|
238935
239108
|
try {
|
|
@@ -238941,6 +239114,7 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
238941
239114
|
generateRunner: options.generateRunner,
|
|
238942
239115
|
recipeRunner: options.recipeRunner,
|
|
238943
239116
|
onPlan: (total, work) => {
|
|
239117
|
+
sectionsTotal = work;
|
|
238944
239118
|
tracker?.done("index", withUsage("index", `${work} of ${total} section${total === 1 ? "" : "s"} changed`));
|
|
238945
239119
|
cur = STEPS.indexOf("extract");
|
|
238946
239120
|
tracker?.start("extract", `0 views`);
|
|
@@ -238957,27 +239131,30 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
238957
239131
|
},
|
|
238958
239132
|
onAuthorProgress: (done, total) => {
|
|
238959
239133
|
advanceTo("author");
|
|
238960
|
-
|
|
238961
|
-
|
|
238962
|
-
|
|
238963
|
-
|
|
238964
|
-
tracker?.
|
|
238965
|
-
},
|
|
238966
|
-
onBirthPhase: (phase, total) => {
|
|
238967
|
-
if (phase === "build") {
|
|
238968
|
-
building = true;
|
|
239134
|
+
authorDone = done;
|
|
239135
|
+
authorTotal = total;
|
|
239136
|
+
if (done >= total) {
|
|
239137
|
+
authorFinished = true;
|
|
239138
|
+
tracker?.done("author", withUsage("author", `${done}/${total} claim${total === 1 ? "" : "s"}`));
|
|
238969
239139
|
} else {
|
|
238970
|
-
|
|
238971
|
-
if (!birthSeen && total !== void 0)
|
|
238972
|
-
birthTotal = total;
|
|
239140
|
+
tracker?.detail("author", authorDetail());
|
|
238973
239141
|
}
|
|
239142
|
+
},
|
|
239143
|
+
onGroundProgress: (captured, planned) => {
|
|
239144
|
+
groundCaptured = captured;
|
|
239145
|
+
groundPlanned = planned;
|
|
239146
|
+
if (authorFinished)
|
|
239147
|
+
return;
|
|
239148
|
+
advanceTo("author");
|
|
239149
|
+
tracker?.detail("author", authorDetail());
|
|
239150
|
+
},
|
|
239151
|
+
onBirthPhase: (phase) => {
|
|
239152
|
+
building = phase === "build";
|
|
238974
239153
|
renderValidate();
|
|
238975
239154
|
},
|
|
238976
|
-
onBirthProgress: (done
|
|
239155
|
+
onBirthProgress: (done) => {
|
|
238977
239156
|
building = false;
|
|
238978
|
-
birthSeen = true;
|
|
238979
239157
|
birthDone = done;
|
|
238980
|
-
birthTotal = total;
|
|
238981
239158
|
renderValidate();
|
|
238982
239159
|
},
|
|
238983
239160
|
onRetryProgress: (done, total) => {
|
|
@@ -238985,6 +239162,12 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
238985
239162
|
retryDone = done;
|
|
238986
239163
|
retryTotal = total;
|
|
238987
239164
|
renderValidate();
|
|
239165
|
+
},
|
|
239166
|
+
onSectionSettled: (settled, total) => {
|
|
239167
|
+
sectionsDone = settled;
|
|
239168
|
+
sectionsTotal = total;
|
|
239169
|
+
if (validateStarted)
|
|
239170
|
+
renderValidate();
|
|
238988
239171
|
}
|
|
238989
239172
|
});
|
|
238990
239173
|
for (let i = cur; i < STEPS.length; i++)
|
|
@@ -239008,7 +239191,7 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
|
239008
239191
|
}
|
|
239009
239192
|
}
|
|
239010
239193
|
}
|
|
239011
|
-
var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate"];
|
|
239194
|
+
var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate", "guard.retry"];
|
|
239012
239195
|
function sumGuardUsage() {
|
|
239013
239196
|
const usage = getStageUsage();
|
|
239014
239197
|
let calls = 0;
|
|
@@ -239056,6 +239239,8 @@ async function guardRunInProcess(repoRoot, options = {}) {
|
|
|
239056
239239
|
tracker?.done("run", `${n} scenario${n === 1 ? "" : "s"}`);
|
|
239057
239240
|
} else if (result.status === "build-failed") {
|
|
239058
239241
|
tracker?.error("build", `Build failed (\`${result.build.command}\`)${result.build.timedOut ? " \u2014 timed out" : ""}`);
|
|
239242
|
+
} else if (result.status === "entry-preflight-failed") {
|
|
239243
|
+
tracker?.error("build", `Entry failed to start: \`${result.preflight.entry}\` (rebuild via \`${result.buildCommand}\`)`);
|
|
239059
239244
|
}
|
|
239060
239245
|
return result;
|
|
239061
239246
|
}
|
|
@@ -239136,7 +239321,9 @@ router14.post("/:id/guard/run", async (req, res, next) => {
|
|
|
239136
239321
|
res.json({ status: "ok", summary: result.latest.summary });
|
|
239137
239322
|
return;
|
|
239138
239323
|
}
|
|
239139
|
-
if (result.status !== "build-failed"
|
|
239324
|
+
if (result.status !== "build-failed" && result.status !== "entry-preflight-failed") {
|
|
239325
|
+
emitSpecComplete(repoId, "guard-run");
|
|
239326
|
+
}
|
|
239140
239327
|
res.json({ status: result.status, message: runFailureMessage(result) });
|
|
239141
239328
|
} catch (e) {
|
|
239142
239329
|
emitSpecProgress(repoId, { step: "error", percent: 100, detail: e.message });
|
|
@@ -239155,6 +239342,12 @@ function runFailureMessage(result) {
|
|
|
239155
239342
|
return "No scenarios found under .truecourse/scenarios/. Generate scenarios first.";
|
|
239156
239343
|
case "build-failed":
|
|
239157
239344
|
return `Build failed (\`${result.build.command}\`)${result.build.timedOut ? " \u2014 timed out" : ""}. No scenarios ran.`;
|
|
239345
|
+
case "entry-preflight-failed":
|
|
239346
|
+
return formatEntryPreflightError({
|
|
239347
|
+
entry: result.preflight.entry,
|
|
239348
|
+
buildCommand: result.buildCommand,
|
|
239349
|
+
stderr: result.preflight.stderr
|
|
239350
|
+
});
|
|
239158
239351
|
}
|
|
239159
239352
|
}
|
|
239160
239353
|
var guard_actions_default = router14;
|