@outcomeeng/spx 0.6.9 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -95,7 +95,7 @@ var DEFAULT_CLI_IO = {
95
95
  import { inspect } from "util";
96
96
 
97
97
  // src/commands/agent/resume.ts
98
- import { readdir, readFile, stat } from "fs/promises";
98
+ import { open, readdir, stat } from "fs/promises";
99
99
  import { homedir } from "os";
100
100
 
101
101
  // src/domains/agent/protocol.ts
@@ -118,17 +118,23 @@ var AGENT_SESSION_STORE = {
118
118
  CODEX_SESSIONS_DIR: "sessions",
119
119
  CLAUDE_DIR: ".claude",
120
120
  CLAUDE_PROJECTS_DIR: "projects",
121
+ CLAUDE_SUBAGENTS_DIR: "subagents",
121
122
  JSONL_EXTENSION: ".jsonl",
122
123
  TEXT_ENCODING: "utf8"
123
124
  };
124
125
  var AGENT_RESUME_LIMITS = {
125
126
  RECENT_DAYS: 7,
126
- DISPLAYED_CANDIDATES: 20,
127
+ PER_AGENT_DISPLAYED_CANDIDATES: 5,
128
+ METADATA_HEAD_BYTES: 131072,
127
129
  HOURS_PER_DAY: 24,
128
130
  MINUTES_PER_HOUR: 60,
129
131
  SECONDS_PER_MINUTE: 60,
130
132
  MILLISECONDS_PER_SECOND: 1e3,
131
- ROOT_RESOLUTION_CONCURRENCY: 8
133
+ READ_CONCURRENCY: 8
134
+ };
135
+ var AGENT_RESUME_SCOPE = {
136
+ WORKTREE: "worktree",
137
+ BRANCH: "branch"
132
138
  };
133
139
  var AGENT_RESUME_MODE = {
134
140
  PICK: "pick",
@@ -151,6 +157,8 @@ var AGENT_SESSION_JSON_FIELDS = {
151
157
  SESSION_ID_CAMEL: "sessionId",
152
158
  ID: "id",
153
159
  PAYLOAD: "payload",
160
+ GIT: "git",
161
+ BRANCH: "branch",
154
162
  GIT_BRANCH: "gitBranch",
155
163
  THREAD_SOURCE: "thread_source"
156
164
  };
@@ -164,10 +172,19 @@ var CODEX_SESSION_ORIGINATOR = {
164
172
  VSCODE_HYPHEN: "codex-vscode",
165
173
  EXEC: "codex-exec"
166
174
  };
175
+ var CODEX_SESSION_THREAD_SOURCE = {
176
+ SUBAGENT: "subagent"
177
+ };
167
178
  var AGENT_RESUME_RECENT_WINDOW_MS = AGENT_RESUME_LIMITS.RECENT_DAYS * AGENT_RESUME_LIMITS.HOURS_PER_DAY * AGENT_RESUME_LIMITS.MINUTES_PER_HOUR * AGENT_RESUME_LIMITS.SECONDS_PER_MINUTE * AGENT_RESUME_LIMITS.MILLISECONDS_PER_SECOND;
168
179
 
169
180
  // src/domains/agent/resume.ts
170
- import { resolve as resolve3 } from "path";
181
+ import { isAbsolute, relative, resolve as resolve3, sep } from "path";
182
+ function worktreeResumeScope() {
183
+ return { kind: AGENT_RESUME_SCOPE.WORKTREE };
184
+ }
185
+ function branchResumeScope(branch) {
186
+ return { kind: AGENT_RESUME_SCOPE.BRANCH, branch };
187
+ }
171
188
  var AGENT_RESUME_PICKER_ACTION = {
172
189
  MOVE_UP: "move-up",
173
190
  MOVE_DOWN: "move-down",
@@ -227,34 +244,59 @@ function codexSessionStoreDir(homeDir) {
227
244
  function claudeCodeSessionStoreDir(homeDir) {
228
245
  return resolve3(homeDir, AGENT_SESSION_STORE.CLAUDE_DIR, AGENT_SESSION_STORE.CLAUDE_PROJECTS_DIR);
229
246
  }
247
+ var CLAUDE_PROJECT_PATH_SEPARATORS = /[/\\]/g;
248
+ var CLAUDE_PROJECT_ENCODED_SEPARATOR = "-";
249
+ function claudeProjectDirName(cwd) {
250
+ return cwd.replace(CLAUDE_PROJECT_PATH_SEPARATORS, CLAUDE_PROJECT_ENCODED_SEPARATOR);
251
+ }
230
252
  async function discoverAgentResumeCandidates(options) {
231
- const invocationRoot = await options.resolveWorktreeRoot(options.invocationDir);
232
- if (invocationRoot === null) {
253
+ const scope2 = await resolveAgentResumeScopeContext(options);
254
+ if (scope2 === null) {
233
255
  return [];
234
256
  }
235
- const drafts = [
236
- ...await discoverCodexResumeCandidates(
237
- codexSessionStoreDir(options.homeDir),
257
+ const cap = AGENT_RESUME_LIMITS.PER_AGENT_DISPLAYED_CANDIDATES;
258
+ const [codex, claude] = await Promise.all([
259
+ collectAgentCandidates(
260
+ AGENT_SESSION_KIND.CODEX,
261
+ await recentStoreFiles(
262
+ await collectJsonlFiles(codexSessionStoreDir(options.homeDir), options.fs),
263
+ options.fs,
264
+ options.nowMs
265
+ ),
238
266
  options.fs,
239
- options.nowMs
267
+ cap,
268
+ scope2.match,
269
+ parseCodexHead
240
270
  ),
241
- ...await discoverClaudeCodeResumeCandidates(
242
- claudeCodeSessionStoreDir(options.homeDir),
271
+ collectAgentCandidates(
272
+ AGENT_SESSION_KIND.CLAUDE_CODE,
273
+ await recentStoreFiles(
274
+ await claudeTranscriptFiles(claudeCodeSessionStoreDir(options.homeDir), options.fs, scope2.claudeDirAccepts),
275
+ options.fs,
276
+ options.nowMs
277
+ ),
243
278
  options.fs,
244
- options.nowMs
279
+ cap,
280
+ scope2.match,
281
+ parseClaudeHead
245
282
  )
246
- ];
247
- const sortedDrafts = [...drafts];
248
- sortedDrafts.sort(compareCandidates);
249
- const rootResults = await mapWithConcurrency(
250
- sortedDrafts,
251
- AGENT_RESUME_LIMITS.ROOT_RESOLUTION_CONCURRENCY,
252
- async (candidate) => ({
253
- candidate,
254
- root: await options.resolveWorktreeRoot(candidate.cwd)
255
- })
256
- );
257
- return rootResults.filter((result) => result.root !== null && samePath(result.root, invocationRoot)).map((result) => result.candidate).slice(0, AGENT_RESUME_LIMITS.DISPLAYED_CANDIDATES);
283
+ ]);
284
+ return [...codex, ...claude].sort(compareCandidates);
285
+ }
286
+ async function resolveAgentResumeScopeContext(options) {
287
+ if (options.scope.kind === AGENT_RESUME_SCOPE.BRANCH) {
288
+ const target = options.scope.branch;
289
+ return { match: (core) => core.branch === target, claudeDirAccepts: () => true };
290
+ }
291
+ const invocationRoot = await options.resolveWorktreeRoot(options.invocationDir);
292
+ if (invocationRoot === null) {
293
+ return null;
294
+ }
295
+ const projectPrefix = claudeProjectDirName(invocationRoot);
296
+ return {
297
+ match: (core) => isPathInsideOrEqual(invocationRoot, core.cwd),
298
+ claudeDirAccepts: (dirName) => dirName === projectPrefix || dirName.startsWith(`${projectPrefix}${CLAUDE_PROJECT_ENCODED_SEPARATOR}`)
299
+ };
258
300
  }
259
301
  function buildAgentResumeLaunchCommand(candidate) {
260
302
  if (candidate.agent === AGENT_SESSION_KIND.CODEX) {
@@ -282,28 +324,49 @@ function renderAgentResumeList(candidates) {
282
324
  function renderAgentResumeJson(candidates) {
283
325
  return JSON.stringify(candidates, null, 2);
284
326
  }
285
- async function discoverCodexResumeCandidates(root, fs8, nowMs) {
286
- return discoverStoreCandidates(root, fs8, nowMs, parseCodexCandidateFile);
327
+ async function recentStoreFiles(paths, fs8, nowMs) {
328
+ const stats = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
329
+ const stat8 = await fs8.stat(path7).catch(() => null);
330
+ if (stat8 === null || !isRecentAgentSessionMtime(stat8.mtimeMs, nowMs)) {
331
+ return null;
332
+ }
333
+ return { path: path7, modifiedAtMs: stat8.mtimeMs };
334
+ });
335
+ return stats.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
287
336
  }
288
- async function discoverClaudeCodeResumeCandidates(root, fs8, nowMs) {
289
- return discoverStoreCandidates(root, fs8, nowMs, parseClaudeCodeCandidateFile);
337
+ async function claudeTranscriptFiles(root, fs8, dirAccepts) {
338
+ const projectDirs = (await fs8.readDir(root).catch(() => [])).filter((entry) => entry.isDirectory && dirAccepts(entry.name)).map((entry) => resolve3(root, entry.name));
339
+ const perDir = await mapWithConcurrency(projectDirs, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (dir) => {
340
+ const entries = await fs8.readDir(dir).catch(() => []);
341
+ return entries.filter((entry) => entry.isFile && entry.name.endsWith(AGENT_SESSION_STORE.JSONL_EXTENSION)).map((entry) => resolve3(dir, entry.name));
342
+ });
343
+ return perDir.flat();
290
344
  }
291
- async function discoverStoreCandidates(root, fs8, nowMs, parseCandidate) {
292
- const files = await collectJsonlFiles(root, fs8);
345
+ async function collectAgentCandidates(agent, files, fs8, cap, match, parseHead) {
346
+ const seen = /* @__PURE__ */ new Set();
293
347
  const candidates = [];
294
348
  for (const file of files) {
295
- const stat8 = await fs8.stat(file).catch(() => null);
296
- if (stat8 === null || !isRecentAgentSessionMtime(stat8.mtimeMs, nowMs)) {
297
- continue;
349
+ if (candidates.length >= cap) {
350
+ break;
298
351
  }
299
- const content = await fs8.readFile(file).catch(() => null);
300
- if (content === null) {
352
+ const head = await fs8.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
353
+ if (head === null) {
301
354
  continue;
302
355
  }
303
- const candidate = parseCandidate(file, content, stat8.mtimeMs);
304
- if (candidate !== null) {
305
- candidates.push(candidate);
356
+ const core = parseHead(head);
357
+ if (core === null || !core.interactive || !match(core) || seen.has(core.sessionId)) {
358
+ continue;
306
359
  }
360
+ seen.add(core.sessionId);
361
+ candidates.push({
362
+ agent,
363
+ sessionId: core.sessionId,
364
+ cwd: core.cwd,
365
+ sourcePath: file.path,
366
+ modifiedAtMs: file.modifiedAtMs,
367
+ updatedAt: core.updatedAt,
368
+ branch: core.branch
369
+ });
307
370
  }
308
371
  return candidates;
309
372
  }
@@ -323,58 +386,61 @@ async function collectJsonlFiles(root, fs8) {
323
386
  function isRecentAgentSessionMtime(modifiedAtMs, nowMs) {
324
387
  return modifiedAtMs <= nowMs && nowMs - modifiedAtMs <= AGENT_RESUME_RECENT_WINDOW_MS;
325
388
  }
326
- function parseCodexCandidateFile(sourcePath, content, modifiedAtMs) {
327
- let sessionId = null;
328
- let cwd = null;
329
- let updatedAt = null;
330
- let isInteractiveSession = false;
331
- for (const line of content.split("\n")) {
389
+ function parseCodexHead(head) {
390
+ for (const line of head.split("\n")) {
332
391
  const row = parseJsonObject(line);
333
392
  if (row === null) {
334
393
  continue;
335
394
  }
336
- isInteractiveSession = isCodexInteractiveSessionRow(row) || isInteractiveSession;
337
- sessionId ??= firstString(row, [
395
+ if (firstString(row, [[AGENT_SESSION_JSON_FIELDS.TYPE]]) !== AGENT_SESSION_ROW_TYPE.CODEX_SESSION_META) {
396
+ continue;
397
+ }
398
+ const sessionId = firstString(row, [
338
399
  [AGENT_SESSION_JSON_FIELDS.SESSION_ID],
339
400
  [AGENT_SESSION_JSON_FIELDS.ID],
340
401
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID],
341
402
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.ID]
342
403
  ]);
343
- cwd = firstString(row, [
404
+ const cwd = firstString(row, [
344
405
  [AGENT_SESSION_JSON_FIELDS.CWD],
345
406
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.CWD]
346
- ]) ?? cwd;
347
- updatedAt = maxIsoTimestamp(updatedAt, firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]));
348
- }
349
- if (!isInteractiveSession || sessionId === null || cwd === null) {
350
- return null;
407
+ ]);
408
+ if (sessionId === null || cwd === null) {
409
+ return null;
410
+ }
411
+ const originator = firstString(row, [
412
+ [AGENT_SESSION_JSON_FIELDS.ORIGINATOR],
413
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.ORIGINATOR]
414
+ ]);
415
+ const threadSource = firstString(row, [
416
+ [AGENT_SESSION_JSON_FIELDS.THREAD_SOURCE],
417
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.THREAD_SOURCE]
418
+ ]);
419
+ return {
420
+ sessionId,
421
+ cwd,
422
+ branch: firstString(row, [
423
+ [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.GIT, AGENT_SESSION_JSON_FIELDS.BRANCH],
424
+ [AGENT_SESSION_JSON_FIELDS.GIT, AGENT_SESSION_JSON_FIELDS.BRANCH]
425
+ ]),
426
+ updatedAt: firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]),
427
+ interactive: isCodexInteractive(originator, threadSource)
428
+ };
351
429
  }
352
- return {
353
- agent: AGENT_SESSION_KIND.CODEX,
354
- sessionId,
355
- cwd,
356
- sourcePath,
357
- modifiedAtMs,
358
- updatedAt,
359
- branch: null
360
- };
430
+ return null;
361
431
  }
362
- function isCodexInteractiveSessionRow(row) {
363
- if (firstString(row, [[AGENT_SESSION_JSON_FIELDS.TYPE]]) !== AGENT_SESSION_ROW_TYPE.CODEX_SESSION_META) {
432
+ function isCodexInteractive(originator, threadSource) {
433
+ if (threadSource === CODEX_SESSION_THREAD_SOURCE.SUBAGENT) {
364
434
  return false;
365
435
  }
366
- const originator = firstString(row, [
367
- [AGENT_SESSION_JSON_FIELDS.ORIGINATOR],
368
- [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.ORIGINATOR]
369
- ]);
370
436
  return originator === CODEX_SESSION_ORIGINATOR.TUI || originator === CODEX_SESSION_ORIGINATOR.CLI || originator === CODEX_SESSION_ORIGINATOR.VSCODE || originator === CODEX_SESSION_ORIGINATOR.VSCODE_HYPHEN;
371
437
  }
372
- function parseClaudeCodeCandidateFile(sourcePath, content, modifiedAtMs) {
438
+ function parseClaudeHead(head) {
373
439
  let sessionId = null;
374
440
  let cwd = null;
375
- let updatedAt = null;
376
441
  let branch = null;
377
- for (const line of content.split("\n")) {
442
+ let updatedAt = null;
443
+ for (const line of head.split("\n")) {
378
444
  const row = parseJsonObject(line);
379
445
  if (row === null) {
380
446
  continue;
@@ -385,25 +451,20 @@ function parseClaudeCodeCandidateFile(sourcePath, content, modifiedAtMs) {
385
451
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID_CAMEL],
386
452
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.SESSION_ID]
387
453
  ]);
388
- cwd = firstString(row, [
454
+ cwd ??= firstString(row, [
389
455
  [AGENT_SESSION_JSON_FIELDS.CWD],
390
456
  [AGENT_SESSION_JSON_FIELDS.PAYLOAD, AGENT_SESSION_JSON_FIELDS.CWD]
391
- ]) ?? cwd;
392
- updatedAt = maxIsoTimestamp(updatedAt, firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]));
393
- branch = firstString(row, [[AGENT_SESSION_JSON_FIELDS.GIT_BRANCH]]) ?? branch;
457
+ ]);
458
+ updatedAt ??= firstString(row, [[AGENT_SESSION_JSON_FIELDS.TIMESTAMP]]);
459
+ branch ??= firstString(row, [[AGENT_SESSION_JSON_FIELDS.GIT_BRANCH]]);
460
+ if (sessionId !== null && cwd !== null && branch !== null) {
461
+ break;
462
+ }
394
463
  }
395
464
  if (sessionId === null || cwd === null) {
396
465
  return null;
397
466
  }
398
- return {
399
- agent: AGENT_SESSION_KIND.CLAUDE_CODE,
400
- sessionId,
401
- cwd,
402
- sourcePath,
403
- modifiedAtMs,
404
- updatedAt,
405
- branch
406
- };
467
+ return { sessionId, cwd, branch, updatedAt, interactive: true };
407
468
  }
408
469
  function parseJsonObject(line) {
409
470
  const trimmed = line.trim();
@@ -439,15 +500,6 @@ function valueAtPath(row, path7) {
439
500
  function isRecord(value) {
440
501
  return typeof value === "object" && value !== null && !Array.isArray(value);
441
502
  }
442
- function maxIsoTimestamp(left, right) {
443
- if (right === null) {
444
- return left;
445
- }
446
- if (left === null) {
447
- return right;
448
- }
449
- return Date.parse(right) > Date.parse(left) ? right : left;
450
- }
451
503
  function compareCandidates(left, right) {
452
504
  const modifiedDiff = right.modifiedAtMs - left.modifiedAtMs;
453
505
  if (modifiedDiff !== 0) {
@@ -455,8 +507,9 @@ function compareCandidates(left, right) {
455
507
  }
456
508
  return `${left.agent}:${left.sessionId}`.localeCompare(`${right.agent}:${right.sessionId}`);
457
509
  }
458
- function samePath(left, right) {
459
- return resolve3(left) === resolve3(right);
510
+ function isPathInsideOrEqual(parent, child) {
511
+ const rel = relative(resolve3(parent), resolve3(child));
512
+ return rel.length === 0 || !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
460
513
  }
461
514
  async function mapWithConcurrency(items, concurrency, mapper) {
462
515
  const results = new Array(items.length);
@@ -478,7 +531,7 @@ async function mapWithConcurrency(items, concurrency, mapper) {
478
531
 
479
532
  // src/git/root.ts
480
533
  import { execa } from "execa";
481
- import { basename, dirname, isAbsolute, join, resolve as resolve4 } from "path";
534
+ import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
482
535
 
483
536
  // src/git/environment.ts
484
537
  function withoutGitEnvironment(env) {
@@ -659,7 +712,7 @@ async function detectGitCommonDirProductRoot(cwd = CONFIG_PROCESS_CWD.read(), de
659
712
  };
660
713
  }
661
714
  const commonDir = extractStdout(commonDirResult.stdout);
662
- const absoluteCommonDir = isAbsolute(commonDir) ? commonDir : resolve4(toplevel, commonDir);
715
+ const absoluteCommonDir = isAbsolute2(commonDir) ? commonDir : resolve4(toplevel, commonDir);
663
716
  const gitCommonDirProductRoot = dirname(absoluteCommonDir);
664
717
  return {
665
718
  productDir: gitCommonDirProductRoot,
@@ -822,7 +875,7 @@ async function gatherGitFacts(cwd = CONFIG_PROCESS_CWD.read(), deps = defaultGit
822
875
  };
823
876
  }
824
877
  const rawCommonDir = extractStdout(commonDirResult.stdout);
825
- const commonDir = isAbsolute(rawCommonDir) ? rawCommonDir : resolve4(worktreeRoot, rawCommonDir);
878
+ const commonDir = isAbsolute2(rawCommonDir) ? rawCommonDir : resolve4(worktreeRoot, rawCommonDir);
826
879
  const bareResult = await deps.execa(
827
880
  GIT_ROOT_COMMAND.EXECUTABLE,
828
881
  [...GIT_CORE_BARE_ARGS],
@@ -845,8 +898,15 @@ var nodeAgentSessionFileSystem = {
845
898
  isFile: entry.isFile()
846
899
  }));
847
900
  },
848
- async readFile(path7) {
849
- return readFile(path7, AGENT_SESSION_STORE.TEXT_ENCODING);
901
+ async readHead(path7, maxBytes) {
902
+ const handle = await open(path7, "r");
903
+ try {
904
+ const buffer = Buffer.alloc(maxBytes);
905
+ const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
906
+ return buffer.toString(AGENT_SESSION_STORE.TEXT_ENCODING, 0, bytesRead);
907
+ } finally {
908
+ await handle.close();
909
+ }
850
910
  },
851
911
  async stat(path7) {
852
912
  const result = await stat(path7);
@@ -868,6 +928,7 @@ async function loadAgentResumeCandidates(options) {
868
928
  invocationDir: options.cwd,
869
929
  homeDir: deps.homeDir(),
870
930
  nowMs: deps.nowMs(),
931
+ scope: options.scope,
871
932
  fs: deps.fs,
872
933
  resolveWorktreeRoot: deps.resolveWorktreeRoot
873
934
  });
@@ -1182,7 +1243,11 @@ var AGENT_CLI = {
1182
1243
  flags: {
1183
1244
  latest: "--latest",
1184
1245
  list: "--list",
1185
- json: "--json"
1246
+ json: "--json",
1247
+ branch: "--branch"
1248
+ },
1249
+ optionArgs: {
1250
+ branch: "--branch <name>"
1186
1251
  }
1187
1252
  };
1188
1253
  var AGENT_CLI_EXIT = {
@@ -1213,6 +1278,21 @@ function handleError(invocation, error) {
1213
1278
  writeError(invocation, `Error: ${message}`);
1214
1279
  return invocation.io.exit(AGENT_CLI_EXIT.FAILURE);
1215
1280
  }
1281
+ function resumeScopeFromOptions(options) {
1282
+ return options.branch === void 0 ? worktreeResumeScope() : branchResumeScope(options.branch);
1283
+ }
1284
+ async function dispatchInteractiveResume(mode, commandOptions, deps, invocation) {
1285
+ const candidates = await loadAgentResumeCandidates(commandOptions);
1286
+ if (candidates.length === 0) {
1287
+ writeError(invocation, AGENT_RESUME_TEXT.NO_MATCHES);
1288
+ return AGENT_CLI_EXIT.FAILURE;
1289
+ }
1290
+ if (mode === AGENT_RESUME_MODE.LATEST) {
1291
+ return deps.launchCandidate(candidates[0]);
1292
+ }
1293
+ const pickerResult = await deps.pickCandidate(candidates);
1294
+ return pickerResult.kind === AGENT_RESUME_PICKER_RESULT.SELECTED ? deps.launchCandidate(pickerResult.candidate) : AGENT_CLI_EXIT.SUCCESS;
1295
+ }
1216
1296
  function createAgentDomain(deps = {}) {
1217
1297
  const resolvedDeps = {
1218
1298
  ...DEFAULT_AGENT_CLI_DEPENDENCIES,
@@ -1223,7 +1303,7 @@ function createAgentDomain(deps = {}) {
1223
1303
  description: "Find and resume coding-agent sessions",
1224
1304
  register: (program, invocation) => {
1225
1305
  const agentCmd = program.command(AGENT_CLI.commandName).description("Find and resume coding-agent sessions");
1226
- agentCmd.command(AGENT_CLI.resumeCommandName).description("Resume a Codex or Claude Code agent session for this worktree").option(AGENT_CLI.flags.latest, "Launch the newest matching session").option(AGENT_CLI.flags.list, "List matching sessions").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").action(async (options) => {
1306
+ agentCmd.command(AGENT_CLI.resumeCommandName).description("Resume a Codex or Claude Code agent session for this worktree").option(AGENT_CLI.flags.latest, "Launch the newest matching session").option(AGENT_CLI.flags.list, "List matching sessions").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").option(AGENT_CLI.optionArgs.branch, "Scope to sessions started on the named branch, across worktrees").action(async (options) => {
1227
1307
  let requestedExitCode = AGENT_CLI_EXIT.SUCCESS;
1228
1308
  try {
1229
1309
  const mode = resolveAgentResumeMode(options);
@@ -1233,6 +1313,7 @@ function createAgentDomain(deps = {}) {
1233
1313
  } else {
1234
1314
  const commandOptions = {
1235
1315
  cwd: invocation.resolveEffectiveInvocationDir(),
1316
+ scope: resumeScopeFromOptions(options),
1236
1317
  deps: resolvedDeps.resumeDeps
1237
1318
  };
1238
1319
  if (mode === AGENT_RESUME_MODE.JSON) {
@@ -1243,18 +1324,7 @@ function createAgentDomain(deps = {}) {
1243
1324
  writeOutput(invocation, await listAgentResumeSessions(commandOptions));
1244
1325
  return;
1245
1326
  }
1246
- const candidates = await loadAgentResumeCandidates(commandOptions);
1247
- if (candidates.length === 0) {
1248
- writeError(invocation, AGENT_RESUME_TEXT.NO_MATCHES);
1249
- requestedExitCode = AGENT_CLI_EXIT.FAILURE;
1250
- } else if (mode === AGENT_RESUME_MODE.LATEST) {
1251
- requestedExitCode = await resolvedDeps.launchCandidate(candidates[0]);
1252
- } else {
1253
- const pickerResult = await resolvedDeps.pickCandidate(candidates);
1254
- if (pickerResult.kind === AGENT_RESUME_PICKER_RESULT.SELECTED) {
1255
- requestedExitCode = await resolvedDeps.launchCandidate(pickerResult.candidate);
1256
- }
1257
- }
1327
+ requestedExitCode = await dispatchInteractiveResume(mode, commandOptions, resolvedDeps, invocation);
1258
1328
  }
1259
1329
  } catch (error) {
1260
1330
  handleError(invocation, error);
@@ -2475,14 +2545,14 @@ async function compactRetrieveCommand(options) {
2475
2545
  }
2476
2546
 
2477
2547
  // src/commands/compact/store.ts
2478
- import { readFile as readFile2 } from "fs/promises";
2548
+ import { readFile } from "fs/promises";
2479
2549
  var UTF8_ENCODING = "utf8";
2480
2550
  async function compactStoreCommand(options) {
2481
2551
  const sessionToken = resolveCompactSessionToken(options.sessionId, options.env ?? process.env);
2482
2552
  if (sessionToken === void 0) return 1;
2483
2553
  let transcript;
2484
2554
  try {
2485
- transcript = await readFile2(options.transcript, UTF8_ENCODING);
2555
+ transcript = await readFile(options.transcript, UTF8_ENCODING);
2486
2556
  } catch {
2487
2557
  return 1;
2488
2558
  }
@@ -2538,7 +2608,7 @@ var compactDomain = {
2538
2608
  };
2539
2609
 
2540
2610
  // src/config/index.ts
2541
- import { readFile as readFile3 } from "fs/promises";
2611
+ import { readFile as readFile2 } from "fs/promises";
2542
2612
  import { join as join4 } from "path";
2543
2613
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
2544
2614
  import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
@@ -2725,7 +2795,7 @@ function validateBoolean(path7, value) {
2725
2795
  }
2726
2796
  function validateAgent(path7, value) {
2727
2797
  if (typeof value !== "string" || !isAgent(value)) {
2728
- return { ok: false, error: `${path7} must be a registered configured agent` };
2798
+ return { ok: false, error: `${path7} must be a registered agent` };
2729
2799
  }
2730
2800
  return { ok: true, value };
2731
2801
  }
@@ -2734,7 +2804,7 @@ function isAgent(value) {
2734
2804
  }
2735
2805
  function validateAgentArray(path7, value) {
2736
2806
  if (!Array.isArray(value) || value.length === 0) {
2737
- return { ok: false, error: `${path7} must be a non-empty array of registered configured agents` };
2807
+ return { ok: false, error: `${path7} must be a non-empty array of registered agents` };
2738
2808
  }
2739
2809
  const agents = [];
2740
2810
  const seen = /* @__PURE__ */ new Set();
@@ -2742,7 +2812,7 @@ function validateAgentArray(path7, value) {
2742
2812
  const agent = validateAgent(`${path7}.${index}`, entry);
2743
2813
  if (!agent.ok) return agent;
2744
2814
  if (seen.has(agent.value)) {
2745
- return { ok: false, error: `${path7}.${index} repeats registered configured agent ${agent.value}` };
2815
+ return { ok: false, error: `${path7}.${index} repeats registered agent ${agent.value}` };
2746
2816
  }
2747
2817
  seen.add(agent.value);
2748
2818
  agents.push(agent.value);
@@ -2896,6 +2966,13 @@ function validatePluginBootstrap(raw) {
2896
2966
  skills.value
2897
2967
  );
2898
2968
  if (!skillUniqueness.ok) return skillUniqueness;
2969
+ const bootstrapNameUniqueness = validatePluginBootstrapNameUniqueness(
2970
+ sectionPath,
2971
+ marketplaces.value,
2972
+ plugins.value,
2973
+ skills.value
2974
+ );
2975
+ if (!bootstrapNameUniqueness.ok) return bootstrapNameUniqueness;
2899
2976
  return {
2900
2977
  ok: true,
2901
2978
  value: {
@@ -2944,6 +3021,38 @@ function validateNamedAgentEntryUniqueness(path7, entries) {
2944
3021
  }
2945
3022
  return { ok: true, value: void 0 };
2946
3023
  }
3024
+ function validatePluginBootstrapNameUniqueness(sectionPath, marketplaces, plugins, skills) {
3025
+ const namesByAgent = /* @__PURE__ */ new Map();
3026
+ const entries = [
3027
+ ...marketplaces.map((entry, index) => ({
3028
+ agent: entry.agent,
3029
+ name: entry.name,
3030
+ path: `${sectionPath}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.MARKETPLACES}.${index}`
3031
+ })),
3032
+ ...plugins.map((entry, index) => ({
3033
+ agent: entry.agent,
3034
+ name: entry.name,
3035
+ path: `${sectionPath}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.PLUGINS}.${index}`
3036
+ })),
3037
+ ...skills.map((entry, index) => ({
3038
+ agent: entry.agent,
3039
+ name: entry.name,
3040
+ path: `${sectionPath}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.SKILLS}.${index}`
3041
+ }))
3042
+ ];
3043
+ for (const entry of entries) {
3044
+ const agentNames = namesByAgent.get(entry.agent) ?? /* @__PURE__ */ new Set();
3045
+ if (agentNames.has(entry.name)) {
3046
+ return {
3047
+ ok: false,
3048
+ error: `${entry.path}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.NAME} ${JSON.stringify(entry.name)} is already used by another ${entry.agent} bootstrap entry`
3049
+ };
3050
+ }
3051
+ agentNames.add(entry.name);
3052
+ namesByAgent.set(entry.agent, agentNames);
3053
+ }
3054
+ return { ok: true, value: void 0 };
3055
+ }
2947
3056
  function validatePluginMarketplaceReferences(path7, marketplaces, plugins) {
2948
3057
  const marketplacesByAgent = /* @__PURE__ */ new Map();
2949
3058
  for (const marketplace of marketplaces) {
@@ -4160,7 +4269,7 @@ async function readProductConfigFile(productDir) {
4160
4269
  const path7 = join4(productDir, filename);
4161
4270
  let raw;
4162
4271
  try {
4163
- raw = await readFile3(path7, "utf8");
4272
+ raw = await readFile2(path7, "utf8");
4164
4273
  } catch (error) {
4165
4274
  if (isFileNotFound(error)) continue;
4166
4275
  return { ok: false, error: `failed to read ${filename}: ${toMessage(error)}` };
@@ -4444,7 +4553,7 @@ var configDomain = {
4444
4553
  };
4445
4554
 
4446
4555
  // src/interfaces/cli/diagnose.ts
4447
- import { readFile as readFile4 } from "fs/promises";
4556
+ import { readFile as readFile3 } from "fs/promises";
4448
4557
  import { Option } from "commander";
4449
4558
 
4450
4559
  // src/domains/diagnose/types.ts
@@ -5957,11 +6066,12 @@ function worktreePoolReadingFromSnapshot(snapshot) {
5957
6066
  }
5958
6067
  function sessionEnvironmentReadingFromSnapshot(snapshot, environment) {
5959
6068
  const currentWorktree = snapshot.worktrees.find((worktree) => worktree.root === snapshot.currentWorktreeRoot);
6069
+ const worktreeClaimed = currentWorktree?.status === OCCUPANCY_STATUS.RUNNING;
5960
6070
  return {
5961
6071
  errored: snapshot.errored,
5962
6072
  hookPresent: environment.hookPresent,
5963
- sessionIdentity: environment.sessionIdentity,
5964
- worktreeClaimed: currentWorktree?.status === OCCUPANCY_STATUS.RUNNING
6073
+ sessionIdentity: environment.sessionIdentity || worktreeClaimed && currentWorktree.sessionId !== void 0,
6074
+ worktreeClaimed
5965
6075
  };
5966
6076
  }
5967
6077
  async function exportedClaimReadingFromEnv(env, deps) {
@@ -6231,7 +6341,7 @@ var diagnoseDomain = {
6231
6341
  isTty: Boolean(process.stdout.isTTY)
6232
6342
  }),
6233
6343
  registry: defaultRegistry(),
6234
- fs: { readFile: (path7) => readFile4(path7, "utf8") }
6344
+ fs: { readFile: (path7) => readFile3(path7, "utf8") }
6235
6345
  });
6236
6346
  if (!result.ok) {
6237
6347
  handleError2(result.error, invocation.io);
@@ -6946,9 +7056,6 @@ function compareJournalRunsOldestFirst(left, right) {
6946
7056
  const createdAtOrder = left.createdAtMs - right.createdAtMs;
6947
7057
  return createdAtOrder === 0 ? compareAsciiStrings(left.runToken, right.runToken) : createdAtOrder;
6948
7058
  }
6949
- function applyJournalRunListLimit(runs, limit) {
6950
- return limit === void 0 ? runs : runs.slice(0, limit);
6951
- }
6952
7059
  function journalRunFilePath(scope2) {
6953
7060
  const typeRunsDir = journalRunsDir(scope2);
6954
7061
  if (!typeRunsDir.ok) return typeRunsDir;
@@ -7369,6 +7476,9 @@ var JOURNAL_RUNTIME_ERROR = {
7369
7476
  RENDER_FAILED: "journal render failed",
7370
7477
  RUN_NOT_FOUND: "journal run not found; open the run before operating on it"
7371
7478
  };
7479
+ function applyJournalRunListLimit(runs, limit) {
7480
+ return runs.slice(0, limit);
7481
+ }
7372
7482
  function bindRunFilePath(ref) {
7373
7483
  return journalRunFilePath({
7374
7484
  productDir: ref.productDir,
@@ -7541,7 +7651,11 @@ async function readSealedJournalRunSet(scope2, options = {}) {
7541
7651
  }
7542
7652
  return {
7543
7653
  ok: true,
7544
- value: runs.sort((left, right) => compareJournalRunsOldestFirst(left.metadata, right.metadata)).map((run) => ({ runToken: run.metadata.runToken, metadata: run.metadata, events: run.events }))
7654
+ value: runs.sort((left, right) => compareJournalRunsNewestFirst(left.metadata, right.metadata)).slice(0, scope2.limit).sort((left, right) => compareJournalRunsOldestFirst(left.metadata, right.metadata)).map((run) => ({
7655
+ runToken: run.metadata.runToken,
7656
+ metadata: run.metadata,
7657
+ events: run.events.slice(0, scope2.eventLimit)
7658
+ }))
7545
7659
  };
7546
7660
  }
7547
7661
  async function appendJournalEvent(ref, input, sink, options = {}) {
@@ -7593,7 +7707,12 @@ var JOURNAL_CLI_EXIT_CODE = {
7593
7707
  OK: 0,
7594
7708
  ERROR: 1
7595
7709
  };
7596
- var JOURNAL_CLI_LIST_LIMIT = {
7710
+ var JOURNAL_CLI_RUN_LIMIT = {
7711
+ DEFAULT: 20,
7712
+ MIN: 1
7713
+ };
7714
+ var JOURNAL_CLI_READ_SET_EVENT_LIMIT = {
7715
+ DEFAULT: 100,
7597
7716
  MIN: 1
7598
7717
  };
7599
7718
  var JOURNAL_CLI_ENV = {
@@ -7613,7 +7732,8 @@ var JOURNAL_CLI_ERROR = {
7613
7732
  PULL_REQUEST_UNRESOLVED: "github pull-request number is not resolvable from the environment",
7614
7733
  INVALID_EVENT_INPUT: "journal append event input is missing a required CloudEvents field",
7615
7734
  INVALID_CURSOR: "journal read cursor must be a whole non-negative integer",
7616
- INVALID_LIST_LIMIT: "journal list limit must be a positive whole integer",
7735
+ INVALID_RUN_LIMIT: "journal run limit must be a positive whole integer",
7736
+ INVALID_READ_SET_EVENT_LIMIT: "journal read-set event limit must be a positive whole integer",
7617
7737
  INVALID_SEALED_FILTER: "journal list sealed filter is not registered",
7618
7738
  INVALID_TERMINAL_STATE_FILTER: "journal list terminal-state filter is not registered",
7619
7739
  OPEN_HYDRATION_FAILED: "journal open failed to hydrate the pull request's prior runs"
@@ -7813,13 +7933,21 @@ function parseJournalCursor(raw) {
7813
7933
  const value = Number.parseInt(raw, DECIMAL_RADIX);
7814
7934
  return Number.isSafeInteger(value) ? { ok: true, value } : { ok: false, error: JOURNAL_CLI_ERROR.INVALID_CURSOR };
7815
7935
  }
7816
- function parseJournalListLimit(raw) {
7817
- if (raw === void 0) return { ok: true, value: void 0 };
7936
+ function parseJournalRunLimit(raw) {
7937
+ if (raw === void 0) return { ok: true, value: JOURNAL_CLI_RUN_LIMIT.DEFAULT };
7818
7938
  if (!POSITIVE_INTEGER_PATTERN.test(raw)) {
7819
- return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_LIST_LIMIT };
7939
+ return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_RUN_LIMIT };
7820
7940
  }
7821
7941
  const value = Number.parseInt(raw, DECIMAL_RADIX);
7822
- return Number.isSafeInteger(value) && value >= JOURNAL_CLI_LIST_LIMIT.MIN ? { ok: true, value } : { ok: false, error: JOURNAL_CLI_ERROR.INVALID_LIST_LIMIT };
7942
+ return Number.isSafeInteger(value) && value >= JOURNAL_CLI_RUN_LIMIT.MIN ? { ok: true, value } : { ok: false, error: JOURNAL_CLI_ERROR.INVALID_RUN_LIMIT };
7943
+ }
7944
+ function parseJournalReadSetEventLimit(raw) {
7945
+ if (raw === void 0) return { ok: true, value: JOURNAL_CLI_READ_SET_EVENT_LIMIT.DEFAULT };
7946
+ if (!POSITIVE_INTEGER_PATTERN.test(raw)) {
7947
+ return { ok: false, error: JOURNAL_CLI_ERROR.INVALID_READ_SET_EVENT_LIMIT };
7948
+ }
7949
+ const value = Number.parseInt(raw, DECIMAL_RADIX);
7950
+ return Number.isSafeInteger(value) && value >= JOURNAL_CLI_READ_SET_EVENT_LIMIT.MIN ? { ok: true, value } : { ok: false, error: JOURNAL_CLI_ERROR.INVALID_READ_SET_EVENT_LIMIT };
7823
7951
  }
7824
7952
  function parseJournalSealedFilter(raw) {
7825
7953
  if (raw === void 0) return { ok: true, value: void 0 };
@@ -7857,7 +7985,7 @@ async function journalListCommand(scope2, deps = {}) {
7857
7985
  if (!sealed.ok) return errorResult(sealed.error);
7858
7986
  const terminalState = parseJournalTerminalFilter(scope2.terminalState);
7859
7987
  if (!terminalState.ok) return errorResult(terminalState.error);
7860
- const limit = parseJournalListLimit(scope2.limit);
7988
+ const limit = parseJournalRunLimit(scope2.limit);
7861
7989
  if (!limit.ok) return errorResult(limit.error);
7862
7990
  const listScope = {
7863
7991
  productDir: await resolveJournalProductDir(deps),
@@ -7865,16 +7993,27 @@ async function journalListCommand(scope2, deps = {}) {
7865
7993
  ...scope2.type === void 0 ? {} : { type: scope2.type },
7866
7994
  ...sealed.value === void 0 ? {} : { sealed: sealed.value },
7867
7995
  ...terminalState.value === void 0 ? {} : { terminalState: terminalState.value },
7868
- ...limit.value === void 0 ? {} : { limit: limit.value }
7996
+ limit: limit.value
7869
7997
  };
7870
7998
  const runs = await listJournalRuns(listScope, verbOptions(deps));
7871
7999
  if (!runs.ok) return errorResult(runs.error);
7872
8000
  return okResult(JSON.stringify(runs.value));
7873
8001
  }
7874
8002
  async function journalReadSetCommand(scope2, deps = {}) {
8003
+ const limit = parseJournalRunLimit(scope2.limit);
8004
+ if (!limit.ok) return errorResult(limit.error);
8005
+ const eventLimit = parseJournalReadSetEventLimit(scope2.eventLimit);
8006
+ if (!eventLimit.ok) return errorResult(eventLimit.error);
7875
8007
  const context = await resolveJournalRunScope(scope2, deps);
7876
8008
  if (!context.ok) return errorResult(context.error);
7877
- const runs = await readSealedJournalRunSet(context.value, verbOptions(deps));
8009
+ const runs = await readSealedJournalRunSet(
8010
+ {
8011
+ ...context.value,
8012
+ eventLimit: eventLimit.value,
8013
+ limit: limit.value
8014
+ },
8015
+ verbOptions(deps)
8016
+ );
7878
8017
  if (!runs.ok) return errorResult(runs.error);
7879
8018
  return okResult(JSON.stringify(runs.value));
7880
8019
  }
@@ -7897,6 +8036,41 @@ async function journalRenderCommand(scope2, deps = {}) {
7897
8036
  return okResult(JSON.stringify(rendered.value));
7898
8037
  }
7899
8038
 
8039
+ // src/interfaces/cli/lib/stream-report.ts
8040
+ var CLI_STREAM_REPORT = {
8041
+ LINE_SEPARATOR: "\n"
8042
+ };
8043
+ function reportCliResult(result, io) {
8044
+ const output = `${result.output}${CLI_STREAM_REPORT.LINE_SEPARATOR}`;
8045
+ if (result.exitCode === 0) io.writeStdout(output);
8046
+ else io.writeStderr(output);
8047
+ io.setExitCode(result.exitCode);
8048
+ }
8049
+
8050
+ // src/interfaces/cli/lib/journal-stream-binding.ts
8051
+ function stdoutStreamSink(io) {
8052
+ return {
8053
+ async emit(event) {
8054
+ io.writeStdout(`${JSON.stringify(event)}${CLI_STREAM_REPORT.LINE_SEPARATOR}`);
8055
+ }
8056
+ };
8057
+ }
8058
+ function stderrStreamSink(io) {
8059
+ return {
8060
+ async emit(event) {
8061
+ io.writeStderr(`${JSON.stringify(event)}${CLI_STREAM_REPORT.LINE_SEPARATOR}`);
8062
+ }
8063
+ };
8064
+ }
8065
+ function createJournalStreamBinding(io, localSink = stdoutStreamSink(io)) {
8066
+ const repository = process.env[JOURNAL_CLI_ENV.GITHUB_REPOSITORY] ?? "";
8067
+ return {
8068
+ localSink,
8069
+ githubClient: createGithubPullRequestCommentClient({ repository, run: runGhApi }),
8070
+ githubRepository: repository
8071
+ };
8072
+ }
8073
+
7900
8074
  // src/interfaces/cli/journal.ts
7901
8075
  var JOURNAL_CLI = {
7902
8076
  commandName: "journal",
@@ -7914,9 +8088,14 @@ var JOURNAL_CLI = {
7914
8088
  branchSlugOption: "--branch-slug <slug>",
7915
8089
  sealedOption: "--sealed <state>",
7916
8090
  terminalStateOption: "--terminal-state <state>",
7917
- limitOption: "--limit <count>"
8091
+ limitOption: "--limit <count>",
8092
+ eventLimitOption: "--event-limit <count>"
8093
+ };
8094
+ var JOURNAL_CLI_HELP = {
8095
+ LIST_RUN_LIMIT: `Maximum number of runs (default: ${JOURNAL_CLI_RUN_LIMIT.DEFAULT})`,
8096
+ READ_SET_EVENT_LIMIT: `Maximum events returned per run (default: ${JOURNAL_CLI_READ_SET_EVENT_LIMIT.DEFAULT})`,
8097
+ READ_SET_RUN_LIMIT: `Maximum number of sealed runs (default: ${JOURNAL_CLI_RUN_LIMIT.DEFAULT})`
7918
8098
  };
7919
- var STREAM_LINE_SEPARATOR = "\n";
7920
8099
  var MALFORMED_EVENT_INPUT_ERROR = "journal append event input is not valid JSON";
7921
8100
  function scope(options) {
7922
8101
  return { type: options.type };
@@ -7935,7 +8114,7 @@ var journalDomain = {
7935
8114
  const journalDeps = () => ({
7936
8115
  cwd: invocation.resolveEffectiveInvocationDir(),
7937
8116
  onWarning: (warning) => {
7938
- if (warning !== void 0) invocation.io.writeStderr(`${warning}${STREAM_LINE_SEPARATOR}`);
8117
+ if (warning !== void 0) invocation.io.writeStderr(`${warning}${CLI_STREAM_REPORT.LINE_SEPARATOR}`);
7939
8118
  }
7940
8119
  });
7941
8120
  const journalCmd = program.command(JOURNAL_CLI.commandName).description(JOURNAL_CLI.description);
@@ -7951,7 +8130,7 @@ var journalDomain = {
7951
8130
  const result = await journalAppendCommand(
7952
8131
  runScope(options),
7953
8132
  input.value,
7954
- streamBinding(invocation.io),
8133
+ createJournalStreamBinding(invocation.io),
7955
8134
  journalDeps()
7956
8135
  );
7957
8136
  if (result.exitCode === JOURNAL_CLI_EXIT_CODE.OK) invocation.io.setExitCode(JOURNAL_CLI_EXIT_CODE.OK);
@@ -7966,15 +8145,17 @@ var journalDomain = {
7966
8145
  journalCmd.command(JOURNAL_CLI.renderCommandName).description("Render the run's event-prefix projection").requiredOption(JOURNAL_CLI.typeOption, "Opaque verification-type scope segment").requiredOption(JOURNAL_CLI.runOption, "Run token reported by open").option(JOURNAL_CLI.branchSlugOption, "Branch slug reported by journal list").action(async (options) => {
7967
8146
  report(await journalRenderCommand(runScope(options), journalDeps()), invocation.io);
7968
8147
  });
7969
- journalCmd.command(JOURNAL_CLI.listCommandName).description("List persisted run metadata").option(JOURNAL_CLI.typeOption, "Opaque verification-type scope segment").option(JOURNAL_CLI.branchSlugOption, "State-store branch slug").option(JOURNAL_CLI.sealedOption, "Sealed-state filter").option(JOURNAL_CLI.terminalStateOption, "Terminal-state filter").option(JOURNAL_CLI.limitOption, "Maximum number of runs").action(async (options) => {
8148
+ journalCmd.command(JOURNAL_CLI.listCommandName).description("List persisted run metadata").option(JOURNAL_CLI.typeOption, "Opaque verification-type scope segment").option(JOURNAL_CLI.branchSlugOption, "State-store branch slug").option(JOURNAL_CLI.sealedOption, "Sealed-state filter").option(JOURNAL_CLI.terminalStateOption, "Terminal-state filter").option(JOURNAL_CLI.limitOption, JOURNAL_CLI_HELP.LIST_RUN_LIMIT).action(async (options) => {
7970
8149
  report(await journalListCommand(options, journalDeps()), invocation.io);
7971
8150
  });
7972
- journalCmd.command(JOURNAL_CLI.readSetCommandName).description("Read sealed runs in one branch and type scope").requiredOption(JOURNAL_CLI.typeOption, "Opaque verification-type scope segment").option(JOURNAL_CLI.branchSlugOption, "State-store branch slug").action(async (options) => {
8151
+ journalCmd.command(JOURNAL_CLI.readSetCommandName).description("Read sealed runs in one branch and type scope").requiredOption(JOURNAL_CLI.typeOption, "Opaque verification-type scope segment").option(JOURNAL_CLI.branchSlugOption, "State-store branch slug").option(JOURNAL_CLI.limitOption, JOURNAL_CLI_HELP.READ_SET_RUN_LIMIT).option(JOURNAL_CLI.eventLimitOption, JOURNAL_CLI_HELP.READ_SET_EVENT_LIMIT).action(async (options) => {
7973
8152
  report(
7974
8153
  await journalReadSetCommand(
7975
8154
  {
7976
8155
  ...scope(options),
7977
- ...options.branchSlug === void 0 ? {} : { branchSlug: options.branchSlug }
8156
+ ...options.branchSlug === void 0 ? {} : { branchSlug: options.branchSlug },
8157
+ ...options.eventLimit === void 0 ? {} : { eventLimit: options.eventLimit },
8158
+ ...options.limit === void 0 ? {} : { limit: options.limit }
7978
8159
  },
7979
8160
  journalDeps()
7980
8161
  ),
@@ -7983,21 +8164,6 @@ var journalDomain = {
7983
8164
  });
7984
8165
  }
7985
8166
  };
7986
- function stdoutStreamSink(io) {
7987
- return {
7988
- async emit(event) {
7989
- io.writeStdout(`${JSON.stringify(event)}${STREAM_LINE_SEPARATOR}`);
7990
- }
7991
- };
7992
- }
7993
- function streamBinding(io) {
7994
- const repository = process.env[JOURNAL_CLI_ENV.GITHUB_REPOSITORY] ?? "";
7995
- return {
7996
- localSink: stdoutStreamSink(io),
7997
- githubClient: createGithubPullRequestCommentClient({ repository, run: runGhApi }),
7998
- githubRepository: repository
7999
- };
8000
- }
8001
8167
  async function readStdinEventInput() {
8002
8168
  const chunks = [];
8003
8169
  for await (const chunk of process.stdin) {
@@ -8010,7 +8176,7 @@ async function readStdinEventInput() {
8010
8176
  }
8011
8177
  }
8012
8178
  function report(result, io) {
8013
- const output = `${result.output}${STREAM_LINE_SEPARATOR}`;
8179
+ const output = `${result.output}${CLI_STREAM_REPORT.LINE_SEPARATOR}`;
8014
8180
  if (result.exitCode === JOURNAL_CLI_EXIT_CODE.OK) io.writeStdout(output);
8015
8181
  else io.writeStderr(output);
8016
8182
  io.setExitCode(result.exitCode);
@@ -9323,7 +9489,7 @@ ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.SESSION_FILE, absolutePath)}`;
9323
9489
  }
9324
9490
 
9325
9491
  // src/commands/session/list.ts
9326
- import { readdir as readdir2, readFile as readFile5 } from "fs/promises";
9492
+ import { readdir as readdir2, readFile as readFile4 } from "fs/promises";
9327
9493
  import { join as join11 } from "path";
9328
9494
  var SESSION_LIST_FORMAT = {
9329
9495
  TEXT: "text",
@@ -9338,7 +9504,7 @@ async function loadSessionsFromDir(dir, status) {
9338
9504
  if (!file.endsWith(".md")) continue;
9339
9505
  const id = file.replace(".md", "");
9340
9506
  const filePath = join11(dir, file);
9341
- const content = await readFile5(filePath, SESSION_FILE_ENCODING);
9507
+ const content = await readFile4(filePath, SESSION_FILE_ENCODING);
9342
9508
  const metadata = parseSessionMetadata(content);
9343
9509
  sessions.push({
9344
9510
  id,
@@ -9401,7 +9567,7 @@ async function listCommand(options) {
9401
9567
  }
9402
9568
 
9403
9569
  // src/commands/session/pickup.ts
9404
- import { mkdir as mkdir3, readdir as readdir3, readFile as readFile6, rename as rename2 } from "fs/promises";
9570
+ import { mkdir as mkdir3, readdir as readdir3, readFile as readFile5, rename as rename2 } from "fs/promises";
9405
9571
  import { join as join12, resolve as resolve7 } from "path";
9406
9572
 
9407
9573
  // src/domains/session/pickup.ts
@@ -9442,7 +9608,7 @@ var PICKUP_TARGET_STATUS = SESSION_STATUSES[1];
9442
9608
  var PICKUP_DEPS = {
9443
9609
  mkdir: mkdir3,
9444
9610
  readdir: readdir3,
9445
- readFile: readFile6,
9611
+ readFile: readFile5,
9446
9612
  rename: rename2
9447
9613
  };
9448
9614
  async function loadTodoSessions(config) {
@@ -9551,7 +9717,7 @@ async function loadPickCandidates(options) {
9551
9717
  }
9552
9718
 
9553
9719
  // src/commands/session/prune.ts
9554
- import { readdir as readdir4, readFile as readFile7, unlink as unlink2 } from "fs/promises";
9720
+ import { readdir as readdir4, readFile as readFile6, unlink as unlink2 } from "fs/promises";
9555
9721
  import { join as join13 } from "path";
9556
9722
 
9557
9723
  // src/domains/session/prune.ts
@@ -9602,7 +9768,7 @@ async function loadArchiveSessions(config) {
9602
9768
  if (!file.endsWith(".md")) continue;
9603
9769
  const id = file.replace(".md", "");
9604
9770
  const filePath = join13(config.archiveDir, file);
9605
- const content = await readFile7(filePath, SESSION_FILE_ENCODING);
9771
+ const content = await readFile6(filePath, SESSION_FILE_ENCODING);
9606
9772
  const metadata = parseSessionMetadata(content);
9607
9773
  sessions.push({
9608
9774
  id,
@@ -9719,7 +9885,7 @@ async function releaseCommand(options) {
9719
9885
  }
9720
9886
 
9721
9887
  // src/commands/session/show.ts
9722
- import { readFile as readFile8, stat as stat5 } from "fs/promises";
9888
+ import { readFile as readFile7, stat as stat5 } from "fs/promises";
9723
9889
  async function findExistingPath(paths, _config) {
9724
9890
  for (let i = 0; i < paths.length; i++) {
9725
9891
  const filePath = paths[i];
@@ -9739,7 +9905,7 @@ async function resolveSession(sessionId, config) {
9739
9905
  if (!found) {
9740
9906
  throw new SessionNotFoundError(sessionId);
9741
9907
  }
9742
- const content = await readFile8(found.path, SESSION_FILE_ENCODING);
9908
+ const content = await readFile7(found.path, SESSION_FILE_ENCODING);
9743
9909
  return { status: found.status, path: found.path, content };
9744
9910
  }
9745
9911
  async function showSingle(sessionId, config) {
@@ -10403,7 +10569,7 @@ var sessionDomain = {
10403
10569
  };
10404
10570
 
10405
10571
  // src/lib/spec-tree/index.ts
10406
- import { readdir as readdir6, readFile as readFile9 } from "fs/promises";
10572
+ import { readdir as readdir6, readFile as readFile8 } from "fs/promises";
10407
10573
  import { join as join14 } from "path";
10408
10574
  var SPEC_TREE_FIELD_KEY = {
10409
10575
  VERSION: "version",
@@ -10488,7 +10654,7 @@ function createFilesystemSpecTreeSource(options) {
10488
10654
  if (ref.path === void 0) {
10489
10655
  throw new Error("Filesystem source refs require a path");
10490
10656
  }
10491
- return readFile9(join14(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
10657
+ return readFile8(join14(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
10492
10658
  }
10493
10659
  };
10494
10660
  }
@@ -10952,7 +11118,7 @@ function formatNextNode(node) {
10952
11118
 
10953
11119
  // src/commands/test/discovery.ts
10954
11120
  import { readdir as readdir7 } from "fs/promises";
10955
- import { join as join15, relative, sep } from "path";
11121
+ import { join as join15, relative as relative2, sep as sep2 } from "path";
10956
11122
  var TESTS_DIRECTORY_NAME = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
10957
11123
  var SPEC_ROOT_DIRECTORY = SPEC_TREE_CONFIG.ROOT_DIRECTORY;
10958
11124
  var POSIX_SEPARATOR = "/";
@@ -10980,7 +11146,7 @@ async function collectTestFiles(directory, insideTestsDir, found) {
10980
11146
  }
10981
11147
  }
10982
11148
  function toPosixRelative(productDir, absolute) {
10983
- return relative(productDir, absolute).split(sep).join(POSIX_SEPARATOR);
11149
+ return relative2(productDir, absolute).split(sep2).join(POSIX_SEPARATOR);
10984
11150
  }
10985
11151
  function compareAscii(left, right) {
10986
11152
  if (left < right) return -1;
@@ -11511,9 +11677,15 @@ function toErrorMessage3(error) {
11511
11677
  // src/lib/git/name-status.ts
11512
11678
  var GIT_NAME_STATUS_FLAG = "--name-status";
11513
11679
  var GIT_NULL_DELIMITED_FLAG = "-z";
11680
+ var GIT_DIFF_COMMAND = "diff";
11681
+ var GIT_RANGE_SEPARATOR = "..";
11682
+ function changesetNameStatusArgs(base, head) {
11683
+ return [GIT_DIFF_COMMAND, GIT_NAME_STATUS_FLAG, GIT_NULL_DELIMITED_FLAG, `${base}${GIT_RANGE_SEPARATOR}${head}`];
11684
+ }
11514
11685
  var GIT_RENAME_STATUS_PREFIX = "R";
11515
11686
  var GIT_COPY_STATUS_PREFIX = "C";
11516
- var NULL_RECORD_SEPARATOR = "\0";
11687
+ var GIT_NULL_RECORD_SEPARATOR = "\0";
11688
+ var NULL_RECORD_SEPARATOR = GIT_NULL_RECORD_SEPARATOR;
11517
11689
  function sortedPathSet(paths) {
11518
11690
  return [...paths].sort(compareAsciiStrings);
11519
11691
  }
@@ -11569,7 +11741,7 @@ var CHANGED_TEST_DIFF_NAME_STATUS_FLAG = GIT_NAME_STATUS_FLAG;
11569
11741
  var CHANGED_TEST_NULL_DELIMITED_FLAG = GIT_NULL_DELIMITED_FLAG;
11570
11742
  var CHANGED_TEST_LS_FILES_OTHERS_FLAG = "--others";
11571
11743
  var CHANGED_TEST_LS_FILES_EXCLUDE_STANDARD_FLAG = "--exclude-standard";
11572
- var LS_FILES_CACHED_FLAG = "--cached";
11744
+ var CHANGED_TEST_LS_FILES_CACHED_FLAG = "--cached";
11573
11745
  var HEAD_REF = "HEAD";
11574
11746
  var ORIGIN_REMOTE = "origin";
11575
11747
  var REF_SEPARATOR = "/";
@@ -11673,7 +11845,7 @@ async function stagedCandidateTestPaths(productDir, git) {
11673
11845
  }
11674
11846
  const result = await runner(
11675
11847
  GIT_ROOT_COMMAND.EXECUTABLE,
11676
- [CHANGED_TEST_LS_FILES_COMMAND, LS_FILES_CACHED_FLAG, CHANGED_TEST_NULL_DELIMITED_FLAG],
11848
+ [CHANGED_TEST_LS_FILES_COMMAND, CHANGED_TEST_LS_FILES_CACHED_FLAG, CHANGED_TEST_NULL_DELIMITED_FLAG],
11677
11849
  { cwd: productDir, reject: false }
11678
11850
  );
11679
11851
  if (result.exitCode !== 0) {
@@ -11699,14 +11871,14 @@ async function readStagedFile(productDir, path7, git) {
11699
11871
  }
11700
11872
  return result.stdout;
11701
11873
  }
11702
- async function relatedTestPaths(sourceFiles, options, baseRef, candidateTestPaths, deps) {
11874
+ async function relatedTestPaths(sourceFiles, options, baseRef, candidateTestPaths2, deps) {
11703
11875
  const testPaths = [];
11704
11876
  const resolved = /* @__PURE__ */ new Set();
11705
11877
  for (const language of deps.registry.languages) {
11706
11878
  if (language.relatedTestPaths === void 0) continue;
11707
11879
  const relatedDeps = deps.relatedDepsFor(language.name);
11708
11880
  const languageResolution = await language.relatedTestPaths(
11709
- { projectRoot: options.productDir, sourcePaths: sourceFiles, candidateTestPaths, baseRef },
11881
+ { projectRoot: options.productDir, sourcePaths: sourceFiles, candidateTestPaths: candidateTestPaths2, baseRef },
11710
11882
  options.staged === true ? { ...relatedDeps, readFile: (path7) => readStagedFile(options.productDir, path7, deps.git) } : relatedDeps
11711
11883
  );
11712
11884
  if (languageResolution.testPaths.length > 0) {
@@ -11726,6 +11898,9 @@ function changedTestProductInputPaths(registry) {
11726
11898
  ...registry.languages.flatMap((language) => language.productInputPaths)
11727
11899
  ].sort(compareAsciiStrings);
11728
11900
  }
11901
+ async function candidateTestPaths(options, git) {
11902
+ return options.staged === true ? stagedCandidateTestPaths(options.productDir, git) : discoverTestFiles(options.productDir);
11903
+ }
11729
11904
  async function planChangedTestSelection(options, deps) {
11730
11905
  const baseRef = options.baseRef ?? await defaultBaseRef(options.productDir, deps.git);
11731
11906
  const [baseSha, headSha] = await Promise.all([
@@ -11734,16 +11909,20 @@ async function planChangedTestSelection(options, deps) {
11734
11909
  ]);
11735
11910
  const paths = await changedPaths(options.productDir, baseSha, options.staged === true, deps.git);
11736
11911
  const partition = partitionChangedPaths(paths, changedTestProductInputPaths(deps.registry));
11737
- let candidateTestPaths = [];
11738
- if (partition.sourceFiles.length > 0) {
11739
- candidateTestPaths = options.staged === true ? await stagedCandidateTestPaths(options.productDir, deps.git) : await discoverTestFiles(options.productDir);
11740
- }
11741
- const related = partition.sourceFiles.length === 0 ? { testPaths: [], unresolved: [] } : await relatedTestPaths(partition.sourceFiles, options, baseRef, candidateTestPaths, deps);
11912
+ const testPaths = partition.sourceFiles.length > 0 || partition.operands.length > 0 ? await candidateTestPaths(options, deps.git) : [];
11913
+ const pathSelectedTests = partition.operands.length === 0 ? [] : resolveTargetedTestFiles(testPaths, { operands: partition.operands, recursive: true }).selected;
11914
+ const related = partition.sourceFiles.length === 0 ? { testPaths: [], unresolved: [] } : await relatedTestPaths(partition.sourceFiles, options, baseRef, testPaths, deps);
11915
+ const dispatchOperands = mergeChangedSetOperands(pathSelectedTests, related.testPaths);
11916
+ const dirtyOperands = partition.operands.length === 0 ? dispatchOperands : mergeChangedSetOperands(partition.operands, related.testPaths);
11742
11917
  return {
11743
11918
  targets: {
11744
- operands: partition.productInputChanged ? [SPEC_ROOT_OPERAND] : mergeChangedSetOperands(partition.operands, related.testPaths),
11919
+ operands: partition.productInputChanged ? [SPEC_ROOT_OPERAND] : dispatchOperands,
11745
11920
  recursive: partition.productInputChanged
11746
11921
  },
11922
+ dirtyTargets: {
11923
+ operands: partition.productInputChanged ? [SPEC_ROOT_OPERAND] : dirtyOperands,
11924
+ recursive: partition.productInputChanged || partition.operands.length > 0
11925
+ },
11747
11926
  baseRef,
11748
11927
  baseSha,
11749
11928
  headSha,
@@ -12070,11 +12249,12 @@ async function runTestsCommand(options, deps) {
12070
12249
  throw new Error(CHANGED_TEST_STAGED_SELECTION_MISSING_ERROR);
12071
12250
  }
12072
12251
  selectedTargets = mergeTargetSelections(options.targets, changedSelection2.targets) ?? changedSelection2.targets;
12252
+ const dirtyTargets = mergeTargetSelections(options.targets, changedSelection2.dirtyTargets) ?? changedSelection2.dirtyTargets;
12073
12253
  await requireWorktreeMatchesIndexForStagedRun(
12074
12254
  options.productDir,
12075
12255
  changedGit,
12076
12256
  changedSelection2,
12077
- selectedTargets,
12257
+ dirtyTargets,
12078
12258
  passingScope
12079
12259
  );
12080
12260
  }
@@ -13095,7 +13275,7 @@ var testingRegistry = {
13095
13275
 
13096
13276
  // src/interfaces/cli/test-runner-deps.ts
13097
13277
  import { createWriteStream } from "fs";
13098
- import { mkdtemp, readFile as readFile10 } from "fs/promises";
13278
+ import { mkdtemp, readFile as readFile9 } from "fs/promises";
13099
13279
  import { tmpdir } from "os";
13100
13280
  import { join as join23 } from "path";
13101
13281
  import { finished } from "stream/promises";
@@ -13145,7 +13325,7 @@ function createRunnerDepsFor(productDir, outStream = process.stdout) {
13145
13325
  }
13146
13326
  function createRelatedDepsFor(productDir) {
13147
13327
  const runCommand = createRelatedCommandRunner(productDir);
13148
- return () => ({ runCommand, readFile: (path7) => readFile10(join23(productDir, path7), "utf8") });
13328
+ return () => ({ runCommand, readFile: (path7) => readFile9(join23(productDir, path7), "utf8") });
13149
13329
  }
13150
13330
  function artifactFileName(index, suffix) {
13151
13331
  return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
@@ -13606,14 +13786,14 @@ var testingDomain = createTestingDomain();
13606
13786
 
13607
13787
  // src/interfaces/cli/validation.ts
13608
13788
  import { existsSync as existsSync9, realpathSync } from "fs";
13609
- import { isAbsolute as isAbsolute10, relative as relative8, resolve as resolve11, sep as sep3 } from "path";
13789
+ import { isAbsolute as isAbsolute11, relative as relative9, resolve as resolve11, sep as sep4 } from "path";
13610
13790
 
13611
13791
  // src/commands/validation/formatting.ts
13612
13792
  import { existsSync, statSync } from "fs";
13613
- import { isAbsolute as isAbsolute3, join as join24, relative as relative3 } from "path";
13793
+ import { isAbsolute as isAbsolute4, join as join24, relative as relative4 } from "path";
13614
13794
 
13615
13795
  // src/validation/config/path-filter.ts
13616
- import { isAbsolute as isAbsolute2, relative as relative2 } from "path";
13796
+ import { isAbsolute as isAbsolute3, relative as relative3 } from "path";
13617
13797
  function hasEffectiveValidationPathMetadata(filter) {
13618
13798
  return "hasIncludeFilter" in filter && typeof filter.hasIncludeFilter === "boolean" && "noMatchingIncludes" in filter && typeof filter.noMatchingIncludes === "boolean";
13619
13799
  }
@@ -13624,7 +13804,7 @@ function nonEmpty(values) {
13624
13804
  return values?.filter((value) => value.length > 0) ?? [];
13625
13805
  }
13626
13806
  function toProjectRelativeValidationPath(projectRoot, path7) {
13627
- return isAbsolute2(path7) ? relative2(projectRoot, path7) : path7;
13807
+ return isAbsolute3(path7) ? relative3(projectRoot, path7) : path7;
13628
13808
  }
13629
13809
  function intersectIncludes(baseInclude, toolInclude) {
13630
13810
  const base = nonEmpty(baseInclude);
@@ -13882,12 +14062,12 @@ async function formattingCommand(options) {
13882
14062
  const scopedFiles = hasExplicitScope ? files.flatMap(
13883
14063
  (filePath) => formattingPathOperandsForValidationPathFilter(
13884
14064
  cwd,
13885
- isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath,
14065
+ isAbsolute4(filePath) ? relative4(cwd, filePath) : filePath,
13886
14066
  pathFilter
13887
14067
  )
13888
14068
  ) : void 0;
13889
14069
  const scopedExcludes = hasExplicitScope && files.some(
13890
- (filePath) => isFormattingFileOperand(cwd, isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath)
14070
+ (filePath) => isFormattingFileOperand(cwd, isAbsolute4(filePath) ? relative4(cwd, filePath) : filePath)
13891
14071
  ) ? [] : validationPathFilterExcludes(pathFilter);
13892
14072
  if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
13893
14073
  const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
@@ -13941,7 +14121,7 @@ var formattingValidationLanguage = {
13941
14121
  };
13942
14122
 
13943
14123
  // src/commands/validation/markdown.ts
13944
- import { isAbsolute as isAbsolute4, join as join26 } from "path";
14124
+ import { isAbsolute as isAbsolute5, join as join26 } from "path";
13945
14125
 
13946
14126
  // src/validation/steps/markdown.ts
13947
14127
  import { existsSync as existsSync2, statSync as statSync2 } from "fs";
@@ -14247,7 +14427,7 @@ async function markdownCommand(options) {
14247
14427
  return formatMarkdownResult(result, skippedOutput, quiet, durationMs);
14248
14428
  }
14249
14429
  function markdownValidationOperandPath(productDir, filePath) {
14250
- return isAbsolute4(filePath) ? filePath : join26(productDir, filePath);
14430
+ return isAbsolute5(filePath) ? filePath : join26(productDir, filePath);
14251
14431
  }
14252
14432
  function defaultMarkdownTargets(productDir, pathFilter) {
14253
14433
  return getDefaultDirectories(productDir).flatMap(
@@ -15147,7 +15327,7 @@ var ParseErrorCode;
15147
15327
 
15148
15328
  // src/validation/config/scope.ts
15149
15329
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
15150
- import { isAbsolute as isAbsolute5, join as join27, relative as relative4, resolve as resolve9 } from "path";
15330
+ import { isAbsolute as isAbsolute6, join as join27, relative as relative5, resolve as resolve9 } from "path";
15151
15331
  var TSCONFIG_FILES = {
15152
15332
  full: "tsconfig.json",
15153
15333
  production: "tsconfig.production.json"
@@ -15184,7 +15364,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
15184
15364
  FILE: "file"
15185
15365
  };
15186
15366
  function resolveProjectPath(projectRoot, path7) {
15187
- return isAbsolute5(path7) ? path7 : join27(projectRoot, path7);
15367
+ return isAbsolute6(path7) ? path7 : join27(projectRoot, path7);
15188
15368
  }
15189
15369
  function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
15190
15370
  try {
@@ -15521,14 +15701,14 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
15521
15701
  return included && !excluded;
15522
15702
  }
15523
15703
  function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
15524
- const resolvedPath = isAbsolute5(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
15525
- const relativePath = relative4(projectRoot, resolvedPath);
15704
+ const resolvedPath = isAbsolute6(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
15705
+ const relativePath = relative5(projectRoot, resolvedPath);
15526
15706
  const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR4);
15527
- return relativePath.length === 0 || !segments.includes("..") && !isAbsolute5(relativePath);
15707
+ return relativePath.length === 0 || !segments.includes("..") && !isAbsolute6(relativePath);
15528
15708
  }
15529
15709
  function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
15530
- const resolvedPath = isAbsolute5(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
15531
- const relativePath = relative4(projectRoot, resolvedPath);
15710
+ const resolvedPath = isAbsolute6(path7) ? resolve9(path7) : resolve9(projectRoot, path7);
15711
+ const relativePath = relative5(projectRoot, resolvedPath);
15532
15712
  return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
15533
15713
  }
15534
15714
  function toExplicitTypeScriptScopeTarget(projectRoot, originalPath, deps = defaultScopeDeps) {
@@ -16227,7 +16407,7 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
16227
16407
  // src/validation/steps/knip.ts
16228
16408
  import { existsSync as existsSync4 } from "fs";
16229
16409
  import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
16230
- import { isAbsolute as isAbsolute6, join as join29 } from "path";
16410
+ import { isAbsolute as isAbsolute7, join as join29 } from "path";
16231
16411
  var defaultKnipProcessRunner = lifecycleProcessRunner;
16232
16412
  var KNIP_COMMAND_TOKENS = {
16233
16413
  COMMAND: "knip",
@@ -16319,7 +16499,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
16319
16499
  await deps.mkdir(tempParentDir, { recursive: true });
16320
16500
  const tempDir = await deps.mkdtemp(join29(tempParentDir, "validate-knip-"));
16321
16501
  const configPath = join29(tempDir, TSCONFIG_FILES.full);
16322
- const toProjectPathPattern = (pattern) => isAbsolute6(pattern) ? pattern : join29(projectRoot, pattern);
16502
+ const toProjectPathPattern = (pattern) => isAbsolute7(pattern) ? pattern : join29(projectRoot, pattern);
16323
16503
  const project = [
16324
16504
  ...typescriptScope.directories.flatMap(
16325
16505
  (directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
@@ -16845,17 +17025,17 @@ function formatLintResult(result, quiet, durationMs) {
16845
17025
  }
16846
17026
 
16847
17027
  // src/validation/literal/index.ts
16848
- import { readFile as readFile12 } from "fs/promises";
16849
- import { isAbsolute as isAbsolute8, relative as relative6, resolve as resolve10 } from "path";
17028
+ import { readFile as readFile11 } from "fs/promises";
17029
+ import { isAbsolute as isAbsolute9, relative as relative7, resolve as resolve10 } from "path";
16850
17030
 
16851
17031
  // src/lib/file-inclusion/pipeline.ts
16852
- import { readdir as readdir9, readFile as readFile11, stat as stat6 } from "fs/promises";
16853
- import { join as join33, relative as relative5, sep as sep2 } from "path";
17032
+ import { readdir as readdir9, readFile as readFile10, stat as stat6 } from "fs/promises";
17033
+ import { join as join33, relative as relative6, sep as sep3 } from "path";
16854
17034
 
16855
17035
  // src/lib/file-inclusion/ignore-source.ts
16856
17036
  import { execFileSync as execFileSync3 } from "child_process";
16857
17037
  import { existsSync as existsSync7 } from "fs";
16858
- import { isAbsolute as isAbsolute7, join as join32 } from "path";
17038
+ import { isAbsolute as isAbsolute8, join as join32 } from "path";
16859
17039
  var GIT_EXECUTABLE2 = "git";
16860
17040
  var GIT_LS_FILES_ARGS = {
16861
17041
  LS_FILES: "ls-files",
@@ -16973,7 +17153,7 @@ function optionalExcludeFromArgs(path7) {
16973
17153
  return existsSync7(path7) ? excludeFromArgs(path7) : [];
16974
17154
  }
16975
17155
  function resolveGitPath(productDir, path7) {
16976
- return isAbsolute7(path7) ? path7 : join32(productDir, path7);
17156
+ return isAbsolute8(path7) ? path7 : join32(productDir, path7);
16977
17157
  }
16978
17158
  function readInfoExcludePath(productDir) {
16979
17159
  const commonDir = readOptionalGit(productDir, [
@@ -17165,7 +17345,7 @@ async function isDirectory(absolutePath) {
17165
17345
  }
17166
17346
  async function isGitdirPointerFile(absolutePath) {
17167
17347
  try {
17168
- const content = await readFile11(absolutePath, "utf8");
17348
+ const content = await readFile10(absolutePath, "utf8");
17169
17349
  return content.startsWith(GITDIR_POINTER_PREFIX);
17170
17350
  } catch (err) {
17171
17351
  if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
@@ -17185,8 +17365,8 @@ async function readDirectoryEntries2(absoluteDir) {
17185
17365
  }
17186
17366
  }
17187
17367
  function normalizeProductPath(productDir, absolutePath) {
17188
- const rel = relative5(productDir, absolutePath);
17189
- return sep2 === "/" ? rel : rel.split(sep2).join("/");
17368
+ const rel = relative6(productDir, absolutePath);
17369
+ return sep3 === "/" ? rel : rel.split(sep3).join("/");
17190
17370
  }
17191
17371
  async function isGitMetadataEntry(absolutePath, entry) {
17192
17372
  if (entry.name !== GIT_INTERNAL_DIRECTORY) return false;
@@ -17734,8 +17914,8 @@ var DEFAULT_LITERAL_COLLECT_OPTIONS = {
17734
17914
  async function validateLiteralReuse(input) {
17735
17915
  const config = input.config ?? literalConfigDescriptor.defaults;
17736
17916
  const explicitPaths = input.explicitFiles?.map((f) => {
17737
- const abs = isAbsolute8(f) ? f : resolve10(input.productDir, f);
17738
- return relative6(input.productDir, abs).split(/[\\/]/g).join("/");
17917
+ const abs = isAbsolute9(f) ? f : resolve10(input.productDir, f);
17918
+ return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
17739
17919
  });
17740
17920
  if (input.pathConfig !== void 0 && (explicitPaths === void 0 || explicitPaths.length === 0) && validationPathFilterHasNoMatchingIncludes(input.pathConfig)) {
17741
17921
  return {
@@ -17764,7 +17944,7 @@ async function validateLiteralReuse(input) {
17764
17944
  const testOccurrencesByFile = /* @__PURE__ */ new Map();
17765
17945
  const indexedOccurrencesByFile = /* @__PURE__ */ new Map();
17766
17946
  for (const abs of candidateFiles) {
17767
- const rel = relative6(input.productDir, abs).split(/[\\/]/g).join("/");
17947
+ const rel = relative7(input.productDir, abs).split(/[\\/]/g).join("/");
17768
17948
  const content = await readSafe(abs);
17769
17949
  if (content === null) continue;
17770
17950
  const occurrences = collectLiterals(content, rel, collectOptions);
@@ -17789,7 +17969,7 @@ async function validateLiteralReuse(input) {
17789
17969
  }
17790
17970
  async function readSafe(path7) {
17791
17971
  try {
17792
- return await readFile12(path7, "utf8");
17972
+ return await readFile11(path7, "utf8");
17793
17973
  } catch (err) {
17794
17974
  if (typeof err === "object" && err !== null && "code" in err) {
17795
17975
  const code = err.code;
@@ -18064,7 +18244,7 @@ function formatLoc(loc) {
18064
18244
  // src/validation/steps/typescript.ts
18065
18245
  import { existsSync as existsSync8, mkdirSync, rmSync, writeFileSync } from "fs";
18066
18246
  import { mkdtemp as mkdtemp3 } from "fs/promises";
18067
- import { isAbsolute as isAbsolute9, join as join34, relative as relative7 } from "path";
18247
+ import { isAbsolute as isAbsolute10, join as join34, relative as relative8 } from "path";
18068
18248
  var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
18069
18249
  var defaultTypeScriptDeps = {
18070
18250
  mkdtemp: mkdtemp3,
@@ -18091,7 +18271,7 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
18091
18271
  const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
18092
18272
  const configPath = join34(tempDir, "tsconfig.json");
18093
18273
  const baseConfigFile = TSCONFIG_FILES[scope2];
18094
- const absoluteFiles = files.map((file) => isAbsolute9(file) ? file : join34(projectRoot, file));
18274
+ const absoluteFiles = files.map((file) => isAbsolute10(file) ? file : join34(projectRoot, file));
18095
18275
  const tempConfig = {
18096
18276
  extends: join34(projectRoot, baseConfigFile),
18097
18277
  files: absoluteFiles,
@@ -18108,8 +18288,8 @@ async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, dep
18108
18288
  const configPath = join34(tempDir, "tsconfig.json");
18109
18289
  const baseConfigFile = TSCONFIG_FILES[scope2];
18110
18290
  const toTemporaryConfigPathPattern = (pattern) => {
18111
- const absolutePattern = isAbsolute9(pattern) ? pattern : join34(projectRoot, pattern);
18112
- return relative7(tempDir, absolutePattern);
18291
+ const absolutePattern = isAbsolute10(pattern) ? pattern : join34(projectRoot, pattern);
18292
+ return relative8(tempDir, absolutePattern);
18113
18293
  };
18114
18294
  const tempConfig = {
18115
18295
  extends: join34(projectRoot, baseConfigFile),
@@ -18651,8 +18831,8 @@ function normalizeProductPathOperand(productDir, effectiveInvocationDir, operand
18651
18831
  const resolvedProductDir = canonicalExistingPath(resolve11(productDir));
18652
18832
  const resolvedInvocationDir = canonicalExistingPath(resolve11(effectiveInvocationDir));
18653
18833
  const absoluteOperand = canonicalExistingPath(resolve11(resolvedInvocationDir, operand));
18654
- const relativeOperand = relative8(resolvedProductDir, absoluteOperand);
18655
- if (relativeOperand === ".." || relativeOperand.startsWith(`..${sep3}`) || isAbsolute10(relativeOperand)) {
18834
+ const relativeOperand = relative9(resolvedProductDir, absoluteOperand);
18835
+ if (relativeOperand === ".." || relativeOperand.startsWith(`..${sep4}`) || isAbsolute11(relativeOperand)) {
18656
18836
  return void 0;
18657
18837
  }
18658
18838
  return relativeOperand.length > 0 ? relativeOperand.replaceAll("\\", "/") : ".";
@@ -18856,7 +19036,7 @@ var validationDomain = {
18856
19036
  };
18857
19037
 
18858
19038
  // src/commands/verification-context/cli.ts
18859
- import { isAbsolute as isAbsolute11, win32 } from "path";
19039
+ import { isAbsolute as isAbsolute12, win32 } from "path";
18860
19040
 
18861
19041
  // src/domains/verification-context/context.ts
18862
19042
  var VERIFICATION_CONTEXT_SCHEMA_VERSION = "verification-context.v1";
@@ -19007,7 +19187,7 @@ function normalizeFileSubjectPath(path7) {
19007
19187
  VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL
19008
19188
  );
19009
19189
  const segments = normalized.split(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL);
19010
- if (isAbsolute11(path7) || win32.isAbsolute(path7) || windowsRoot.length > 0 || segments.includes(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.PARENT_DIRECTORY.SEGMENT)) {
19190
+ if (isAbsolute12(path7) || win32.isAbsolute(path7) || windowsRoot.length > 0 || segments.includes(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.PARENT_DIRECTORY.SEGMENT)) {
19011
19191
  return void 0;
19012
19192
  }
19013
19193
  return normalized;
@@ -19057,17 +19237,6 @@ async function verificationContextCreateCommand(options, deps = {}) {
19057
19237
  return okResult2(JSON.stringify(persisted.value));
19058
19238
  }
19059
19239
 
19060
- // src/interfaces/cli/lib/stream-report.ts
19061
- var CLI_STREAM_REPORT = {
19062
- LINE_SEPARATOR: "\n"
19063
- };
19064
- function reportCliResult(result, io) {
19065
- const output = `${result.output}${CLI_STREAM_REPORT.LINE_SEPARATOR}`;
19066
- if (result.exitCode === 0) io.writeStdout(output);
19067
- else io.writeStderr(output);
19068
- io.setExitCode(result.exitCode);
19069
- }
19070
-
19071
19240
  // src/interfaces/cli/verification-context.ts
19072
19241
  var VERIFICATION_CONTEXT_CLI = {
19073
19242
  commandName: "verification-context",
@@ -19094,6 +19263,514 @@ var verificationContextDomain = {
19094
19263
  }
19095
19264
  };
19096
19265
 
19266
+ // src/interfaces/cli/verify.ts
19267
+ import { readFile as readFile12 } from "fs/promises";
19268
+
19269
+ // src/commands/verify/cli.ts
19270
+ import { dirname as dirname12 } from "path";
19271
+
19272
+ // src/domains/verify/verify.ts
19273
+ import { join as join36 } from "path";
19274
+ var VERIFY_SCOPE_TYPE = {
19275
+ CHANGESET: "changeset",
19276
+ WORKING_TREE: "working-tree"
19277
+ };
19278
+ var VERIFY_VERB = {
19279
+ START: "start",
19280
+ INPUT: "input",
19281
+ APPEND_SCOPE: "append-scope",
19282
+ APPEND_FINDING: "append-finding"
19283
+ };
19284
+ var VERIFY_VERIFICATION_TYPE = {
19285
+ REVIEW: "review"
19286
+ };
19287
+ var REVIEW_FINDING_DISPOSITION = {
19288
+ BLOCKING: "BLOCKING",
19289
+ DEBT: "DEBT"
19290
+ };
19291
+ var VERIFY_APPEND_EVENT_TYPE = {
19292
+ SCOPE: "io.spx.verify.scope",
19293
+ FINDING: "io.spx.verify.finding"
19294
+ };
19295
+ var VERIFY_EVENT_SOURCE = "/spx/verify";
19296
+ var VERIFY_APPEND_EVENT_FIELD = {
19297
+ IDEMPOTENCY_KEY: "idempotencyKey",
19298
+ PAYLOAD: "payload"
19299
+ };
19300
+ var VERIFY_INPUT_SOURCE = {
19301
+ STDIN: "stdin"
19302
+ };
19303
+ var VERIFY_SCOPE_SEPARATOR = "..";
19304
+ var VERIFY_SCOPE_ERROR = {
19305
+ MALFORMED_CHANGESET: "verify changeset scope must be <base>..<head>",
19306
+ UNSUPPORTED_SCOPE_TYPE: "verify scope type has no verification-context substrate representation"
19307
+ };
19308
+ var VERIFY_INPUT_DIGEST_PATH = "verify run input";
19309
+ function parseChangesetScope(scope2) {
19310
+ const separatorIndex = scope2.indexOf(VERIFY_SCOPE_SEPARATOR);
19311
+ if (separatorIndex < 0) return { ok: false, error: VERIFY_SCOPE_ERROR.MALFORMED_CHANGESET };
19312
+ const base = scope2.slice(0, separatorIndex);
19313
+ const head = scope2.slice(separatorIndex + VERIFY_SCOPE_SEPARATOR.length);
19314
+ if (base.length === 0 || head.length === 0 || head.includes(VERIFY_SCOPE_SEPARATOR)) {
19315
+ return { ok: false, error: VERIFY_SCOPE_ERROR.MALFORMED_CHANGESET };
19316
+ }
19317
+ return { ok: true, value: { base, head } };
19318
+ }
19319
+ function buildRunLocator(parts) {
19320
+ return {
19321
+ runToken: parts.runToken,
19322
+ verificationType: parts.verificationType,
19323
+ scopeType: parts.scopeType,
19324
+ scopeIdentity: parts.scopeIdentity,
19325
+ backendIdentity: parts.backendIdentity,
19326
+ storageNamespace: parts.storageNamespace,
19327
+ runTarget: parts.runTarget
19328
+ };
19329
+ }
19330
+ function digestRunInput(source, content) {
19331
+ const digest = digestDescriptorSection({ source, content }, VERIFY_INPUT_DIGEST_PATH);
19332
+ if (!digest.ok) return digest;
19333
+ return { ok: true, value: digest.value.sha256 };
19334
+ }
19335
+ var VERIFY_INPUT_RECORD = {
19336
+ PREFIX: "input-",
19337
+ SUFFIX: ".json"
19338
+ };
19339
+ function verifyRunsDir(scope2) {
19340
+ const branchScope = branchScopeDir(scope2.productDir, scope2.branchSlug);
19341
+ if (!branchScope.ok) return branchScope;
19342
+ return runsDir(branchScope.value, scope2.type);
19343
+ }
19344
+ function verifyInputRecordPath(scope2) {
19345
+ const runs = verifyRunsDir(scope2);
19346
+ if (!runs.ok) return runs;
19347
+ const token = validateScopeToken(scope2.runToken);
19348
+ if (!token.ok) return token;
19349
+ return {
19350
+ ok: true,
19351
+ value: join36(runs.value, `${VERIFY_INPUT_RECORD.PREFIX}${token.value}${VERIFY_INPUT_RECORD.SUFFIX}`)
19352
+ };
19353
+ }
19354
+ var VERIFY_APPEND_ATTEMPT = 1;
19355
+ function parseAppendPayload(raw) {
19356
+ try {
19357
+ return JSON.parse(raw);
19358
+ } catch {
19359
+ return void 0;
19360
+ }
19361
+ }
19362
+ function isJsonRecord(value) {
19363
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19364
+ }
19365
+ function isReviewFindingDisposition(value) {
19366
+ return Object.values(REVIEW_FINDING_DISPOSITION).includes(value);
19367
+ }
19368
+ function validateReviewFinding(payload) {
19369
+ if (!isJsonRecord(payload)) return void 0;
19370
+ const { disposition, summary } = payload;
19371
+ if (!isReviewFindingDisposition(disposition)) return void 0;
19372
+ if (typeof summary !== "string" || summary.length === 0) return void 0;
19373
+ return { disposition, summary };
19374
+ }
19375
+ var FINDING_VALIDATORS = {
19376
+ [VERIFY_VERIFICATION_TYPE.REVIEW]: validateReviewFinding
19377
+ };
19378
+ function findingValidatorFor(verificationType) {
19379
+ return FINDING_VALIDATORS[verificationType];
19380
+ }
19381
+ function findAppendedSequence(events, idempotencyKey, eventType) {
19382
+ const match = events.find(
19383
+ (event) => event.type === eventType && isJsonRecord(event.data) && event.data[VERIFY_APPEND_EVENT_FIELD.IDEMPOTENCY_KEY] === idempotencyKey
19384
+ );
19385
+ return match?.seq;
19386
+ }
19387
+ function buildAppendEvent(args) {
19388
+ return {
19389
+ id: args.idempotencyKey,
19390
+ source: VERIFY_EVENT_SOURCE,
19391
+ type: args.eventType,
19392
+ time: args.at.toISOString(),
19393
+ attempt: VERIFY_APPEND_ATTEMPT,
19394
+ data: {
19395
+ [VERIFY_APPEND_EVENT_FIELD.IDEMPOTENCY_KEY]: args.idempotencyKey,
19396
+ [VERIFY_APPEND_EVENT_FIELD.PAYLOAD]: args.payload
19397
+ }
19398
+ };
19399
+ }
19400
+
19401
+ // src/commands/verify/cli.ts
19402
+ var VERIFY_CLI_EXIT_CODE = {
19403
+ OK: 0,
19404
+ ERROR: 1
19405
+ };
19406
+ var VERIFY_CLI_ENV = {
19407
+ BRANCH: SPX_VERIFY_ENV.BRANCH
19408
+ };
19409
+ var VERIFY_CLI_ERROR = {
19410
+ INPUT_REQUIRED: "spx verify start requires --input <input-source>",
19411
+ RUN_REQUIRED: "spx verify existing-run verbs require an explicit --run <run-token>",
19412
+ RUN_NOT_FOUND: "spx verify could not locate the requested run",
19413
+ CHANGED_SCOPE_FAILED: "spx verify could not derive the changeset changed-file scope",
19414
+ INPUT_PERSIST_FAILED: "spx verify could not persist the recorded run input",
19415
+ INPUT_READ_FAILED: "spx verify could not read the recorded run input",
19416
+ PAYLOAD_REQUIRED: "spx verify append verbs require --payload <payload-source>",
19417
+ IDEMPOTENCY_KEY_REQUIRED: "spx verify append verbs require --idempotency-key <key>",
19418
+ PAYLOAD_READ_FAILED: "spx verify could not read the append payload",
19419
+ PAYLOAD_INVALID: "spx verify append payload is not valid JSON",
19420
+ FINDING_INVALID: "spx verify append-finding payload failed verification-type validation",
19421
+ UNSUPPORTED_VERIFICATION_TYPE: "spx verify append-finding has no finding validator for the verification type",
19422
+ APPEND_FAILED: "spx verify could not append the evidence event"
19423
+ };
19424
+ function okResult3(output) {
19425
+ return { exitCode: VERIFY_CLI_EXIT_CODE.OK, output };
19426
+ }
19427
+ function errorResult3(error) {
19428
+ return { exitCode: VERIFY_CLI_EXIT_CODE.ERROR, output: error };
19429
+ }
19430
+ function toMessage2(error) {
19431
+ return error instanceof Error ? error.message : JSON.stringify(error);
19432
+ }
19433
+ function forwardDeps(deps) {
19434
+ return {
19435
+ ...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
19436
+ ...deps.git === void 0 ? {} : { git: deps.git },
19437
+ ...deps.branch === void 0 ? {} : { branch: deps.branch },
19438
+ ...deps.processEnv === void 0 ? {} : { processEnv: deps.processEnv },
19439
+ ...deps.fs === void 0 ? {} : { fs: deps.fs },
19440
+ ...deps.now === void 0 ? {} : { now: deps.now }
19441
+ };
19442
+ }
19443
+ async function resolveVerifyScope(deps) {
19444
+ const cwd = deps.cwd ?? CONFIG_PROCESS_CWD.read();
19445
+ const git = deps.git ?? defaultGitDependencies;
19446
+ const processEnv = deps.processEnv ?? process.env;
19447
+ const product = await detectGitCommonDirProductRoot(cwd, git);
19448
+ const probedBranch = product.isGitRepo ? await getCurrentBranch(cwd, git) ?? void 0 : void 0;
19449
+ const headSha = (product.isGitRepo ? await getHeadSha(cwd, git) : null) ?? SPX_VERIFY_HEAD_SHA.MISSING;
19450
+ const branchName = deps.branch ?? processEnv[VERIFY_CLI_ENV.BRANCH] ?? probedBranch;
19451
+ const branchIdentity = resolveBranchIdentity({ ...branchName === void 0 ? {} : { branchName }, headSha });
19452
+ const backend = resolveJournalBackend(readJournalCliEnvironment(processEnv).backend);
19453
+ if (!backend.ok) return backend;
19454
+ return {
19455
+ ok: true,
19456
+ value: {
19457
+ productDir: product.productDir,
19458
+ branchSlug: slugBranchIdentity(branchIdentity),
19459
+ backendIdentity: backend.value
19460
+ }
19461
+ };
19462
+ }
19463
+ async function resolveChangedScope(scope2, deps) {
19464
+ const cwd = deps.cwd ?? CONFIG_PROCESS_CWD.read();
19465
+ const git = deps.git ?? defaultGitDependencies;
19466
+ const diff = await git.execa(GIT_ROOT_COMMAND.EXECUTABLE, [...changesetNameStatusArgs(scope2.base, scope2.head)], {
19467
+ cwd,
19468
+ reject: false
19469
+ });
19470
+ if (diff.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
19471
+ return { ok: false, error: `${VERIFY_CLI_ERROR.CHANGED_SCOPE_FAILED}: ${diff.stderr}` };
19472
+ }
19473
+ return { ok: true, value: pathsFromNameStatus(diff.stdout) };
19474
+ }
19475
+ async function persistInputRecord(runScope2, record6, deps) {
19476
+ const path7 = verifyInputRecordPath(runScope2);
19477
+ if (!path7.ok) return path7;
19478
+ const fs8 = deps.fs ?? defaultFileSystem;
19479
+ try {
19480
+ await fs8.mkdir(dirname12(path7.value), { recursive: true });
19481
+ await fs8.writeFile(path7.value, JSON.stringify(record6));
19482
+ return { ok: true, value: void 0 };
19483
+ } catch (error) {
19484
+ return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_PERSIST_FAILED}: ${toMessage2(error)}` };
19485
+ }
19486
+ }
19487
+ async function readInputRecordAt(path7, deps) {
19488
+ const fs8 = deps.fs ?? defaultFileSystem;
19489
+ try {
19490
+ const content = await fs8.readFile(path7, STATE_STORE_TEXT_ENCODING);
19491
+ return { ok: true, value: JSON.parse(content) };
19492
+ } catch (error) {
19493
+ if (hasErrorCode(error, ERROR_CODE_NOT_FOUND)) return { ok: true, value: void 0 };
19494
+ return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_READ_FAILED}: ${toMessage2(error)}` };
19495
+ }
19496
+ }
19497
+ function verifyRunNotFoundDiagnostic(context) {
19498
+ return [
19499
+ VERIFY_CLI_ERROR.RUN_NOT_FOUND,
19500
+ `run=${context.runToken}`,
19501
+ `verification-type=${context.verificationType}`,
19502
+ `scope-type=${context.scopeType}`,
19503
+ `scope=${context.scopeIdentity}`,
19504
+ `backend=${context.backendIdentity}`,
19505
+ `namespace=${context.storageNamespace}`,
19506
+ `target=${context.searchedTarget}`
19507
+ ].join(" ");
19508
+ }
19509
+ async function verifyStartCommand(options, deps) {
19510
+ if (options.scopeType !== VERIFY_SCOPE_TYPE.CHANGESET) return errorResult3(VERIFY_SCOPE_ERROR.UNSUPPORTED_SCOPE_TYPE);
19511
+ if (options.input.trim().length === 0) return errorResult3(VERIFY_CLI_ERROR.INPUT_REQUIRED);
19512
+ const scope2 = parseChangesetScope(options.scope);
19513
+ if (!scope2.ok) return errorResult3(scope2.error);
19514
+ const resolved = await resolveVerifyScope(deps);
19515
+ if (!resolved.ok) return errorResult3(resolved.error);
19516
+ const inputContent = await deps.readInputSource(options.input);
19517
+ const inputDigest = digestRunInput(options.input, inputContent);
19518
+ if (!inputDigest.ok) return errorResult3(inputDigest.error);
19519
+ const context = await verificationContextCreateCommand(
19520
+ {
19521
+ subject: VERIFICATION_CONTEXT_SUBJECT_KIND.CHANGESET,
19522
+ base: scope2.value.base,
19523
+ head: scope2.value.head,
19524
+ predicate: options.verificationType,
19525
+ workflow: options.verificationType
19526
+ },
19527
+ forwardDeps(deps)
19528
+ );
19529
+ if (context.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return errorResult3(context.output);
19530
+ const contextDigest = JSON.parse(context.output).digest;
19531
+ const opened = await journalOpenCommand(
19532
+ { type: options.verificationType, branchSlug: resolved.value.branchSlug },
19533
+ forwardDeps(deps)
19534
+ );
19535
+ if (opened.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return errorResult3(opened.output);
19536
+ const { runToken, runFile } = JSON.parse(opened.output);
19537
+ const runScope2 = {
19538
+ productDir: resolved.value.productDir,
19539
+ branchSlug: resolved.value.branchSlug,
19540
+ type: options.verificationType,
19541
+ runToken
19542
+ };
19543
+ const recorded = { source: options.input, digest: inputDigest.value, content: inputContent };
19544
+ const persisted = await persistInputRecord(runScope2, recorded, deps);
19545
+ if (!persisted.ok) return errorResult3(persisted.error);
19546
+ const changedScope = await resolveChangedScope(scope2.value, deps);
19547
+ if (!changedScope.ok) return errorResult3(changedScope.error);
19548
+ const namespace = verifyRunsDir(runScope2);
19549
+ if (!namespace.ok) return errorResult3(namespace.error);
19550
+ const locator = buildRunLocator({
19551
+ runToken,
19552
+ verificationType: options.verificationType,
19553
+ scopeType: options.scopeType,
19554
+ scopeIdentity: options.scope,
19555
+ backendIdentity: resolved.value.backendIdentity,
19556
+ storageNamespace: namespace.value,
19557
+ runTarget: runFile
19558
+ });
19559
+ const report2 = {
19560
+ runToken,
19561
+ contextDigest,
19562
+ changedScope: changedScope.value,
19563
+ input: { source: options.input, digest: inputDigest.value },
19564
+ locator
19565
+ };
19566
+ return okResult3(JSON.stringify(report2));
19567
+ }
19568
+ async function verifyInputCommand(options, deps) {
19569
+ if (options.run.trim().length === 0) return errorResult3(VERIFY_CLI_ERROR.RUN_REQUIRED);
19570
+ const resolved = await resolveVerifyScope(deps);
19571
+ if (!resolved.ok) return errorResult3(resolved.error);
19572
+ const runScope2 = {
19573
+ productDir: resolved.value.productDir,
19574
+ branchSlug: resolved.value.branchSlug,
19575
+ type: options.verificationType,
19576
+ runToken: options.run
19577
+ };
19578
+ const path7 = verifyInputRecordPath(runScope2);
19579
+ if (!path7.ok) return errorResult3(path7.error);
19580
+ const namespace = verifyRunsDir(runScope2);
19581
+ if (!namespace.ok) return errorResult3(namespace.error);
19582
+ const record6 = await readInputRecordAt(path7.value, deps);
19583
+ if (!record6.ok) return errorResult3(record6.error);
19584
+ if (record6.value === void 0) {
19585
+ return errorResult3(
19586
+ verifyRunNotFoundDiagnostic({
19587
+ runToken: options.run,
19588
+ verificationType: options.verificationType,
19589
+ scopeType: options.scopeType,
19590
+ scopeIdentity: options.scope,
19591
+ backendIdentity: resolved.value.backendIdentity,
19592
+ storageNamespace: namespace.value,
19593
+ searchedTarget: path7.value
19594
+ })
19595
+ );
19596
+ }
19597
+ const report2 = {
19598
+ source: record6.value.source,
19599
+ digest: record6.value.digest,
19600
+ content: record6.value.content
19601
+ };
19602
+ return okResult3(JSON.stringify(report2));
19603
+ }
19604
+ async function readRunJournalEvents(scope2, deps) {
19605
+ const read = await journalReadCommand(scope2, String(JOURNAL_SEQ_BASE), forwardDeps(deps));
19606
+ if (read.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return { ok: false, error: read.output };
19607
+ return { ok: true, value: JSON.parse(read.output) };
19608
+ }
19609
+ async function prepareAppend(options, deps) {
19610
+ if (options.run.trim().length === 0) return { ok: false, error: VERIFY_CLI_ERROR.RUN_REQUIRED };
19611
+ if (options.payload.trim().length === 0) return { ok: false, error: VERIFY_CLI_ERROR.PAYLOAD_REQUIRED };
19612
+ if (options.idempotencyKey.trim().length === 0) {
19613
+ return { ok: false, error: VERIFY_CLI_ERROR.IDEMPOTENCY_KEY_REQUIRED };
19614
+ }
19615
+ const readPayload = deps.readPayloadSource;
19616
+ const binding = deps.journalBinding;
19617
+ if (readPayload === void 0 || binding === void 0) return { ok: false, error: VERIFY_CLI_ERROR.APPEND_FAILED };
19618
+ const resolved = await resolveVerifyScope(deps);
19619
+ if (!resolved.ok) return resolved;
19620
+ const runScope2 = {
19621
+ productDir: resolved.value.productDir,
19622
+ branchSlug: resolved.value.branchSlug,
19623
+ type: options.verificationType,
19624
+ runToken: options.run
19625
+ };
19626
+ const namespace = verifyRunsDir(runScope2);
19627
+ if (!namespace.ok) return namespace;
19628
+ const inputPath = verifyInputRecordPath(runScope2);
19629
+ if (!inputPath.ok) return inputPath;
19630
+ const inputRecord = await readInputRecordAt(inputPath.value, deps);
19631
+ if (!inputRecord.ok) return inputRecord;
19632
+ if (inputRecord.value === void 0) {
19633
+ return { ok: false, error: appendRunNotFoundDiagnostic(options, resolved.value.backendIdentity, namespace.value) };
19634
+ }
19635
+ return {
19636
+ ok: true,
19637
+ value: {
19638
+ readPayload,
19639
+ binding,
19640
+ journalScope: { type: options.verificationType, runToken: options.run, branchSlug: resolved.value.branchSlug },
19641
+ namespace: namespace.value,
19642
+ backendIdentity: resolved.value.backendIdentity
19643
+ }
19644
+ };
19645
+ }
19646
+ function appendRunNotFoundDiagnostic(options, backendIdentity, namespace) {
19647
+ return verifyRunNotFoundDiagnostic({
19648
+ runToken: options.run,
19649
+ verificationType: options.verificationType,
19650
+ scopeType: options.scopeType,
19651
+ scopeIdentity: options.scope,
19652
+ backendIdentity,
19653
+ storageNamespace: namespace,
19654
+ searchedTarget: namespace
19655
+ });
19656
+ }
19657
+ function validateAppendFinding(verb, verificationType, payload) {
19658
+ if (verb !== VERIFY_VERB.APPEND_FINDING) return void 0;
19659
+ const validator = findingValidatorFor(verificationType);
19660
+ if (validator === void 0) return VERIFY_CLI_ERROR.UNSUPPORTED_VERIFICATION_TYPE;
19661
+ if (validator(payload) === void 0) return VERIFY_CLI_ERROR.FINDING_INVALID;
19662
+ return void 0;
19663
+ }
19664
+ function appendEventType(verb) {
19665
+ return verb === VERIFY_VERB.APPEND_FINDING ? VERIFY_APPEND_EVENT_TYPE.FINDING : VERIFY_APPEND_EVENT_TYPE.SCOPE;
19666
+ }
19667
+ async function verifyAppend(options, deps, verb) {
19668
+ const prepared = await prepareAppend(options, deps);
19669
+ if (!prepared.ok) return errorResult3(prepared.error);
19670
+ const { readPayload, binding, journalScope, namespace, backendIdentity } = prepared.value;
19671
+ const eventType = appendEventType(verb);
19672
+ const before = await readRunJournalEvents(journalScope, deps);
19673
+ if (!before.ok) {
19674
+ if (before.error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
19675
+ return errorResult3(appendRunNotFoundDiagnostic(options, backendIdentity, namespace));
19676
+ }
19677
+ return errorResult3(`${VERIFY_CLI_ERROR.APPEND_FAILED}: ${before.error}`);
19678
+ }
19679
+ const existing = findAppendedSequence(before.value, options.idempotencyKey, eventType);
19680
+ if (existing !== void 0) {
19681
+ const report3 = { sequence: existing, idempotent: true };
19682
+ return okResult3(JSON.stringify(report3));
19683
+ }
19684
+ let rawPayload;
19685
+ try {
19686
+ rawPayload = await readPayload(options.payload);
19687
+ } catch (error) {
19688
+ return errorResult3(`${VERIFY_CLI_ERROR.PAYLOAD_READ_FAILED}: ${toMessage2(error)}`);
19689
+ }
19690
+ const parsed = parseAppendPayload(rawPayload);
19691
+ if (parsed === void 0) return errorResult3(VERIFY_CLI_ERROR.PAYLOAD_INVALID);
19692
+ const findingError = validateAppendFinding(verb, options.verificationType, parsed);
19693
+ if (findingError !== void 0) return errorResult3(findingError);
19694
+ const event = buildAppendEvent({
19695
+ eventType,
19696
+ idempotencyKey: options.idempotencyKey,
19697
+ payload: parsed,
19698
+ at: deps.now?.() ?? /* @__PURE__ */ new Date()
19699
+ });
19700
+ const appended = await journalAppendCommand(journalScope, event, binding, forwardDeps(deps));
19701
+ if (appended.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
19702
+ return errorResult3(`${VERIFY_CLI_ERROR.APPEND_FAILED}: ${appended.output}`);
19703
+ }
19704
+ const after = await readRunJournalEvents(journalScope, deps);
19705
+ if (!after.ok) return errorResult3(`${VERIFY_CLI_ERROR.APPEND_FAILED}: ${after.error}`);
19706
+ const sequence = findAppendedSequence(after.value, options.idempotencyKey, eventType);
19707
+ if (sequence === void 0) return errorResult3(VERIFY_CLI_ERROR.APPEND_FAILED);
19708
+ const report2 = { sequence, idempotent: false };
19709
+ return okResult3(JSON.stringify(report2));
19710
+ }
19711
+ async function verifyAppendScopeCommand(options, deps) {
19712
+ return verifyAppend(options, deps, VERIFY_VERB.APPEND_SCOPE);
19713
+ }
19714
+ async function verifyAppendFindingCommand(options, deps) {
19715
+ return verifyAppend(options, deps, VERIFY_VERB.APPEND_FINDING);
19716
+ }
19717
+
19718
+ // src/interfaces/cli/verify.ts
19719
+ var VERIFY_CLI = {
19720
+ commandName: "verify",
19721
+ description: "Record and replay a typed verification run",
19722
+ startCommandName: VERIFY_VERB.START,
19723
+ inputCommandName: VERIFY_VERB.INPUT,
19724
+ appendScopeCommandName: VERIFY_VERB.APPEND_SCOPE,
19725
+ appendFindingCommandName: VERIFY_VERB.APPEND_FINDING,
19726
+ verificationTypeOption: "--verification-type <type>",
19727
+ scopeTypeOption: "--scope-type <scope-type>",
19728
+ scopeOption: "--scope <base>..<head>",
19729
+ inputOption: "--input <input-source>",
19730
+ runOption: "--run <token>",
19731
+ payloadOption: "--payload <payload-source>",
19732
+ idempotencyKeyOption: "--idempotency-key <key>"
19733
+ };
19734
+ var CLI_SOURCE_ENCODING = "utf8";
19735
+ async function readStdinText() {
19736
+ const chunks = [];
19737
+ for await (const chunk of process.stdin) {
19738
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
19739
+ }
19740
+ return Buffer.concat(chunks).toString(CLI_SOURCE_ENCODING);
19741
+ }
19742
+ async function readCliSource(source) {
19743
+ if (source === VERIFY_INPUT_SOURCE.STDIN) return readStdinText();
19744
+ return readFile12(source, CLI_SOURCE_ENCODING);
19745
+ }
19746
+ var verifyDomain = {
19747
+ name: VERIFY_CLI.commandName,
19748
+ description: VERIFY_CLI.description,
19749
+ register: (program, invocation) => {
19750
+ const deps = () => ({
19751
+ cwd: invocation.resolveEffectiveInvocationDir(),
19752
+ readInputSource: readCliSource,
19753
+ readPayloadSource: readCliSource,
19754
+ // The append verbs write a single structured JSON result to stdout, so the run's event
19755
+ // stream goes to stderr under the local backend rather than sharing the result channel.
19756
+ journalBinding: createJournalStreamBinding(invocation.io, stderrStreamSink(invocation.io))
19757
+ });
19758
+ const command = program.command(VERIFY_CLI.commandName).description(VERIFY_CLI.description);
19759
+ command.command(VERIFY_CLI.startCommandName).description("Start a changeset-scoped verification run and report its run locator").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.inputOption, "Verification input source; stdin or a file path").action(async (options) => {
19760
+ reportCliResult(await verifyStartCommand(options, deps()), invocation.io);
19761
+ });
19762
+ command.command(VERIFY_CLI.inputCommandName).description("Replay the verification input recorded at start").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").action(async (options) => {
19763
+ reportCliResult(await verifyInputCommand(options, deps()), invocation.io);
19764
+ });
19765
+ command.command(VERIFY_CLI.appendScopeCommandName).description("Record the inspected scope for a started run").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").requiredOption(VERIFY_CLI.payloadOption, "Append payload source; stdin or a file path").requiredOption(VERIFY_CLI.idempotencyKeyOption, "Caller-supplied idempotency key for the append").action(async (options) => {
19766
+ reportCliResult(await verifyAppendScopeCommand(options, deps()), invocation.io);
19767
+ });
19768
+ command.command(VERIFY_CLI.appendFindingCommandName).description("Record a validated verification finding for a started run").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").requiredOption(VERIFY_CLI.payloadOption, "Append payload source; stdin or a file path").requiredOption(VERIFY_CLI.idempotencyKeyOption, "Caller-supplied idempotency key for the append").action(async (options) => {
19769
+ reportCliResult(await verifyAppendFindingCommand(options, deps()), invocation.io);
19770
+ });
19771
+ }
19772
+ };
19773
+
19097
19774
  // src/interfaces/cli/worktree.ts
19098
19775
  import { randomBytes as nodeRandomBytes4 } from "crypto";
19099
19776
 
@@ -19135,7 +19812,7 @@ function resolveReleaseSessionId(explicitSessionId, env) {
19135
19812
  }
19136
19813
 
19137
19814
  // src/commands/worktree/status.ts
19138
- import { basename as basename8, dirname as dirname12, sep as sep4 } from "path";
19815
+ import { basename as basename8, dirname as dirname13, sep as sep5 } from "path";
19139
19816
  var WORKTREE_STATUS_FORMAT = {
19140
19817
  JSON: "json",
19141
19818
  TEXT: "text"
@@ -19226,7 +19903,7 @@ function renderTextStatus(records) {
19226
19903
  const sections = [];
19227
19904
  const sectionByParent = /* @__PURE__ */ new Map();
19228
19905
  for (const record6 of records) {
19229
- const parent = renderParentDirectory(dirname12(record6.worktreeRoot));
19906
+ const parent = renderParentDirectory(dirname13(record6.worktreeRoot));
19230
19907
  const children = sectionByParent.get(parent);
19231
19908
  const rendered = renderTextStatusChild(record6);
19232
19909
  if (children === void 0) {
@@ -19240,7 +19917,7 @@ function renderTextStatus(records) {
19240
19917
  return renderPlainTree({ sections });
19241
19918
  }
19242
19919
  function renderParentDirectory(parent) {
19243
- return parent.endsWith(sep4) ? parent : `${parent}${sep4}`;
19920
+ return parent.endsWith(sep5) ? parent : `${parent}${sep5}`;
19244
19921
  }
19245
19922
  function renderTextStatusChild(record6) {
19246
19923
  if (record6.status === OCCUPANCY_STATUS.RUNNING) {
@@ -19365,6 +20042,7 @@ var CLI_DOMAINS = [
19365
20042
  testingDomain,
19366
20043
  validationDomain,
19367
20044
  verificationContextDomain,
20045
+ verifyDomain,
19368
20046
  worktreeDomain
19369
20047
  ];
19370
20048