truecourse 0.7.0-next.1 → 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/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
- if (!opts.skipBuild && executable.length > 0) {
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 = [];
@@ -197279,35 +197366,49 @@ function prefilterDocs(docs, manualIncludes = []) {
197279
197366
  skipped: [...reasons].map(([path73, reason]) => ({ path: path73, reason }))
197280
197367
  };
197281
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
+ }
197282
197387
  async function filterByRelevance(repoRoot, docs, opts = {}) {
197283
197388
  if (opts.enabled === false || docs.length === 0) {
197284
197389
  return { included: docs, skipped: [] };
197285
197390
  }
197286
- const manualSet = new Set(opts.manualIncludes ?? []);
197287
197391
  const runner2 = opts.runner ?? spawnRelevanceRunner({ transport: opts.transport, model: opts.model, fallbackModel: opts.fallbackModel });
197288
197392
  const concurrency = opts.concurrency ?? defaultConcurrency();
197289
- const { toClassify, skipped: prefilterSkipped } = prefilterDocs(docs, opts.manualIncludes ?? []);
197290
- 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]));
197291
197395
  const total = docs.length;
197292
197396
  let done = 0;
197293
197397
  const markDone = () => opts.onProgress?.(++done, total);
197294
197398
  opts.onProgress?.(0, total);
197295
- for (let i = 0; i < prefilterReason.size; i++)
197399
+ const resolvedUpfront = prefilterReason.size + plan.known.size;
197400
+ for (let i = 0; i < resolvedUpfront; i++)
197296
197401
  markDone();
197297
- const verdicts = /* @__PURE__ */ new Map();
197402
+ const verdicts = new Map(plan.known);
197403
+ const pending = plan.needsCall;
197298
197404
  let cursor = 0;
197299
197405
  let active12 = 0;
