@iamu-dev/iamu 0.8.0-beta.5 → 0.8.0-beta.6

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/index.js CHANGED
@@ -6687,7 +6687,7 @@ var init_wire_compat = __esm({
6687
6687
  function cloudSupportsGuidance(capabilities) {
6688
6688
  return Boolean(capabilities?.includes(GUIDANCE_CAPABILITY) || capabilities?.includes(DESIGNER_FINDINGS_CAPABILITY));
6689
6689
  }
6690
- async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure) {
6690
+ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure, onServerClock) {
6691
6691
  const controller = new AbortController();
6692
6692
  const timer = setTimeout(() => controller.abort(), timeoutMs);
6693
6693
  try {
@@ -6697,6 +6697,9 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6697
6697
  signal: controller.signal
6698
6698
  };
6699
6699
  const res = await fetchFn(`${apiUrl.replace(/\/+$/, "")}/api/meta`, init2);
6700
+ const offset = serverClockOffset(res);
6701
+ if (offset !== null)
6702
+ onServerClock?.(offset);
6700
6703
  if (res.status === 404)
6701
6704
  return [];
6702
6705
  if (!res.ok) {
@@ -6718,6 +6721,16 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6718
6721
  clearTimeout(timer);
6719
6722
  }
6720
6723
  }
6724
+ function serverClockOffset(res) {
6725
+ const headers = res?.headers;
6726
+ if (!headers || typeof headers.get !== "function")
6727
+ return null;
6728
+ const raw = headers.get.call(headers, "date");
6729
+ if (typeof raw !== "string")
6730
+ return null;
6731
+ const at = Date.parse(raw);
6732
+ return Number.isNaN(at) ? null : at - Date.now();
6733
+ }
6721
6734
  function shapeForServer(pending, capabilities) {
6722
6735
  const knows = capabilities.includes(DESIGNER_FINDINGS_CAPABILITY);
6723
6736
  const knowsProvenance = capabilities.includes(FINDING_PROVENANCE_CAPABILITY);
@@ -10687,7 +10700,7 @@ function compareIso(a, b) {
10687
10700
  return ta - tb;
10688
10701
  return String(a ?? "").localeCompare(String(b ?? ""));
10689
10702
  }
10690
- async function designerFindingsOn(projectRoot, screens, before, targetUrl = null) {
10703
+ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null, settled = /* @__PURE__ */ new Set()) {
10691
10704
  const latest = /* @__PURE__ */ new Map();
10692
10705
  const cache = /* @__PURE__ */ new Map();
10693
10706
  const wanted = new Set([...screens].map((k) => normalizeScreenKey(k, targetUrl)));
@@ -10711,7 +10724,7 @@ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null
10711
10724
  }
10712
10725
  const all = [...latest.values()].sort((a, b) => compareIso(a.created_at, b.created_at));
10713
10726
  return {
10714
- open: all.filter(unsettled).map((fm, n) => ({
10727
+ open: all.filter((fm) => unsettled(fm) && !settled.has(fm.id)).map((fm, n) => ({
10715
10728
  ref: `P${n + 1}`,
10716
10729
  id: fm.id,
10717
10730
  title: fm.title ?? fm.slug,
@@ -11204,6 +11217,26 @@ async function readReviewEvidence(projectRoot) {
11204
11217
  }
11205
11218
  return out2;
11206
11219
  }
11220
+ async function verifiedDesignerFixes(projectRoot, before) {
11221
+ const addressed = (await listDesignerFindings(projectRoot)).map((f) => f.frontmatter).filter((fm) => fm.triage_status === "addressed" && !fm.design_cause_of);
11222
+ const out2 = /* @__PURE__ */ new Set();
11223
+ if (addressed.length === 0)
11224
+ return out2;
11225
+ const cutoff = toMillis(before);
11226
+ const runs = (await readRunEvidence(projectRoot)).filter((r) => {
11227
+ if (cutoff === null)
11228
+ return true;
11229
+ const start2 = toMillis(r.started_at);
11230
+ return start2 !== null && start2 < cutoff;
11231
+ });
11232
+ const states = deriveFixStates(addressed, runs);
11233
+ for (const fm of addressed) {
11234
+ if (fm.fix_status === "verified_resolved" || states.get(fm.slug)?.fix_state === "verified") {
11235
+ out2.add(fm.id);
11236
+ }
11237
+ }
11238
+ return out2;
11239
+ }
11207
11240
  async function computeFixStates(projectRoot) {
11208
11241
  const { entities } = await listEntities(projectRoot, "finding");
11209
11242
  const findings2 = entities.map((e) => e.frontmatter);
@@ -12265,6 +12298,7 @@ async function canonicalFindingFiles(projectRoot, scenarioSlug, canonicalId) {
12265
12298
  return out2;
12266
12299
  }
12267
12300
  async function markAddressedResolved(files, note) {
12301
+ const failed = [];
12268
12302
  const trimmedNote = note?.trim();
12269
12303
  for (const f of files) {
12270
12304
  if (f.triage_status !== "addressed")
@@ -12280,9 +12314,14 @@ async function markAddressedResolved(files, note) {
12280
12314
  if (updated !== raw)
12281
12315
  await atomicWriteFile(f.file, updated);
12282
12316
  });
12283
- } catch {
12317
+ } catch (err) {
12318
+ failed.push({
12319
+ file: f.file,
12320
+ error: err instanceof Error ? err.message : String(err)
12321
+ });
12284
12322
  }
12285
12323
  }
12324
+ return failed;
12286
12325
  }
12287
12326
  function originOf(url) {
12288
12327
  if (!url)
@@ -12465,7 +12504,9 @@ async function updateFixStatus(projectRoot, slug, status2) {
12465
12504
  await atomicWriteFile(filePath, updated);
12466
12505
  }
12467
12506
  });
12468
- } catch {
12507
+ return null;
12508
+ } catch (err) {
12509
+ return err instanceof Error ? err.message : String(err);
12469
12510
  }
12470
12511
  }
12471
12512
  async function listRunsInternal(projectRoot) {
@@ -12915,6 +12956,7 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12915
12956
  body: form,
12916
12957
  signal: controller.signal
12917
12958
  });
12959
+ const clockOffset = serverClockOffset(response);
12918
12960
  if (response.ok) {
12919
12961
  let body = null;
12920
12962
  try {
@@ -12960,7 +13002,8 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12960
13002
  runCloudId: parsed.data.cloud_walkthrough_id,
12961
13003
  contentHash,
12962
13004
  cloudOutcome: parsed.data.status,
12963
- ...recordedFindings.length > 0 ? { recordedFindings } : {}
13005
+ ...recordedFindings.length > 0 ? { recordedFindings } : {},
13006
+ ...clockOffset !== null ? { serverClockOffsetMs: clockOffset } : {}
12964
13007
  };
12965
13008
  }
12966
13009
  if (response.status === 409) {
@@ -13607,6 +13650,7 @@ async function drainRetryQueue(projectRoot, deps) {
13607
13650
  const deleteUrl = projectEndpoint(config.cloud_api_url, config.cloud_project_id, "delete");
13608
13651
  const remaining = [];
13609
13652
  const failures = [];
13653
+ const pushedRuns = [];
13610
13654
  let drained = 0;
13611
13655
  let dropped = 0;
13612
13656
  let notAttempted = 0;
@@ -13718,6 +13762,8 @@ async function drainRetryQueue(projectRoot, deps) {
13718
13762
  });
13719
13763
  if (result.mirrored || result.skippedReason === "bundle_too_large") {
13720
13764
  drained++;
13765
+ if (result.status === "pushed")
13766
+ pushedRuns.push(entry.entity_id);
13721
13767
  } else if (result.skippedReason === "missing_run" || result.skippedReason === "unsupported") {
13722
13768
  drained++;
13723
13769
  } else if (result.skippedReason === "cloud_conflict") {
@@ -13809,7 +13855,8 @@ async function drainRetryQueue(projectRoot, deps) {
13809
13855
  dropped,
13810
13856
  remaining: after.length,
13811
13857
  failures,
13812
- warnings
13858
+ warnings,
13859
+ ...pushedRuns.length > 0 ? { pushedRuns } : {}
13813
13860
  };
13814
13861
  }
13815
13862
  function unreadableBody(text, isDelete) {
@@ -14255,10 +14302,50 @@ async function ownEarlierWrites(projectRoot, batch, conflicts) {
14255
14302
  }
14256
14303
  return out2;
14257
14304
  }
14305
+ function findingRun(frontmatter) {
14306
+ if (typeof frontmatter.run === "string" && frontmatter.run) {
14307
+ return frontmatter.run;
14308
+ }
14309
+ const seen = frontmatter.seen_in;
14310
+ if (typeof seen !== "string")
14311
+ return null;
14312
+ const i = seen.indexOf(":");
14313
+ return i >= 0 ? seen.slice(i + 1) : seen;
14314
+ }
14315
+ async function rowsFromOwnBundles(projectRoot, batch, conflicts, adopt) {
14316
+ if (!adopt || adopt.runs.size === 0 || conflicts.length === 0)
14317
+ return [];
14318
+ const bases = await readBaseHashes(projectRoot);
14319
+ const byId = new Map(batch.map((e) => [e.id, e]));
14320
+ const out2 = [];
14321
+ for (const c of conflicts) {
14322
+ const entity = byId.get(c.entity_id);
14323
+ if (!entity || entity.type !== "finding")
14324
+ continue;
14325
+ if (!c.cloud_hash?.startsWith("cloud:"))
14326
+ continue;
14327
+ const run = findingRun(entity.frontmatter);
14328
+ if (!run || !adopt.runs.has(run))
14329
+ continue;
14330
+ const at = c.cloud_updated_at ? Date.parse(c.cloud_updated_at) : Number.NaN;
14331
+ const slack = adopt.serverClock ? ADOPT_CLOCK_SLACK_MS : 0;
14332
+ if (Number.isNaN(at) || at < adopt.since - slack)
14333
+ continue;
14334
+ if (adopt.identity && c.cloud_changed_by !== adopt.identity)
14335
+ continue;
14336
+ await recordCloudBase(projectRoot, c.entity_id, c.cloud_hash);
14337
+ out2.push({
14338
+ entity,
14339
+ restamped: Boolean(c.base_hash || bases[c.entity_id])
14340
+ });
14341
+ }
14342
+ return out2;
14343
+ }
14258
14344
  async function pushBatches(target, batches, byType, opts = {}) {
14259
14345
  let pushed = 0;
14260
14346
  let enqueued = 0;
14261
14347
  const selfResolved = [];
14348
+ const adopted = [];
14262
14349
  const conflicts = [];
14263
14350
  const overwritten = [];
14264
14351
  const errors = [];
@@ -14280,19 +14367,41 @@ async function pushBatches(target, batches, byType, opts = {}) {
14280
14367
  conflicts,
14281
14368
  overwritten,
14282
14369
  errors,
14283
- selfResolved
14370
+ selfResolved,
14371
+ adopted
14284
14372
  };
14285
14373
  }
14286
14374
  absorb(first);
14287
14375
  let unresolved = first.conflicts;
14288
14376
  const own = await ownEarlierWrites(target.projectRoot, batch, unresolved);
14289
- if (own.length > 0) {
14290
- const ownIds = new Set(own.map((e) => e.id));
14291
- unresolved = unresolved.filter((c) => !ownIds.has(c.entity_id));
14292
- const again = await pushOneBatch(target, own, byType, `batch ${i} own-version retry`);
14377
+ const ownIds = new Set(own.map((e) => e.id));
14378
+ const basesBefore = opts.adopt ? await readCloudBase(target.projectRoot) : {};
14379
+ const fromBundles = await rowsFromOwnBundles(target.projectRoot, batch, unresolved.filter((c) => !ownIds.has(c.entity_id)), opts.adopt);
14380
+ const resend = [...own, ...fromBundles.map((a) => a.entity)];
14381
+ if (resend.length > 0) {
14382
+ const resendIds = new Set(resend.map((e) => e.id));
14383
+ unresolved = unresolved.filter((c) => !resendIds.has(c.entity_id));
14384
+ const again = await pushOneBatch(target, resend, byType, `batch ${i} own-version retry`);
14293
14385
  absorb(again);
14294
14386
  conflicts.push(...again.conflicts);
14295
- selfResolved.push(...again.landed);
14387
+ const landed2 = new Set(again.landed);
14388
+ selfResolved.push(...own.filter((e) => landed2.has(e.id)).map((e) => e.id));
14389
+ for (const a of fromBundles) {
14390
+ if (landed2.has(a.entity.id)) {
14391
+ adopted.push({
14392
+ slug: a.entity.slug,
14393
+ restamped: a.restamped,
14394
+ serverClock: opts.adopt?.serverClock ?? false
14395
+ });
14396
+ continue;
14397
+ }
14398
+ const prior = basesBefore[a.entity.id];
14399
+ if (prior === void 0) {
14400
+ await forgetCloudBase(target.projectRoot, a.entity.id);
14401
+ } else {
14402
+ await recordCloudBase(target.projectRoot, a.entity.id, prior);
14403
+ }
14404
+ }
14296
14405
  }
14297
14406
  if (!opts.keepMine || unresolved.length === 0) {
14298
14407
  conflicts.push(...unresolved);
@@ -14345,8 +14454,17 @@ async function pushBatches(target, batches, byType, opts = {}) {
14345
14454
  }
14346
14455
  await forgetQueuedUpserts(target.projectRoot, revoked);
14347
14456
  }
14348
- return { pushed, enqueued, conflicts, overwritten, errors, selfResolved };
14457
+ return {
14458
+ pushed,
14459
+ enqueued,
14460
+ conflicts,
14461
+ overwritten,
14462
+ errors,
14463
+ selfResolved,
14464
+ adopted
14465
+ };
14349
14466
  }
14467
+ var ADOPT_CLOCK_SLACK_MS;
14350
14468
  var init_push = __esm({
14351
14469
  "../../packages/local-store/dist/sync/push.js"() {
14352
14470
  "use strict";
@@ -14354,6 +14472,7 @@ var init_push = __esm({
14354
14472
  init_server_reason();
14355
14473
  init_sync_tracking();
14356
14474
  init_types();
14475
+ ADOPT_CLOCK_SLACK_MS = 5e3;
14357
14476
  }
14358
14477
  });
14359
14478
 
@@ -14748,15 +14867,33 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14748
14867
  const hold = deps.capabilities === null;
14749
14868
  let held = 0;
14750
14869
  const recordedFindings = /* @__PURE__ */ new Set();
14870
+ const pushedRuns = /* @__PURE__ */ new Set();
14871
+ let serverClockOffsetMs = null;
14751
14872
  let runIds;
14752
14873
  try {
14753
14874
  runIds = await listRuns(projectRoot);
14754
14875
  } catch (err) {
14755
14876
  errors.push(`walkthrough scan failed: ${err instanceof Error ? err.message : String(err)} \u2014 check that iamu/.runs/ exists and is readable`);
14756
- return { summary, errors, withheldRuns, held, recordedFindings };
14877
+ return {
14878
+ summary,
14879
+ errors,
14880
+ withheldRuns,
14881
+ held,
14882
+ recordedFindings,
14883
+ pushedRuns,
14884
+ serverClockOffsetMs
14885
+ };
14757
14886
  }
14758
14887
  if (runIds.length === 0)
14759
- return { summary, errors, withheldRuns, held, recordedFindings };
14888
+ return {
14889
+ summary,
14890
+ errors,
14891
+ withheldRuns,
14892
+ held,
14893
+ recordedFindings,
14894
+ pushedRuns,
14895
+ serverClockOffsetMs
14896
+ };
14760
14897
  const lastPush = await readLastPush(projectRoot);
14761
14898
  const pendingWalkthroughQueueEntries = /* @__PURE__ */ new Set();
14762
14899
  const retryQueue = await readRetryQueue(projectRoot);
@@ -14823,6 +14960,9 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14823
14960
  });
14824
14961
  if (result.status === "pushed" || result.status === "already_pushed") {
14825
14962
  summary.pushed++;
14963
+ if (result.status === "pushed")
14964
+ pushedRuns.add(runId);
14965
+ serverClockOffsetMs ??= result.serverClockOffsetMs ?? null;
14826
14966
  for (const id of result.recordedFindings ?? [])
14827
14967
  recordedFindings.add(id);
14828
14968
  } else if (result.skippedReason === "bundle_too_large" || result.skippedReason === "unsupported" || result.skippedReason === "missing_run" || result.skippedReason === "cloud_conflict") {
@@ -14845,7 +14985,15 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14845
14985
  }
14846
14986
  }
14847
14987
  }
14848
- return { summary, errors, withheldRuns, held, recordedFindings };
14988
+ return {
14989
+ summary,
14990
+ errors,
14991
+ withheldRuns,
14992
+ held,
14993
+ recordedFindings,
14994
+ pushedRuns,
14995
+ serverClockOffsetMs
14996
+ };
14849
14997
  }
14850
14998
  var HOLD_SHAPES, WALKTHROUGH_SKIP_HINTS;
14851
14999
  var init_walkthroughs = __esm({
@@ -14895,6 +15043,7 @@ function mergePushProgress(target, outcome) {
14895
15043
  if (!outcome)
14896
15044
  return;
14897
15045
  target.selfResolved.push(...outcome.selfResolved ?? []);
15046
+ target.adopted.push(...outcome.adopted ?? []);
14898
15047
  target.pushed += outcome.pushed;
14899
15048
  target.enqueued += outcome.enqueued;
14900
15049
  target.conflicts.push(...outcome.conflicts);
@@ -14941,6 +15090,7 @@ async function pushDeferredFindings(projectRoot, capabilities, deferredFindings,
14941
15090
  return pushBatches(target, toBatches(retryFindings, batchSize), byType, pushOpts);
14942
15091
  }
14943
15092
  async function runSync(projectRoot, deps) {
15093
+ const startedAt = Date.now();
14944
15094
  const config = await readConfig(projectRoot);
14945
15095
  if (!config.cloud_linked) {
14946
15096
  return emptySyncResult([
@@ -14955,11 +15105,20 @@ async function runSync(projectRoot, deps) {
14955
15105
  const apiKey = deps.apiKey;
14956
15106
  const fetchFn = deps.fetch ?? globalThis.fetch;
14957
15107
  let capabilitiesFailure = "";
15108
+ let serverClockOffsetMs = null;
14958
15109
  const capabilities = deps.capabilities !== void 0 ? deps.capabilities : await fetchServerCapabilities(config.cloud_api_url, apiKey, fetchFn, deps.timeoutMs ?? DEFAULT_TIMEOUT_MS4, (detail) => {
14959
15110
  capabilitiesFailure = detail;
15111
+ }, (offset) => {
15112
+ serverClockOffsetMs = offset;
14960
15113
  });
14961
15114
  deps = { ...deps, capabilities };
14962
- const drain = await drainRetryQueue(projectRoot, deps);
15115
+ const { pushedRuns: drainedRuns = [], ...drain } = await drainRetryQueue(projectRoot, deps);
15116
+ const adoptFrom = (runs) => ({
15117
+ runs: /* @__PURE__ */ new Set([...drainedRuns, ...runs]),
15118
+ since: startedAt + (serverClockOffsetMs ?? 0),
15119
+ serverClock: serverClockOffsetMs !== null,
15120
+ identity: deps.identity ?? null
15121
+ });
14963
15122
  const scanned = await scanPending(projectRoot);
14964
15123
  if (scanned.errors.length === 0) {
14965
15124
  await pruneSentHashes(projectRoot, scanned.seenIds);
@@ -14983,14 +15142,15 @@ async function runSync(projectRoot, deps) {
14983
15142
  const pushOpts = { keepMine: deps.keepMine };
14984
15143
  const guidance = scan.pending.filter((e) => e.type === "guidance");
14985
15144
  const nonGuidance = scan.pending.filter((e) => e.type !== "guidance");
14986
- const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, pushOpts);
15145
+ const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, { ...pushOpts, adopt: adoptFrom([]) });
14987
15146
  const progress = {
14988
15147
  pushed: phase1.pushed,
14989
15148
  enqueued: phase1.enqueued,
14990
15149
  conflicts: [...phase1.conflicts],
14991
15150
  overwritten: [...phase1.overwritten],
14992
15151
  errors,
14993
- selfResolved: [...phase1.selfResolved]
15152
+ selfResolved: [...phase1.selfResolved],
15153
+ adopted: [...phase1.adopted]
14994
15154
  };
14995
15155
  progress.errors.push(...phase1.errors);
14996
15156
  const notices = [];
@@ -15000,10 +15160,11 @@ async function runSync(projectRoot, deps) {
15000
15160
  const guidanceResult = await pushGuidanceBatch(target, guidance, batchSize, byType, { ...pushOpts, detectUnsupported: !cloudSupportsGuidance(capabilities) }, notices);
15001
15161
  mergePushProgress(progress, guidanceResult);
15002
15162
  const walkthroughs = await syncWalkthroughs(projectRoot, deps, scan.unpushableScenarios);
15163
+ serverClockOffsetMs ??= walkthroughs.serverClockOffsetMs;
15003
15164
  if (walkthroughs.errors.length > 0) {
15004
15165
  progress.errors.push(...walkthroughs.errors);
15005
15166
  }
15006
- const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, pushOpts, reportedRefErrors, progress.errors);
15167
+ const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, { ...pushOpts, adopt: adoptFrom(walkthroughs.pushedRuns) }, reportedRefErrors, progress.errors);
15007
15168
  mergePushProgress(progress, phase3);
15008
15169
  if (capabilities === null) {
15009
15170
  const heldFindings = shaped.heldFindings + deferredFindings.size;
@@ -15024,6 +15185,11 @@ async function runSync(projectRoot, deps) {
15024
15185
  const deletion = await syncDeletions(projectRoot, config, deps);
15025
15186
  progress.errors.push(...deletion.errors);
15026
15187
  notices.push(...deletion.notices);
15188
+ for (const a of progress.adopted) {
15189
+ const what = a.restamped ? "overwrote the cloud row re-stamped during this sync (its version token came from this sync's own bundle of the finding's run)" : "overwrote the cloud row stamped during this sync (its version token came from this sync's own bundle)";
15190
+ const clock = a.serverClock ? "" : "; the server sent no clock, so only rows stamped after this sync began by this machine's clock counted";
15191
+ notices.push(`${a.slug}: ${what}${clock}`);
15192
+ }
15027
15193
  if (progress.selfResolved.length > 0) {
15028
15194
  const n = progress.selfResolved.length;
15029
15195
  notices.push(`${n} conflict${n === 1 ? " was" : "s were"} this machine's own earlier push (it reached the cloud without being recorded); re-sent against it`);
@@ -19273,7 +19439,14 @@ async function buildRunDesignerInput(projectRoot, runId) {
19273
19439
  await chooseKeyFrames(rd, grouped.candidates);
19274
19440
  const screens = grouped.screens;
19275
19441
  const screenKeys = new Set(screens.map((s) => s.key));
19276
- const earlier = await designerFindingsOn(projectRoot, screenKeys, manifest?.created_at ?? null, target);
19442
+ const earlier = await designerFindingsOn(
19443
+ projectRoot,
19444
+ screenKeys,
19445
+ manifest?.created_at ?? null,
19446
+ target,
19447
+ // A fix a designer's review already verified is not asked about again.
19448
+ await verifiedDesignerFixes(projectRoot, manifest?.created_at ?? null)
19449
+ );
19277
19450
  for (const o of earlier.open)
19278
19451
  refs2[o.ref] = o.id;
19279
19452
  const fullFrameScreens = new Set(screens.filter((s) => s.visits.some((v) => v.steps.some((st) => existsSync4(path34.join(rd, "screenshots", `step-${st.step}.full.png`))))).map((s) => s.key));
@@ -19429,6 +19602,7 @@ var init_designer_input = __esm({
19429
19602
  init_designer_files();
19430
19603
  init_designer_ignored();
19431
19604
  init_designer_ledger();
19605
+ init_fix_state();
19432
19606
  init_walkthrough_canonical2();
19433
19607
  init_dist();
19434
19608
  LONG_VISIT = 6;
@@ -22381,6 +22555,69 @@ function stepNumberOf(s) {
22381
22555
  const m = /^(?:step-)?(\d+)$/i.exec(s.trim());
22382
22556
  return m ? Number(m[1]) : null;
22383
22557
  }
22558
+ async function writeDesignerFixStatus(projectRoot, runId, verification, checks) {
22559
+ const verified = [];
22560
+ const untriaged = [];
22561
+ const warnings = [];
22562
+ const out2 = { verified, untriaged, warnings };
22563
+ const resolved = new Set(verification.resolved);
22564
+ const persists = new Set(verification.persists);
22565
+ if (resolved.size + persists.size === 0)
22566
+ return out2;
22567
+ const notWritten = (what, err) => warnings.push(`could not update ${what}: ${err instanceof Error ? err.message : String(err)}; designer.json holds the verification`);
22568
+ let files;
22569
+ try {
22570
+ files = (await listDesignerFindings(projectRoot)).map((f) => f.frontmatter).filter((fm) => !fm.design_cause_of && fm.canonical_id && (resolved.has(fm.canonical_id) || persists.has(fm.canonical_id)));
22571
+ } catch (err) {
22572
+ notWritten("the checked findings' fix_status", err);
22573
+ return out2;
22574
+ }
22575
+ const checkOf = new Map(checks.map((c) => [c.canonical_id, c]));
22576
+ for (const cid of verification.resolved) {
22577
+ const mine = files.filter((fm) => fm.canonical_id === cid);
22578
+ if (mine.some((fm) => fm.triage_status === "addressed"))
22579
+ continue;
22580
+ const latest = mine.filter((fm) => fm.triage_status !== "dismissed").sort((a, b) => String(a.created_at ?? "").localeCompare(String(b.created_at ?? ""))).at(-1);
22581
+ if (latest) {
22582
+ untriaged.push({
22583
+ ref: checkOf.get(cid)?.ref ?? null,
22584
+ finding: latest.slug
22585
+ });
22586
+ }
22587
+ }
22588
+ let states;
22589
+ try {
22590
+ states = deriveFixStates(files, await readRunEvidence(projectRoot));
22591
+ } catch (err) {
22592
+ notWritten("the checked findings' fix_status", err);
22593
+ return out2;
22594
+ }
22595
+ const decidedHere = (slug, state) => {
22596
+ const s = states.get(slug);
22597
+ return s?.fix_state === state && s.decided_by === runId;
22598
+ };
22599
+ for (const fm of files) {
22600
+ if (fm.triage_status !== "addressed" || !fm.canonical_id)
22601
+ continue;
22602
+ if (resolved.has(fm.canonical_id) && decidedHere(fm.slug, "verified")) {
22603
+ const [failed] = await markAddressedResolved([
22604
+ {
22605
+ file: entityFilePath(projectRoot, "finding", fm.slug),
22606
+ triage_status: fm.triage_status
22607
+ }
22608
+ ], checkOf.get(fm.canonical_id)?.note ?? void 0);
22609
+ if (failed)
22610
+ notWritten(fm.slug, failed.error);
22611
+ else
22612
+ verified.push(fm.slug);
22613
+ } else if (persists.has(fm.canonical_id) && decidedHere(fm.slug, "recurred")) {
22614
+ const error = await updateFixStatus(projectRoot, fm.slug, "verified_persists");
22615
+ if (error)
22616
+ notWritten(fm.slug, error);
22617
+ }
22618
+ }
22619
+ return out2;
22620
+ }
22384
22621
  async function readRefs2(rd) {
22385
22622
  try {
22386
22623
  return JSON.parse(await fs49.readFile(designerRefsFile(rd), "utf8"));
@@ -22701,6 +22938,11 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22701
22938
  };
22702
22939
  await atomicWriteFile(file, `${JSON.stringify(record, null, 2)}
22703
22940
  `);
22941
+ const fixes = await writeDesignerFixStatus(projectRoot, runId, verification, checks);
22942
+ warnings.push(...fixes.warnings);
22943
+ for (const u of fixes.untriaged) {
22944
+ warnings.push(`checked ${u.ref ?? "?"}: ${u.finding} is gone but was never marked addressed; its fix_status is left alone (checked_but_untriaged)`);
22945
+ }
22704
22946
  const bySeverity = {};
22705
22947
  const ruleCount = groupDesignerRules(rules);
22706
22948
  for (const i of issues)
@@ -22714,7 +22956,9 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22714
22956
  proposals: proposals.written,
22715
22957
  rejected,
22716
22958
  warnings,
22717
- ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {}
22959
+ ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {},
22960
+ ...fixes.verified.length > 0 ? { verified: fixes.verified } : {},
22961
+ ...fixes.untriaged.length > 0 ? { checked_but_untriaged: fixes.untriaged } : {}
22718
22962
  };
22719
22963
  }
22720
22964
  var DESIGNER_CATEGORIES, steps, refs, designerAnswerSchema;
@@ -22724,13 +22968,14 @@ var init_designer_record = __esm({
22724
22968
  init_atomic();
22725
22969
  init_design_proposals();
22726
22970
  init_design_review();
22727
- init_paths();
22728
22971
  init_url_path();
22729
22972
  init_review_issues();
22730
22973
  init_designer_files();
22731
22974
  init_designer_input();
22732
22975
  init_designer_ledger();
22733
22976
  init_entity();
22977
+ init_fix_state();
22978
+ init_paths();
22734
22979
  init_walkthrough_canonical2();
22735
22980
  DESIGNER_CATEGORIES = [
22736
22981
  "hierarchy",
@@ -23847,15 +24092,15 @@ function resolveApiUrl(configured) {
23847
24092
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
23848
24093
  return url;
23849
24094
  }
23850
- function parseStore(raw, path77) {
24095
+ function parseStore(raw, path78) {
23851
24096
  let parsed;
23852
24097
  try {
23853
24098
  parsed = JSON.parse(raw);
23854
24099
  } catch (err) {
23855
- throw new CredentialsFileError(path77, "not valid JSON", err);
24100
+ throw new CredentialsFileError(path78, "not valid JSON", err);
23856
24101
  }
23857
24102
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
23858
- throw new CredentialsFileError(path77, "expected a JSON object");
24103
+ throw new CredentialsFileError(path78, "expected a JSON object");
23859
24104
  }
23860
24105
  return parsed;
23861
24106
  }
@@ -23863,26 +24108,26 @@ function isNotFound(err) {
23863
24108
  return typeof err === "object" && err !== null && err.code === "ENOENT";
23864
24109
  }
23865
24110
  async function readCredentialsStore() {
23866
- const path77 = getCredentialsPath();
24111
+ const path78 = getCredentialsPath();
23867
24112
  let raw;
23868
24113
  try {
23869
- raw = await readFile(path77, "utf-8");
24114
+ raw = await readFile(path78, "utf-8");
23870
24115
  } catch (err) {
23871
24116
  if (isNotFound(err)) return {};
23872
24117
  warnUnreadableStoreOnce(err);
23873
24118
  throw err;
23874
24119
  }
23875
24120
  try {
23876
- return parseStore(raw, path77);
24121
+ return parseStore(raw, path78);
23877
24122
  } catch (err) {
23878
24123
  warnUnreadableStoreOnce(err);
23879
24124
  throw err;
23880
24125
  }
23881
24126
  }
23882
24127
  async function writeCredentialsStore(store) {
23883
- const path77 = getCredentialsPath();
23884
- await mkdir(dirname(path77), { recursive: true, mode: 448 });
23885
- await atomicWriteFile(path77, `${JSON.stringify(store, null, 2)}
24128
+ const path78 = getCredentialsPath();
24129
+ await mkdir(dirname(path78), { recursive: true, mode: 448 });
24130
+ await atomicWriteFile(path78, `${JSON.stringify(store, null, 2)}
23886
24131
  `, {
23887
24132
  mode: 384
23888
24133
  });
@@ -23910,16 +24155,16 @@ function resolveApiKey(apiUrl) {
23910
24155
  }
23911
24156
  }
23912
24157
  const normalized = url.replace(/\/+$/, "");
23913
- for (const path77 of candidatePaths) {
24158
+ for (const path78 of candidatePaths) {
23914
24159
  let raw;
23915
24160
  try {
23916
- raw = readFileSync3(path77, "utf-8");
24161
+ raw = readFileSync3(path78, "utf-8");
23917
24162
  } catch (err) {
23918
24163
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
23919
24164
  continue;
23920
24165
  }
23921
24166
  try {
23922
- const store = parseStore(raw, path77);
24167
+ const store = parseStore(raw, path78);
23923
24168
  const entry = store[url] ?? store[normalized] ?? store[`${normalized}/`];
23924
24169
  if (typeof entry !== "object" || entry === null) continue;
23925
24170
  const token = entry.token;
@@ -23960,12 +24205,12 @@ var init_credentials = __esm({
23960
24205
  * @param reason - What is wrong with it.
23961
24206
  * @param cause - Underlying parse error, if any.
23962
24207
  */
23963
- constructor(path77, reason, cause) {
24208
+ constructor(path78, reason, cause) {
23964
24209
  super(
23965
- `Credentials file ${path77} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
24210
+ `Credentials file ${path78} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
23966
24211
  {
23967
24212
  cause,
23968
- suggestedAction: `Delete ${path77} and run \`iamu auth login\`, or set IAMU_TOKEN.`
24213
+ suggestedAction: `Delete ${path78} and run \`iamu auth login\`, or set IAMU_TOKEN.`
23969
24214
  }
23970
24215
  );
23971
24216
  }
@@ -24831,8 +25076,8 @@ import { dirname as dirname3, join as join5, relative, resolve as resolve2 } fro
24831
25076
  import { fileURLToPath as fileURLToPath2 } from "url";
24832
25077
  import chalk6 from "chalk";
24833
25078
  function getVersion() {
24834
- if ("0.8.0-beta.5") {
24835
- return "0.8.0-beta.5";
25079
+ if ("0.8.0-beta.6") {
25080
+ return "0.8.0-beta.6";
24836
25081
  }
24837
25082
  try {
24838
25083
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -24955,10 +25200,10 @@ function hasGeneratedByMarker(content) {
24955
25200
  function withoutMarker(content) {
24956
25201
  return content.replace(/^generatedBy:[^\r\n]*\r?\n/m, "");
24957
25202
  }
24958
- function cliSkillState(path77, template) {
25203
+ function cliSkillState(path78, template) {
24959
25204
  let content;
24960
25205
  try {
24961
- content = readFileSync7(path77, "utf-8");
25206
+ content = readFileSync7(path78, "utf-8");
24962
25207
  } catch {
24963
25208
  return null;
24964
25209
  }
@@ -24966,9 +25211,9 @@ function cliSkillState(path77, template) {
24966
25211
  if (template === void 0) return "edited";
24967
25212
  return withoutMarker(content) === withoutMarker(template) ? "clean" : "edited";
24968
25213
  }
24969
- function isCliWrittenPrompt(path77) {
25214
+ function isCliWrittenPrompt(path78) {
24970
25215
  try {
24971
- const content = readFileSync7(path77, "utf-8");
25216
+ const content = readFileSync7(path78, "utf-8");
24972
25217
  return hasGeneratedByMarker(content) || /^#\s+iamu:/m.test(content) || /^description:\s*.*iamu/im.test(content);
24973
25218
  } catch {
24974
25219
  return false;
@@ -24978,10 +25223,10 @@ function planSkillFiles(hosts, projectRoot, version) {
24978
25223
  const skillTemplates = loadSkillTemplates(version);
24979
25224
  const files = [];
24980
25225
  const keptEdited = [];
24981
- const removeIfClean = (host, path77, name) => {
24982
- const state = cliSkillState(path77, skillTemplates.get(name));
24983
- if (state === "clean") files.push({ host, path: path77, content: null });
24984
- else if (state === "edited") keptEdited.push({ host, path: path77 });
25226
+ const removeIfClean = (host, path78, name) => {
25227
+ const state = cliSkillState(path78, skillTemplates.get(name));
25228
+ if (state === "clean") files.push({ host, path: path78, content: null });
25229
+ else if (state === "edited") keptEdited.push({ host, path: path78 });
24985
25230
  };
24986
25231
  for (const host of hosts) {
24987
25232
  if (pluginProvidesSkills(host, projectRoot)) {
@@ -25078,9 +25323,9 @@ async function findStaleSkills(projectRoot) {
25078
25323
  }
25079
25324
  return stale;
25080
25325
  }
25081
- async function removeSkillFile(path77) {
25082
- await rm(path77, { force: true });
25083
- await rmdir(dirname3(path77)).catch(() => {
25326
+ async function removeSkillFile(path78) {
25327
+ await rm(path78, { force: true });
25328
+ await rmdir(dirname3(path78)).catch(() => {
25084
25329
  });
25085
25330
  }
25086
25331
  async function installSkillsForHosts(hosts, projectRoot, version, opts) {
@@ -25882,29 +26127,29 @@ async function writeSyncMcpEntry(mcpPath) {
25882
26127
  return { removedLegacy };
25883
26128
  }
25884
26129
  async function registerSyncMcpForHost(host, projectRoot) {
25885
- const path77 = host.getMcpConfigPath(projectRoot);
25886
- if (path77 === null) {
26130
+ const path78 = host.getMcpConfigPath(projectRoot);
26131
+ if (path78 === null) {
25887
26132
  return {
25888
26133
  status: "manual",
25889
26134
  instruction: host.manualMcpSetup ?? `add an "iamu-sync" server that runs \`${SYNC_SERVER_COMMAND.join(" ")}\``
25890
26135
  };
25891
26136
  }
25892
26137
  try {
25893
- const { removedLegacy } = await writeSyncMcpEntry(path77);
25894
- return { status: "written", path: path77, removedLegacy };
26138
+ const { removedLegacy } = await writeSyncMcpEntry(path78);
26139
+ return { status: "written", path: path78, removedLegacy };
25895
26140
  } catch (err) {
25896
26141
  return {
25897
26142
  status: "failed",
25898
- path: path77,
26143
+ path: path78,
25899
26144
  message: err instanceof Error ? err.message : String(err)
25900
26145
  };
25901
26146
  }
25902
26147
  }
25903
26148
  async function removeLegacyMcpForHost(host, projectRoot) {
25904
- const path77 = host.getMcpConfigPath(projectRoot);
25905
- if (path77 === null) return null;
26149
+ const path78 = host.getMcpConfigPath(projectRoot);
26150
+ if (path78 === null) return null;
25906
26151
  try {
25907
- return await removeLegacyIamuEntry(path77) ? { status: "legacy_removed", path: path77, removedLegacy: true } : null;
26152
+ return await removeLegacyIamuEntry(path78) ? { status: "legacy_removed", path: path78, removedLegacy: true } : null;
25908
26153
  } catch {
25909
26154
  return null;
25910
26155
  }
@@ -33482,6 +33727,7 @@ __export(triage_exports, {
33482
33727
  triage: () => triage
33483
33728
  });
33484
33729
  import { promises as fs65 } from "fs";
33730
+ import path62 from "path";
33485
33731
  import chalk29 from "chalk";
33486
33732
  async function awaitingSteps(projectRoot, findings2) {
33487
33733
  const scenarios = /* @__PURE__ */ new Set();
@@ -33620,14 +33866,53 @@ async function triage(refs2, opts) {
33620
33866
  );
33621
33867
  return;
33622
33868
  }
33869
+ let note = opts.note;
33870
+ if (opts.noteFile !== void 0) {
33871
+ if (opts.note !== void 0) {
33872
+ commandError("Pass --note or --note-file, not both.", {
33873
+ ...opts,
33874
+ code: "invalid_field_combination",
33875
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note-file <path>`
33876
+ });
33877
+ return;
33878
+ }
33879
+ try {
33880
+ const text = opts.noteFile === "-" ? await readStdinText() : await fs65.readFile(
33881
+ path62.resolve(process.cwd(), opts.noteFile),
33882
+ "utf8"
33883
+ );
33884
+ note = text.trim();
33885
+ } catch (err) {
33886
+ commandError(
33887
+ `Could not read --note-file ${opts.noteFile}: ${err instanceof Error ? err.message : String(err)}`,
33888
+ {
33889
+ ...opts,
33890
+ code: "note_read_error",
33891
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33892
+ }
33893
+ );
33894
+ return;
33895
+ }
33896
+ if (!note) {
33897
+ commandError(`--note-file ${opts.noteFile} is empty`, {
33898
+ ...opts,
33899
+ code: "note_empty",
33900
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33901
+ });
33902
+ return;
33903
+ }
33904
+ }
33623
33905
  let reason = null;
33624
33906
  if (opts.reason !== void 0) {
33625
33907
  if (status2.data !== "dismissed") {
33626
- commandError("--reason applies only to --status dismissed", {
33627
- ...opts,
33628
- code: "invalid_reason_usage",
33629
- fix: `iamu triage ${refs2.join(" ")} --status dismissed --reason ${opts.reason}`
33630
- });
33908
+ commandError(
33909
+ "--reason applies only to --status dismissed \u2014 to record why or what changed, use --note",
33910
+ {
33911
+ ...opts,
33912
+ code: "invalid_reason_usage",
33913
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33914
+ }
33915
+ );
33631
33916
  return;
33632
33917
  }
33633
33918
  if (!DISMISS_REASONS.includes(opts.reason)) {
@@ -33819,7 +34104,7 @@ async function triage(refs2, opts) {
33819
34104
  const rule = await createGuidanceRule(projectRoot, {
33820
34105
  kind: effectiveReason,
33821
34106
  rule: ruleText,
33822
- reason: opts.note,
34107
+ reason: note,
33823
34108
  scope,
33824
34109
  sourceCanonicalIds: allTargets.map((f) => f.frontmatter.canonical_id).filter((id) => Boolean(id && UUID_RE4.test(id)))
33825
34110
  });
@@ -33830,7 +34115,7 @@ async function triage(refs2, opts) {
33830
34115
  status: status2.data,
33831
34116
  reason: effectiveReason,
33832
34117
  duplicateOf: duplicateTarget,
33833
- ...opts.note !== void 0 ? { note: opts.note } : {},
34118
+ ...note !== void 0 ? { note } : {},
33834
34119
  by: "triage",
33835
34120
  cascade: true,
33836
34121
  guidance: guidance ? { id: guidance.id, all: isSweep } : null,
@@ -33915,6 +34200,7 @@ var init_triage = __esm({
33915
34200
  init_dist2();
33916
34201
  init_dist();
33917
34202
  init_agent_commands();
34203
+ init_answer_input();
33918
34204
  init_complete();
33919
34205
  init_context4();
33920
34206
  SCOPES = ["project", "outline", "capability"];
@@ -34382,7 +34668,7 @@ var init_browse_driver = __esm({
34382
34668
 
34383
34669
  // src/commands/browse-actions.ts
34384
34670
  import { existsSync as existsSync15 } from "fs";
34385
- import path62 from "path";
34671
+ import path63 from "path";
34386
34672
  function byRole(page, role, name, opts) {
34387
34673
  const loc = page.getByRole(role, { name, exact: false });
34388
34674
  if (opts.nth !== void 0) {
@@ -34683,7 +34969,7 @@ async function fillIndexedElement(page, index, text, submit) {
34683
34969
  await fillOrSelect(loc.first(), text, submit);
34684
34970
  }
34685
34971
  async function setFilesOnElement(el, filePath) {
34686
- const resolved = path62.resolve(filePath);
34972
+ const resolved = path63.resolve(filePath);
34687
34973
  if (SENSITIVE_UPLOAD_PATH.test(resolved)) {
34688
34974
  throw new Error(
34689
34975
  `Refusing to upload sensitive path: "${resolved}". Choose a test fixture file inside the project or temp directory.`
@@ -34916,7 +35202,7 @@ var init_browse_actions = __esm({
34916
35202
 
34917
35203
  // src/commands/step-numbering.ts
34918
35204
  import { open, readFile as readFile6, readdir, stat as stat2, unlink } from "fs/promises";
34919
- import path63 from "path";
35205
+ import path64 from "path";
34920
35206
  function reservationMarker() {
34921
35207
  const marker = {
34922
35208
  reservation: { pid: process.pid, ts: Date.now() }
@@ -34958,7 +35244,7 @@ async function peekNextStepNumber(stepsDir) {
34958
35244
  for (const f of files) {
34959
35245
  const m = /^(\d+)\.json$/.exec(f);
34960
35246
  if (!m) continue;
34961
- const fullPath = path63.join(stepsDir, f);
35247
+ const fullPath = path64.join(stepsDir, f);
34962
35248
  const st = await stat2(fullPath).catch(() => null);
34963
35249
  if (!st) continue;
34964
35250
  if (await isAbandonedReservation(fullPath, st)) {
@@ -34976,7 +35262,7 @@ async function reserveStepFile(stepsDir, limit) {
34976
35262
  if (candidate > limit.maxSteps) {
34977
35263
  throw new Error(stepLimitMessage(limit.maxSteps, limit.runId));
34978
35264
  }
34979
- const stepPath = path63.join(stepsDir, stepFileName(candidate));
35265
+ const stepPath = path64.join(stepsDir, stepFileName(candidate));
34980
35266
  try {
34981
35267
  const handle = await open(stepPath, "wx");
34982
35268
  try {
@@ -35022,7 +35308,7 @@ __export(step_exports, {
35022
35308
  });
35023
35309
  import { existsSync as existsSync16 } from "fs";
35024
35310
  import { copyFile, mkdir as mkdir4, rename, stat as stat3 } from "fs/promises";
35025
- import path64 from "path";
35311
+ import path65 from "path";
35026
35312
  import chalk31 from "chalk";
35027
35313
  function parseMaxSteps(val) {
35028
35314
  if (typeof val === "number" && Number.isFinite(val) && val > 0) return val;
@@ -35038,11 +35324,11 @@ function parseMaxSteps(val) {
35038
35324
  }
35039
35325
  async function recordStep(input) {
35040
35326
  const runDirPath = runDir(input.projectRoot, input.runId);
35041
- if (!existsSync16(path64.join(runDirPath, "walkthrough.json"))) {
35327
+ if (!existsSync16(path65.join(runDirPath, "walkthrough.json"))) {
35042
35328
  throw new Error(`Run not found: ${input.runId}`);
35043
35329
  }
35044
- const stepsDir = path64.join(runDirPath, "steps");
35045
- const screenshotsDir = path64.join(runDirPath, "screenshots");
35330
+ const stepsDir = path65.join(runDirPath, "steps");
35331
+ const screenshotsDir = path65.join(runDirPath, "screenshots");
35046
35332
  await mkdir4(stepsDir, { recursive: true });
35047
35333
  await mkdir4(screenshotsDir, { recursive: true });
35048
35334
  const shotSource = input.shot ? await existingShot(input.shot) : null;
@@ -35057,9 +35343,9 @@ async function recordStep(input) {
35057
35343
  let screenshotRef = null;
35058
35344
  if (shotSource) {
35059
35345
  const destName = `step-${stepNumber2}.png`;
35060
- const destPath = path64.join(screenshotsDir, destName);
35346
+ const destPath = path65.join(screenshotsDir, destName);
35061
35347
  finalScreenshotPath = destPath;
35062
- if (path64.resolve(destPath) !== shotSource) {
35348
+ if (path65.resolve(destPath) !== shotSource) {
35063
35349
  if (input.moveShot) {
35064
35350
  await rename(shotSource, destPath).catch(
35065
35351
  () => copyFile(shotSource, destPath)
@@ -35068,16 +35354,16 @@ async function recordStep(input) {
35068
35354
  await copyFile(shotSource, destPath);
35069
35355
  }
35070
35356
  }
35071
- screenshotRef = path64.posix.join("screenshots", destName);
35357
+ screenshotRef = path65.posix.join("screenshots", destName);
35072
35358
  }
35073
35359
  let fullRef = null;
35074
35360
  if (input.fullShot && existsSync16(input.fullShot)) {
35075
35361
  const fullName = `step-${stepNumber2}.full.png`;
35076
35362
  const moved = await rename(
35077
35363
  input.fullShot,
35078
- path64.join(screenshotsDir, fullName)
35364
+ path65.join(screenshotsDir, fullName)
35079
35365
  ).then(() => true).catch(() => false);
35080
- if (moved) fullRef = path64.posix.join("screenshots", fullName);
35366
+ if (moved) fullRef = path65.posix.join("screenshots", fullName);
35081
35367
  }
35082
35368
  parsedStep = runStepSchema.parse({
35083
35369
  step_number: stepNumber2,
@@ -35124,7 +35410,7 @@ async function recordStep(input) {
35124
35410
  };
35125
35411
  }
35126
35412
  async function existingShot(shot) {
35127
- const resolved = path64.resolve(shot);
35413
+ const resolved = path65.resolve(shot);
35128
35414
  const isFile = await stat3(resolved).then((s) => s.isFile()).catch(() => false);
35129
35415
  if (!isFile) {
35130
35416
  throw new Error(
@@ -35185,7 +35471,7 @@ async function stepCommand(runArg, opts) {
35185
35471
  {
35186
35472
  step_number: res.step.step_number,
35187
35473
  cancelled: true,
35188
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35474
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35189
35475
  action: res.step.action,
35190
35476
  note: res.step.note,
35191
35477
  url: res.step.url,
@@ -35213,7 +35499,7 @@ async function stepCommand(runArg, opts) {
35213
35499
  JSON.stringify(
35214
35500
  {
35215
35501
  step_number: res.step.step_number,
35216
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35502
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35217
35503
  action: res.step.action,
35218
35504
  note: res.step.note,
35219
35505
  url: res.step.url,
@@ -35242,7 +35528,7 @@ async function stepCommand(runArg, opts) {
35242
35528
  console.log(` ${chalk31.dim("shot:")} ${res.step.screenshot}`);
35243
35529
  }
35244
35530
  console.log(
35245
- ` ${chalk31.dim("file:")} ${path64.relative(projectRoot, res.stepPath)}`
35531
+ ` ${chalk31.dim("file:")} ${path65.relative(projectRoot, res.stepPath)}`
35246
35532
  );
35247
35533
  console.log();
35248
35534
  } else {
@@ -35314,16 +35600,16 @@ import {
35314
35600
  import { rm as rm2 } from "fs/promises";
35315
35601
  import { createServer } from "net";
35316
35602
  import { homedir as homedir3 } from "os";
35317
- import path65 from "path";
35603
+ import path66 from "path";
35318
35604
  import chalk32 from "chalk";
35319
35605
  function stateDir() {
35320
- return path65.join(
35321
- process.env.IAMU_HOME ?? path65.join(homedir3(), ".iamu"),
35606
+ return path66.join(
35607
+ process.env.IAMU_HOME ?? path66.join(homedir3(), ".iamu"),
35322
35608
  "browse"
35323
35609
  );
35324
35610
  }
35325
35611
  function profileDir() {
35326
- return path65.join(stateDir(), "profile");
35612
+ return path66.join(stateDir(), "profile");
35327
35613
  }
35328
35614
  function privateDir(dir) {
35329
35615
  mkdirSync3(dir, { recursive: true, mode: 448 });
@@ -35334,7 +35620,7 @@ function privateDir(dir) {
35334
35620
  }
35335
35621
  function stateFile(run) {
35336
35622
  const key = run && STATE_KEY.test(run) && `${run}.json` !== SHARED_STATE ? `${run}.json` : SHARED_STATE;
35337
- return path65.join(stateDir(), key);
35623
+ return path66.join(stateDir(), key);
35338
35624
  }
35339
35625
  function loadState(file) {
35340
35626
  try {
@@ -35375,7 +35661,7 @@ function storedStates() {
35375
35661
  }
35376
35662
  const out2 = [];
35377
35663
  for (const name of names2.sort()) {
35378
- const file = path65.join(stateDir(), name);
35664
+ const file = path66.join(stateDir(), name);
35379
35665
  const state = loadState(file);
35380
35666
  if (state) out2.push({ state, file });
35381
35667
  }
@@ -35393,16 +35679,16 @@ function removeState(file) {
35393
35679
  }
35394
35680
  function runStillOpen(state, now = Date.now()) {
35395
35681
  if (!state.run || !state.project) return false;
35396
- const dir = path65.join(state.project, "iamu", ".runs", state.run);
35682
+ const dir = path66.join(state.project, "iamu", ".runs", state.run);
35397
35683
  try {
35398
- const manifestPath = path65.join(dir, "walkthrough.json");
35684
+ const manifestPath = path66.join(dir, "walkthrough.json");
35399
35685
  const manifest = JSON.parse(readFileSync10(manifestPath, "utf8"));
35400
35686
  if (typeof manifest.status !== "string" || !OPEN_RUN_STATUSES.has(manifest.status)) {
35401
35687
  return false;
35402
35688
  }
35403
35689
  let last = statSync3(manifestPath).mtimeMs;
35404
35690
  try {
35405
- last = Math.max(last, statSync3(path65.join(dir, "steps")).mtimeMs);
35691
+ last = Math.max(last, statSync3(path66.join(dir, "steps")).mtimeMs);
35406
35692
  } catch {
35407
35693
  }
35408
35694
  return now - last < RUN_IDLE_MS;
@@ -35501,7 +35787,7 @@ async function requestedViewport(opts) {
35501
35787
  let manifest;
35502
35788
  try {
35503
35789
  manifest = JSON.parse(
35504
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
35790
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
35505
35791
  );
35506
35792
  } catch {
35507
35793
  fail(
@@ -35680,7 +35966,7 @@ function computePageFingerprint(url, bodySnippet) {
35680
35966
  return createHash6("sha256").update(`${url}|${bodySnippet.trim().replace(/\s+/g, " ")}`).digest("hex").slice(0, 16);
35681
35967
  }
35682
35968
  function checkAndRecordStagnation(runDirPath, fingerprint, action) {
35683
- const cacheFile = path65.join(runDirPath, ".stagnation.json");
35969
+ const cacheFile = path66.join(runDirPath, ".stagnation.json");
35684
35970
  let previous = null;
35685
35971
  try {
35686
35972
  if (existsSync17(cacheFile)) {
@@ -35961,8 +36247,8 @@ async function actOnPage(opts, attached, state, fn, onPostShot) {
35961
36247
  let contentSnippet;
35962
36248
  if (opts.shot) {
35963
36249
  await waitForVisualStability(page);
35964
- screenshot = path65.resolve(opts.shot);
35965
- mkdirSync3(path65.dirname(screenshot), { recursive: true });
36250
+ screenshot = path66.resolve(opts.shot);
36251
+ mkdirSync3(path66.dirname(screenshot), { recursive: true });
35966
36252
  await page.screenshot({
35967
36253
  path: screenshot,
35968
36254
  fullPage: Boolean(opts.full),
@@ -36172,7 +36458,7 @@ function httpOrigin(raw) {
36172
36458
  function readTargetUrl(runDirPath) {
36173
36459
  try {
36174
36460
  const m = JSON.parse(
36175
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36461
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36176
36462
  );
36177
36463
  return typeof m.target_url === "string" ? m.target_url : null;
36178
36464
  } catch {
@@ -36184,7 +36470,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36184
36470
  let manifest = {};
36185
36471
  try {
36186
36472
  manifest = JSON.parse(
36187
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36473
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36188
36474
  );
36189
36475
  } catch {
36190
36476
  }
@@ -36195,7 +36481,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36195
36481
  if (designer2.includes(key) || scrolled) {
36196
36482
  return { take: true, lastHeight: null };
36197
36483
  }
36198
- const stepsDir = path65.join(runDirPath, "steps");
36484
+ const stepsDir = path66.join(runDirPath, "steps");
36199
36485
  let names2 = [];
36200
36486
  try {
36201
36487
  names2 = readdirSync3(stepsDir).filter((n) => /^\d+\.json$/.test(n)).sort();
@@ -36206,7 +36492,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36206
36492
  for (const name of names2) {
36207
36493
  try {
36208
36494
  const earlier = JSON.parse(
36209
- readFileSync10(path65.join(stepsDir, name), "utf8")
36495
+ readFileSync10(path66.join(stepsDir, name), "utf8")
36210
36496
  );
36211
36497
  if (typeof earlier.url === "string" && stepScreenKey(earlier.url, target) === key) {
36212
36498
  seen = true;
@@ -36273,7 +36559,7 @@ function printStep(opts, record, projectRoot, stagnationWarning, challengeWarnin
36273
36559
  if (opts.json) {
36274
36560
  const doc = {
36275
36561
  step_number: step.step_number,
36276
- path: path65.relative(projectRoot, record.stepPath).split(path65.sep).join("/"),
36562
+ path: path66.relative(projectRoot, record.stepPath).split(path66.sep).join("/"),
36277
36563
  action: step.action,
36278
36564
  note: step.note,
36279
36565
  url: step.url,
@@ -36391,7 +36677,7 @@ async function clearStorage(opts, driver) {
36391
36677
  try {
36392
36678
  if (dir) {
36393
36679
  target = JSON.parse(
36394
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
36680
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
36395
36681
  ).target_url;
36396
36682
  }
36397
36683
  } catch {
@@ -36578,15 +36864,15 @@ async function browseStep(args2, opts, driver) {
36578
36864
  ))
36579
36865
  return;
36580
36866
  const maxSteps = parseMaxSteps(opts.maxSteps);
36581
- if (await peekNextStepNumber(path65.join(runDirPath, "steps")) > maxSteps) {
36867
+ if (await peekNextStepNumber(path66.join(runDirPath, "steps")) > maxSteps) {
36582
36868
  return fail(stepLimitMessage(maxSteps, opts.run), opts);
36583
36869
  }
36584
- const pendingShot = path65.join(
36870
+ const pendingShot = path66.join(
36585
36871
  runDirPath,
36586
36872
  "screenshots",
36587
36873
  `.pending-${randomUUID11()}.png`
36588
36874
  );
36589
- const pendingFull = path65.join(
36875
+ const pendingFull = path66.join(
36590
36876
  runDirPath,
36591
36877
  "screenshots",
36592
36878
  `.pending-${randomUUID11()}.full.png`
@@ -38469,18 +38755,18 @@ __export(preflight_exports, {
38469
38755
  });
38470
38756
  import { spawnSync } from "child_process";
38471
38757
  import { existsSync as existsSync20, promises as fs67 } from "fs";
38472
- import path66 from "path";
38758
+ import path67 from "path";
38473
38759
  import chalk36 from "chalk";
38474
38760
  function detectPackageManager(targetDir) {
38475
- if (existsSync20(path66.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
38476
- if (existsSync20(path66.join(targetDir, "yarn.lock"))) return "yarn";
38477
- if (existsSync20(path66.join(targetDir, "bun.lockb")) || existsSync20(path66.join(targetDir, "bun.lock"))) {
38761
+ if (existsSync20(path67.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
38762
+ if (existsSync20(path67.join(targetDir, "yarn.lock"))) return "yarn";
38763
+ if (existsSync20(path67.join(targetDir, "bun.lockb")) || existsSync20(path67.join(targetDir, "bun.lock"))) {
38478
38764
  return "bun";
38479
38765
  }
38480
38766
  return "npm";
38481
38767
  }
38482
38768
  async function detectPreflightCommand(targetDir) {
38483
- const pkgPath = path66.join(targetDir, "package.json");
38769
+ const pkgPath = path67.join(targetDir, "package.json");
38484
38770
  if (existsSync20(pkgPath)) {
38485
38771
  try {
38486
38772
  const raw = await fs67.readFile(pkgPath, "utf8");
@@ -38493,7 +38779,7 @@ async function detectPreflightCommand(targetDir) {
38493
38779
  } catch {
38494
38780
  }
38495
38781
  }
38496
- const tsconfigPath = path66.join(targetDir, "tsconfig.json");
38782
+ const tsconfigPath = path67.join(targetDir, "tsconfig.json");
38497
38783
  if (existsSync20(tsconfigPath)) {
38498
38784
  return { command: "npx", args: ["--no-install", "tsc", "--noEmit"] };
38499
38785
  }
@@ -38532,7 +38818,7 @@ async function runPreflightCheck(targetDir) {
38532
38818
  };
38533
38819
  }
38534
38820
  async function preflight(opts = {}) {
38535
- const targetDir = opts.cwd ? path66.resolve(opts.cwd) : process.cwd();
38821
+ const targetDir = opts.cwd ? path67.resolve(opts.cwd) : process.cwd();
38536
38822
  const result = await runPreflightCheck(targetDir);
38537
38823
  if (result.ok) {
38538
38824
  if (opts.json) {
@@ -39041,12 +39327,12 @@ __export(validate_exports, {
39041
39327
  });
39042
39328
  import { randomUUID as randomUUID12 } from "crypto";
39043
39329
  import { promises as fs68 } from "fs";
39044
- import path67 from "path";
39330
+ import path68 from "path";
39045
39331
  import chalk38 from "chalk";
39046
39332
  function inferTypeFromPath(filePath, projectRoot) {
39047
- const rel = path67.relative(path67.join(projectRoot, "iamu"), filePath);
39048
- if (rel.startsWith("..") || path67.isAbsolute(rel)) return null;
39049
- const segments = rel.split(path67.sep);
39333
+ const rel = path68.relative(path68.join(projectRoot, "iamu"), filePath);
39334
+ if (rel.startsWith("..") || path68.isAbsolute(rel)) return null;
39335
+ const segments = rel.split(path68.sep);
39050
39336
  if (segments.length < 2) return null;
39051
39337
  return TYPE_BY_ALIAS.get(segments[0]) ?? null;
39052
39338
  }
@@ -39063,7 +39349,7 @@ function applyZodValidation(schema2, data, result) {
39063
39349
  }
39064
39350
  function validateFile(filePath, raw, projectRoot) {
39065
39351
  const result = { path: filePath, valid: true, errors: [] };
39066
- if (path67.basename(filePath) === "config.json") {
39352
+ if (path68.basename(filePath) === "config.json") {
39067
39353
  let parsedJson;
39068
39354
  try {
39069
39355
  parsedJson = JSON.parse(raw);
@@ -39090,11 +39376,11 @@ function validateFile(filePath, raw, projectRoot) {
39090
39376
  );
39091
39377
  return result;
39092
39378
  }
39093
- if (path67.basename(filePath) === "testability.md") {
39379
+ if (path68.basename(filePath) === "testability.md") {
39094
39380
  applyZodValidation(testabilityFrontmatterSchema, frontmatter, result);
39095
39381
  return result;
39096
39382
  }
39097
- if (path67.basename(filePath) === "context.yml") {
39383
+ if (path68.basename(filePath) === "context.yml") {
39098
39384
  applyZodValidation(contextFrontmatterSchema, frontmatter, result);
39099
39385
  const warnings = [];
39100
39386
  const hasNotes = Boolean(
@@ -39156,7 +39442,7 @@ async function buildRepairLookups(projectRoot) {
39156
39442
  }
39157
39443
  }
39158
39444
  const runScenario = /* @__PURE__ */ new Map();
39159
- const runsDir2 = path67.join(projectRoot, "iamu", ".runs");
39445
+ const runsDir2 = path68.join(projectRoot, "iamu", ".runs");
39160
39446
  let runIds = [];
39161
39447
  try {
39162
39448
  runIds = await fs68.readdir(runsDir2);
@@ -39166,7 +39452,7 @@ async function buildRepairLookups(projectRoot) {
39166
39452
  try {
39167
39453
  const manifest = JSON.parse(
39168
39454
  await fs68.readFile(
39169
- path67.join(runsDir2, runId, "walkthrough.json"),
39455
+ path68.join(runsDir2, runId, "walkthrough.json"),
39170
39456
  "utf8"
39171
39457
  )
39172
39458
  );
@@ -39197,7 +39483,7 @@ async function fixFindings(projectRoot, results, dryRun) {
39197
39483
  parsed.frontmatter,
39198
39484
  {
39199
39485
  ...lookups,
39200
- fileSlug: path67.basename(result.path, ".md"),
39486
+ fileSlug: path68.basename(result.path, ".md"),
39201
39487
  newId: randomUUID12,
39202
39488
  body: parsed.body
39203
39489
  }
@@ -39239,7 +39525,7 @@ async function validate(filePath, opts) {
39239
39525
  if (process.exitCode === 1) return;
39240
39526
  const results = [];
39241
39527
  if (filePath) {
39242
- const abs = path67.isAbsolute(filePath) ? filePath : path67.resolve(process.cwd(), filePath);
39528
+ const abs = path68.isAbsolute(filePath) ? filePath : path68.resolve(process.cwd(), filePath);
39243
39529
  try {
39244
39530
  const raw = await fs68.readFile(abs, "utf8");
39245
39531
  results.push(validateFile(abs, raw, projectRoot));
@@ -39255,7 +39541,7 @@ async function validate(filePath, opts) {
39255
39541
  return;
39256
39542
  }
39257
39543
  } else if (opts.run) {
39258
- const findingsDir = path67.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39544
+ const findingsDir = path68.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39259
39545
  let names2 = [];
39260
39546
  try {
39261
39547
  names2 = await fs68.readdir(findingsDir);
@@ -39263,7 +39549,7 @@ async function validate(filePath, opts) {
39263
39549
  }
39264
39550
  for (const name of names2) {
39265
39551
  if (!name.endsWith(".md")) continue;
39266
- const abs = path67.join(findingsDir, name);
39552
+ const abs = path68.join(findingsDir, name);
39267
39553
  try {
39268
39554
  const raw = await fs68.readFile(abs, "utf8");
39269
39555
  const { frontmatter } = parseFrontmatter(raw);
@@ -39272,7 +39558,7 @@ async function validate(filePath, opts) {
39272
39558
  }
39273
39559
  } catch {
39274
39560
  results.push({
39275
- path: path67.relative(projectRoot, abs),
39561
+ path: path68.relative(projectRoot, abs),
39276
39562
  valid: false,
39277
39563
  errors: ["Cannot read file or parse frontmatter"]
39278
39564
  });
@@ -39305,7 +39591,7 @@ async function validate(filePath, opts) {
39305
39591
  } else {
39306
39592
  const typesToCheck = typeFilter ? ENTITY_TYPES.filter((t) => typeFilter.has(t)) : ENTITY_TYPES;
39307
39593
  for (const type2 of typesToCheck) {
39308
- const iamuTypeDir = path67.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39594
+ const iamuTypeDir = path68.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39309
39595
  let names2;
39310
39596
  try {
39311
39597
  names2 = await fs68.readdir(iamuTypeDir);
@@ -39314,13 +39600,13 @@ async function validate(filePath, opts) {
39314
39600
  }
39315
39601
  for (const name of names2) {
39316
39602
  if (!name.endsWith(".md")) continue;
39317
- const abs = path67.join(iamuTypeDir, name);
39603
+ const abs = path68.join(iamuTypeDir, name);
39318
39604
  try {
39319
39605
  const raw = await fs68.readFile(abs, "utf8");
39320
39606
  results.push(validateFile(abs, raw, projectRoot));
39321
39607
  } catch {
39322
39608
  results.push({
39323
- path: path67.relative(projectRoot, abs),
39609
+ path: path68.relative(projectRoot, abs),
39324
39610
  valid: false,
39325
39611
  errors: ["Cannot read file"]
39326
39612
  });
@@ -39328,7 +39614,7 @@ async function validate(filePath, opts) {
39328
39614
  }
39329
39615
  }
39330
39616
  for (const extraFile of ["context.yml", "testability.md", "config.json"]) {
39331
- const extraPath = path67.join(projectRoot, "iamu", extraFile);
39617
+ const extraPath = path68.join(projectRoot, "iamu", extraFile);
39332
39618
  try {
39333
39619
  const raw = await fs68.readFile(extraPath, "utf8");
39334
39620
  results.push(validateFile(extraPath, raw, projectRoot));
@@ -39354,19 +39640,19 @@ async function validate(filePath, opts) {
39354
39640
  valid,
39355
39641
  errors: results.filter((r) => !r.valid).flatMap(
39356
39642
  (r) => r.errors.map((e) => ({
39357
- path: path67.relative(projectRoot, r.path),
39643
+ path: path68.relative(projectRoot, r.path),
39358
39644
  message: e
39359
39645
  }))
39360
39646
  ),
39361
39647
  warnings: results.filter((r) => r.warnings && r.warnings.length > 0).flatMap(
39362
39648
  (r) => (r.warnings ?? []).map((w) => ({
39363
- path: path67.relative(projectRoot, r.path),
39649
+ path: path68.relative(projectRoot, r.path),
39364
39650
  message: w
39365
39651
  }))
39366
39652
  ),
39367
39653
  ...opts.fix ? {
39368
39654
  repaired: repaired.map((r) => ({
39369
- path: path67.relative(projectRoot, r.path),
39655
+ path: path68.relative(projectRoot, r.path),
39370
39656
  changes: r.repairs,
39371
39657
  valid: r.valid
39372
39658
  }))
@@ -39388,7 +39674,7 @@ async function validate(filePath, opts) {
39388
39674
  }
39389
39675
  let hasErrors = false;
39390
39676
  for (const r of results) {
39391
- const relPath = path67.relative(projectRoot, r.path);
39677
+ const relPath = path68.relative(projectRoot, r.path);
39392
39678
  if (r.valid) {
39393
39679
  if (r.warnings && r.warnings.length > 0) {
39394
39680
  console.log(` ${symbols.warning} ` + chalk38.yellow(relPath));
@@ -39583,7 +39869,7 @@ __export(new_entity_exports, {
39583
39869
  });
39584
39870
  import { randomUUID as randomUUID13 } from "crypto";
39585
39871
  import { readFile as readFile8 } from "fs/promises";
39586
- import path68 from "path";
39872
+ import path69 from "path";
39587
39873
  import chalk40 from "chalk";
39588
39874
  async function resolveBodyInput(opts, command, flag = "--body") {
39589
39875
  if (opts.bodyFile !== void 0) {
@@ -39596,7 +39882,7 @@ async function resolveBodyInput(opts, command, flag = "--body") {
39596
39882
  return null;
39597
39883
  }
39598
39884
  try {
39599
- const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path68.resolve(process.cwd(), opts.bodyFile), "utf8");
39885
+ const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path69.resolve(process.cwd(), opts.bodyFile), "utf8");
39600
39886
  return { body };
39601
39887
  } catch (err) {
39602
39888
  commandError(
@@ -39846,7 +40132,7 @@ __export(set_entity_exports, {
39846
40132
  setEntity: () => setEntity
39847
40133
  });
39848
40134
  import { promises as fs69 } from "fs";
39849
- import path69 from "path";
40135
+ import path70 from "path";
39850
40136
  import chalk41 from "chalk";
39851
40137
  import { ZodError as ZodError2 } from "zod";
39852
40138
  function normalizeAuthStrategy(raw) {
@@ -40037,7 +40323,7 @@ async function setEntity(type2, slug, opts) {
40037
40323
  }
40038
40324
  raw = Buffer.concat(chunks).toString("utf8");
40039
40325
  } else {
40040
- raw = await fs69.readFile(path69.resolve(projectRoot, opts.file), "utf8");
40326
+ raw = await fs69.readFile(path70.resolve(projectRoot, opts.file), "utf8");
40041
40327
  }
40042
40328
  } catch (err) {
40043
40329
  commandError(
@@ -40623,7 +40909,7 @@ __export(edit_entity_exports, {
40623
40909
  });
40624
40910
  import { spawn as spawn2 } from "child_process";
40625
40911
  import { promises as fs70 } from "fs";
40626
- import path70 from "path";
40912
+ import path71 from "path";
40627
40913
  import chalk42 from "chalk";
40628
40914
  async function editEntity(type2, slug, opts = {}) {
40629
40915
  const ctx = await resolveProject(opts);
@@ -40647,7 +40933,7 @@ async function editEntity(type2, slug, opts = {}) {
40647
40933
  }
40648
40934
  const effectiveSlug = isSpecial ? type2 : slug;
40649
40935
  const resolvedPath = isSpecial ? type2 === "testability" ? testabilityPath(projectRoot) : contextPath(projectRoot) : await resolveEntityPath(projectRoot, type2, effectiveSlug);
40650
- const filePath = resolvedPath ?? path70.join(
40936
+ const filePath = resolvedPath ?? path71.join(
40651
40937
  projectRoot,
40652
40938
  "iamu",
40653
40939
  ENTITY_DIRS[type2],
@@ -40658,7 +40944,7 @@ async function editEntity(type2, slug, opts = {}) {
40658
40944
  await fs70.access(filePath);
40659
40945
  } catch {
40660
40946
  commandError(
40661
- `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path70.relative(projectRoot, filePath)}.`,
40947
+ `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path71.relative(projectRoot, filePath)}.`,
40662
40948
  {
40663
40949
  ...opts,
40664
40950
  code: "entity_not_found",
@@ -40669,7 +40955,7 @@ async function editEntity(type2, slug, opts = {}) {
40669
40955
  }
40670
40956
  if (!process.stdin.isTTY || process.env.CI) {
40671
40957
  commandError(
40672
- `Cannot open editor in a non-interactive environment. Edit the file directly: ${path70.relative(projectRoot, filePath)}`,
40958
+ `Cannot open editor in a non-interactive environment. Edit the file directly: ${path71.relative(projectRoot, filePath)}`,
40673
40959
  {
40674
40960
  ...opts,
40675
40961
  code: "non_interactive_environment",
@@ -40745,7 +41031,7 @@ async function editEntity(type2, slug, opts = {}) {
40745
41031
  console.log();
40746
41032
  console.log(
40747
41033
  chalk42.dim(
40748
- ` Fix the file and run: iamu validate ${path70.relative(projectRoot, filePath)}`
41034
+ ` Fix the file and run: iamu validate ${path71.relative(projectRoot, filePath)}`
40749
41035
  )
40750
41036
  );
40751
41037
  console.log();
@@ -40775,7 +41061,7 @@ async function editEntity(type2, slug, opts = {}) {
40775
41061
  {
40776
41062
  ...opts,
40777
41063
  code: "validation_error",
40778
- fix: `iamu validate ${path70.relative(projectRoot, filePath)}`
41064
+ fix: `iamu validate ${path71.relative(projectRoot, filePath)}`
40779
41065
  }
40780
41066
  );
40781
41067
  }
@@ -40944,7 +41230,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40944
41230
  const byKey = /* @__PURE__ */ new Map();
40945
41231
  const byPlaceView = /* @__PURE__ */ new Map();
40946
41232
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
40947
- function addSurface(state, place, view, path77, depth) {
41233
+ function addSurface(state, place, view, path78, depth) {
40948
41234
  const key = exploreSurfaceKey(place, view);
40949
41235
  const surface = {
40950
41236
  key,
@@ -40956,7 +41242,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40956
41242
  headings: state.headings,
40957
41243
  fields: state.fields,
40958
41244
  depth,
40959
- path: path77,
41245
+ path: path78,
40960
41246
  actions: []
40961
41247
  };
40962
41248
  surfaces.push(surface);
@@ -40974,9 +41260,9 @@ ${surface.signature}`, key);
40974
41260
  });
40975
41261
  await settle(page, settleMs);
40976
41262
  }
40977
- async function replay(path77) {
40978
- if (!("goto" in (path77[0] ?? {}))) await goto(opts.startUrl);
40979
- for (const step of path77) {
41263
+ async function replay(path78) {
41264
+ if (!("goto" in (path78[0] ?? {}))) await goto(opts.startUrl);
41265
+ for (const step of path78) {
40980
41266
  if ("goto" in step) {
40981
41267
  await goto(step.goto);
40982
41268
  } else {
@@ -41103,8 +41389,8 @@ ${sig}`, key);
41103
41389
  if (stats.surfaces >= budget.max_surfaces) {
41104
41390
  stats.truncated = true;
41105
41391
  } else {
41106
- const path77 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41107
- addSurface(after, place, view, path77, surface.depth + 1);
41392
+ const path78 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41393
+ addSurface(after, place, view, path78, surface.depth + 1);
41108
41394
  }
41109
41395
  }
41110
41396
  }
@@ -41374,7 +41660,7 @@ __export(explore_exports, {
41374
41660
  exploreList: () => exploreList,
41375
41661
  exploreRun: () => exploreRun
41376
41662
  });
41377
- import path71 from "path";
41663
+ import path72 from "path";
41378
41664
  import chalk43 from "chalk";
41379
41665
  function fail2(message, json2) {
41380
41666
  if (json2) console.error(JSON.stringify({ error: message }));
@@ -41514,7 +41800,7 @@ async function exploreRun(url, opts) {
41514
41800
  surfaces: result.surfaces
41515
41801
  };
41516
41802
  const file = await writeExploreMap(ctx.projectRoot, map2);
41517
- const rel = path71.relative(process.cwd(), file);
41803
+ const rel = path72.relative(process.cwd(), file);
41518
41804
  if (opts.json) {
41519
41805
  console.log(JSON.stringify({ id: map2.id, path: rel, stats: map2.stats }));
41520
41806
  return;
@@ -42710,7 +42996,7 @@ __export(new_finding_exports, {
42710
42996
  });
42711
42997
  import { randomUUID as randomUUID14 } from "crypto";
42712
42998
  import { access, readFile as readFile9 } from "fs/promises";
42713
- import path72 from "path";
42999
+ import path73 from "path";
42714
43000
  import chalk46 from "chalk";
42715
43001
  async function newFinding(opts) {
42716
43002
  const ctx = await resolveProject(opts);
@@ -42798,7 +43084,7 @@ async function newFinding(opts) {
42798
43084
  }
42799
43085
  try {
42800
43086
  await access(
42801
- path72.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
43087
+ path73.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
42802
43088
  );
42803
43089
  } catch {
42804
43090
  commandError(
@@ -42817,7 +43103,7 @@ async function newFinding(opts) {
42817
43103
  if (!scenarioSlug) {
42818
43104
  try {
42819
43105
  const manifestRaw = await readFile9(
42820
- path72.join(runDirPath, "walkthrough.json"),
43106
+ path73.join(runDirPath, "walkthrough.json"),
42821
43107
  "utf8"
42822
43108
  );
42823
43109
  const manifest = JSON.parse(manifestRaw);
@@ -42964,7 +43250,7 @@ __export(summary_exports, {
42964
43250
  summaryCommand: () => summaryCommand
42965
43251
  });
42966
43252
  import { promises as fs71 } from "fs";
42967
- import path73 from "path";
43253
+ import path74 from "path";
42968
43254
  import chalk47 from "chalk";
42969
43255
  function endedFrom(value) {
42970
43256
  if (!value || typeof value !== "object") return void 0;
@@ -42995,7 +43281,7 @@ async function summaryCommand(runIdArg, opts) {
42995
43281
  let openQuestions = [];
42996
43282
  let setupNotes = [];
42997
43283
  let model = opts.model?.trim() || void 0;
42998
- const summaryPath = path73.join(runDirPath, "summary.json");
43284
+ const summaryPath = path74.join(runDirPath, "summary.json");
42999
43285
  let blocked;
43000
43286
  let ended;
43001
43287
  try {
@@ -43015,7 +43301,7 @@ async function summaryCommand(runIdArg, opts) {
43015
43301
  if (opts.file === "-") {
43016
43302
  raw = await readStdinText();
43017
43303
  } else {
43018
- raw = await fs71.readFile(path73.resolve(projectRoot, opts.file), "utf8");
43304
+ raw = await fs71.readFile(path74.resolve(projectRoot, opts.file), "utf8");
43019
43305
  }
43020
43306
  const parsed = JSON.parse(raw);
43021
43307
  if (Array.isArray(parsed.what_worked)) {
@@ -43092,7 +43378,7 @@ async function summaryCommand(runIdArg, opts) {
43092
43378
  JSON.stringify(
43093
43379
  {
43094
43380
  run_id: runId,
43095
- path: path73.relative(projectRoot, summaryPath),
43381
+ path: path74.relative(projectRoot, summaryPath),
43096
43382
  summary: data
43097
43383
  },
43098
43384
  null,
@@ -43154,7 +43440,7 @@ __export(advise_exports, {
43154
43440
  readAdvisePrompt: () => readAdvisePrompt
43155
43441
  });
43156
43442
  import { existsSync as existsSync23, promises as fs72 } from "fs";
43157
- import path74 from "path";
43443
+ import path75 from "path";
43158
43444
  import chalk48 from "chalk";
43159
43445
  function readAdvisePrompt(projectRoot) {
43160
43446
  return readInstalledPrompt(projectRoot, "advise.md");
@@ -43212,7 +43498,7 @@ async function advise(runIdArg, opts = {}) {
43212
43498
  findings2 = input.findings.length;
43213
43499
  needed = findings2 >= 2;
43214
43500
  if (!needed) reason = "fewer than two open findings in the project";
43215
- promptPath = opts.promptFile ?? path74.join(analysisDir(projectRoot), "advise_prompt.md");
43501
+ promptPath = opts.promptFile ?? path75.join(analysisDir(projectRoot), "advise_prompt.md");
43216
43502
  } else {
43217
43503
  const runId = resolveRunId(projectRoot, runIdArg ?? opts.run);
43218
43504
  if (!requireRunId(runId, opts, "iamu advise <run-id>")) return;
@@ -43223,12 +43509,12 @@ async function advise(runIdArg, opts = {}) {
43223
43509
  input = await buildAdviseInput(projectRoot, runId, manifest.scenario_slug);
43224
43510
  refsDir = runDirPath;
43225
43511
  findings2 = input.findings.length;
43226
- const advised = existsSync23(path74.join(runDirPath, ADVICE_FILE));
43512
+ const advised = existsSync23(path75.join(runDirPath, ADVICE_FILE));
43227
43513
  needed = !manifest.blocked && findings2 > 0 && !advised;
43228
43514
  if (manifest.blocked) reason = "the run was blocked";
43229
43515
  else if (advised) reason = "the run is already advised";
43230
43516
  else if (findings2 === 0) reason = "no open findings to advise";
43231
- promptPath = opts.promptFile ?? path74.join(runDirPath, "advise_prompt.md");
43517
+ promptPath = opts.promptFile ?? path75.join(runDirPath, "advise_prompt.md");
43232
43518
  }
43233
43519
  if (needed) input = await withCodeIndex(projectRoot, input);
43234
43520
  const { bundle, refs: refs2 } = promptBundle(input);
@@ -43238,11 +43524,11 @@ async function advise(runIdArg, opts = {}) {
43238
43524
  promptSizeWarning(prompt)
43239
43525
  ].filter((w) => w !== null) : [];
43240
43526
  if (prompt) {
43241
- await fs72.mkdir(path74.dirname(promptPath), { recursive: true });
43527
+ await fs72.mkdir(path75.dirname(promptPath), { recursive: true });
43242
43528
  await fs72.writeFile(promptPath, prompt, "utf8");
43243
43529
  await fs72.mkdir(refsDir, { recursive: true });
43244
43530
  await fs72.writeFile(
43245
- path74.join(refsDir, ADVISE_REFS_FILE),
43531
+ path75.join(refsDir, ADVISE_REFS_FILE),
43246
43532
  `${JSON.stringify(refs2, null, 2)}
43247
43533
  `,
43248
43534
  "utf8"
@@ -43257,8 +43543,8 @@ async function advise(runIdArg, opts = {}) {
43257
43543
  ...reason ? { reason } : {},
43258
43544
  mode: opts.project ? "project" : "run",
43259
43545
  findings: findings2,
43260
- prompt_file: prompt ? path74.relative(projectRoot, promptPath).split(path74.sep).join("/") : null,
43261
- prompt_file_abs: prompt ? path74.resolve(promptPath) : null,
43546
+ prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43547
+ prompt_file_abs: prompt ? path75.resolve(promptPath) : null,
43262
43548
  prompt_chars: prompt?.length ?? 0,
43263
43549
  warnings,
43264
43550
  advise_prompt: prompt
@@ -43334,7 +43620,7 @@ async function adviseRecord(runIdArg, opts) {
43334
43620
  const { projectRoot } = ctx;
43335
43621
  if (opts.project) {
43336
43622
  const input2 = await readAnswerInput(opts, "advice", {
43337
- defaultPath: path74.join(analysisDir(projectRoot), "answer.json"),
43623
+ defaultPath: path75.join(analysisDir(projectRoot), "answer.json"),
43338
43624
  code: "missing_advice_input",
43339
43625
  fix: "iamu advise-record --project --data '<json>'"
43340
43626
  });
@@ -43366,7 +43652,7 @@ async function adviseRecord(runIdArg, opts) {
43366
43652
  if (!manifest) return;
43367
43653
  await logTiming(runDirPath, "advise-record", "start");
43368
43654
  const input = await readAnswerInput(opts, "advice", {
43369
- defaultPath: path74.join(runDir(projectRoot, runId), "advise_answer.json"),
43655
+ defaultPath: path75.join(runDir(projectRoot, runId), "advise_answer.json"),
43370
43656
  code: "missing_advice_input",
43371
43657
  fix: `iamu advise-record ${runId} --data '<json>'`
43372
43658
  });
@@ -43416,7 +43702,7 @@ async function directionsList(opts) {
43416
43702
  problem: d.texts.problem,
43417
43703
  change: d.texts.change,
43418
43704
  leading_practice: d.texts.leading_practice,
43419
- path: path74.relative(ctx.projectRoot, d.path)
43705
+ path: path75.relative(ctx.projectRoot, d.path)
43420
43706
  })),
43421
43707
  null,
43422
43708
  2
@@ -43565,7 +43851,7 @@ __export(designer_exports, {
43565
43851
  designerRecord: () => designerRecord
43566
43852
  });
43567
43853
  import { promises as fs73 } from "fs";
43568
- import path75 from "path";
43854
+ import path76 from "path";
43569
43855
  import chalk49 from "chalk";
43570
43856
  import { ZodError as ZodError3 } from "zod";
43571
43857
  function describeError(err) {
@@ -43585,7 +43871,7 @@ async function designer(runId, opts = {}) {
43585
43871
  const { projectRoot, runDirPath } = ctx;
43586
43872
  if (!await completedManifest2(runDirPath, runId, opts)) return;
43587
43873
  const input = await buildRunDesignerInput(projectRoot, runId);
43588
- const promptPath = path75.join(runDirPath, DESIGNER_PROMPT_FILE);
43874
+ const promptPath = path76.join(runDirPath, DESIGNER_PROMPT_FILE);
43589
43875
  const prompt = input.needed ? composeDesignPrompt(
43590
43876
  projectRoot,
43591
43877
  "designer.md",
@@ -43594,7 +43880,7 @@ async function designer(runId, opts = {}) {
43594
43880
  if (prompt) {
43595
43881
  await fs73.writeFile(promptPath, prompt, "utf8");
43596
43882
  await fs73.writeFile(
43597
- path75.join(runDirPath, DESIGNER_REFS_FILE),
43883
+ path76.join(runDirPath, DESIGNER_REFS_FILE),
43598
43884
  `${JSON.stringify(input.refs, null, 2)}
43599
43885
  `,
43600
43886
  "utf8"
@@ -43605,7 +43891,7 @@ async function designer(runId, opts = {}) {
43605
43891
  installedPromptWarning(projectRoot, "designer.md"),
43606
43892
  ...input.warnings
43607
43893
  ].filter((w) => w !== null) : [];
43608
- const answer = path75.join(runDirPath, DESIGNER_ANSWER_FILE);
43894
+ const answer = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
43609
43895
  const result = {
43610
43896
  needed: input.needed,
43611
43897
  reason: input.reason,
@@ -43621,11 +43907,11 @@ async function designer(runId, opts = {}) {
43621
43907
  title: f.title
43622
43908
  })),
43623
43909
  design_files: (input.bundle.design?.files ?? []).map((f) => f.entry),
43624
- prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43910
+ prompt_file: prompt ? path76.relative(projectRoot, promptPath).split(path76.sep).join("/") : null,
43625
43911
  prompt_file_abs: prompt ? promptPath : null,
43626
43912
  prompt_chars: prompt?.length ?? 0,
43627
43913
  answer_file_abs: prompt ? answer : null,
43628
- next: prompt ? `Give a fresh agent ${path75.relative(projectRoot, promptPath)} as its only context, then record its answer: iamu designer-record ${runId} --data '<answer JSON>' (or --file <path>)` : null,
43914
+ next: prompt ? `Give a fresh agent ${path76.relative(projectRoot, promptPath)} as its only context, then record its answer: iamu designer-record ${runId} --data '<answer JSON>' (or --file <path>)` : null,
43629
43915
  hint: input.judged_against === "general-craft" ? await designDocsHint(projectRoot) : null,
43630
43916
  // No design doc recorded or found, and the template not declined:
43631
43917
  // the run summary offers to start one (iamu-design).
@@ -43671,6 +43957,9 @@ function printDesignerRecord(res) {
43671
43957
  ` ${chalk49.cyan("\u25C6")} Proposed doc edit ${p.outcome}: ${p.slug} (iamu design proposals)`
43672
43958
  );
43673
43959
  }
43960
+ for (const slug of res.verified ?? []) {
43961
+ console.log(` ${chalk49.green("\u2713")} Fix verified: ${slug}`);
43962
+ }
43674
43963
  for (const r of res.rejected) {
43675
43964
  console.log(` ${chalk49.yellow("!")} Dropped ${r.id}: ${r.reason}`);
43676
43965
  }
@@ -43756,7 +44045,7 @@ async function designerRecordLocked(projectRoot, runDirPath, runId, manifest, op
43756
44045
  return;
43757
44046
  }
43758
44047
  const input = await readAnswerInput(opts, "designer's review", {
43759
- defaultPath: path75.join(runDirPath, DESIGNER_ANSWER_FILE),
44048
+ defaultPath: path76.join(runDirPath, DESIGNER_ANSWER_FILE),
43760
44049
  code: "missing_designer_answer",
43761
44050
  fix: `iamu designer-record ${runId} --data '<json>'`
43762
44051
  });
@@ -43888,7 +44177,7 @@ __export(post_walk_exports, {
43888
44177
  postWalk: () => postWalk
43889
44178
  });
43890
44179
  import { existsSync as existsSync24, promises as fs74 } from "fs";
43891
- import path76 from "path";
44180
+ import path77 from "path";
43892
44181
  import chalk50 from "chalk";
43893
44182
  async function captureStageJson(fn) {
43894
44183
  const captured = [];
@@ -43931,7 +44220,7 @@ async function postWalk(runIdArg, opts = {}) {
43931
44220
  }
43932
44221
  const runDirPath = requireRun(projectRoot, runId, opts);
43933
44222
  if (!runDirPath) return;
43934
- const manifestPath = path76.join(runDirPath, "walkthrough.json");
44223
+ const manifestPath = path77.join(runDirPath, "walkthrough.json");
43935
44224
  let manifest;
43936
44225
  try {
43937
44226
  manifest = JSON.parse(
@@ -43972,7 +44261,7 @@ async function postWalk(runIdArg, opts = {}) {
43972
44261
  return;
43973
44262
  }
43974
44263
  const recordedNow = [];
43975
- const matchesAnswerPath = path76.join(runDirPath, MATCH_ANSWER_FILE);
44264
+ const matchesAnswerPath = path77.join(runDirPath, MATCH_ANSWER_FILE);
43976
44265
  if (existsSync24(matchesAnswerPath)) {
43977
44266
  const res = await captureStageJson(
43978
44267
  () => matchRecord(runId, { file: matchesAnswerPath, json: true })
@@ -43983,7 +44272,7 @@ async function postWalk(runIdArg, opts = {}) {
43983
44272
  });
43984
44273
  }
43985
44274
  }
43986
- const judgeAnswerPath = path76.join(runDirPath, "judge_answer.json");
44275
+ const judgeAnswerPath = path77.join(runDirPath, "judge_answer.json");
43987
44276
  if (existsSync24(judgeAnswerPath)) {
43988
44277
  const res = await captureStageJson(
43989
44278
  () => evaluateRecord(runId, { file: judgeAnswerPath, json: true })
@@ -43994,8 +44283,8 @@ async function postWalk(runIdArg, opts = {}) {
43994
44283
  });
43995
44284
  }
43996
44285
  }
43997
- const adviseAnswerPath = path76.join(runDirPath, "advise_answer.json");
43998
- if (!opts.critic && !existsSync24(path76.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
44286
+ const adviseAnswerPath = path77.join(runDirPath, "advise_answer.json");
44287
+ if (!opts.critic && !existsSync24(path77.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
43999
44288
  const res = await captureStageJson(
44000
44289
  () => adviseRecord(runId, { file: adviseAnswerPath, json: true })
44001
44290
  );
@@ -44005,7 +44294,7 @@ async function postWalk(runIdArg, opts = {}) {
44005
44294
  });
44006
44295
  }
44007
44296
  }
44008
- const designerAnswerPath = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
44297
+ const designerAnswerPath = path77.join(runDirPath, DESIGNER_ANSWER_FILE);
44009
44298
  let designerWarnings = [];
44010
44299
  let designerInvalidChecked = [];
44011
44300
  if (!existingDesignerFile(runDirPath)) {
@@ -44037,9 +44326,9 @@ async function postWalk(runIdArg, opts = {}) {
44037
44326
  }
44038
44327
  }
44039
44328
  const matchInfo = await captureStageJson(() => match(runId, { json: true }));
44040
- const evalRecorded = existsSync24(path76.join(runDirPath, "verdicts.json"));
44329
+ const evalRecorded = existsSync24(path77.join(runDirPath, "verdicts.json"));
44041
44330
  const evalInfo = evalRecorded ? null : await captureStageJson(() => evaluate(runId, { json: true }));
44042
- const adviseRecorded = existsSync24(path76.join(runDirPath, ADVICE_FILE));
44331
+ const adviseRecorded = existsSync24(path77.join(runDirPath, ADVICE_FILE));
44043
44332
  const awaitingJudge = !opts.critic && !adviseRecorded && !evalRecorded;
44044
44333
  const adviseInfo = opts.critic || adviseRecorded || awaitingJudge ? null : await captureStageJson(() => advise(runId, { json: true }));
44045
44334
  const designerRecorded = Boolean(existingDesignerFile(runDirPath));
@@ -44538,7 +44827,10 @@ function registerFindingsCommands(program) {
44538
44827
  program.command("triage [findings...]").helpGroup("Findings:").description("Set findings' triage status").requiredOption(
44539
44828
  "--status <status>",
44540
44829
  "open, addressed, dismissed, deferred or wont_fix"
44541
- ).option("--note <text>", "Optional triage note or reason").option(
44830
+ ).option(
44831
+ "--note <text>",
44832
+ "Triage note, any status. For addressed: one line on what changed on screen, so the next run's matcher can confirm the fix"
44833
+ ).option("--note-file <path>", "Read the note from a file (- reads stdin)").option(
44542
44834
  "--reason <reason>",
44543
44835
  "Dismiss reason: not_relevant (default), walker_error, out_of_scope or duplicate"
44544
44836
  ).option(
@@ -44555,7 +44847,7 @@ function registerFindingsCommands(program) {
44555
44847
  "Project-wide sweep ('we never want X'): dismiss all matching open persona & designer findings across every scenario/surface and record a project-wide guidance rule"
44556
44848
  ).option("--no-guidance", "Dismiss without writing a tester guidance rule").option("--json", "Output as JSON").addHelpText(
44557
44849
  "after",
44558
- "\nDismissing as not_relevant, walker_error, or out_of_scope teaches the walker: it writes a\ntester guidance rule (skip it with --no-guidance)."
44850
+ '\nDismissing as not_relevant, walker_error, or out_of_scope teaches the walker: it writes a\ntester guidance rule (skip it with --no-guidance).\n\nExamples:\n $ iamu triage <finding> --status addressed --note "Icons on /orders now carry text labels"\n $ iamu triage <finding> --status dismissed --reason walker_error --note "<why>"'
44559
44851
  ).action(
44560
44852
  async (refs2, opts) => {
44561
44853
  const { triage: triage2 } = await Promise.resolve().then(() => (init_triage(), triage_exports));
@@ -44654,9 +44946,9 @@ function registerProjectCommands(program) {
44654
44946
  "after",
44655
44947
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
44656
44948
  ).action(
44657
- async (path77, opts) => {
44949
+ async (path78, opts) => {
44658
44950
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
44659
- await validate2(path77, opts);
44951
+ await validate2(path78, opts);
44660
44952
  }
44661
44953
  );
44662
44954
  program.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(