@nathapp/nax 0.83.6 → 0.83.7

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.
Files changed (2) hide show
  1. package/dist/nax.js +522 -322
  2. package/package.json +2 -2
package/dist/nax.js CHANGED
@@ -322,6 +322,31 @@ var init_approval_audit = __esm(() => {
322
322
  init_secret_spans();
323
323
  });
324
324
 
325
+ // ../nax-agent/src/internal/atomic-write.ts
326
+ import { randomUUID } from "crypto";
327
+ import { rename, rm, writeFile } from "fs/promises";
328
+ async function writeFileAtomic(path, content, options = {}) {
329
+ const staged = `${path}.${randomUUID()}.tmp`;
330
+ try {
331
+ await _atomicWriteDeps.writeFile(staged, content, {
332
+ encoding: "utf8",
333
+ ...options.mode !== undefined ? { mode: options.mode } : {}
334
+ });
335
+ await _atomicWriteDeps.rename(staged, path);
336
+ } catch (error) {
337
+ await _atomicWriteDeps.rm(staged, { force: true });
338
+ throw error;
339
+ }
340
+ }
341
+ var _atomicWriteDeps;
342
+ var init_atomic_write = __esm(() => {
343
+ _atomicWriteDeps = {
344
+ writeFile: (path, content, options) => writeFile(path, content, options),
345
+ rename,
346
+ rm
347
+ };
348
+ });
349
+
325
350
  // ../nax-agent/src/internal/process-alive.ts