197300
197406
  await new Promise((resolve11) => {
197407
+ if (pending.length === 0)
197408
+ return resolve11();
197301
197409
  const launch = () => {
197302
- while (active12 < concurrency && cursor < toClassify.length) {
197303
- const doc = toClassify[cursor++];
197304
- if (manualSet.has(doc.path)) {
197305
- verdicts.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
197306
- markDone();
197307
- if (cursor >= toClassify.length && active12 === 0)
197308
- resolve11();
197309
- continue;
197310
- }
197410
+ while (active12 < concurrency && cursor < pending.length) {
197411
+ const doc = pending[cursor++];
197311
197412
  active12++;
197312
197413
  classifyOne(repoRoot, doc, runner2).then((verdict) => {
197313
197414
  verdicts.set(doc.path, verdict);
@@ -197320,14 +197421,12 @@ async function filterByRelevance(repoRoot, docs, opts = {}) {
197320
197421
  }).finally(() => {
197321
197422
  markDone();
197322
197423
  active12--;
197323
- if (cursor >= toClassify.length && active12 === 0)
197424
+ if (cursor >= pending.length && active12 === 0)
197324
197425
  resolve11();
197325
197426
  else
197326
197427
  launch();
197327
197428
  });
197328
197429
  }
197329
- if (cursor >= toClassify.length && active12 === 0)
197330
- resolve11();
197331
197430
  };
197332
197431
  launch();
197333
197432
  });
@@ -197524,9 +197623,6 @@ async function readCache4(scope, cacheKey) {
197524
197623
  async function writeCache4(scope, cacheKey, verdict) {
197525
197624
  await setCacheEntry(scope, CACHE_NAME5, cacheKey, verdict);
197526
197625
  }
197527
- async function readRelevanceCache(repoRoot, doc) {
197528
- return readCache4(repoRoot, computeCacheKey5(doc));
197529
- }
197530
197626
 
197531
197627
  // packages/spec-consolidator/dist/curate.js
197532
197628
  async function curate(repoRoot, opts = {}) {
@@ -224323,6 +224419,9 @@ var STAGE_DEFAULTS = {
224323
224419
  // Authoring an executable scenario faithful to a spec claim is the hard,
224324
224420
  // load-bearing call (a weak scenario is false confidence) — opus.
224325
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",
224326
224425
  // Proposing a build/entry recipe is a modest structured task — sonnet.
224327
224426
  "guard.recipe": "sonnet",
224328
224427
  "rules.violationGen": "opus"
@@ -225024,11 +225123,11 @@ function spawnGenerateRunner2(opts = {}) {
225024
225123
  return async (ctx) => {
225025
225124
  const refs = ctx.claims.map((c) => c.ref).join(",");
225026
225125
  const isRetry = ctx.claims.some((c) => c.retry);
225027
- const suffix = `${isRetry ? ":retry" : ""}${ctx.correction ? ":correction" : ""}`;
225126
+ const stage = isRetry ? "guard.retry" : "guard.generate";
225028
225127
  const raw = await transport({
225029
- id: `guard.generate:${ctx.doc}:${refs}${suffix}`,
225030
- stage: "guard.generate",
225031
- model: opts.model,
225128
+ id: `${stage}:${ctx.doc}:${refs}${ctx.correction ? ":correction" : ""}`,
225129
+ stage,
225130
+ model: isRetry ? opts.retryModel ?? opts.model : opts.model,
225032
225131
  fallbackModel: opts.fallbackModel,
225033
225132
  system: GENERATE_SYSTEM_PROMPT,
225034
225133
  user: buildAuthorUserPrompt(ctx),
@@ -225340,12 +225439,15 @@ async function captureProbes(opts) {
225340
225439
  const cached = await getCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4);
225341
225440
  if (cached) {
225342
225441
  const parsed = ProbeTranscriptSchema.safeParse(cached);
225343
- if (parsed.success)
225442
+ if (parsed.success) {
225443
+ opts.onProbeCaptured?.();
225344
225444
  return parsed.data;
225445
+ }
225345
225446
  }
225346
225447
  const capture = await exec([...opts.resolvedEntry, ...argv], opts.recipeEnv);
225347
225448
  const transcript = toTranscript(argv, [...opts.displayEntry, ...argv], capture);
225348
225449
  await setCacheEntry(opts.repoRoot, GROUND_CACHE_NAME, key4, transcript);
225450
+ opts.onProbeCaptured?.();
225349
225451
  return transcript;
225350
225452
  }));
225351
225453
  }
@@ -225610,6 +225712,7 @@ function deleteScenarioFiles(repoRoot, ids) {
225610
225712
 
225611
225713
  // packages/guard-generator/dist/generate.js
225612
225714
  var GENERATE_CACHE_NAME = "guard/generate";
225715
+ var ENTRY_PREFLIGHT_ANCHOR = "(entry preflight)";
225613
225716
  function defaultConcurrency3() {
225614
225717
  const env = process.env.TRUECOURSE_MAX_CONCURRENCY;
225615
225718
  if (env) {
@@ -225679,7 +225782,12 @@ async function generateGuards(options) {
225679
225782
  const limit = pLimit(Math.max(1, options.concurrency ?? defaultConcurrency3()));
225680
225783
  const batchSize = Math.max(1, options.batchSize ?? defaultGenerateBatch2());
225681
225784
  const extractRunner = options.extractRunner ?? spawnExtractRunner({ transport: options.transport, model: options.models?.extract, fallbackModel: options.models?.fallback });
225682
- const generateRunner = options.generateRunner ?? spawnGenerateRunner2({ transport: options.transport, model: options.models?.generate, fallbackModel: options.models?.fallback });
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
+ });
225683
225791
  const coverageGaps = [];
225684
225792
  const errors = [];
225685
225793
  const extractionFailures = [];
@@ -225750,11 +225858,15 @@ async function generateGuards(options) {
225750
225858
  return buildPromise;
225751
225859
  };
225752
225860
  let resolvedEntryMemo = null;
225861
+ let groundPlanned = 0;
225862
+ let groundCaptured = 0;
225753
225863
  const groundClaims = async (claimTexts) => {
225754
225864
  const probes = deriveProbes(claimTexts, recipe.entry);
225755
225865
  const build8 = await buildPromise;
225756
- if (!build8.ok)
225866
+ if (!build8.ok || probes.length === 0)
225757
225867
  return [];
225868
+ groundPlanned += probes.length;
225869
+ options.onGroundProgress?.(groundCaptured, groundPlanned);
225758
225870
  resolvedEntryMemo ??= resolveEntry(repoRoot, recipe.entry);
225759
225871
  return captureProbes({
225760
225872
  repoRoot,
@@ -225762,9 +225874,21 @@ async function generateGuards(options) {
225762
225874
  resolvedEntry: resolvedEntryMemo,
225763
225875
  displayEntry: recipe.entry,
225764
225876
  recipeFingerprint,
225765
- recipeEnv: recipe.env
225877
+ recipeEnv: recipe.env,
225878
+ onProbeCaptured: () => options.onGroundProgress?.(++groundCaptured, groundPlanned)
225766
225879
  });
225767
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
+ };
225768
225892
  const priorSections = readPriorManifest(repoRoot);
225769
225893
  const manifestByKey = new Map(priorSections.map((e) => [`${e.doc}\0${e.anchor}`, e]));
225770
225894
  const usedIds = existingScenarioIds(repoRoot);
@@ -225788,9 +225912,11 @@ async function generateGuards(options) {
225788
225912
  });
225789
225913
  settledKeys.add(k);
225790
225914
  writeWorkingManifest();
225915
+ options.onSectionSettled?.(settledKeys.size, plan.work.length);
225791
225916
  };
225792
225917
  const written = [];
225793
225918
  const birthFindings = [];
225919
+ let entryPreflightFailure = null;
225794
225920
  let birthTotal = 0;
225795
225921
  let birthSettled = 0;
225796
225922
  let birthPassed = 0;
@@ -225837,6 +225963,20 @@ async function generateGuards(options) {
225837
225963
  return;
225838
225964
  settleChain = settleChain.then(() => settleCliSection(sectionByKey.get(k), refsBySection.get(k)));
225839
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
+ };
225840
225980
  async function settleCliSection(section, refs) {
225841
225981
  const k = key2(section);
225842
225982
  for (const id of priorIdsOf(k))
@@ -225871,6 +226011,8 @@ async function generateGuards(options) {
225871
226011
  const message = `build failed (\`${build8.command}\`)${build8.timedOut ? " \u2014 timed out" : ""}`;
225872
226012
  for (const c of round1)
225873
226013
  localErrors.push(errorFrom({ candidate: c, result: { failure: { actual: message } } }));
226014
+ } else if (await deadEntry()) {
226015
+ return;
225874
226016
  } else {
225875
226017
  birthTotal += round1.length;
225876
226018
  const r1 = await birthValidate(repoRoot, round1, { skipBuild: true, onPhase: options.onBirthPhase, onScenarioSettled: bumpBirth });
@@ -225947,6 +226089,8 @@ async function generateGuards(options) {
225947
226089
  const authorTotal = authTasks.length;
225948
226090
  let authorDone = 0;
225949
226091
  const bumpAuthor = (n) => options.onAuthorProgress?.(authorDone += n, authorTotal);
226092
+ if (authorTotal > 0)
226093
+ options.onAuthorProgress?.(authorDone, authorTotal);
225950
226094
  const missTasks = [];
225951
226095
  for (const t of authTasks) {
225952
226096
  const cached = await readAuthorCache(repoRoot, t.claim, t.section, recipeFingerprint);
@@ -226018,7 +226162,8 @@ async function generateGuards(options) {
226018
226162
  extractionFailures,
226019
226163
  orphaned,
226020
226164
  birthPassed,
226021
- manifestPath: manifestPath(repoRoot)
226165
+ manifestPath: manifestPath(repoRoot),
226166
+ ...entryPreflightFailure ? { entryPreflight: entryPreflightFailure } : {}
226022
226167
  };
226023
226168
  }
226024
226169
  var key2 = (s) => `${s.doc}\0${s.anchor}`;
@@ -226829,12 +226974,14 @@ function previewChars() {
226829
226974
  return 60 * 50;
226830
226975
  }
226831
226976
  async function estimateScanTokens(repoRoot, prices) {
226977
+ const decisions = readCorpusDecisions(repoRoot);
226978
+ const manualIncludes = decisions.manualIncludes ?? [];
226979
+ const manualExcludes = new Set(decisions.manualExcludes ?? []);
226832
226980
  const docs = discoverDocs(repoRoot);
226833
- const { toClassify } = prefilterDocs(docs);
226834
- const nClassify = toClassify.length;
226835
- const relevance = await Promise.all(toClassify.map((d) => readRelevanceCache(repoRoot, d)));
226836
- const relevanceMissDocs = toClassify.filter((_, i) => relevance[i] === null);
226837
- 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));
226838
226985
  const cachedKeptTagged = await Promise.all(cachedKeptDocs.map((d) => isAreaTagCached(repoRoot, d)));
226839
226986
  const cachedKeptTagMisses = cachedKeptTagged.filter((cached) => !cached).length;
226840
226987
  const estChangedKept = Math.round(relevanceMissDocs.length * KEEP_RATE);
@@ -231252,7 +231399,7 @@ function readToolVersion() {
231252
231399
  if (cachedVersion)
231253
231400
  return cachedVersion;
231254
231401
  if (true) {
231255
- cachedVersion = "0.7.0-next.1";
231402
+ cachedVersion = "0.7.0-next.2";
231256
231403
  return cachedVersion;
231257
231404
  }
231258
231405
  try {
@@ -238876,7 +239023,8 @@ var GUARD_GENERATE_STEPS = [
238876
239023
  var GUARD_STEP_STAGES = {
238877
239024
  index: ["guard.recipe"],
238878
239025
  extract: ["guard.extract"],
238879
- author: ["guard.generate"]
239026
+ author: ["guard.generate"],
239027
+ validate: ["guard.retry"]
238880
239028
  };
238881
239029
  async function estimateGuard(repoRoot) {
238882
239030
  return estimateGuardTokens(repoRoot, await getModelPrices());
@@ -238885,6 +239033,7 @@ function resolveGuardModels(repoRoot) {
238885
239033
  return {
238886
239034
  extract: resolveModel("guard.extract", void 0, repoRoot),
238887
239035
  generate: resolveModel("guard.generate", void 0, repoRoot),
239036
+ retry: resolveModel("guard.retry", void 0, repoRoot),
238888
239037
  recipe: resolveModel("guard.recipe", void 0, repoRoot),
238889
239038
  fallback: resolveFallbackModel(repoRoot) ?? void 0
238890
239039
  };
@@ -238926,10 +239075,20 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
238926
239075
  cur = ni;
238927
239076
  };
238928
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
+ };
238929
239088
  let building = false;
238930
- let birthSeen = false;
238931
239089
  let birthDone = 0;
238932
- let birthTotal = 0;
239090
+ let sectionsDone = 0;
239091
+ let sectionsTotal = 0;
238933
239092
  let retrySeen = false;
238934
239093
  let retryDone = 0;
238935
239094
  let retryTotal = 0;
@@ -238940,10 +239099,10 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
238940
239099
  tracker?.start("validate");
238941
239100
  validateStarted = true;
238942
239101
  }
238943
- const parts = [building ? "building\u2026" : `birth ${birthDone}/${birthTotal}`];
239102
+ const parts = [`sections ${sectionsDone}/${sectionsTotal}`, building ? "building\u2026" : `birth ${birthDone}`];
238944
239103
  if (retrySeen)
238945
- parts.push(`retrying failed claims ${retryDone}/${retryTotal}`);
238946
- tracker?.detail("validate", parts.join(" \xB7 "));
239104
+ parts.push(`retrying ${retryDone}/${retryTotal}`);
239105
+ tracker?.detail("validate", withUsage("validate", parts.join(" \xB7 ")));
238947
239106
  };
238948
239107
  tracker?.start("index");
238949
239108
  try {
@@ -238955,6 +239114,7 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
238955
239114
  generateRunner: options.generateRunner,
238956
239115
  recipeRunner: options.recipeRunner,
238957
239116
  onPlan: (total, work) => {
239117
+ sectionsTotal = work;
238958
239118
  tracker?.done("index", withUsage("index", `${work} of ${total} section${total === 1 ? "" : "s"} changed`));
238959
239119
  cur = STEPS.indexOf("extract");
238960
239120
  tracker?.start("extract", `0 views`);
@@ -238971,27 +239131,30 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
238971
239131
  },
238972
239132
  onAuthorProgress: (done, total) => {
238973
239133
  advanceTo("author");
238974
- const detail = withUsage("author", `${done}/${total} claim${total === 1 ? "" : "s"}`);
238975
- if (done >= total)
238976
- tracker?.done("author", detail);
238977
- else
238978
- tracker?.detail("author", detail);
238979
- },
238980
- onBirthPhase: (phase, total) => {
238981
- if (phase === "build") {
238982
- 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"}`));
238983
239139
  } else {
238984
- building = false;
238985
- if (!birthSeen && total !== void 0)
238986
- birthTotal = total;
239140
+ tracker?.detail("author", authorDetail());
238987
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";
238988
239153
  renderValidate();
238989
239154
  },
238990
- onBirthProgress: (done, total) => {
239155
+ onBirthProgress: (done) => {
238991
239156
  building = false;
238992
- birthSeen = true;
238993
239157
  birthDone = done;
238994
- birthTotal = total;
238995
239158
  renderValidate();
238996
239159
  },
238997
239160
  onRetryProgress: (done, total) => {
@@ -238999,6 +239162,12 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
238999
239162
  retryDone = done;
239000
239163
  retryTotal = total;
239001
239164
  renderValidate();
239165
+ },
239166
+ onSectionSettled: (settled, total) => {
239167
+ sectionsDone = settled;
239168
+ sectionsTotal = total;
239169
+ if (validateStarted)
239170
+ renderValidate();
239002
239171
  }
239003
239172
  });
239004
239173
  for (let i = cur; i < STEPS.length; i++)
@@ -239022,7 +239191,7 @@ async function guardGenerateInProcess(repoRoot, options = {}) {
239022
239191
  }
239023
239192
  }
239024
239193
  }
239025
- var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate"];
239194
+ var GUARD_USAGE_STAGES = ["guard.recipe", "guard.extract", "guard.generate", "guard.retry"];
239026
239195
  function sumGuardUsage() {
239027
239196
  const usage = getStageUsage();
239028
239197
  let calls = 0;
@@ -239070,6 +239239,8 @@ async function guardRunInProcess(repoRoot, options = {}) {
239070
239239
  tracker?.done("run", `${n} scenario${n === 1 ? "" : "s"}`);
239071
239240
  } else if (result.status === "build-failed") {
239072
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}\`)`);
239073
239244
  }
239074
239245
  return result;
239075
239246
  }
@@ -239150,7 +239321,9 @@ router14.post("/:id/guard/run", async (req, res, next) => {
239150
239321
  res.json({ status: "ok", summary: result.latest.summary });
239151
239322
  return;
239152
239323
  }
239153
- if (result.status !== "build-failed") emitSpecComplete(repoId, "guard-run");
239324
+ if (result.status !== "build-failed" && result.status !== "entry-preflight-failed") {
239325
+ emitSpecComplete(repoId, "guard-run");
239326
+ }
239154
239327
  res.json({ status: result.status, message: runFailureMessage(result) });
239155
239328
  } catch (e) {
239156
239329
  emitSpecProgress(repoId, { step: "error", percent: 100, detail: e.message });
@@ -239169,6 +239342,12 @@ function runFailureMessage(result) {
239169
239342
  return "No scenarios found under .truecourse/scenarios/. Generate scenarios first.";
239170
239343
  case "build-failed":
239171
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
+ });
239172
239351
  }
239173
239352
  }
239174
239353
  var guard_actions_default = router14;