326
351
  function isProcessAlive(pid) {
327
352
  if (!Number.isInteger(pid) || pid <= 0)
@@ -335,7 +360,7 @@ function isProcessAlive(pid) {
335
360
  }
336
361
 
337
362
  // ../nax-agent/src/internal/file-lock.ts
338
- import { readFile, stat, unlink, writeFile } from "fs/promises";
363
+ import { readFile, stat, unlink, writeFile as writeFile2 } from "fs/promises";
339
364
  function unlinkQuiet(path) {
340
365
  return _fileLockDeps.unlink(path).catch(() => {});
341
366
  }
@@ -428,7 +453,7 @@ var DEFAULT_RETRY_MS = 10, DEFAULT_TIMEOUT_MS = 5000, EMPTY_LOCK_EVICT_AGE_MS =
428
453
  var init_file_lock = __esm(() => {
429
454
  init_infra();
430
455
  _fileLockDeps = {
431
- writeFile,
456
+ writeFile: writeFile2,
432
457
  readFile,
433
458
  stat,
434
459
  unlink,
@@ -453,7 +478,7 @@ var init_path_file_lock = __esm(() => {
453
478
 
454
479
  // ../nax-agent/src/permissions/approvals-store.ts
455
480
  import { createHash } from "crypto";
456
- import { mkdir as mkdir2, readFile as readFile2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
481
+ import { mkdir as mkdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
457
482
  import { dirname, isAbsolute, join as join2, relative, resolve } from "path";
458
483
  function approvalsPath(outputDir) {
459
484
  return join2(outputDir, "approvals.json");
@@ -529,14 +554,20 @@ async function readApprovals(path) {
529
554
  async function writeApprovalsFile(path, file) {
530
555
  const body = file.taint === undefined ? { entries: file.entries } : { taint: file.taint, entries: file.entries };
531
556
  await mkdir2(dirname(path), { recursive: true });
532
- await writeFile2(path, `${JSON.stringify(body, null, 2)}
533
- `, "utf8");
557
+ await writeFileAtomic(path, `${JSON.stringify(body, null, 2)}
558
+ `);
534
559
  }
535
560
  async function appendApproval(path, entry) {
536
561
  await mkdir2(dirname(path), { recursive: true });
537
562
  await withPathFileLock(path, async () => {
538
- const existing = await readApprovalsFile(path);
539
- await writeApprovalsFile(path, { taint: existing.taint, entries: [...existing.entries, entry] });
563
+ const read = await readApprovalsFileDetailed(path);
564
+ if (read.state === "unparseable") {
565
+ throw new NaxError("approvals.json could not be parsed; not rewriting it", "APPROVALS_STORE_UNPARSEABLE", {
566
+ stage: "permissions",
567
+ path
568
+ });
569
+ }
570
+ await writeApprovalsFile(path, { taint: read.file.taint, entries: [...read.file.entries, entry] });
540
571
  });
541
572
  }
542
573
  async function applyRemoval(path, decide) {
@@ -587,6 +618,8 @@ function isWithin(projectRoot, root) {
587
618
  }
588
619
  var EMPTY_FILE;
589
620
  var init_approvals_store = __esm(() => {
621
+ init_infra();
622
+ init_atomic_write();
590
623
  init_path_file_lock();
591
624
  EMPTY_FILE = { entries: [], taint: undefined };
592
625
  });
@@ -2212,8 +2245,8 @@ var init_chained_store = __esm(() => {
2212
2245
  });
2213
2246
 
2214
2247
  // ../nax-agent/src/native/credentials/fingerprint.ts
2215
- import { createHmac, randomBytes, randomUUID } from "crypto";
2216
- import { link, mkdir as mkdir4, readFile as readFile3, rm, writeFile as writeFile3 } from "fs/promises";
2248
+ import { createHmac, randomBytes, randomUUID as randomUUID2 } from "crypto";
2249
+ import { link, mkdir as mkdir4, readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
2217
2250
  import { dirname as dirname2, join as join5 } from "path";
2218
2251
  function _resetFingerprintSalt() {
2219
2252
  memoPath = undefined;
@@ -2235,7 +2268,7 @@ async function readSaltFile(path) {
2235
2268
  async function createSaltFile(path) {
2236
2269
  await mkdir4(dirname2(path), { recursive: true });
2237
2270
  const candidate = randomBytes(SALT_BYTES);
2238
- const staged = `${path}.${randomUUID()}.staged`;
2271
+ const staged = `${path}.${randomUUID2()}.staged`;
2239
2272
  try {
2240
2273
  await writeFile3(staged, candidate, { mode: 384 });
2241
2274
  try {
@@ -2247,7 +2280,7 @@ async function createSaltFile(path) {
2247
2280
  }
2248
2281
  return candidate;
2249
2282
  } finally {
2250
- await rm(staged, { force: true });
2283
+ await rm2(staged, { force: true });
2251
2284
  }
2252
2285
  }
2253
2286
  async function loadSalt(path) {
@@ -3170,11 +3203,16 @@ async function importPiCredentials(options) {
3170
3203
  cause: error
3171
3204
  });
3172
3205
  }
3206
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
3207
+ throw new NaxError(`The file at ${path} is not a provider credential map.`, "AUTH_IMPORT_SOURCE_UNREADABLE", {
3208
+ path
3209
+ });
3210
+ }
3173
3211
  const store = naxCredentialStore();
3174
3212
  const outcomes = [];
3175
3213
  for (const providerId of Object.keys(parsed).sort()) {
3176
3214
  const entry = parsed[providerId];
3177
- const credential = entry === undefined ? undefined : fromPiEntry(entry);
3215
+ const credential = typeof entry === "object" && entry !== null ? fromPiEntry(entry) : undefined;
3178
3216
  if (credential === undefined) {
3179
3217
  outcomes.push({ providerId, status: "unsupported" });
3180
3218
  continue;
@@ -3543,12 +3581,12 @@ var init_errors = __esm(() => {
3543
3581
  });
3544
3582
 
3545
3583
  // ../nax-agent/src/native/session-affinity.ts
3546
- import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
3584
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
3547
3585
  function nativeSessionId(sessionKey) {
3548
3586
  return createHash3("sha256").update(sessionKey).digest("hex").slice(0, ID_LENGTH);
3549
3587
  }
3550
3588
  function newSessionKey() {
3551
- return randomUUID2();
3589
+ return randomUUID3();
3552
3590
  }
3553
3591
  var ID_LENGTH = 32;
3554
3592
  var init_session_affinity = () => {};
@@ -3728,7 +3766,7 @@ __export(exports_transcript_store, {
3728
3766
  transcriptModelIdentity: () => transcriptModelIdentity,
3729
3767
  transcriptPath: () => transcriptPath
3730
3768
  });
3731
- import { mkdir as mkdir5, readdir, readFile as readFile6, rename, rm as rm2, stat as stat3, writeFile as writeFile4 } from "fs/promises";
3769
+ import { mkdir as mkdir5, readdir, readFile as readFile6, rename as rename2, rm as rm3, stat as stat3 } from "fs/promises";
3732
3770
  import { join as join9 } from "path";
3733
3771
  function transcriptPath(dir, sessionName) {
3734
3772
  return join9(dir, `${sessionName}.transcript.json`);
@@ -3762,7 +3800,7 @@ async function readTranscriptDoc(dir, sessionName) {
3762
3800
  }
3763
3801
  async function writeTranscriptDoc(dir, sessionName, doc) {
3764
3802
  await mkdir5(dir, { recursive: true });
3765
- await writeFile4(transcriptPath(dir, sessionName), JSON.stringify(doc, null, 2), "utf8");
3803
+ await writeFileAtomic(transcriptPath(dir, sessionName), JSON.stringify(doc, null, 2));
3766
3804
  }
3767
3805
  function createFileTranscriptStore(dir) {
3768
3806
  return {
@@ -3789,7 +3827,7 @@ async function saveTranscript(dir, sessionName, messages, identity = {}) {
3789
3827
  async function retainTranscript(dir, sessionName) {
3790
3828
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
3791
3829
  try {
3792
- await rename(transcriptPath(dir, sessionName), join9(dir, `${sessionName}.transcript.failed-${stamp}.json`));
3830
+ await rename2(transcriptPath(dir, sessionName), join9(dir, `${sessionName}.transcript.failed-${stamp}.json`));
3793
3831
  } catch (err) {
3794
3832
  if (err.code === "ENOENT")
3795
3833
  return;
@@ -3797,7 +3835,7 @@ async function retainTranscript(dir, sessionName) {
3797
3835
  }
3798
3836
  }
3799
3837
  async function deleteTranscript(dir, sessionName) {
3800
- await rm2(transcriptPath(dir, sessionName), { force: true });
3838
+ await rm3(transcriptPath(dir, sessionName), { force: true });
3801
3839
  }
3802
3840
  function _resetTranscriptTruncationWarningForTests() {
3803
3841
  hasWarnedAboutTranscriptTruncation = false;
@@ -3821,7 +3859,7 @@ async function pruneRetainedTranscripts(dir, maxRetained = MAX_RETAINED_TRANSCRI
3821
3859
  }));
3822
3860
  withMtimes.sort((a, b) => a.mtimeMs - b.mtimeMs);
3823
3861
  const toDelete = withMtimes.slice(0, withMtimes.length - maxRetained);
3824
- await Promise.all(toDelete.map((f) => rm2(f.path, { force: true })));
3862
+ await Promise.all(toDelete.map((f) => rm3(f.path, { force: true })));
3825
3863
  if (!hasWarnedAboutTranscriptTruncation) {
3826
3864
  hasWarnedAboutTranscriptTruncation = true;
3827
3865
  getLogger().warn("native-session", "Transcript retention cap reached \u2014 oldest kept-on-failure transcripts pruned", {
@@ -3835,6 +3873,7 @@ async function pruneRetainedTranscripts(dir, maxRetained = MAX_RETAINED_TRANSCRI
3835
3873
  var LEGACY_SAVED_AT, MAX_RETAINED_TRANSCRIPTS = 50, hasWarnedAboutTranscriptTruncation = false;
3836
3874
  var init_transcript_store = __esm(() => {
3837
3875
  init_infra();
3876
+ init_atomic_write();
3838
3877
  init_transcript_identity();
3839
3878
  init_transcript_identity();
3840
3879
  LEGACY_SAVED_AT = new Date(0).toISOString();
@@ -19554,7 +19593,7 @@ var init_schemas_sandbox = __esm(() => {
19554
19593
  });
19555
19594
 
19556
19595
  // ../nax-agent/src/sandbox/git-guards.ts
19557
- import { lstat, readdir as readdir2, readFile as readFile7, rm as rm3 } from "fs/promises";
19596
+ import { lstat, readdir as readdir2, readFile as readFile7, rm as rm4 } from "fs/promises";
19558
19597
  import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, resolve as resolve8 } from "path";
19559
19598
  function commonDirOf(git) {
19560
19599
  if (git.kind === "none")
@@ -19640,7 +19679,7 @@ var init_git_guards = __esm(() => {
19640
19679
  }
19641
19680
  },
19642
19681
  readText: (p) => readFile7(p, "utf8"),
19643
- remove: (p) => rm3(p, { recursive: true, force: true })
19682
+ remove: (p) => rm4(p, { recursive: true, force: true })
19644
19683
  };
19645
19684
  });
19646
19685
 
@@ -19671,7 +19710,7 @@ var init_messages = __esm(() => {
19671
19710
  });
19672
19711
 
19673
19712
  // ../nax-agent/src/sandbox/launcher.ts
19674
- import { randomUUID as randomUUID3 } from "crypto";
19713
+ import { randomUUID as randomUUID4 } from "crypto";
19675
19714
  import { mkdir as mkdir6 } from "fs/promises";
19676
19715
  function logicalArgv(req) {
19677
19716
  return req.spec.kind === "shell" ? [req.spec.shell, "-c", req.spec.command] : req.spec.argv;
@@ -19800,7 +19839,7 @@ var init_launcher = __esm(() => {
19800
19839
  DISABLED_SANDBOX_STATE = { kind: "disabled" };
19801
19840
  _launcherDeps = {
19802
19841
  runArgv,
19803
- newCommandId: () => randomUUID3(),
19842
+ newCommandId: () => randomUUID4(),
19804
19843
  mkdir: (path) => mkdir6(path, { recursive: true }).then(() => {
19805
19844
  return;
19806
19845
  })
@@ -19937,9 +19976,12 @@ function hardenedGitEnv(base) {
19937
19976
  function gitSpawnEnv(overlay) {
19938
19977
  return hardenedGitEnv(overlay === undefined ? process.env : { ...process.env, ...overlay });
19939
19978
  }
19979
+ function isGitProgram(token) {
19980
+ return token === "git" || (token?.endsWith("/git") ?? false);
19981
+ }
19940
19982
  function hardenedGitArgv(argv) {
19941
19983
  const out = [...argv];
19942
- let i = 1;
19984
+ let i = !isGitProgram(out[0]) && isGitProgram(out[1]) ? 2 : 1;
19943
19985
  while (i < out.length && out[i].startsWith("-")) {
19944
19986
  i += GLOBAL_OPTIONS_WITH_VALUE.has(out[i]) ? 2 : 1;
19945
19987
  }
@@ -20282,6 +20324,8 @@ function isUsableSharedParent() {
20282
20324
  const stats = _sessionTmpDeps.lstat(SHARED_TMP_PARENT);
20283
20325
  if (!stats.isDirectory() || stats.isSymbolicLink())
20284
20326
  return false;
20327
+ if (stats.uid !== _sessionTmpDeps.uid())
20328
+ return false;
20285
20329
  } catch (err) {
20286
20330
  return err.code === "ENOENT";
20287
20331
  }
@@ -20735,7 +20779,7 @@ var init_edit_region = __esm(() => {
20735
20779
  });
20736
20780
 
20737
20781
  // ../nax-agent/src/tools/edit.ts
20738
- import { readFile as readFile8, stat as stat5, writeFile as writeFile5 } from "fs/promises";
20782
+ import { readFile as readFile8, stat as stat5, writeFile as writeFile4 } from "fs/promises";
20739
20783
  function countOccurrences(haystack, needle) {
20740
20784
  if (needle === "")
20741
20785
  return 0;
@@ -20753,7 +20797,7 @@ var init_edit = __esm(() => {
20753
20797
  _editDeps = {
20754
20798
  stat: (path) => stat5(path),
20755
20799
  readFile: (path, encoding) => readFile8(path, encoding),
20756
- writeFile: (path, data, encoding) => writeFile5(path, data, encoding)
20800
+ writeFile: (path, data, encoding) => writeFile4(path, data, encoding)
20757
20801
  };
20758
20802
  editTool = {
20759
20803
  name: "Edit",
@@ -23713,7 +23757,7 @@ var init_denial_redirect = __esm(() => {
23713
23757
  });
23714
23758
 
23715
23759
  // ../nax-agent/src/tools/runtime-calltool.ts
23716
- import { randomUUID as randomUUID4 } from "crypto";
23760
+ import { randomUUID as randomUUID5 } from "crypto";
23717
23761
  function resolvePolicyIdentity(name, tool, input) {
23718
23762
  const argvField = tool.scope.argvField;
23719
23763
  const hasArgv = argvField !== undefined && input[argvField] !== undefined;
@@ -23724,7 +23768,7 @@ function openCallShadowTap(p) {
23724
23768
  if (p.shadow === undefined)
23725
23769
  return;
23726
23770
  return openShadowTap(p.shadow, {
23727
- key: randomUUID4(),
23771
+ key: randomUUID5(),
23728
23772
  identity: p.identity,
23729
23773
  command: p.tool.scope.commandField === undefined ? undefined : p.input[p.tool.scope.commandField],
23730
23774
  argv: p.hasArgv && p.argvField !== undefined ? p.input[p.argvField] : undefined,
@@ -23855,7 +23899,7 @@ var init_runtime_calltool = __esm(() => {
23855
23899
 
23856
23900
  // ../nax-agent/src/tools/scratchpad.ts
23857
23901
  import { existsSync as existsSync2 } from "fs";
23858
- import { mkdir as mkdir8, writeFile as writeFile6 } from "fs/promises";
23902
+ import { mkdir as mkdir8, writeFile as writeFile5 } from "fs/promises";
23859
23903
  import { dirname as dirname8, join as join18, sep as sep10 } from "path";
23860
23904
  var SCRATCHPAD_DIR = ".nax/scratchpad", MAX_MATCHES2 = 500, scratchpadWriteTool, scratchpadReadTool, scratchpadListTool;
23861
23905
  var init_scratchpad = __esm(() => {
@@ -23893,7 +23937,7 @@ var init_scratchpad = __esm(() => {
23893
23937
  }
23894
23938
  try {
23895
23939
  await mkdir8(dirname8(target), { recursive: true });
23896
- await writeFile6(target, content, "utf8");
23940
+ await writeFile5(target, content, "utf8");
23897
23941
  return { content: `wrote ${size} bytes` };
23898
23942
  } catch (err) {
23899
23943
  return { content: err instanceof Error ? err.message : String(err), isError: true };
@@ -23985,7 +24029,7 @@ ${content}`, resultBytesPreTruncation: fullBytes };
23985
24029
  });
23986
24030
 
23987
24031
  // ../nax-agent/src/tools/spill.ts
23988
- import { mkdir as mkdir9, writeFile as writeFile7 } from "fs/promises";
24032
+ import { mkdir as mkdir9, writeFile as writeFile6 } from "fs/promises";
23989
24033
  import { dirname as dirname9, join as join19 } from "path";
23990
24034
  function fileSafe(value) {
23991
24035
  return value.replace(/[^A-Za-z0-9_-]/g, "_");
@@ -24111,14 +24155,14 @@ var init_spill = __esm(() => {
24111
24155
  _spillDeps = {
24112
24156
  mkdir: (path) => mkdir9(path, { recursive: true }),
24113
24157
  writeFile: async (path, data) => {
24114
- await writeFile7(path, data, "utf8");
24158
+ await writeFile6(path, data, "utf8");
24115
24159
  return Buffer.byteLength(data);
24116
24160
  }
24117
24161
  };
24118
24162
  });
24119
24163
 
24120
24164
  // ../nax-agent/src/tools/tool-audit.ts
24121
- import { mkdir as mkdir10, writeFile as writeFile8 } from "fs/promises";
24165
+ import { mkdir as mkdir10, writeFile as writeFile7 } from "fs/promises";
24122
24166
  import { join as join20 } from "path";
24123
24167
  function registerToolAuditSink(runId, sink) {
24124
24168
  const sinks = openToolAuditSinks.get(runId);
@@ -24173,7 +24217,7 @@ function createToolAuditSink(opts) {
24173
24217
  calls: redactRowStrings(calls)
24174
24218
  }, null, 2);
24175
24219
  const prefix = runId !== undefined ? `${runId}-` : "";
24176
- await writeFile8(join20(opts.dir, `${prefix}${nextFileStamp()}-${opts.sessionName}.json`), body);
24220
+ await writeFile7(join20(opts.dir, `${prefix}${nextFileStamp()}-${opts.sessionName}.json`), body);
24177
24221
  };
24178
24222
  const sink = {
24179
24223
  record(entry) {
@@ -24214,7 +24258,7 @@ var init_tool_audit = __esm(() => {
24214
24258
  });
24215
24259
 
24216
24260
  // ../nax-agent/src/tools/write.ts
24217
- import { mkdir as mkdir11, writeFile as writeFile9 } from "fs/promises";
24261
+ import { mkdir as mkdir11, writeFile as writeFile8 } from "fs/promises";
24218
24262
  import { dirname as dirname10 } from "path";
24219
24263
  var writeTool;
24220
24264
  var init_write = __esm(() => {
@@ -24246,7 +24290,7 @@ var init_write = __esm(() => {
24246
24290
  }
24247
24291
  try {
24248
24292
  await mkdir11(dirname10(target), { recursive: true });
24249
- await writeFile9(target, content, "utf8");
24293
+ await writeFile8(target, content, "utf8");
24250
24294
  return { content: `wrote ${Buffer.byteLength(content, "utf8")} bytes` };
24251
24295
  } catch (err) {
24252
24296
  return { content: err instanceof Error ? err.message : String(err), isError: true };
@@ -24256,7 +24300,7 @@ var init_write = __esm(() => {
24256
24300
  });
24257
24301
 
24258
24302
  // ../nax-agent/src/tools/runtime.ts
24259
- import { randomUUID as randomUUID5 } from "crypto";
24303
+ import { randomUUID as randomUUID6 } from "crypto";
24260
24304
  function registerBuiltinCodingTools() {
24261
24305
  if (builtinsRegistered)
24262
24306
  return;
@@ -24340,7 +24384,7 @@ function createCodingToolRuntime(opts) {
24340
24384
  return body;
24341
24385
  return applyModelTruncationPolicy(body, {
24342
24386
  toolName,
24343
- callId: context?.toolCallId ?? randomUUID5(),
24387
+ callId: context?.toolCallId ?? randomUUID6(),
24344
24388
  root: opts.policy.root,
24345
24389
  maxBytes
24346
24390
  });
@@ -25516,7 +25560,6 @@ async function runModelRoundTrip(state, params, summarizeFailed) {
25516
25560
  }
25517
25561
  state.roundTrips += 1;
25518
25562
  params.usage.add(res.usage, res.costUsd, res.rates);
25519
- state.output = res.text;
25520
25563
  const promptTokens = inputClassTokens(res.usage);
25521
25564
  state.lastUsage = { promptTokens };
25522
25565
  state.anchorIndex = state.messages.length - 1;
@@ -25544,6 +25587,7 @@ async function runModelRoundTrip(state, params, summarizeFailed) {
25544
25587
  roundTrip: state.roundTrips
25545
25588
  });
25546
25589
  const assistantText = afterResponse.text ?? res.text;
25590
+ state.output = assistantText;
25547
25591
  const assistantToolCalls = afterResponse.toolCalls ?? res.toolCalls;
25548
25592
  const assistantThinking = afterResponse.thinking ?? res.thinking;
25549
25593
  state.messages.push({
@@ -25910,7 +25954,7 @@ async function streamComplete(client, model, req, onDelta) {
25910
25954
  var init_stream_complete = () => {};
25911
25955
 
25912
25956
  // ../nax-agent/src/native/session-adapter.ts
25913
- import { randomUUID as randomUUID6 } from "crypto";
25957
+ import { randomUUID as randomUUID7 } from "crypto";
25914
25958
  function summaryPrompt(previousSummary) {
25915
25959
  const base = "Summarize the conversation above so it can be dropped from context. " + "Record what was attempted, what was rejected and why, any decisions that still bind, " + "and list the files read and the files modified. Be specific: this summary is the only " + "memory of this work that survives.";
25916
25960
  if (previousSummary === undefined)
@@ -25998,7 +26042,7 @@ class NativeSessionAdapter {
25998
26042
  const instructionInteraction = createInstructionInteraction(this.state.repositoryInstructions.get(handle.id), opts.interactionHandler);
25999
26043
  const deadline = createTurnDeadline(timeoutSeconds);
26000
26044
  const hooks = this.state.streamHooks.get(handle.id);
26001
- const callId = randomUUID6();
26045
+ const callId = randomUUID7();
26002
26046
  const owner = this.state.transcriptOwners.get(handle.id);
26003
26047
  const eventBase = {
26004
26048
  callId,
@@ -26194,8 +26238,8 @@ var init_native = __esm(() => {
26194
26238
  });
26195
26239
 
26196
26240
  // ../nax-agent/src/session/agent-session-deps.ts
26197
- import { randomUUID as randomUUID7 } from "crypto";
26198
- import { mkdtemp, rm as rm4, stat as stat6 } from "fs/promises";
26241
+ import { randomUUID as randomUUID8 } from "crypto";
26242
+ import { mkdtemp, rm as rm5, stat as stat6 } from "fs/promises";
26199
26243
  import { tmpdir as tmpdir4 } from "os";
26200
26244
  import { join as join21 } from "path";
26201
26245
  var MAX_UNDELIVERED_CONTROL_EVENTS = 1000, _agentSessionDeps;
@@ -26204,7 +26248,7 @@ var init_agent_session_deps = __esm(() => {
26204
26248
  now: () => Date.now(),
26205
26249
  setTimeout: (fn, ms) => setTimeout(fn, ms),
26206
26250
  clearTimeout: (handle) => clearTimeout(handle),
26207
- randomUUID: () => randomUUID7(),
26251
+ randomUUID: () => randomUUID8(),
26208
26252
  isDirectory: async (path) => {
26209
26253
  try {
26210
26254
  return (await stat6(path)).isDirectory();
@@ -26213,7 +26257,7 @@ var init_agent_session_deps = __esm(() => {
26213
26257
  }
26214
26258
  },
26215
26259
  makeScratchRoot: () => mkdtemp(join21(tmpdir4(), "nax-agent-session-")),
26216
- removeScratchRoot: (dir) => rm4(dir, { recursive: true, force: true }),
26260
+ removeScratchRoot: (dir) => rm5(dir, { recursive: true, force: true }),
26217
26261
  controlEventCap: MAX_UNDELIVERED_CONTROL_EVENTS
26218
26262
  };
26219
26263
  });
@@ -29273,7 +29317,7 @@ function commandSpecIncludes(spec, fragment) {
29273
29317
  return normalizeCommandSpec(spec).some((entry) => entry.includes(fragment));
29274
29318
  }
29275
29319
  function replaceInCommandSpec(spec, searchValue, replacement) {
29276
- const replace = (command) => command.replaceAll(searchValue, replacement);
29320
+ const replace = (command) => command.replaceAll(searchValue, () => replacement);
29277
29321
  return typeof spec === "string" ? replace(spec) : spec.map(replace);
29278
29322
  }
29279
29323
  function renderCommandSpec(spec) {
@@ -29352,6 +29396,7 @@ __export(exports_internal, {
29352
29396
  _agentSessionDeps: () => _agentSessionDeps,
29353
29397
  _approvalsTaintDeps: () => _approvalsTaintDeps,
29354
29398
  _argvExecDeps: () => _argvExecDeps,
29399
+ _atomicWriteDeps: () => _atomicWriteDeps,
29355
29400
  _authDeps: () => _authDeps,
29356
29401
  _bashToolDeps: () => _bashToolDeps,
29357
29402
  _clientDeps: () => _clientDeps,
@@ -29632,6 +29677,7 @@ __export(exports_internal, {
29632
29677
  withPathFileLock: () => withPathFileLock,
29633
29678
  wrapExternalHandler: () => wrapExternalHandler,
29634
29679
  writeApprovalsFile: () => writeApprovalsFile,
29680
+ writeFileAtomic: () => writeFileAtomic,
29635
29681
  writeSpill: () => writeSpill,
29636
29682
  writeTool: () => writeTool
29637
29683
  });
@@ -29662,6 +29708,7 @@ var init_internal = __esm(() => {
29662
29708
  init_spin_breaker();
29663
29709
  init_agent_output_env();
29664
29710
  init_argv_exec();
29711
+ init_atomic_write();
29665
29712
  init_file_lock();
29666
29713
  init_git_env();
29667
29714
  init_git_exec();
@@ -31022,7 +31069,7 @@ var init_inert_bash_stages = __esm(() => {
31022
31069
 
31023
31070
  // src/utils/json-file.ts
31024
31071
  import { existsSync as existsSync5 } from "fs";
31025
- import { rename as rename2, unlink as unlink3 } from "fs/promises";
31072
+ import { rename as rename3, unlink as unlink3 } from "fs/promises";
31026
31073
  async function loadJsonFile(path, context = "json-file") {
31027
31074
  if (!existsSync5(path)) {
31028
31075
  return null;
@@ -31057,7 +31104,7 @@ async function atomicWriteText(path, content, context = "json-file") {
31057
31104
  const tmpPath = `${path}.tmp-${crypto.randomUUID()}`;
31058
31105
  try {
31059
31106
  await Bun.write(tmpPath, content);
31060
- await rename2(tmpPath, path);
31107
+ await rename3(tmpPath, path);
31061
31108
  } catch (err) {
31062
31109
  await unlink3(tmpPath).catch(() => {});
31063
31110
  const logger = getLogger2();
@@ -31370,6 +31417,12 @@ function _applyFinishAutoFlowShim(conf, warn = defaultConfigWarn) {
31370
31417
  }
31371
31418
  return { ...conf, finish: { ...lifted, ...explicitFinish } };
31372
31419
  }
31420
+ function _applyTddStrategyAliasShim(conf) {
31421
+ const tdd = conf.tdd;
31422
+ if (!tdd || typeof tdd !== "object" || tdd.strategy !== "tdd-simple")
31423
+ return conf;
31424
+ return { ...conf, tdd: { ...tdd, strategy: "simple" } };
31425
+ }
31373
31426
  function applyConfigCompatShims(conf, logger, dedupe) {
31374
31427
  const log = dedupe.wrapLogger(logger);
31375
31428
  const warn = dedupe.warn;
@@ -31386,6 +31439,7 @@ function applyConfigCompatShims(conf, logger, dedupe) {
31386
31439
  out = _applyFinishAutoFlowShim(out, warn);
31387
31440
  out = _applyRemovedOptimizerKeysShim(out, warn);
31388
31441
  out = _applyRemovedCrossPackageDepthShim(out, warn);
31442
+ out = _applyTddStrategyAliasShim(out);
31389
31443
  return out;
31390
31444
  }
31391
31445
  var SECURITY_SENSITIVE_KEY_PATHS, REMOVED_FINISH_KEYS;
@@ -32913,7 +32967,7 @@ var init_schemas_execution = __esm(() => {
32913
32967
  });
32914
32968
  TddConfigSchema = exports_external.object({
32915
32969
  maxRetries: exports_external.number().int().nonnegative(),
32916
- strategy: exports_external.enum(["auto", "strict", "lite", "off"]).default("auto"),
32970
+ strategy: exports_external.enum(["auto", "strict", "lite", "simple", "off"]).default("auto"),
32917
32971
  sessionTiers: exports_external.object({
32918
32972
  testWriter: ConfiguredModelSchema.default("fast"),
32919
32973
  verifier: ConfiguredModelSchema.default("fast"),
@@ -36201,18 +36255,20 @@ var init_spec_structure_grammar = __esm(() => {
36201
36255
  function declaredStoryIds(lines) {
36202
36256
  const ids = new Set;
36203
36257
  let inScope = false;
36204
- let inSkippedSubsection = false;
36258
+ let skipLevel = 0;
36205
36259
  for (const line of lines) {
36206
36260
  if (/^##\s/.test(line)) {
36207
36261
  inScope = /^##\s+(Stories|Acceptance Criteria)\b/i.test(line);
36208
- inSkippedSubsection = false;
36262
+ skipLevel = 0;
36209
36263
  continue;
36210
36264
  }
36211
36265
  if (!inScope)
36212
36266
  continue;
36213
- if (/^#{3,6}\s/.test(line))
36214
- inSkippedSubsection = GROUPED_PATH_SUBSECTION.test(line);
36215
- if (inSkippedSubsection)
36267
+ const level = /^(#{3,6})\s/.exec(line)?.[1]?.length;
36268
+ if (level !== undefined && (skipLevel === 0 || level <= skipLevel)) {
36269
+ skipLevel = GROUPED_PATH_SUBSECTION.test(line) ? level : 0;
36270
+ }
36271
+ if (skipLevel > 0)
36216
36272
  continue;
36217
36273
  const heading = /^#{1,6}\s+(US-\d+)\b/i.exec(line);
36218
36274
  if (heading?.[1]) {
@@ -38089,11 +38145,15 @@ import path2 from "path";
38089
38145
  async function acceptCommand(options) {
38090
38146
  const logger = getLogger2();
38091
38147
  const { feature, override, reason } = options;
38092
- if (!override.match(/^AC-\d+$/i)) {
38093
- logger.error("cli", "Invalid AC ID format", { override, expected: "AC-1, AC-2, etc." });
38094
- throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected: "AC-1, AC-2, etc." });
38095
- }
38096
- const acId = override.toUpperCase();
38148
+ const expected = "AC-1, or <package>::AC-1 (package path relative to the repo root)";
38149
+ const match = /^(?:(.+)::)?(AC-\d+)$/i.exec(override);
38150
+ const pkg = match?.[1];
38151
+ const ac = match?.[2];
38152
+ if (!ac || pkg === "") {
38153
+ logger.error("cli", "Invalid AC ID format", { override, expected });
38154
+ throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected });
38155
+ }
38156
+ const acId = pkg === undefined ? ac.toUpperCase() : `${pkg}::${ac.toUpperCase()}`;
38097
38157
  const projectDirResult = findProjectDir(process.cwd());
38098
38158
  if (!projectDirResult) {
38099
38159
  logger.error("cli", "Not in a nax project directory", { hint: "Run 'nax init' first" });
@@ -38494,7 +38554,7 @@ var package_default;
38494
38554
  var init_package = __esm(() => {
38495
38555
  package_default = {
38496
38556
  name: "@nathapp/nax",
38497
- version: "0.83.6",
38557
+ version: "0.83.7",
38498
38558
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
38499
38559
  type: "module",
38500
38560
  bin: {
@@ -38552,7 +38612,7 @@ var init_package = __esm(() => {
38552
38612
  "test:unit": "bun test ./test/unit/ --timeout=60000",
38553
38613
  "test:integration": "bun test ./test/integration/ --timeout=60000",
38554
38614
  "test:ui": "bun test ./test/ui/ --timeout=60000",
38555
- "test:e2e": "timeout -k 5s 180s bun test test/e2e/ --timeout=60000",
38615
+ "test:e2e": "bun run scripts/run-tests.ts --e2e",
38556
38616
  "test:coverage": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files",
38557
38617
  "test:coverage:report": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files --report",
38558
38618
  "test:coverage:update": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files --update-baseline",
@@ -39781,19 +39841,24 @@ function isTestFileByPatterns(filePath2, patterns) {
39781
39841
  }
39782
39842
  function globsToPathspec(patterns) {
39783
39843
  const result = [];
39784
- const seen = new Set;
39844
+ const add = (pathspec) => {
39845
+ if (!result.includes(pathspec))
39846
+ result.push(pathspec);
39847
+ };
39785
39848
  for (const pattern of patterns) {
39786
- const lastStar = pattern.lastIndexOf("*");
39787
- if (lastStar === -1)
39849
+ const segment = pattern.slice(pattern.lastIndexOf("/") + 1);
39850
+ const firstStar = segment.indexOf("*");
39851
+ if (firstStar === -1)
39788
39852
  continue;
39789
- const suffix = pattern.slice(lastStar + 1);
39790
- if (suffix.length === 0)
39853
+ if (firstStar > 0) {
39854
+ add(`:!${segment}`);
39855
+ add(`:!*/${segment}`);
39791
39856
  continue;
39792
- const pathspec = `:!*${suffix}`;
39793
- if (!seen.has(pathspec)) {
39794
- result.push(pathspec);
39795
- seen.add(pathspec);
39796
39857
  }
39858
+ const suffix = segment.slice(segment.lastIndexOf("*") + 1);
39859
+ if (suffix.length === 0 || BARE_EXTENSION.test(suffix))
39860
+ continue;
39861
+ add(`:!*${suffix}`);
39797
39862
  }
39798
39863
  return result;
39799
39864
  }
@@ -39807,9 +39872,24 @@ function extractTestDirs(globs) {
39807
39872
  }
39808
39873
  return [...dirs];
39809
39874
  }
39810
- var DEFAULT_TEST_FILE_PATTERNS, DEFAULT_SCAN_TEST_DIRS, DEFAULT_TS_DERIVE_SUFFIXES, DEFAULT_SEPARATED_TEST_DIRS;
39875
+ function extractWholeTestDirs(globs) {
39876
+ const dirs = new Set;
39877
+ for (const glob2 of globs) {
39878
+ const segments = glob2.split("/");
39879
+ const first = segments[0];
39880
+ if (!first || first.includes("*") || segments.length < 2)
39881
+ continue;
39882
+ const filename = segments[segments.length - 1] ?? "";
39883
+ const coversEverything = filename === "*" || filename === "**" || filename.startsWith("*") && BARE_EXTENSION.test(filename.slice(1));
39884
+ if (coversEverything)
39885
+ dirs.add(first);
39886
+ }
39887
+ return [...dirs];
39888
+ }
39889
+ var DEFAULT_TEST_FILE_PATTERNS, BARE_EXTENSION, DEFAULT_SCAN_TEST_DIRS, DEFAULT_TS_DERIVE_SUFFIXES, DEFAULT_SEPARATED_TEST_DIRS;
39811
39890
  var init_conventions = __esm(() => {
39812
39891
  DEFAULT_TEST_FILE_PATTERNS = Object.freeze(["test/**/*.test.ts"]);
39892
+ BARE_EXTENSION = /^\.[A-Za-z0-9]+$/;
39813
39893
  DEFAULT_SCAN_TEST_DIRS = Object.freeze([
39814
39894
  "test",
39815
39895
  "tests",
@@ -40292,29 +40372,11 @@ async function detectFromFileScan(workdir) {
40292
40372
  const filtered = files.filter((f) => !isExcluded(f));
40293
40373
  if (filtered.length === 0)
40294
40374
  return null;
40295
- const counts = {};
40296
- for (const suffix of CANDIDATE_SUFFIXES) {
40297
- counts[suffix] = 0;
40298
- }
40299
- for (const file2 of filtered) {
40300
- for (const suffix of CANDIDATE_SUFFIXES) {
40301
- if (file2.endsWith(suffix)) {
40302
- counts[suffix] = (counts[suffix] ?? 0) + 1;
40303
- }
40304
- }
40305
- }
40306
40375
  const totalFiles = filtered.length;
40307
- const patterns = [];
40308
- for (const suffix of CANDIDATE_SUFFIXES) {
40309
- const count = counts[suffix] ?? 0;
40310
- if (count === 0)
40311
- continue;
40312
- if (count >= MIN_COUNT_THRESHOLD || count / totalFiles >= MIN_FRACTION_THRESHOLD) {
40313
- const glob2 = SUFFIX_TO_GLOB[suffix];
40314
- if (glob2)
40315
- patterns.push(glob2);
40316
- }
40317
- }
40376
+ const patterns = CANDIDATES.filter((candidate) => {
40377
+ const count = filtered.filter((file2) => candidate.matches(file2)).length;
40378
+ return count > 0 && (count >= MIN_COUNT_THRESHOLD || count / totalFiles >= MIN_FRACTION_THRESHOLD);
40379
+ }).map((candidate) => candidate.glob);
40318
40380
  if (patterns.length === 0)
40319
40381
  return null;
40320
40382
  return {
@@ -40323,41 +40385,35 @@ async function detectFromFileScan(workdir) {
40323
40385
  patterns
40324
40386
  };
40325
40387
  }
40326
- var DRAIN_TIMEOUT2, EXCLUDED_DIR_PREFIXES, MIN_COUNT_THRESHOLD = 5, MIN_FRACTION_THRESHOLD = 0.1, FILE_SCAN_GIT_TIMEOUT_MS = 4000, FILE_SCAN_DRAIN_TIMEOUT_MS = 2000, CANDIDATE_SUFFIXES, SUFFIX_TO_GLOB, _fileScanDeps;
40388
+ var DRAIN_TIMEOUT2, EXCLUDED_DIR_PREFIXES, MIN_COUNT_THRESHOLD = 5, MIN_FRACTION_THRESHOLD = 0.1, FILE_SCAN_GIT_TIMEOUT_MS = 4000, FILE_SCAN_DRAIN_TIMEOUT_MS = 2000, bySuffix = (suffix) => ({
40389
+ glob: `**/*${suffix}`,
40390
+ matches: (path3) => path3.endsWith(suffix)
40391
+ }), byBasenamePrefix = (prefix, extension) => ({
40392
+ glob: `**/${prefix}*${extension}`,
40393
+ matches: (path3) => {
40394
+ const base = path3.slice(path3.lastIndexOf("/") + 1);
40395
+ return base.startsWith(prefix) && base.endsWith(extension) && base.length > prefix.length + extension.length;
40396
+ }
40397
+ }), CANDIDATES, _fileScanDeps;
40327
40398
  var init_file_scan = __esm(() => {
40328
40399
  init_internal();
40329
40400
  DRAIN_TIMEOUT2 = Symbol("drain-timeout");
40330
40401
  EXCLUDED_DIR_PREFIXES = ["node_modules/", "dist/", "build/", ".nax/", "coverage/", ".git/"];
40331
- CANDIDATE_SUFFIXES = [
40332
- ".test.ts",
40333
- ".test.tsx",
40334
- ".test.js",
40335
- ".test.jsx",
40336
- ".spec.ts",
40337
- ".spec.tsx",
40338
- ".spec.js",
40339
- ".spec.jsx",
40340
- ".e2e-spec.ts",
40341
- ".e2e-spec.js",
40342
- "_test.go",
40343
- "_test.py",
40344
- "test_.py"
40402
+ CANDIDATES = [
40403
+ bySuffix(".test.ts"),
40404
+ bySuffix(".test.tsx"),
40405
+ bySuffix(".test.js"),
40406
+ bySuffix(".test.jsx"),
40407
+ bySuffix(".spec.ts"),
40408
+ bySuffix(".spec.tsx"),
40409
+ bySuffix(".spec.js"),
40410
+ bySuffix(".spec.jsx"),
40411
+ bySuffix(".e2e-spec.ts"),
40412
+ bySuffix(".e2e-spec.js"),
40413
+ bySuffix("_test.go"),
40414
+ bySuffix("_test.py"),
40415
+ byBasenamePrefix("test_", ".py")
40345
40416
  ];
40346
- SUFFIX_TO_GLOB = {
40347
- ".test.ts": "**/*.test.ts",
40348
- ".test.tsx": "**/*.test.tsx",
40349
- ".test.js": "**/*.test.js",
40350
- ".test.jsx": "**/*.test.jsx",
40351
- ".spec.ts": "**/*.spec.ts",
40352
- ".spec.tsx": "**/*.spec.tsx",
40353
- ".spec.js": "**/*.spec.js",
40354
- ".spec.jsx": "**/*.spec.jsx",
40355
- ".e2e-spec.ts": "**/*.e2e-spec.ts",
40356
- ".e2e-spec.js": "**/*.e2e-spec.js",
40357
- "_test.go": "**/*_test.go",
40358
- "_test.py": "**/*_test.py",
40359
- "test_.py": "**/test_*.py"
40360
- };
40361
40417
  _fileScanDeps = {
40362
40418
  spawn: Bun.spawn,
40363
40419
  killProcessGroup,
@@ -40839,7 +40895,7 @@ async function detectFromCargoToml(workdir) {
40839
40895
  const path3 = `${workdir}/Cargo.toml`;
40840
40896
  if (!await _frameworkDefaultsDeps.fileExists(path3))
40841
40897
  return null;
40842
- return { type: "manifest", framework: "rust", path: path3, patterns: ["tests/**/*.rs", "src/**/*.rs"] };
40898
+ return { type: "manifest", framework: "rust", path: path3, patterns: ["tests/**/*.rs"] };
40843
40899
  }
40844
40900
  async function detectFromPyprojectDeps(workdir) {
40845
40901
  const path3 = `${workdir}/pyproject.toml`;
@@ -41751,7 +41807,7 @@ function resolveReviewExcludePatterns(userExplicit, resolved) {
41751
41807
  const result = new Set;
41752
41808
  for (const p of resolved.pathspec)
41753
41809
  result.add(p);
41754
- for (const d of resolved.testDirs)
41810
+ for (const d of extractWholeTestDirs(resolved.globs))
41755
41811
  result.add(`:!${d}/`);
41756
41812
  for (const d of WELL_KNOWN_TEST_DIRS2)
41757
41813
  result.add(`:!${d}/`);
@@ -42371,6 +42427,7 @@ __export(exports_test_runners, {
42371
42427
  detectTestFilePatterns: () => detectTestFilePatterns,
42372
42428
  discoverWorkspacePackages: () => discoverWorkspacePackages,
42373
42429
  extractTestDirs: () => extractTestDirs,
42430
+ extractWholeTestDirs: () => extractWholeTestDirs,
42374
42431
  findPackageDir: () => findPackageDir,
42375
42432
  formatFailureSummary: () => formatFailureSummary,
42376
42433
  globsToPathspec: () => globsToPathspec,
@@ -44648,7 +44705,7 @@ var init_hermetic = __esm(() => {
44648
44705
  });
44649
44706
 
44650
44707
  // src/prompts/sections/protocol-region.ts
44651
- import { randomUUID as randomUUID8 } from "crypto";
44708
+ import { randomUUID as randomUUID9 } from "crypto";
44652
44709
  function call(tool, input) {
44653
44710
  return `${tool} ${JSON.stringify(input)}`;
44654
44711
  }
@@ -44765,7 +44822,7 @@ var KIND_PATTERN, NONCE, PROTOCOL_REGION_MARKER_PREFIX = "<!--nax:", REGION, OWN
44765
44822
  var init_protocol_region = __esm(() => {
44766
44823
  init_errors4();
44767
44824
  KIND_PATTERN = /^[a-z][a-z-]*$/;
44768
- NONCE = randomUUID8().slice(0, 8);
44825
+ NONCE = randomUUID9().slice(0, 8);
44769
44826
  REGION = new RegExp(`<!--nax:([a-z][a-z-]*):([a-f0-9]+) (\\{.*?\\})-->\\n` + `((?:(?!<!--nax:[a-z][a-z-]*:[a-f0-9]+ )[\\s\\S])*?)` + `<!--\\/nax:\\1-->\\n?`, "g");
44770
44827
  OWN_OPEN = new RegExp(`<!--nax:[a-z][a-z-]*:${NONCE} `, "g");
44771
44828
  REGISTRY = {
@@ -47324,7 +47381,7 @@ ${section}`);
47324
47381
  const failingFiles = Array.from(new Set(findings.map((f) => f.file).filter((f) => !!f)));
47325
47382
  if (failingFiles.length > 0) {
47326
47383
  const perFileLines = failingFiles.map((file2) => {
47327
- const scopedCmd = opts.testScopedTemplate ? opts.testScopedTemplate.replace("{{files}}", file2) : `${opts.testCommand} ${file2}`;
47384
+ const scopedCmd = opts.testScopedTemplate ? opts.testScopedTemplate.replaceAll("{{files}}", () => file2) : `${opts.testCommand} ${file2}`;
47328
47385
  if (opts.testScopedTemplate && opts.fileScopeCommandName) {
47329
47386
  return ` ${wrapAffordance("run-test", { command: opts.fileScopeCommandName, files: file2 }, scopedCmd)}`;
47330
47387
  }
@@ -47754,7 +47811,7 @@ ${output}
47754
47811
  const cmd = testCommand ?? "";
47755
47812
  const failingFiles = Array.from(new Set(failures.map((f) => f.file)));
47756
47813
  const testCommands = failingFiles.map((file2) => {
47757
- const scopedCmd = testScopedTemplate ? testScopedTemplate.replace("{{files}}", file2) : cmd ? `${cmd} ${file2}` : file2;
47814
+ const scopedCmd = testScopedTemplate ? testScopedTemplate.replaceAll("{{files}}", () => file2) : cmd ? `${cmd} ${file2}` : file2;
47758
47815
  return ` ${scopedCmd}`;
47759
47816
  }).join(`
47760
47817
  `);
@@ -49845,7 +49902,7 @@ async function loadCanonicalRules(workdir, options = {}) {
49845
49902
  if (allViolations.length > 0) {
49846
49903
  throw new NeutralityLintError(allViolations);
49847
49904
  }
49848
- rules.sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || (a.id ?? a.fileName).localeCompare(b.id ?? b.fileName));
49905
+ rules.sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || byCodePoint(a.id ?? a.fileName, b.id ?? b.fileName));
49849
49906
  logger.debug("canonical-loader", "Scanned canonical rules store", {
49850
49907
  fileCount: rules.length,
49851
49908
  files: rules.map((r) => r.path)
@@ -49879,6 +49936,7 @@ async function loadCanonicalRules(workdir, options = {}) {
49879
49936
  }
49880
49937
  var CANONICAL_RULES_DIR = ".nax/rules", _canonicalLoaderDeps, NEUTRALITY_RULES, DEFAULT_CANONICAL_RULES_BUDGET_TOKENS = 8192, RULES_BUDGET_WARNING_RATIO = 0.75, RULE_ALLOW_MARKER, NeutralityLintError, RulesFrontmatterLintError;
49881
49938
  var init_canonical_loader = __esm(() => {
49939
+ init_internal();
49882
49940
  init_errors4();
49883
49941
  init_logger2();
49884
49942
  init_rules_frontmatter();
@@ -50557,7 +50615,7 @@ function applySectionBudget(sections2, budgetTokens) {
50557
50615
  overageTokens: totalTokens
50558
50616
  };
50559
50617
  }
50560
- const sorted = [...sections2].sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || ownerIdentifier(a).localeCompare(ownerIdentifier(b)) || a.ordinal - b.ordinal);
50618
+ const sorted = [...sections2].sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || byCodePoint(ownerIdentifier(a), ownerIdentifier(b)) || a.ordinal - b.ordinal);
50561
50619
  const kept = [];
50562
50620
  const droppedIds = [];
50563
50621
  const closedOwners = new Set;
@@ -50589,6 +50647,7 @@ function applySectionBudget(sections2, budgetTokens) {
50589
50647
  };
50590
50648
  }
50591
50649
  var init_rule_budget = __esm(() => {
50650
+ init_internal();
50592
50651
  init_rules_frontmatter();
50593
50652
  });
50594
50653
 
@@ -51687,7 +51746,7 @@ var init_loop_handler_forwarding = __esm(() => {
51687
51746
  });
51688
51747
 
51689
51748
  // src/session/manager-deps.ts
51690
- import { randomUUID as randomUUID9 } from "crypto";
51749
+ import { randomUUID as randomUUID10 } from "crypto";
51691
51750
  import { mkdir as mkdir14 } from "fs/promises";
51692
51751
  import { isAbsolute as isAbsolute13, join as join39, relative as relative18, sep as sep13 } from "path";
51693
51752
  function resolveProjectDirFromScratchDir(scratchDir) {
@@ -51729,7 +51788,7 @@ var init_manager_deps = __esm(() => {
51729
51788
  _sessionManagerDeps = {
51730
51789
  now: () => new Date().toISOString(),
51731
51790
  nowMs: () => Date.now(),
51732
- uuid: () => randomUUID9(),
51791
+ uuid: () => randomUUID10(),
51733
51792
  sessionScratchDir: (projectDir, featureName, sessionId) => join39(featureDir(projectDir, featureName), "sessions", sessionId),
51734
51793
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
51735
51794
  await mkdir14(scratchDir, { recursive: true });
@@ -52220,10 +52279,9 @@ class SessionManager {
52220
52279
  const reuse = decideReuse(liveHandle, this._findByName(name), opts);
52221
52280
  if (liveHandle && reuse === "reuse")
52222
52281
  return liveHandle;
52223
- if (liveHandle && reuse === "close-then-reopen") {
52224
- await this.closeSession(liveHandle);
52225
- this._busySessions.add(name);
52226
- } else if (liveHandle)
52282
+ if (liveHandle && reuse === "close-then-reopen")
52283
+ await this.closeSession(liveHandle, true);
52284
+ else if (liveHandle)
52227
52285
  this._liveHandles.delete(name);
52228
52286
  const adapter = this._getAdapter(opts.agentName);
52229
52287
  if (!adapter) {
@@ -52252,6 +52310,7 @@ class SessionManager {
52252
52310
  this._liveHandles.set(name, handle);
52253
52311
  const protocolIds = handle.protocolIds ?? NULL_PROTOCOL_IDS;
52254
52312
  if (!existingDescriptor) {
52313
+ this._cancelledSessions.delete(name);
52255
52314
  const created = this.create({
52256
52315
  role: opts.role ?? "main",
52257
52316
  agent: opts.agentName,
@@ -52287,7 +52346,7 @@ class SessionManager {
52287
52346
  });
52288
52347
  return handle;
52289
52348
  }
52290
- async closeSession(handle) {
52349
+ async closeSession(handle, keepOpenGuard = false) {
52291
52350
  const desc = this._findByName(handle.id);
52292
52351
  const adapter = this._getAdapter(handle.agentName);
52293
52352
  this._liveHandles.delete(handle.id);
@@ -52305,7 +52364,8 @@ class SessionManager {
52305
52364
  if (desc && desc.state === "RUNNING") {
52306
52365
  this.transition(desc.id, "COMPLETED");
52307
52366
  }
52308
- this._busySessions.delete(handle.id);
52367
+ if (!keepOpenGuard)
52368
+ this._busySessions.delete(handle.id);
52309
52369
  this._cancelledSessions.delete(handle.id);
52310
52370
  this._watchdogCancels.clear(handle.id);
52311
52371
  }
@@ -52414,7 +52474,7 @@ function recordAgentHandoff(sessionManager, sessionName, newAgent, reason) {
52414
52474
  }
52415
52475
 
52416
52476
  // src/session/scratch-purge.ts
52417
- import { mkdir as mkdir15, rename as rename3, rm as rm5 } from "fs/promises";
52477
+ import { mkdir as mkdir15, rename as rename4, rm as rm6 } from "fs/promises";
52418
52478
  import { dirname as dirname15, join as join40 } from "path";
52419
52479
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
52420
52480
  const sessionsDir = join40(featureDir(projectDir, featureName), "sessions");
@@ -52466,10 +52526,10 @@ var init_scratch_purge = __esm(() => {
52466
52526
  },
52467
52527
  fileExists: (path5) => Bun.file(path5).exists(),
52468
52528
  readFile: (path5) => Bun.file(path5).text(),
52469
- remove: (path5) => rm5(path5, { recursive: true, force: true }),
52529
+ remove: (path5) => rm6(path5, { recursive: true, force: true }),
52470
52530
  move: async (src, dest) => {
52471
52531
  await mkdir15(dirname15(dest), { recursive: true });
52472
- await rename3(src, dest);
52532
+ await rename4(src, dest);
52473
52533
  },
52474
52534
  now: () => Date.now()
52475
52535
  };
@@ -52557,7 +52617,7 @@ var init_session2 = __esm(() => {
52557
52617
  var DEFAULT_MAX_TOKENS_PER_CALL = 2048;
52558
52618
 
52559
52619
  // src/context/fragments/store.ts
52560
- import { mkdir as mkdir17, rm as rm6, stat as stat7 } from "fs/promises";
52620
+ import { mkdir as mkdir17, rm as rm7, stat as stat7 } from "fs/promises";
52561
52621
  import { dirname as dirname17, join as join41 } from "path";
52562
52622
  function fragmentPath(projectDir, featureId, storyId) {
52563
52623
  validatePathSegment(storyId, "storyId");
@@ -52671,7 +52731,7 @@ var init_store = __esm(() => {
52671
52731
  return files.sort(byCodePoint);
52672
52732
  },
52673
52733
  removeFile: async (path5) => {
52674
- await rm6(path5, { force: true });
52734
+ await rm7(path5, { force: true });
52675
52735
  }
52676
52736
  };
52677
52737
  });
@@ -54335,6 +54395,27 @@ var init_manifest_purge = __esm(() => {
54335
54395
  };
54336
54396
  });
54337
54397
 
54398
+ // src/context/engine/digest-carrier.ts
54399
+ function boostedDigestChunk(request) {
54400
+ const digest = request.priorStageDigest?.trim();
54401
+ return digest && (request.planDigestBoost ?? 1) > 1 ? request.priorStageDigest : undefined;
54402
+ }
54403
+ function resolveDigestCarrier(request, boostedDigest, packed) {
54404
+ if (boostedDigest === undefined) {
54405
+ return { digestAsChunk: false, renderPriorDigest: request.priorStageDigest, manifestRequest: request };
54406
+ }
54407
+ const landed = packed.some((c) => c.providerId === PLAN_DIGEST_PROVIDER_ID);
54408
+ if (landed) {
54409
+ return {
54410
+ digestAsChunk: true,
54411
+ renderPriorDigest: undefined,
54412
+ manifestRequest: { ...request, priorStageDigest: undefined }
54413
+ };
54414
+ }
54415
+ return { digestAsChunk: false, renderPriorDigest: request.priorStageDigest, manifestRequest: request };
54416
+ }
54417
+ var PLAN_DIGEST_PROVIDER_ID = "plan-digest";
54418
+
54338
54419
  // src/context/engine/orchestrator-rebuild-helpers.ts
54339
54420
  function toContextChunk(packed) {
54340
54421
  const providerId = packed.providerId ?? packed.id.split(":")[0] ?? "unknown";
@@ -54538,7 +54619,7 @@ var init_render = __esm(() => {
54538
54619
  });
54539
54620
 
54540
54621
  // src/context/engine/rebuild.ts
54541
- import { randomUUID as randomUUID10 } from "crypto";
54622
+ import { randomUUID as randomUUID11 } from "crypto";
54542
54623
  function toPackedChunks(prior, newAgentId, targetAgentId) {
54543
54624
  const priorAgentForNeutralize = prior.agentId ?? "";
54544
54625
  return prior.chunks.map((c) => {
@@ -54665,7 +54746,7 @@ var init_rebuild = __esm(() => {
54665
54746
  init_render();
54666
54747
  init_scratch_neutralizer();
54667
54748
  DEFAULT_REBUILD_DEPS = {
54668
- uuid: () => randomUUID10(),
54749
+ uuid: () => randomUUID11(),
54669
54750
  getLogger: () => getLogger2()
54670
54751
  };
54671
54752
  });
@@ -54679,13 +54760,13 @@ function roleMultiplier(chunkRoles, callerRole) {
54679
54760
  return ROLE_MISMATCH_WEIGHT;
54680
54761
  }
54681
54762
  function scoreChunk(chunk, callerRole, minScore = MIN_SCORE, stale = false, providerWeights) {
54682
- const rm7 = roleMultiplier(chunk.role, callerRole);
54683
- const roleFiltered = rm7 === 0;
54763
+ const rm8 = roleMultiplier(chunk.role, callerRole);
54764
+ const roleFiltered = rm8 === 0;
54684
54765
  const kindWeight = KIND_WEIGHTS[chunk.kind] ?? 0.5;
54685
54766
  const isStale = chunk.staleCandidate === true || stale;
54686
54767
  const freshnessMultiplier = isStale ? chunk.scoreMultiplier ?? STALENESS_PENALTY : 1;
54687
54768
  const effectivenessMultiplier = chunk.providerId !== undefined ? providerWeights?.[chunk.providerId] ?? 1 : 1;
54688
- const score = chunk.rawScore * rm7 * kindWeight * freshnessMultiplier * effectivenessMultiplier;
54769
+ const score = chunk.rawScore * rm8 * kindWeight * freshnessMultiplier * effectivenessMultiplier;
54689
54770
  const belowMinScore = !roleFiltered && score < minScore;
54690
54771
  return { ...chunk, score, roleFiltered, belowMinScore };
54691
54772
  }
@@ -54711,7 +54792,7 @@ var init_scoring = __esm(() => {
54711
54792
  });
54712
54793
 
54713
54794
  // src/context/engine/orchestrator.ts
54714
- import { createHash as createHash10, randomUUID as randomUUID11 } from "crypto";
54795
+ import { createHash as createHash10, randomUUID as randomUUID12 } from "crypto";
54715
54796
  function buildPullToolDescriptors(stageToolNames, pullConfig) {
54716
54797
  if (!pullConfig?.enabled || stageToolNames.length === 0)
54717
54798
  return [];
@@ -54720,6 +54801,28 @@ function buildPullToolDescriptors(stageToolNames, pullConfig) {
54720
54801
  const override = configured2 !== undefined && configured2 !== DEFAULT_MAX_CALLS_PER_SESSION ? configured2 : undefined;
54721
54802
  return stageToolNames.filter((name) => allowed.length === 0 || allowed.includes(name)).map((name) => PULL_TOOL_REGISTRY[name]).filter((d) => d !== undefined).map((d) => ({ ...d, maxCallsPerSession: override ?? d.maxCallsPerSession }));
54722
54803
  }
54804
+ function appendPlanDigestChunk(raw, results, digest, boost) {
54805
+ const hash2 = createHash10("sha256").update(digest).digest("hex").slice(0, 8);
54806
+ const tokens = Math.ceil(digest.length / 4);
54807
+ raw.push({
54808
+ id: `${PLAN_DIGEST_PROVIDER_ID}:${hash2}`,
54809
+ providerId: PLAN_DIGEST_PROVIDER_ID,
54810
+ kind: "session",
54811
+ scope: "session",
54812
+ role: ["all"],
54813
+ content: digest,
54814
+ tokens,
54815
+ rawScore: 0.9 * boost
54816
+ });
54817
+ results.push({
54818
+ providerId: PLAN_DIGEST_PROVIDER_ID,
54819
+ status: "ok",
54820
+ source: undefined,
54821
+ chunkCount: 1,
54822
+ durationMs: 0,
54823
+ tokensProduced: tokens
54824
+ });
54825
+ }
54723
54826
  async function fetchWithTimeout(provider, request, timeoutMs = PROVIDER_FETCH_TIMEOUT_MS) {
54724
54827
  const controller = new AbortController;
54725
54828
  let handle;
@@ -54731,7 +54834,7 @@ async function fetchWithTimeout(provider, request, timeoutMs = PROVIDER_FETCH_TI
54731
54834
  reject(new Error(`Provider "${provider.id}" timed out`));
54732
54835
  }, timeoutMs);
54733
54836
  });
54734
- const fetchPromise = provider.fetch(request, controller.signal).then((result) => result, (err) => {
54837
+ const fetchPromise = (async () => provider.fetch(request, controller.signal))().then((result) => result, (err) => {
54735
54838
  if (timedOut) {
54736
54839
  return new Promise(() => {});
54737
54840
  }
@@ -54797,8 +54900,9 @@ class ContextOrchestrator {
54797
54900
  }
54798
54901
  const profileBudget = agentProfile.caps.preferredPromptTokens;
54799
54902
  const stageCeiling = Math.min(request.budgetTokens, profileBudget, request.availableBudgetTokens ?? Number.POSITIVE_INFINITY);
54903
+ const boostedDigest = boostedDigestChunk(request);
54800
54904
  const trimmedPriorDigest = request.priorStageDigest?.trim();
54801
- const priorDigestTokens = trimmedPriorDigest ? Math.ceil(trimmedPriorDigest.length / 4) : 0;
54905
+ const priorDigestTokens = boostedDigest === undefined && trimmedPriorDigest ? Math.ceil(trimmedPriorDigest.length / 4) : 0;
54802
54906
  let effectiveBudgetTokens = Math.max(0, stageCeiling - DIGEST_RESERVE_TOKENS - priorDigestTokens - FIXED_RENDER_OVERHEAD_TOKENS);
54803
54907
  const providerSourceMap = request.providerIds === undefined ? buildProviderSourceMap(stageConfig.providerIds, request.extraProviderIds ?? []) : new Map;
54804
54908
  const allowedIds = request.providerIds ?? [...providerSourceMap.keys()];
@@ -54876,28 +54980,8 @@ class ContextOrchestrator {
54876
54980
  }));
54877
54981
  const allRaw = fetchResults.flatMap(({ provider, result }) => result.chunks.map((c) => enrichRaw(c, provider.id)));
54878
54982
  const providerResults = fetchResults.map(({ providerStatus }) => providerStatus);
54879
- if (request.priorStageDigest && (request.planDigestBoost ?? 1) > 1) {
54880
- const boost = request.planDigestBoost ?? 1;
54881
- const hash2 = createHash10("sha256").update(request.priorStageDigest).digest("hex").slice(0, 8);
54882
- const tokens = Math.ceil(request.priorStageDigest.length / 4);
54883
- allRaw.push({
54884
- id: `plan-digest:${hash2}`,
54885
- providerId: "plan-digest",
54886
- kind: "session",
54887
- scope: "session",
54888
- role: ["all"],
54889
- content: request.priorStageDigest,
54890
- tokens,
54891
- rawScore: 0.9 * boost
54892
- });
54893
- providerResults.push({
54894
- providerId: "plan-digest",
54895
- status: "ok",
54896
- source: undefined,
54897
- chunkCount: 1,
54898
- durationMs: 0,
54899
- tokensProduced: tokens
54900
- });
54983
+ if (boostedDigest !== undefined) {
54984
+ appendPlanDigestChunk(allRaw, providerResults, boostedDigest, request.planDigestBoost ?? 1);
54901
54985
  }
54902
54986
  assertUniqueChunkIds(allRaw, request);
54903
54987
  const allPullTools = agentProfile.caps.supportsToolCalls ? buildPullToolDescriptors(stageConfig.pullToolNames ?? [], request.pullConfig) : [];
@@ -54929,7 +55013,10 @@ class ContextOrchestrator {
54929
55013
  excludedNonFloorChunkCount: budgetExcludedIds.length
54930
55014
  });
54931
55015
  }
54932
- const renderOptions = { priorStageDigest: request.priorStageDigest };
55016
+ const digestCarrier = resolveDigestCarrier(request, boostedDigest, packed);
55017
+ const renderOptions = {
55018
+ priorStageDigest: digestCarrier.renderPriorDigest
55019
+ };
54933
55020
  const pushMarkdown = request.agentId !== undefined ? renderForAgent(packed, request.agentId, renderOptions) : renderChunks(packed, renderOptions);
54934
55021
  const digest = buildDigest(packed);
54935
55022
  const dTokens = digestTokens(digest);
@@ -54939,7 +55026,7 @@ class ContextOrchestrator {
54939
55026
  const staleIds = new Set(scored.filter((c) => c.staleCandidate === true).map((c) => c.id));
54940
55027
  const manifest = buildManifest({
54941
55028
  requestId,
54942
- request,
55029
+ request: digestCarrier.manifestRequest,
54943
55030
  packed,
54944
55031
  usedTokens,
54945
55032
  digestTokens: dTokens,
@@ -55014,7 +55101,7 @@ var init_orchestrator = __esm(() => {
55014
55101
  init_stage_config();
55015
55102
  _orchestratorDeps = {
55016
55103
  now: () => Date.now(),
55017
- uuid: () => randomUUID11(),
55104
+ uuid: () => randomUUID12(),
55018
55105
  getLogger: getLogger2,
55019
55106
  rebuild
55020
55107
  };
@@ -55607,7 +55694,7 @@ __export(exports_tracker, {
55607
55694
  saveRunMetrics: () => saveRunMetrics,
55608
55695
  toFallbackHops: () => toFallbackHops
55609
55696
  });
55610
- import { rename as rename4 } from "fs/promises";
55697
+ import { rename as rename5 } from "fs/promises";
55611
55698
  import path5 from "path";
55612
55699
  function tokensFromSnapshot(snapshot, divisor = 1) {
55613
55700
  if (!snapshot || snapshot.tokenUsageCount !== snapshot.callCount)
@@ -55829,7 +55916,7 @@ async function loadExistingMetricsOrQuarantine(metricsPath) {
55829
55916
  } catch (err) {
55830
55917
  const quarantinePath = `${metricsPath}.corrupt-${new Date().toISOString().replace(/[:.]/g, "-")}`;
55831
55918
  try {
55832
- await rename4(metricsPath, quarantinePath);
55919
+ await rename5(metricsPath, quarantinePath);
55833
55920
  } catch (renameErr) {
55834
55921
  getLogger2().warn("metrics", "Failed to quarantine corrupt metrics.json \u2014 leaving in place", {
55835
55922
  metricsPath,
@@ -56597,8 +56684,8 @@ var init_registry4 = __esm(() => {
56597
56684
  });
56598
56685
 
56599
56686
  // src/trust/store.ts
56600
- import { randomUUID as randomUUID12 } from "crypto";
56601
- import { mkdir as mkdir18, rename as rename5, stat as stat8, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
56687
+ import { randomUUID as randomUUID13 } from "crypto";
56688
+ import { mkdir as mkdir18, rename as rename6, stat as stat8, unlink as unlink4, writeFile as writeFile9 } from "fs/promises";
56602
56689
  import { dirname as dirname20, isAbsolute as isAbsolute15, join as join48 } from "path";
56603
56690
  function trustStorePath() {
56604
56691
  return join48(globalConfigDir(), "trust.json");
@@ -56670,11 +56757,11 @@ async function readFoldersForWrite(storePath) {
56670
56757
  return read.state === "ok" ? read.file.folders : [];
56671
56758
  }
56672
56759
  async function writeTrustStore(storePath, file2) {
56673
- const tempPath = `${storePath}.${process.pid}.${randomUUID12()}.tmp`;
56760
+ const tempPath = `${storePath}.${process.pid}.${randomUUID13()}.tmp`;
56674
56761
  try {
56675
- await writeFile10(tempPath, `${JSON.stringify(file2, null, 2)}
56762
+ await writeFile9(tempPath, `${JSON.stringify(file2, null, 2)}
56676
56763
  `, { mode: 384 });
56677
- await rename5(tempPath, storePath);
56764
+ await rename6(tempPath, storePath);
56678
56765
  } finally {
56679
56766
  await unlink4(tempPath).catch(() => {});
56680
56767
  }
@@ -56970,8 +57057,8 @@ var init_version = __esm(() => {
56970
57057
  NAX_AI_VERSION = CATALOG_VERSION;
56971
57058
  NAX_COMMIT = (() => {
56972
57059
  try {
56973
- if (/^[0-9a-f]{6,10}$/.test("eab634ebe"))
56974
- return "eab634ebe";
57060
+ if (/^[0-9a-f]{6,10}$/.test("cfab34e07"))
57061
+ return "cfab34e07";
56975
57062
  } catch {}
56976
57063
  try {
56977
57064
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -69061,7 +69148,7 @@ var init_client4 = __esm(() => {
69061
69148
 
69062
69149
  // src/mcp/lock.ts
69063
69150
  import { createHash as createHash17 } from "crypto";
69064
- import { mkdir as mkdir22, readFile as readFile9, writeFile as writeFile11 } from "fs/promises";
69151
+ import { mkdir as mkdir22, readFile as readFile9, writeFile as writeFile10 } from "fs/promises";
69065
69152
  import { join as join55 } from "path";
69066
69153
  function mcpLockPath(projectRoot) {
69067
69154
  return join55(projectRoot, PROJECT_NAX_DIR, MCP_LOCK_FILENAME);
@@ -69093,7 +69180,7 @@ async function writeMcpLock(projectRoot, lock) {
69093
69180
  Object.fromEntries(Object.entries(tools2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
69094
69181
  ]));
69095
69182
  await mkdir22(join55(projectRoot, PROJECT_NAX_DIR), { recursive: true });
69096
- await writeFile11(mcpLockPath(projectRoot), `${JSON.stringify({ version: 1, servers }, null, 2)}
69183
+ await writeFile10(mcpLockPath(projectRoot), `${JSON.stringify({ version: 1, servers }, null, 2)}
69097
69184
  `);
69098
69185
  }
69099
69186
  function applyLock(locked, discovered) {
@@ -69314,7 +69401,7 @@ var init_provider = __esm(() => {
69314
69401
  });
69315
69402
 
69316
69403
  // src/mcp/rollup.ts
69317
- import { mkdir as mkdir23, writeFile as writeFile12 } from "fs/promises";
69404
+ import { mkdir as mkdir23, writeFile as writeFile11 } from "fs/promises";
69318
69405
  import { join as join56 } from "path";
69319
69406
  function buildMcpRollup(args) {
69320
69407
  const byServer = new Map;
@@ -69347,7 +69434,7 @@ async function writeMcpRollup(outputDir, rollup) {
69347
69434
  return;
69348
69435
  const dir = join56(outputDir, "mcp");
69349
69436
  await mkdir23(dir, { recursive: true });
69350
- await writeFile12(join56(dir, `${rollup.runId}-servers.json`), `${JSON.stringify(rollup, null, 2)}
69437
+ await writeFile11(join56(dir, `${rollup.runId}-servers.json`), `${JSON.stringify(rollup, null, 2)}
69351
69438
  `);
69352
69439
  }
69353
69440
  var init_rollup = () => {};
@@ -73076,7 +73163,7 @@ var init_runners = __esm(() => {
73076
73163
 
73077
73164
  // src/verification/test-baseline.ts
73078
73165
  import { existsSync as existsSync10 } from "fs";
73079
- import { mkdir as mkdir24, readdir as readdir6, rm as rm7 } from "fs/promises";
73166
+ import { mkdir as mkdir24, readdir as readdir6, rm as rm8 } from "fs/promises";
73080
73167
  import { join as join65 } from "path";
73081
73168
  function runBaselinePath(root, featureId) {
73082
73169
  return join65(featureDir(root, featureId), RUN_BASELINE_FILENAME);
@@ -73106,7 +73193,7 @@ async function clearStoryBaselines(root, featureId) {
73106
73193
  return [];
73107
73194
  throw err;
73108
73195
  });
73109
- await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => rm7(join65(storiesDir, entry.name, STORY_BASELINE_FILENAME), { force: true })));
73196
+ await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => rm8(join65(storiesDir, entry.name, STORY_BASELINE_FILENAME), { force: true })));
73110
73197
  }
73111
73198
  async function resolveStoryBaseline(root, featureId, storyId, executionMode) {
73112
73199
  if (executionMode === "parallel") {
@@ -74378,12 +74465,13 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
74378
74465
  const secondParsed = validateAdversarialShape(tryParseLLMJson(secondTurn.output));
74379
74466
  const costUsd = (turn.estimatedCostUsd ?? 0) + (secondTurn.estimatedCostUsd ?? 0);
74380
74467
  const dropCount = drops.length;
74381
- if (!secondParsed) {
74382
- return {
74383
- ...turn,
74384
- output: withRepromptMarker(turn.output, { dropCount, outcome: "parse-failed", costUsd })
74385
- };
74386
- }
74468
+ const keepFirstTurn = (outcome) => ({
74469
+ ...turn,
74470
+ output: withRepromptMarker(turn.output, { dropCount, outcome, costUsd }),
74471
+ estimatedCostUsd: costUsd
74472
+ });
74473
+ if (!secondParsed)
74474
+ return keepFirstTurn("parse-failed");
74387
74475
  const { accepted: secondAccepted } = filterByAcQuote(secondParsed.findings, acceptanceCriteria);
74388
74476
  const secondBlocking = secondAccepted.filter((f) => isBlockingSeverity(f.severity, threshold));
74389
74477
  if (secondBlocking.length > 0) {
@@ -74411,10 +74499,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
74411
74499
  estimatedCostUsd: costUsd
74412
74500
  };
74413
74501
  }
74414
- return {
74415
- ...turn,
74416
- output: withRepromptMarker(turn.output, { dropCount, outcome: "still-dropped", costUsd })
74417
- };
74502
+ return keepFirstTurn("still-dropped");
74418
74503
  }
74419
74504
  async function maybeRepromptForInspection(turn, parsed, rawObject, ctx) {
74420
74505
  if (ctx.input.adversarialConfig.demandInspectionTrail === false)
@@ -77943,7 +78028,7 @@ var init_scope = __esm(() => {
77943
78028
  });
77944
78029
 
77945
78030
  // src/review/fix-review/tree-snapshot/index.ts
77946
- import { mkdtemp as mkdtemp2, rm as rm8 } from "fs/promises";
78031
+ import { mkdtemp as mkdtemp2, rm as rm9 } from "fs/promises";
77947
78032
  import { tmpdir as tmpdir6 } from "os";
77948
78033
  import { join as join69 } from "path";
77949
78034
  async function runGit(args, cwd, indexOverlay, timeoutMs = _treeSnapshotDeps.gitTimeoutMs) {
@@ -78030,7 +78115,7 @@ var init_tree_snapshot = __esm(() => {
78030
78115
  spawn: typedSpawn,
78031
78116
  gitTimeoutMs: TREE_SNAPSHOT_GIT_TIMEOUT_MS,
78032
78117
  mkdtemp: mkdtemp2,
78033
- rm: rm8,
78118
+ rm: rm9,
78034
78119
  tmpdir: tmpdir6
78035
78120
  };
78036
78121
  });
@@ -79147,33 +79232,62 @@ function parseHeaderPath(rawLine, precededByMinusHeader) {
79147
79232
  function isMinusHeader(rawLine) {
79148
79233
  return rawLine.startsWith("--- ");
79149
79234
  }
79150
- function extractDiffLineRanges(diff) {
79151
- const ranges = new Map;
79152
- if (!diff)
79153
- return ranges;
79154
- let currentPath = null;
79235
+ function bodyLineCost(line) {
79236
+ if (line === "" || line.startsWith(" "))
79237
+ return { old: 1, new: 1 };
79238
+ if (line.startsWith("-"))
79239
+ return { old: 1, new: 0 };
79240
+ if (line.startsWith("+"))
79241
+ return { old: 0, new: 1 };
79242
+ if (line.startsWith("\\"))
79243
+ return { old: 0, new: 0 };
79244
+ return null;
79245
+ }
79246
+ function* scanDiff(diff) {
79247
+ let oldLeft = 0;
79248
+ let newLeft = 0;
79155
79249
  let prevWasMinusHeader = false;
79156
79250
  for (const rawLine of diff.split(/\r?\n/)) {
79251
+ if (oldLeft > 0 || newLeft > 0) {
79252
+ const cost = bodyLineCost(rawLine);
79253
+ if (cost !== null) {
79254
+ oldLeft = Math.max(0, oldLeft - cost.old);
79255
+ newLeft = Math.max(0, newLeft - cost.new);
79256
+ continue;
79257
+ }
79258
+ oldLeft = 0;
79259
+ newLeft = 0;
79260
+ }
79157
79261
  const wasMinusHeader = prevWasMinusHeader;
79158
79262
  prevWasMinusHeader = isMinusHeader(rawLine);
79159
79263
  if (rawLine.startsWith(HEADER_PREFIX_NOPREFIX)) {
79160
79264
  const path9 = parseHeaderPath(rawLine, wasMinusHeader);
79161
79265
  if (path9 !== null || rawLine.startsWith(HEADER_PREFIX) || wasMinusHeader)
79162
- currentPath = path9;
79266
+ yield { kind: "header", path: path9 };
79163
79267
  continue;
79164
79268
  }
79165
- if (!currentPath)
79166
- continue;
79167
79269
  const match = HUNK_REGEX.exec(rawLine);
79168
79270
  if (!match)
79169
79271
  continue;
79170
- const newStart = Number(match[3]);
79171
- const newCount = match[4] === undefined ? 1 : Number(match[4]);
79172
- if (newCount <= 0)
79272
+ oldLeft = match[2] === undefined ? 1 : Number(match[2]);
79273
+ newLeft = match[4] === undefined ? 1 : Number(match[4]);
79274
+ yield { kind: "hunk", start: Number(match[3]), count: newLeft };
79275
+ }
79276
+ }
79277
+ function extractDiffLineRanges(diff) {
79278
+ const ranges = new Map;
79279
+ if (!diff)
79280
+ return ranges;
79281
+ let currentPath = null;
79282
+ for (const event of scanDiff(diff)) {
79283
+ if (event.kind === "header") {
79284
+ currentPath = event.path;
79285
+ continue;
79286
+ }
79287
+ if (!currentPath || event.count <= 0)
79173
79288
  continue;
79174
79289
  const entry = ranges.get(currentPath) ?? [];
79175
- entry.push({ start: newStart, end: newStart + newCount - 1 });
79176
- ranges.set(currentPath, entry);
79290
+ ranges.set(currentPath, [...entry, { start: event.start, end: event.start + event.count - 1 }]);
79177
79291
  }
79178
79292
  return ranges;
79179
79293
  }
@@ -80108,12 +80222,13 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
80108
80222
  const secondParsed = validateLLMShape(tryParseLLMJson(secondTurn.output));
80109
80223
  const costUsd = (turn.estimatedCostUsd ?? 0) + (secondTurn.estimatedCostUsd ?? 0);
80110
80224
  const dropCount = drops.length;
80111
- if (!secondParsed) {
80112
- return {
80113
- ...turn,
80114
- output: withRepromptMarker2(turn.output, { dropCount, outcome: "parse-failed", costUsd })
80115
- };
80116
- }
80225
+ const keepFirstTurn = (outcome) => ({
80226
+ ...turn,
80227
+ output: withRepromptMarker2(turn.output, { dropCount, outcome, costUsd }),
80228
+ estimatedCostUsd: costUsd
80229
+ });
80230
+ if (!secondParsed)
80231
+ return keepFirstTurn("parse-failed");
80117
80232
  const { accepted: secondAccepted } = filterByAcGroundingMinimal(secondParsed.findings, acceptanceCriteria);
80118
80233
  const secondBlocking = secondAccepted.filter((f) => isBlockingSeverity(f.severity, threshold));
80119
80234
  if (secondBlocking.length > 0) {
@@ -80141,10 +80256,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
80141
80256
  estimatedCostUsd: costUsd
80142
80257
  };
80143
80258
  }
80144
- return {
80145
- ...turn,
80146
- output: withRepromptMarker2(turn.output, { dropCount, outcome: "still-dropped", costUsd })
80147
- };
80259
+ return keepFirstTurn("still-dropped");
80148
80260
  }
80149
80261
  async function requoteBlockingFindings(findings, ctx) {
80150
80262
  const threshold = ctx.input.blockingThreshold ?? "error";
@@ -82313,9 +82425,10 @@ function resolveHopCompleteOptions(options, currentAgent, primaryAgent, tier, mo
82313
82425
  return { ...options, modelDef: resolveModel(model) };
82314
82426
  return { ...options, modelDef: options.modelDefFor?.(currentAgent, tier) ?? options.modelDef };
82315
82427
  }
82316
- function resolveFinalDispatch(options, primaryAgent, fallbacks, finalTier) {
82428
+ function resolveFinalDispatch(options, primaryAgent, outcome) {
82429
+ const { fallbacks, finalTier, finalTarget } = outcome;
82317
82430
  const agentName = fallbacks.at(-1)?.newAgent ?? primaryAgent;
82318
- const hopOptions = resolveHopCompleteOptions(options, agentName, primaryAgent, finalTier);
82431
+ const hopOptions = resolveHopCompleteOptions(options, agentName, primaryAgent, finalTier, finalTarget?.model);
82319
82432
  return { agentName, options: finalTier !== undefined ? { ...hopOptions, modelTier: finalTier } : hopOptions };
82320
82433
  }
82321
82434
  function buildFallbackRecord(input) {
@@ -83102,8 +83215,15 @@ function parseForm(request) {
83102
83215
  const fields = entries.map(([key, property]) => fieldOf(key, property, required2.has(key)));
83103
83216
  return fields.every((field) => field !== undefined) ? { ok: true, fields: withCompanions(fields) } : REFUSED;
83104
83217
  }
83218
+ function ownAnswerHint(field) {
83219
+ if (field.companion === undefined)
83220
+ return "";
83221
+ if (!field.required)
83222
+ return ", or type your own answer";
83223
+ return field.kind === "multi" ? "; you may add your own after at least one choice" : "";
83224
+ }
83105
83225
  function instruction(field) {
83106
- const own = field.companion === undefined ? "" : ", or type your own answer";
83226
+ const own = ownAnswerHint(field);
83107
83227
  const skip = field.required ? "" : " Leave empty to skip.";
83108
83228
  if (field.kind === "single")
83109
83229
  return `Reply with one number or choice${own}.${skip}`;
@@ -92762,7 +92882,7 @@ var init_prompt_retry = __esm(() => {
92762
92882
  });
92763
92883
 
92764
92884
  // src/agents/acp/stream-bridge.ts
92765
- import { randomUUID as randomUUID13 } from "crypto";
92885
+ import { randomUUID as randomUUID14 } from "crypto";
92766
92886
  function toolUpdate(toolName) {
92767
92887
  return toolName === undefined ? { kind: "agent.tool_call_update" } : { kind: "agent.tool_call_update", toolName };
92768
92888
  }
@@ -92815,7 +92935,7 @@ function sender(ctx) {
92815
92935
  };
92816
92936
  }
92817
92937
  function startCall(ctx, now = Date.now) {
92818
- const callId = randomUUID13();
92938
+ const callId = randomUUID14();
92819
92939
  const send = sender(ctx);
92820
92940
  const base = {
92821
92941
  callId,
@@ -92864,7 +92984,7 @@ var init_stream_bridge = __esm(() => {
92864
92984
  });
92865
92985
 
92866
92986
  // src/agents/acp/turn-loop.ts
92867
- import { randomUUID as randomUUID14 } from "crypto";
92987
+ import { randomUUID as randomUUID15 } from "crypto";
92868
92988
  function used(state) {
92869
92989
  return state.turnCount + state.asked;
92870
92990
  }
@@ -92955,7 +93075,7 @@ async function runIteration(loop) {
92955
93075
  }
92956
93076
  const timer = armDeadline(controller, loop.deadline.remainingMs());
92957
93077
  const call2 = startCall(session2.stream);
92958
- const turnId = opts.turnId ?? randomUUID14();
93078
+ const turnId = opts.turnId ?? randomUUID15();
92959
93079
  try {
92960
93080
  session2.opts.onActiveCall?.(call2.callId, async () => controller.abort(new WatchdogCancel));
92961
93081
  session2.slot.set({
@@ -93908,7 +94028,7 @@ class AgentManager {
93908
94028
  sessionName,
93909
94029
  prompt,
93910
94030
  response: outcome.result.output,
93911
- ...resolveFinalDispatch(augmented, agentName, outcome.fallbacks, outcome.finalTier),
94031
+ ...resolveFinalDispatch(augmented, agentName, outcome),
93912
94032
  stage,
93913
94033
  resolvedPermissions,
93914
94034
  tokenUsage: outcome.result.tokenUsage,
@@ -95168,7 +95288,7 @@ var init_config_descriptions = __esm(() => {
95168
95288
  "quality.stripEnvVars": "Environment variables to strip during verification",
95169
95289
  tdd: "Test-driven development configuration",
95170
95290
  "tdd.maxRetries": "Max retries per TDD session before escalating",
95171
- "tdd.strategy": "TDD strategy: auto | strict | lite | off",
95291
+ "tdd.strategy": 'TDD strategy: auto | strict | lite | off | simple ("tdd-simple" is accepted for simple)',
95172
95292
  "tdd.sessionTiers": "Per-session model tier overrides",
95173
95293
  "tdd.sessionTiers.testWriter": "Model tier for test-writer session",
95174
95294
  "tdd.sessionTiers.implementer": "Ignored by design \u2014 implementer follows story.routing.modelTier + escalation, not this field. Kept optional so legacy configs still parse.",
@@ -98902,8 +99022,41 @@ var init_fix_diagnosis = __esm(() => {
98902
99022
  init_import_resolution();
98903
99023
  });
98904
99024
 
99025
+ // src/utils/drain-within.ts
99026
+ async function drainWithin(drain, deadlineMs) {
99027
+ let timer;
99028
+ const expired = new Promise((resolve37) => {
99029
+ timer = setTimeout(() => resolve37(""), deadlineMs);
99030
+ });
99031
+ try {
99032
+ return await Promise.race([drain, expired]);
99033
+ } finally {
99034
+ clearTimeout(timer);
99035
+ }
99036
+ }
99037
+
98905
99038
  // src/acceptance/hardening.ts
98906
99039
  import path12 from "path";
99040
+ async function awaitExitAndDrain(proc, timers) {
99041
+ const stdoutDrain = new Response(proc.stdout).text().catch(() => "");
99042
+ const stderrDrain = new Response(proc.stderr).text().catch(() => "");
99043
+ let exitCode;
99044
+ let output;
99045
+ try {
99046
+ exitCode = await proc.exited;
99047
+ const [stdout, stderr] = await Promise.all([
99048
+ drainWithin(stdoutDrain, _hardeningDeps.drainTimeoutMs),
99049
+ drainWithin(stderrDrain, _hardeningDeps.drainTimeoutMs)
99050
+ ]);
99051
+ output = `${stdout}
99052
+ ${stderr}`;
99053
+ } finally {
99054
+ clearTimeout(timers.killTimer);
99055
+ if (timers.sigkillTimer)
99056
+ clearTimeout(timers.sigkillTimer);
99057
+ }
99058
+ return { exitCode, output };
99059
+ }
98907
99060
  async function processPackageGroup(ctx, packageDir, groupStories, language, result) {
98908
99061
  await assertTrusted(packageDir, "acceptance-command");
98909
99062
  const logger = getSafeLogger2();
@@ -98986,30 +99139,23 @@ async function processPackageGroup(ctx, packageDir, groupStories, language, resu
98986
99139
  detached: true
98987
99140
  });
98988
99141
  let exitedBeforeSigkill = false;
98989
- let sigkillTimer;
98990
99142
  proc.exited.then(() => {
98991
99143
  exitedBeforeSigkill = true;
98992
99144
  }).catch(() => {});
98993
99145
  const timeoutMs = config2.acceptance?.timeoutMs ?? DEFAULT_HARDENING_TIMEOUT_MS;
98994
- const killTimer = setTimeout(() => {
98995
- killProcessGroup(proc.pid, "SIGTERM");
98996
- sigkillTimer = setTimeout(() => {
98997
- sigkillTimer = undefined;
98998
- if (!exitedBeforeSigkill) {
98999
- killProcessGroup(proc.pid, "SIGKILL");
99000
- }
99001
- }, HARDENING_SIGKILL_GRACE_PERIOD_MS);
99002
- }, timeoutMs);
99003
- const [exitCode, stdout, stderr] = await Promise.all([
99004
- proc.exited,
99005
- new Response(proc.stdout).text().catch(() => ""),
99006
- new Response(proc.stderr).text().catch(() => "")
99007
- ]);
99008
- clearTimeout(killTimer);
99009
- if (sigkillTimer)
99010
- clearTimeout(sigkillTimer);
99011
- const output = `${stdout}
99012
- ${stderr}`;
99146
+ const timers = {
99147
+ killTimer: setTimeout(() => {
99148
+ killProcessGroup(proc.pid, "SIGTERM");
99149
+ timers.sigkillTimer = setTimeout(() => {
99150
+ timers.sigkillTimer = undefined;
99151
+ if (!exitedBeforeSigkill) {
99152
+ killProcessGroup(proc.pid, "SIGKILL");
99153
+ }
99154
+ }, HARDENING_SIGKILL_GRACE_PERIOD_MS);
99155
+ }, timeoutMs),
99156
+ sigkillTimer: undefined
99157
+ };
99158
+ const { exitCode, output } = await awaitExitAndDrain(proc, timers);
99013
99159
  const failedACs = parseTestFailures(output);
99014
99160
  const failedSet = new Set(failedACs.map((ac) => ac.toUpperCase()));
99015
99161
  if (exitCode !== 0 && failedACs.length === 0) {
@@ -99114,19 +99260,59 @@ var init_hardening = __esm(() => {
99114
99260
  writeFile: async (p, c) => {
99115
99261
  await Bun.write(p, c);
99116
99262
  },
99117
- detectLanguage
99263
+ detectLanguage,
99264
+ drainTimeoutMs: 2000
99265
+ };
99266
+ });
99267
+
99268
+ // src/acceptance/override-keys.ts
99269
+ import { relative as relative26 } from "path";
99270
+ function scopedOverrideKey(workdir, packageDir, acId) {
99271
+ return `${relative26(workdir, packageDir) || "."}${OVERRIDE_SCOPE_SEPARATOR}${acId}`;
99272
+ }
99273
+ function definingPackageCount(acId, counts) {
99274
+ const n = Number(NUMBERED_AC.exec(acId)?.[1] ?? 0);
99275
+ if (n < 1)
99276
+ return 0;
99277
+ return [...counts.values()].filter((count2) => count2 >= n).length;
99278
+ }
99279
+ function createOverrideLookup(opts) {
99280
+ const table = opts.overrides ?? {};
99281
+ const reported = new Set;
99282
+ return {
99283
+ reasonFor(packageDir, acId) {
99284
+ const scoped2 = table[scopedOverrideKey(opts.workdir, packageDir, acId)];
99285
+ if (scoped2 !== undefined)
99286
+ return scoped2;
99287
+ const bare = table[acId];
99288
+ if (bare === undefined || !opts.multiPackage)
99289
+ return bare;
99290
+ if (definingPackageCount(acId, opts.acCountByPackageDir) === 1)
99291
+ return bare;
99292
+ if (!reported.has(acId)) {
99293
+ reported.add(acId);
99294
+ opts.onIgnoredBareKey(acId);
99295
+ }
99296
+ return;
99297
+ }
99118
99298
  };
99299
+ }
99300
+ var OVERRIDE_SCOPE_SEPARATOR = "::", NUMBERED_AC;
99301
+ var init_override_keys = __esm(() => {
99302
+ NUMBERED_AC = /^AC-(\d+)$/;
99119
99303
  });
99120
99304
 
99121
99305
  // src/acceptance/index.ts
99122
99306
  var exports_acceptance = {};
99123
99307
  __export(exports_acceptance, {
99308
+ OVERRIDE_SCOPE_SEPARATOR: () => OVERRIDE_SCOPE_SEPARATOR,
99124
99309
  _acceptanceExecutionDeps: () => _acceptanceExecutionDeps,
99125
99310
  _groupDeps: () => _groupDeps,
99126
99311
  acceptanceTestFilename: () => acceptanceTestFilename2,
99127
99312
  buildAcceptanceRunCommand: () => buildAcceptanceRunCommand,
99128
99313
  buildAcceptanceTestPrompt: () => buildAcceptanceTestPrompt,
99129
99314
  checkAcceptanceCoverage: () => checkAcceptanceCoverage,
99315
+ createOverrideLookup: () => createOverrideLookup,
99130
99316
  effectiveAcceptanceFramework: () => effectiveAcceptanceFramework,
99131
99317
  findExistingAcceptanceTestPath: () => findExistingAcceptanceTestPath,
99132
99318
  findMissingAcceptanceTestPaths: () => findMissingAcceptanceTestPaths,
@@ -99144,6 +99330,7 @@ __export(exports_acceptance, {
99144
99330
  resolveSuggestedPackageFeatureTestPath: () => resolveSuggestedPackageFeatureTestPath,
99145
99331
  resolveSuggestedTestFile: () => resolveSuggestedTestFile,
99146
99332
  runHardeningPass: () => runHardeningPass,
99333
+ scopedOverrideKey: () => scopedOverrideKey,
99147
99334
  substituteAcceptanceTestPath: () => substituteAcceptanceTestPath,
99148
99335
  suggestedTestFilename: () => suggestedTestFilename,
99149
99336
  warnMissingAcceptanceTests: () => warnMissingAcceptanceTests
@@ -99154,13 +99341,14 @@ var init_acceptance2 = __esm(() => {
99154
99341
  init_fix_diagnosis();
99155
99342
  init_generator();
99156
99343
  init_hardening();
99344
+ init_override_keys();
99157
99345
  init_refinement();
99158
99346
  init_test_path();
99159
99347
  });
99160
99348
 
99161
99349
  // src/cli/features-acceptance.ts
99162
99350
  import { existsSync as existsSync16 } from "fs";
99163
- import { join as join79, relative as relative26 } from "path";
99351
+ import { join as join79, relative as relative27 } from "path";
99164
99352
  async function resolveFeatureAcceptance(featureName, workdir) {
99165
99353
  let enabled = true;
99166
99354
  try {
@@ -99181,11 +99369,11 @@ async function resolveFeatureAcceptance(featureName, workdir) {
99181
99369
  const prd = await loadPRD(prdPath);
99182
99370
  const testGroups = await groupStoriesByPackage(prd, repoRoot, featureName, config2.acceptance?.testPath, config2.project?.language);
99183
99371
  const groups = await Promise.all(testGroups.map(async (g) => {
99184
- const packageDir = relative26(repoRoot, g.packageDir);
99372
+ const packageDir = relative27(repoRoot, g.packageDir);
99185
99373
  const command2 = await resolveGroupCommand(repoRoot, packageDir, config2.acceptance?.command);
99186
99374
  return {
99187
99375
  packageDir,
99188
- testPath: relative26(repoRoot, g.testPath),
99376
+ testPath: relative27(repoRoot, g.testPath),
99189
99377
  exists: await Bun.file(g.testPath).exists(),
99190
99378
  command: command2,
99191
99379
  cwd: packageDir,
@@ -99240,7 +99428,7 @@ var init_feature_name = __esm(() => {
99240
99428
 
99241
99429
  // src/cli/features-resolve.ts
99242
99430
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
99243
- import { join as join80, relative as relative27 } from "path";
99431
+ import { join as join80, relative as relative28 } from "path";
99244
99432
  async function resolveTestPatterns(workdir) {
99245
99433
  try {
99246
99434
  const config2 = await loadConfig(workdir);
@@ -99263,17 +99451,17 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99263
99451
  ];
99264
99452
  const docsSpecExact = join80(repoRoot, "docs", "specs", `SPEC-${name}.md`);
99265
99453
  candidates.push({ abs: docsSpecExact, kind: "markdown" });
99266
- const checked = candidates.map((c) => relative27(repoRoot, c.abs));
99454
+ const checked = candidates.map((c) => relative28(repoRoot, c.abs));
99267
99455
  for (const { abs, kind } of candidates.slice(0, 2)) {
99268
99456
  if (kind === "markdown") {
99269
99457
  const nonEmpty2 = await isNonEmptyFile(abs);
99270
99458
  if (nonEmpty2) {
99271
- return { source: { kind, path: relative27(repoRoot, abs) }, checked };
99459
+ return { source: { kind, path: relative28(repoRoot, abs) }, checked };
99272
99460
  }
99273
99461
  }
99274
99462
  }
99275
99463
  if (await isNonEmptyFile(docsSpecExact)) {
99276
- return { source: { kind: "markdown", path: relative27(repoRoot, docsSpecExact) }, checked };
99464
+ return { source: { kind: "markdown", path: relative28(repoRoot, docsSpecExact) }, checked };
99277
99465
  }
99278
99466
  const docsSpecsDir = join80(repoRoot, "docs", "specs");
99279
99467
  if (existsSync17(docsSpecsDir)) {
@@ -99281,7 +99469,7 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99281
99469
  for (const match of glob2.scanSync({ cwd: docsSpecsDir, absolute: false })) {
99282
99470
  const abs = join80(docsSpecsDir, match);
99283
99471
  if (await isNonEmptyFile(abs)) {
99284
- const relPath = relative27(repoRoot, abs);
99472
+ const relPath = relative28(repoRoot, abs);
99285
99473
  if (!checked.includes(relPath))
99286
99474
  checked.push(relPath);
99287
99475
  return { source: { kind: "markdown", path: relPath }, checked };
@@ -99289,7 +99477,7 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99289
99477
  }
99290
99478
  }
99291
99479
  const prdAbs = join80(naxDir, "features", name, "prd.json");
99292
- const prdRel = relative27(repoRoot, prdAbs);
99480
+ const prdRel = relative28(repoRoot, prdAbs);
99293
99481
  if (!checked.includes(prdRel))
99294
99482
  checked.push(prdRel);
99295
99483
  if (existsSync17(prdAbs)) {
@@ -99341,8 +99529,8 @@ async function resolveFeatureSpec(name, workdir) {
99341
99529
  return {
99342
99530
  status: "ok",
99343
99531
  featureName: null,
99344
- specSource: { kind: "markdown", path: relative27(repoRoot, abs) },
99345
- message: `resolved spec: ${relative27(repoRoot, abs)}`
99532
+ specSource: { kind: "markdown", path: relative28(repoRoot, abs) },
99533
+ message: `resolved spec: ${relative28(repoRoot, abs)}`
99346
99534
  };
99347
99535
  }
99348
99536
  if (name !== undefined && name.trim() !== "") {
@@ -99791,7 +99979,7 @@ var init_windsurf = __esm(() => {
99791
99979
 
99792
99980
  // src/context/generator/index.ts
99793
99981
  import { existsSync as existsSync19 } from "fs";
99794
- import { join as join82, relative as relative28 } from "path";
99982
+ import { join as join82, relative as relative29 } from "path";
99795
99983
  async function loadContextContent(options, config2) {
99796
99984
  if (!_generatorDeps.existsSync(options.contextPath)) {
99797
99985
  throw new NaxError(`Context file not found: ${options.contextPath}`, "CONTEXT_FILE_NOT_FOUND", {
@@ -99922,7 +100110,7 @@ async function discoverWorkspacePackages2(repoRoot) {
99922
100110
  }
99923
100111
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
99924
100112
  const resolvedRepoRoot = repoRoot ?? packageDir;
99925
- const relativePkgPath = relative28(resolvedRepoRoot, packageDir);
100113
+ const relativePkgPath = relative29(resolvedRepoRoot, packageDir);
99926
100114
  const contextPath = join82(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
99927
100115
  if (!_generatorDeps.existsSync(contextPath)) {
99928
100116
  return [
@@ -100206,7 +100394,7 @@ var init_generate = __esm(() => {
100206
100394
 
100207
100395
  // src/cli/init-context.ts
100208
100396
  import { mkdir as mkdir25, readdir as readdir7 } from "fs/promises";
100209
- import { basename as basename11, join as join84, relative as relative29, sep as sep18 } from "path";
100397
+ import { basename as basename11, join as join84, relative as relative30, sep as sep18 } from "path";
100210
100398
  async function bunFileExists(path13) {
100211
100399
  return Bun.file(path13).exists();
100212
100400
  }
@@ -100233,7 +100421,7 @@ async function walkDir(root, current, out, maxFiles) {
100233
100421
  continue;
100234
100422
  await walkDir(root, join84(current, entry2.name), out, maxFiles);
100235
100423
  } else if (entry2.isFile()) {
100236
- out.push(relative29(root, join84(current, entry2.name)).replaceAll(sep18, "/"));
100424
+ out.push(relative30(root, join84(current, entry2.name)).replaceAll(sep18, "/"));
100237
100425
  }
100238
100426
  }
100239
100427
  }
@@ -101437,7 +101625,7 @@ var init_resume_hydrate = __esm(() => {
101437
101625
  });
101438
101626
 
101439
101627
  // src/tdd/rollback.ts
101440
- import { rm as rm9 } from "fs/promises";
101628
+ import { rm as rm10 } from "fs/promises";
101441
101629
  import { join as join88 } from "path";
101442
101630
  async function runGitBounded2(args, workdir) {
101443
101631
  if (!workdir) {
@@ -101558,18 +101746,18 @@ var init_rollback = __esm(() => {
101558
101746
  autoCommitIfDirty,
101559
101747
  getUntrackedPaths,
101560
101748
  getGitRoot,
101561
- rm: rm9,
101749
+ rm: rm10,
101562
101750
  timeoutMs: ROLLBACK_GIT_TIMEOUT_MS,
101563
101751
  drainTimeoutMs: ROLLBACK_DRAIN_TIMEOUT_MS
101564
101752
  };
101565
101753
  });
101566
101754
 
101567
101755
  // src/utils/paths.ts
101568
- import { join as join89, relative as relative30, sep as sep19 } from "path";
101756
+ import { join as join89, relative as relative31, sep as sep19 } from "path";
101569
101757
  function packageDirRelative(projectDir, workdir) {
101570
101758
  if (!projectDir || !workdir || workdir === projectDir)
101571
101759
  return;
101572
- const rel = relative30(projectDir, workdir);
101760
+ const rel = relative31(projectDir, workdir);
101573
101761
  if (rel === ".." || rel.startsWith(`..${sep19}`))
101574
101762
  return;
101575
101763
  return rel && rel !== "." ? rel : undefined;
@@ -106166,8 +106354,8 @@ var init_escalation = __esm(() => {
106166
106354
  });
106167
106355
 
106168
106356
  // src/execution/lock.ts
106169
- import { randomUUID as randomUUID15 } from "crypto";
106170
- import { rename as rename6, unlink as unlink6 } from "fs/promises";
106357
+ import { randomUUID as randomUUID16 } from "crypto";
106358
+ import { rename as rename7, unlink as unlink6 } from "fs/promises";
106171
106359
  import { hostname as hostname3 } from "os";
106172
106360
  import path14 from "path";
106173
106361
  async function serializeLockOperation(workdir, operation) {
@@ -106210,7 +106398,7 @@ function parseHolder(raw) {
106210
106398
  }
106211
106399
  }
106212
106400
  async function publishFileAtomically(targetPath, content) {
106213
- const tempPath = `${targetPath}.${randomUUID15()}.tmp`;
106401
+ const tempPath = `${targetPath}.${randomUUID16()}.tmp`;
106214
106402
  try {
106215
106403
  await Bun.write(tempPath, content);
106216
106404
  const fs = await import("fs");
@@ -106383,14 +106571,14 @@ var init_lock2 = __esm(() => {
106383
106571
  init_logger2();
106384
106572
  pendingLockOperations = new Map;
106385
106573
  _lockDeps = {
106386
- rename: rename6,
106574
+ rename: rename7,
106387
106575
  host: () => hostname3(),
106388
106576
  readLockText: (lockPath) => Bun.file(lockPath).text()
106389
106577
  };
106390
106578
  });
106391
106579
 
106392
106580
  // src/execution/feature-lock.ts
106393
- import { mkdir as mkdir28, rename as rename7, unlink as unlink7 } from "fs/promises";
106581
+ import { mkdir as mkdir28, rename as rename8, unlink as unlink7 } from "fs/promises";
106394
106582
  import { hostname as hostname4 } from "os";
106395
106583
  import path15 from "path";
106396
106584
  function validateFeatureId2(featureId) {
@@ -106629,7 +106817,7 @@ var init_feature_lock = __esm(() => {
106629
106817
  featureLockPath,
106630
106818
  host: () => hostname4(),
106631
106819
  isProcessAlive,
106632
- rename: rename7,
106820
+ rename: rename8,
106633
106821
  tryExclusiveCreate,
106634
106822
  unlink: unlink7
106635
106823
  };
@@ -107799,6 +107987,17 @@ var init_acceptance3 = __esm(() => {
107799
107987
  storiesByPackageDir.set(pkgDir, (storiesByPackageDir.get(pkgDir) ?? 0) + 1);
107800
107988
  acsByPackageDir.set(pkgDir, (acsByPackageDir.get(pkgDir) ?? 0) + s.acceptanceCriteria.length);
107801
107989
  }
107990
+ const overrideLookup = createOverrideLookup({
107991
+ overrides: ctx.prd.acceptanceOverrides,
107992
+ workdir: ctx.workdir,
107993
+ acCountByPackageDir: acsByPackageDir,
107994
+ multiPackage: testGroups.length > 1,
107995
+ onIgnoredBareKey: (acId) => logger.warn("acceptance", "Bare acceptance override ignored: several packages define this AC id", {
107996
+ storyId: ctx.story.id,
107997
+ acId,
107998
+ hint: `scope it to one package: "<package>::${acId}"`
107999
+ })
108000
+ });
107802
108001
  const allFailedACs = [];
107803
108002
  const seenAcKeys = new Set;
107804
108003
  const allFindings = [];
@@ -107856,14 +108055,14 @@ var init_acceptance3 = __esm(() => {
107856
108055
  const output = execution.output ?? "";
107857
108056
  allOutputParts.push(output);
107858
108057
  const { failedACs, taggedFailureCount } = parseTestFailuresDetailed(output);
107859
- const overrides = ctx.prd.acceptanceOverrides ?? {};
107860
- const actualFailures = failedACs.filter((acId) => !overrides[acId]);
107861
- const overriddenFailures = failedACs.filter((acId) => overrides[acId]);
108058
+ const reasonFor = (acId) => overrideLookup.reasonFor(packageDir, acId);
108059
+ const actualFailures = failedACs.filter((acId) => !reasonFor(acId));
108060
+ const overriddenFailures = failedACs.filter((acId) => reasonFor(acId));
107862
108061
  if (overriddenFailures.length > 0) {
107863
108062
  logger.warn("acceptance", "Skipped failures (overridden)", {
107864
108063
  storyId: ctx.story.id,
107865
108064
  overriddenFailures,
107866
- overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
108065
+ overrides: overriddenFailures.map((acId) => ({ acId, reason: reasonFor(acId) }))
107867
108066
  });
107868
108067
  }
107869
108068
  if (failedACs.length === 0 && exitCode !== 0) {
@@ -108973,7 +109172,7 @@ var init_scope_files = __esm(() => {
108973
109172
  });
108974
109173
 
108975
109174
  // src/pipeline/stages/context.ts
108976
- import { randomUUID as randomUUID16 } from "crypto";
109175
+ import { randomUUID as randomUUID17 } from "crypto";
108977
109176
  import { join as join99 } from "path";
108978
109177
  async function runV2Path(ctx) {
108979
109178
  const logger = getLogger2();
@@ -109214,7 +109413,7 @@ var init_context3 = __esm(() => {
109214
109413
  createOrchestrator: createDefaultOrchestrator,
109215
109414
  loadPlugins: loadPluginProviders,
109216
109415
  v1FeatureProvider: () => new FeatureContextProvider,
109217
- uuid: () => randomUUID16(),
109416
+ uuid: () => randomUUID17(),
109218
109417
  readDigest: readDigestFile,
109219
109418
  writeDigest: writeDigestFile,
109220
109419
  loadFeatureManifests,
@@ -110856,7 +111055,8 @@ async function executeHook(hookDef, ctx, workdir) {
110856
111055
  stdin: new Response(contextJson),
110857
111056
  stdout: "pipe",
110858
111057
  stderr: "pipe",
110859
- env: buildAllowedEnv({ env: env2 })
111058
+ env: buildAllowedEnv({ env: env2 }),
111059
+ detached: true
110860
111060
  });
110861
111061
  let timedOut = false;
110862
111062
  let killTimeoutId;
@@ -111759,7 +111959,7 @@ var init_headless_formatter = __esm(() => {
111759
111959
 
111760
111960
  // src/execution/lifecycle/run-tmp-wipe.ts
111761
111961
  import { lstatSync as lstatSync4 } from "fs";
111762
- import { rm as rm10 } from "fs/promises";
111962
+ import { rm as rm11 } from "fs/promises";
111763
111963
  function inspect(path19) {
111764
111964
  try {
111765
111965
  return _runTmpWipeDeps.exists(path19) ? { state: "present" } : { state: "absent" };
@@ -111799,7 +111999,7 @@ var init_run_tmp_wipe = __esm(() => {
111799
111999
  init_internal();
111800
112000
  init_logger2();
111801
112001
  _runTmpWipeDeps = {
111802
- remove: (path19) => rm10(path19, { recursive: true, force: true }),
112002
+ remove: (path19) => rm11(path19, { recursive: true, force: true }),
111803
112003
  exists: (path19) => {
111804
112004
  try {
111805
112005
  lstatSync4(path19);
@@ -111814,7 +112014,7 @@ var init_run_tmp_wipe = __esm(() => {
111814
112014
  });
111815
112015
 
111816
112016
  // src/execution/lifecycle/scratchpad-wipe.ts
111817
- import { rm as rm11 } from "fs/promises";
112017
+ import { rm as rm12 } from "fs/promises";
111818
112018
  import { join as join102 } from "path";
111819
112019
  async function wipeScratchpad(workdir, opts = {}) {
111820
112020
  if (opts.dryRun)
@@ -111837,7 +112037,7 @@ var init_scratchpad_wipe = __esm(() => {
111837
112037
  init_internal();
111838
112038
  init_logger2();
111839
112039
  _scratchpadWipeDeps = {
111840
- remove: (path19) => rm11(path19, { recursive: true, force: true })
112040
+ remove: (path19) => rm12(path19, { recursive: true, force: true })
111841
112041
  };
111842
112042
  });
111843
112043
 
@@ -113220,7 +113420,7 @@ var init_rtk = __esm(() => {
113220
113420
  });
113221
113421
 
113222
113422
  // src/execution/status-file.ts
113223
- import { rename as rename8, unlink as unlink8 } from "fs/promises";
113423
+ import { rename as rename9, unlink as unlink8 } from "fs/promises";
113224
113424
  import { resolve as resolve39 } from "path";
113225
113425
  function countProgress(prd) {
113226
113426
  const stories = prd.userStories;
@@ -113284,7 +113484,7 @@ async function writeStatusFile(filePath2, status) {
113284
113484
  await unlink8(tmpPath);
113285
113485
  } catch {}
113286
113486
  await Bun.write(tmpPath, JSON.stringify(status, null, 2));
113287
- await rename8(tmpPath, resolvedPath);
113487
+ await rename9(tmpPath, resolvedPath);
113288
113488
  }
113289
113489
  var init_status_file = __esm(() => {
113290
113490
  init_errors4();
@@ -115160,7 +115360,7 @@ async function* streamJsonlLines(file2) {
115160
115360
  }
115161
115361
 
115162
115362
  // src/plugins/builtin/curator/rollup-prune.ts
115163
- import { appendFile as appendFile6, rename as rename9, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
115363
+ import { appendFile as appendFile6, rename as rename10, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
115164
115364
  async function scanProjectRunIds(rollupPath, projectKey) {
115165
115365
  const maxTsByRunId = new Map;
115166
115366
  for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
@@ -115205,7 +115405,7 @@ async function pruneRollupUnlocked(input) {
115205
115405
  buffer = "";
115206
115406
  };
115207
115407
  try {
115208
- await writeFile13(tmpPath, "");
115408
+ await writeFile12(tmpPath, "");
115209
115409
  for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
115210
115410
  if (!line.trim())
115211
115411
  continue;
@@ -115241,7 +115441,7 @@ async function pruneRollupUnlocked(input) {
115241
115441
  await flush();
115242
115442
  }
115243
115443
  await flush();
115244
- await rename9(tmpPath, rollupPath);
115444
+ await rename10(tmpPath, rollupPath);
115245
115445
  } catch (err) {
115246
115446
  await unlink9(tmpPath).catch(() => {});
115247
115447
  throw err;
@@ -115640,8 +115840,8 @@ function collectRectify(context, entry2, data) {
115640
115840
  ts: stringValue(entry2.timestamp ?? entry2.ts, now()),
115641
115841
  kind: "rectify-cycle",
115642
115842
  payload: {
115643
- iteration: numberValue(data.attempt ?? data.rectifyAttempt ?? data.iteration, 1),
115644
- status: stringValue(data.status, "started")
115843
+ iteration: numberValue(data.iterationCount, 1),
115844
+ status: data.exitReason === "resolved" ? "passed" : "failed"
115645
115845
  }
115646
115846
  };
115647
115847
  }
@@ -115733,7 +115933,7 @@ async function collectFromRunJsonl(context) {
115733
115933
  observations.push(collectPullCall(context, entry2, data));
115734
115934
  } else if (stage === "acceptance" && message === "verdict") {
115735
115935
  observations.push(collectAcceptanceVerdict(context, entry2, data));
115736
- } else if (stage === "rectify" && message === "Starting rectification loop") {
115936
+ } else if (stage === "story-orchestrator" && typeof data.iterationCount === "number") {
115737
115937
  observations.push(collectRectify(context, entry2, data));
115738
115938
  } else if (stage === "escalation" && message.includes("Escalating")) {
115739
115939
  observations.push(collectEscalation(context, entry2, data));
@@ -116098,7 +116298,7 @@ function renderProposals(proposals, runId, observationCount, provenance) {
116098
116298
  }
116099
116299
 
116100
116300
  // src/plugins/builtin/curator/rollup.ts
116101
- import { appendFile as appendFile7, mkdir as mkdir30, writeFile as writeFile14 } from "fs/promises";
116301
+ import { appendFile as appendFile7, mkdir as mkdir30, writeFile as writeFile13 } from "fs/promises";
116102
116302
  import * as path22 from "path";
116103
116303
  async function appendToRollup(observations, rollupPath) {
116104
116304
  const dir = path22.dirname(rollupPath);
@@ -116111,7 +116311,7 @@ async function appendToRollupUnlocked(observations, rollupPath) {
116111
116311
  if (observations.length === 0) {
116112
116312
  const f = Bun.file(rollupPath);
116113
116313
  if (!await f.exists()) {
116114
- await writeFile14(rollupPath, "");
116314
+ await writeFile13(rollupPath, "");
116115
116315
  }
116116
116316
  return;
116117
116317
  }
@@ -117183,7 +117383,7 @@ var init_language_commands = __esm(() => {
117183
117383
  });
117184
117384
 
117185
117385
  // src/review/scoped-lint.ts
117186
- import { join as join109, relative as relative32 } from "path";
117386
+ import { join as join109, relative as relative33 } from "path";
117187
117387
  function normalizePath3(path25) {
117188
117388
  return path25.replaceAll("\\", "/").replace(/^\.\//, "");
117189
117389
  }
@@ -117209,7 +117409,7 @@ async function listChangedFiles(workdir, baseRef) {
117209
117409
  function inferActivePackageDir(workdir, projectDir) {
117210
117410
  if (!projectDir)
117211
117411
  return;
117212
- const rel = normalizePath3(relative32(projectDir, workdir));
117412
+ const rel = normalizePath3(relative33(projectDir, workdir));
117213
117413
  if (!rel || rel === "." || rel.startsWith(".."))
117214
117414
  return;
117215
117415
  return rel;
@@ -118294,8 +118494,8 @@ function resolveCanonicalTargetPath(projectDir, canonicalFile) {
118294
118494
  const root = resolve42(projectDir);
118295
118495
  if (target !== root && !target.startsWith(root + sep21))
118296
118496
  return null;
118297
- const relative33 = target.slice(root.length + 1).replaceAll(sep21, "/");
118298
- return CURATOR_TARGET_SHAPES.some((shape) => shape.test(relative33)) ? target : null;
118497
+ const relative34 = target.slice(root.length + 1).replaceAll(sep21, "/");
118498
+ return CURATOR_TARGET_SHAPES.some((shape) => shape.test(relative34)) ? target : null;
118299
118499
  }
118300
118500
  function isWithinCanonicalRulesDir(projectDir, targetPath) {
118301
118501
  const rulesRoot = resolve42(projectDir, CANONICAL_RULES_DIR);
@@ -118881,7 +119081,7 @@ __export(exports_migrate, {
118881
119081
  partitionTrackedCandidates: () => partitionTrackedCandidates
118882
119082
  });
118883
119083
  import { existsSync as existsSync30 } from "fs";
118884
- import { mkdir as mkdir32, readdir as readdir10, rename as rename10 } from "fs/promises";
119084
+ import { mkdir as mkdir32, readdir as readdir10, rename as rename11 } from "fs/promises";
118885
119085
  import path26 from "path";
118886
119086
  async function detectGeneratedContent(naxDir) {
118887
119087
  if (!existsSync30(naxDir))
@@ -119066,7 +119266,7 @@ async function migrateCommand(options) {
119066
119266
  const archiveBase = path26.join(globalConfigDir(), "_archive");
119067
119267
  const archiveDest = path26.join(archiveBase, `${options.reclaim}-${Date.now()}`);
119068
119268
  await mkdir32(archiveBase, { recursive: true });
119069
- await rename10(src, archiveDest);
119269
+ await rename11(src, archiveDest);
119070
119270
  logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
119071
119271
  return;
119072
119272
  }
@@ -119165,7 +119365,7 @@ async function migrateCommand(options) {
119165
119365
  Remove the destination or run nax migrate --dry-run to inspect.`, "MIGRATE_CONFLICT", { stage: "migrate", src: candidate.srcPath, dest });
119166
119366
  }
119167
119367
  try {
119168
- await rename10(candidate.srcPath, dest);
119368
+ await rename11(candidate.srcPath, dest);
119169
119369
  } catch (err) {
119170
119370
  const isXdev = err instanceof Error && "code" in err && err.code === "EXDEV";
119171
119371
  if (isXdev) {
@@ -122019,7 +122219,7 @@ var init_queue_file_lock = __esm(() => {
122019
122219
  });
122020
122220
 
122021
122221
  // src/execution/queue-handler.ts
122022
- import { rename as rename11, unlink as unlink12 } from "fs/promises";
122222
+ import { rename as rename12, unlink as unlink12 } from "fs/promises";
122023
122223
  import path29 from "path";
122024
122224
  function getSafeLogger9() {
122025
122225
  try {
@@ -122039,7 +122239,7 @@ async function claimCommandsLocked(queuePath, processingPath, logger) {
122039
122239
  return null;
122040
122240
  }
122041
122241
  try {
122042
- await rename11(queuePath, processingPath);
122242
+ await rename12(queuePath, processingPath);
122043
122243
  } catch (error48) {
122044
122244
  logger?.warn("queue", "Failed to rename queue file for processing", {
122045
122245
  error: error48.message
@@ -122215,7 +122415,7 @@ var init_run_cost_reconcile = __esm(() => {
122215
122415
  });
122216
122416
 
122217
122417
  // src/finish/audit.ts
122218
- import { appendFile as appendFile8, mkdir as mkdir34, readFile as readFile10, writeFile as writeFile15 } from "fs/promises";
122418
+ import { appendFile as appendFile8, mkdir as mkdir34, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
122219
122419
  import { join as join120 } from "path";
122220
122420
  function roundsPath(t) {
122221
122421
  return join120(t.auditDir, `${t.runId}.jsonl`);
@@ -122243,7 +122443,7 @@ async function updateLedger(t, result) {
122243
122443
  };
122244
122444
  try {
122245
122445
  await mkdir34(t.auditDir, { recursive: true });
122246
- await writeFile15(ledgerPath(t.auditDir), `${JSON.stringify(entry2, null, 2)}
122446
+ await writeFile14(ledgerPath(t.auditDir), `${JSON.stringify(entry2, null, 2)}
122247
122447
  `, "utf8");
122248
122448
  } catch {}
122249
122449
  }
@@ -122293,7 +122493,7 @@ async function writeResult(t, result, options = {}) {
122293
122493
  const rounds = await readRounds(t);
122294
122494
  const withRounds = rounds.length > 0 ? { ...result, rounds } : result;
122295
122495
  await mkdir34(t.auditDir, { recursive: true });
122296
- await writeFile15(resultPath(t), `${JSON.stringify(withRounds, null, 2)}
122496
+ await writeFile14(resultPath(t), `${JSON.stringify(withRounds, null, 2)}
122297
122497
  `, "utf8");
122298
122498
  if (options.ledger === false)
122299
122499
  return;
@@ -122826,7 +123026,7 @@ function languageRunner(language) {
122826
123026
  function buildAcceptanceCommand(repoRoot, group) {
122827
123027
  const absFile = shellQuote(`${repoRoot}/${group.testPath}`);
122828
123028
  const template = group.command ?? `${languageRunner(group.language)} {{FILE}}`;
122829
- return template.replace(/\{\{FILE\}\}|\{\{file\}\}|\{\{files\}\}/g, absFile);
123029
+ return template.replace(/\{\{FILE\}\}|\{\{file\}\}|\{\{files\}\}/g, () => absFile);
122830
123030
  }
122831
123031
  async function runAcceptanceGate(repoRoot, groups, opts = {}) {
122832
123032
  const chunks = [];
@@ -125060,7 +125260,7 @@ var init_interaction3 = __esm(() => {
125060
125260
  });
125061
125261
 
125062
125262
  // src/pipeline/subscribers/registry.ts
125063
- import { mkdir as mkdir36, writeFile as writeFile16 } from "fs/promises";
125263
+ import { mkdir as mkdir36, writeFile as writeFile15 } from "fs/promises";
125064
125264
  import { basename as basename16, join as join124 } from "path";
125065
125265
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
125066
125266
  const logger = getSafeLogger2();
@@ -125080,7 +125280,7 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
125080
125280
  eventsDir: join124(outputDir, "features", feature, "runs"),
125081
125281
  registeredAt: new Date().toISOString()
125082
125282
  };
125083
- await writeFile16(metaFile, JSON.stringify(meta3, null, 2));
125283
+ await writeFile15(metaFile, JSON.stringify(meta3, null, 2));
125084
125284
  } catch (err) {
125085
125285
  logger?.warn("registry-writer", "Failed to write meta.json (non-fatal)", {
125086
125286
  path: metaFile,
@@ -128014,7 +128214,7 @@ __export(exports_story_size_gate, {
128014
128214
  function countBulletPoints(text) {
128015
128215
  const lines2 = text.split(`
128016
128216
  `);
128017
- const bulletPattern = /^\s*[-*\u2022]|\d+\./;
128217
+ const bulletPattern = /^\s*(?:[-*\u2022]|\d+\.)\s/;
128018
128218
  return lines2.filter((line) => bulletPattern.test(line)).length;
128019
128219
  }
128020
128220
  function analyzeStory(story, config2) {
@@ -153098,9 +153298,9 @@ function parseSchedule(input, now3) {
153098
153298
  const trimmed = input.trim();
153099
153299
  if (trimmed === "")
153100
153300
  return { ok: false, error: `Empty schedule value. ${ACCEPTED}` };
153101
- const relative33 = parseRelative(trimmed, now3);
153102
- if (relative33)
153103
- return relative33;
153301
+ const relative34 = parseRelative(trimmed, now3);
153302
+ if (relative34)
153303
+ return relative34;
153104
153304
  const timeOfDay = parseTimeOfDay(trimmed, now3);
153105
153305
  if (timeOfDay)
153106
153306
  return timeOfDay;