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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -6685,10 +6685,13 @@ var init_wire_compat = __esm({
6685
6685
  });
6686
6686
 
6687
6687
  // ../../packages/local-store/dist/sync/server-meta.js
6688
+ function cloudDistinguishesHumanEdits(capabilities) {
6689
+ return Boolean(capabilities?.includes(SYNC_TOKEN_CAPABILITY));
6690
+ }
6688
6691
  function cloudSupportsGuidance(capabilities) {
6689
6692
  return Boolean(capabilities?.includes(GUIDANCE_CAPABILITY) || capabilities?.includes(DESIGNER_FINDINGS_CAPABILITY));
6690
6693
  }
6691
- async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure) {
6694
+ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure, onServerClock) {
6692
6695
  const controller = new AbortController();
6693
6696
  const timer = setTimeout(() => controller.abort(), timeoutMs);
6694
6697
  try {
@@ -6698,6 +6701,9 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6698
6701
  signal: controller.signal
6699
6702
  };
6700
6703
  const res = await fetchFn(`${apiUrl.replace(/\/+$/, "")}/api/meta`, init2);
6704
+ const offset = serverClockOffset(res);
6705
+ if (offset !== null)
6706
+ onServerClock?.(offset);
6701
6707
  if (res.status === 404)
6702
6708
  return [];
6703
6709
  if (!res.ok) {
@@ -6719,6 +6725,16 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6719
6725
  clearTimeout(timer);
6720
6726
  }
6721
6727
  }
6728
+ function serverClockOffset(res) {
6729
+ const headers = res?.headers;
6730
+ if (!headers || typeof headers.get !== "function")
6731
+ return null;
6732
+ const raw = headers.get.call(headers, "date");
6733
+ if (typeof raw !== "string")
6734
+ return null;
6735
+ const at = Date.parse(raw);
6736
+ return Number.isNaN(at) ? null : at - Date.now();
6737
+ }
6722
6738
  function shapeForServer(pending, capabilities) {
6723
6739
  const knows = capabilities.includes(DESIGNER_FINDINGS_CAPABILITY);
6724
6740
  const knowsProvenance = capabilities.includes(FINDING_PROVENANCE_CAPABILITY);
@@ -6740,7 +6756,7 @@ function shapeForServer(pending, capabilities) {
6740
6756
  }
6741
6757
  return { pending: out2, heldDesigner };
6742
6758
  }
6743
- var DESIGNER_FINDINGS_CAPABILITY, GUIDANCE_CAPABILITY, WALKTHROUGH_DELETE_CAPABILITY, FINDING_PROVENANCE_CAPABILITY, BUNDLE_FINDING_HASH_CAPABILITY, RUN_BLOCKED_CAPABILITY;
6759
+ var DESIGNER_FINDINGS_CAPABILITY, GUIDANCE_CAPABILITY, WALKTHROUGH_DELETE_CAPABILITY, FINDING_PROVENANCE_CAPABILITY, BUNDLE_FINDING_HASH_CAPABILITY, RUN_BLOCKED_CAPABILITY, SYNC_TOKEN_CAPABILITY;
6744
6760
  var init_server_meta = __esm({
6745
6761
  "../../packages/local-store/dist/sync/server-meta.js"() {
6746
6762
  "use strict";
@@ -6752,6 +6768,7 @@ var init_server_meta = __esm({
6752
6768
  FINDING_PROVENANCE_CAPABILITY = "finding-provenance";
6753
6769
  BUNDLE_FINDING_HASH_CAPABILITY = "bundle-finding-hash";
6754
6770
  RUN_BLOCKED_CAPABILITY = "run-blocked";
6771
+ SYNC_TOKEN_CAPABILITY = "sync-token";
6755
6772
  }
6756
6773
  });
6757
6774
 
@@ -10688,7 +10705,7 @@ function compareIso(a, b) {
10688
10705
  return ta - tb;
10689
10706
  return String(a ?? "").localeCompare(String(b ?? ""));
10690
10707
  }
10691
- async function designerFindingsOn(projectRoot, screens, before, targetUrl = null) {
10708
+ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null, settled = /* @__PURE__ */ new Set()) {
10692
10709
  const latest = /* @__PURE__ */ new Map();
10693
10710
  const cache = /* @__PURE__ */ new Map();
10694
10711
  const wanted = new Set([...screens].map((k) => normalizeScreenKey(k, targetUrl)));
@@ -10712,7 +10729,7 @@ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null
10712
10729
  }
10713
10730
  const all = [...latest.values()].sort((a, b) => compareIso(a.created_at, b.created_at));
10714
10731
  return {
10715
- open: all.filter(unsettled).map((fm, n) => ({
10732
+ open: all.filter((fm) => unsettled(fm) && !settled.has(fm.id)).map((fm, n) => ({
10716
10733
  ref: `P${n + 1}`,
10717
10734
  id: fm.id,
10718
10735
  title: fm.title ?? fm.slug,
@@ -11205,6 +11222,26 @@ async function readReviewEvidence(projectRoot) {
11205
11222
  }
11206
11223
  return out2;
11207
11224
  }
11225
+ async function verifiedDesignerFixes(projectRoot, before) {
11226
+ const addressed = (await listDesignerFindings(projectRoot)).map((f) => f.frontmatter).filter((fm) => fm.triage_status === "addressed" && !fm.design_cause_of);
11227
+ const out2 = /* @__PURE__ */ new Set();
11228
+ if (addressed.length === 0)
11229
+ return out2;
11230
+ const cutoff = toMillis(before);
11231
+ const runs = (await readRunEvidence(projectRoot)).filter((r) => {
11232
+ if (cutoff === null)
11233
+ return true;
11234
+ const start2 = toMillis(r.started_at);
11235
+ return start2 !== null && start2 < cutoff;
11236
+ });
11237
+ const states = deriveFixStates(addressed, runs);
11238
+ for (const fm of addressed) {
11239
+ if (fm.fix_status === "verified_resolved" || states.get(fm.slug)?.fix_state === "verified") {
11240
+ out2.add(fm.id);
11241
+ }
11242
+ }
11243
+ return out2;
11244
+ }
11208
11245
  async function computeFixStates(projectRoot) {
11209
11246
  const { entities } = await listEntities(projectRoot, "finding");
11210
11247
  const findings2 = entities.map((e) => e.frontmatter);
@@ -12266,6 +12303,7 @@ async function canonicalFindingFiles(projectRoot, scenarioSlug, canonicalId) {
12266
12303
  return out2;
12267
12304
  }
12268
12305
  async function markAddressedResolved(files, note) {
12306
+ const failed = [];
12269
12307
  const trimmedNote = note?.trim();
12270
12308
  for (const f of files) {
12271
12309
  if (f.triage_status !== "addressed")
@@ -12281,9 +12319,14 @@ async function markAddressedResolved(files, note) {
12281
12319
  if (updated !== raw)
12282
12320
  await atomicWriteFile(f.file, updated);
12283
12321
  });
12284
- } catch {
12322
+ } catch (err) {
12323
+ failed.push({
12324
+ file: f.file,
12325
+ error: err instanceof Error ? err.message : String(err)
12326
+ });
12285
12327
  }
12286
12328
  }
12329
+ return failed;
12287
12330
  }
12288
12331
  function originOf(url) {
12289
12332
  if (!url)
@@ -12466,7 +12509,9 @@ async function updateFixStatus(projectRoot, slug, status2) {
12466
12509
  await atomicWriteFile(filePath, updated);
12467
12510
  }
12468
12511
  });
12469
- } catch {
12512
+ return null;
12513
+ } catch (err) {
12514
+ return err instanceof Error ? err.message : String(err);
12470
12515
  }
12471
12516
  }
12472
12517
  async function listRunsInternal(projectRoot) {
@@ -12916,6 +12961,7 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12916
12961
  body: form,
12917
12962
  signal: controller.signal
12918
12963
  });
12964
+ const clockOffset = serverClockOffset(response);
12919
12965
  if (response.ok) {
12920
12966
  let body = null;
12921
12967
  try {
@@ -12961,7 +13007,8 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12961
13007
  runCloudId: parsed.data.cloud_walkthrough_id,
12962
13008
  contentHash,
12963
13009
  cloudOutcome: parsed.data.status,
12964
- ...recordedFindings.length > 0 ? { recordedFindings } : {}
13010
+ ...recordedFindings.length > 0 ? { recordedFindings } : {},
13011
+ ...clockOffset !== null ? { serverClockOffsetMs: clockOffset } : {}
12965
13012
  };
12966
13013
  }
12967
13014
  if (response.status === 409) {
@@ -13608,6 +13655,7 @@ async function drainRetryQueue(projectRoot, deps) {
13608
13655
  const deleteUrl = projectEndpoint(config.cloud_api_url, config.cloud_project_id, "delete");
13609
13656
  const remaining = [];
13610
13657
  const failures = [];
13658
+ const pushedRuns = [];
13611
13659
  let drained = 0;
13612
13660
  let dropped = 0;
13613
13661
  let notAttempted = 0;
@@ -13719,6 +13767,8 @@ async function drainRetryQueue(projectRoot, deps) {
13719
13767
  });
13720
13768
  if (result.mirrored || result.skippedReason === "bundle_too_large") {
13721
13769
  drained++;
13770
+ if (result.status === "pushed")
13771
+ pushedRuns.push(entry.entity_id);
13722
13772
  } else if (result.skippedReason === "missing_run" || result.skippedReason === "unsupported") {
13723
13773
  drained++;
13724
13774
  } else if (result.skippedReason === "cloud_conflict") {
@@ -13810,7 +13860,8 @@ async function drainRetryQueue(projectRoot, deps) {
13810
13860
  dropped,
13811
13861
  remaining: after.length,
13812
13862
  failures,
13813
- warnings
13863
+ warnings,
13864
+ ...pushedRuns.length > 0 ? { pushedRuns } : {}
13814
13865
  };
13815
13866
  }
13816
13867
  function unreadableBody(text, isDelete) {
@@ -14234,6 +14285,18 @@ async function pushOneBatch(target, batch, byType, label, opts = {}) {
14234
14285
  out2.errors.push(failure.message);
14235
14286
  return out2;
14236
14287
  }
14288
+ function cloudTokenOrigin(hash, humanTokens) {
14289
+ if (hash.startsWith("cloud-sync-"))
14290
+ return "projection";
14291
+ if (hash.startsWith("cloud-"))
14292
+ return humanTokens ? "person" : "unknown";
14293
+ if (hash.startsWith("cloud:"))
14294
+ return "unknown";
14295
+ return null;
14296
+ }
14297
+ function isCloudToken(hash) {
14298
+ return cloudTokenOrigin(hash, false) !== null;
14299
+ }
14237
14300
  async function ownEarlierWrites(projectRoot, batch, conflicts) {
14238
14301
  if (conflicts.length === 0)
14239
14302
  return [];
@@ -14247,7 +14310,7 @@ async function ownEarlierWrites(projectRoot, batch, conflicts) {
14247
14310
  const entity = byId.get(c.entity_id);
14248
14311
  if (!entity || c.base_hash || bases[c.entity_id])
14249
14312
  continue;
14250
- if (!c.cloud_hash || c.cloud_hash.startsWith("cloud:"))
14313
+ if (!c.cloud_hash || isCloudToken(c.cloud_hash))
14251
14314
  continue;
14252
14315
  if (!sent[c.entity_id]?.includes(c.cloud_hash))
14253
14316
  continue;
@@ -14256,10 +14319,59 @@ async function ownEarlierWrites(projectRoot, batch, conflicts) {
14256
14319
  }
14257
14320
  return out2;
14258
14321
  }
14322
+ function findingRuns(frontmatter) {
14323
+ const out2 = /* @__PURE__ */ new Set();
14324
+ if (typeof frontmatter.run === "string" && frontmatter.run) {
14325
+ out2.add(frontmatter.run);
14326
+ }
14327
+ const seen = frontmatter.seen_in;
14328
+ if (typeof seen === "string") {
14329
+ const m = /^(?:ux|designer):(.+)$/.exec(seen);
14330
+ if (m?.[1])
14331
+ out2.add(m[1]);
14332
+ }
14333
+ return out2;
14334
+ }
14335
+ async function rowsToAdopt(projectRoot, batch, conflicts, adopt) {
14336
+ if (!adopt || conflicts.length === 0)
14337
+ return [];
14338
+ const bases = await readBaseHashes(projectRoot);
14339
+ const byId = new Map(batch.map((e) => [e.id, e]));
14340
+ const out2 = [];
14341
+ for (const c of conflicts) {
14342
+ const entity = byId.get(c.entity_id);
14343
+ if (!entity || entity.type !== "finding" || !c.cloud_hash)
14344
+ continue;
14345
+ const origin = cloudTokenOrigin(c.cloud_hash, adopt.humanTokens);
14346
+ if (origin === null || origin === "person")
14347
+ continue;
14348
+ if (origin === "unknown" && !stampedByThisPass(c, entity, adopt)) {
14349
+ continue;
14350
+ }
14351
+ await recordCloudBase(projectRoot, c.entity_id, c.cloud_hash);
14352
+ out2.push(origin === "projection" ? { entity, restamped: false, kind: "projection" } : {
14353
+ entity,
14354
+ restamped: Boolean(c.base_hash || bases[c.entity_id]),
14355
+ kind: "bundle"
14356
+ });
14357
+ }
14358
+ return out2;
14359
+ }
14360
+ function stampedByThisPass(c, entity, adopt) {
14361
+ const runs = findingRuns(entity.frontmatter);
14362
+ if (![...runs].some((r) => adopt.runs.has(r)))
14363
+ return false;
14364
+ const at = c.cloud_updated_at ? Date.parse(c.cloud_updated_at) : Number.NaN;
14365
+ const slack = adopt.serverClock ? ADOPT_CLOCK_SLACK_MS : 0;
14366
+ if (Number.isNaN(at) || at < adopt.since - slack)
14367
+ return false;
14368
+ return !adopt.identity || c.cloud_changed_by === adopt.identity;
14369
+ }
14259
14370
  async function pushBatches(target, batches, byType, opts = {}) {
14260
14371
  let pushed = 0;
14261
14372
  let enqueued = 0;
14262
14373
  const selfResolved = [];
14374
+ const adopted = [];
14263
14375
  const conflicts = [];
14264
14376
  const overwritten = [];
14265
14377
  const errors = [];
@@ -14281,19 +14393,42 @@ async function pushBatches(target, batches, byType, opts = {}) {
14281
14393
  conflicts,
14282
14394
  overwritten,
14283
14395
  errors,
14284
- selfResolved
14396
+ selfResolved,
14397
+ adopted
14285
14398
  };
14286
14399
  }
14287
14400
  absorb(first);
14288
14401
  let unresolved = first.conflicts;
14289
14402
  const own = await ownEarlierWrites(target.projectRoot, batch, unresolved);
14290
- if (own.length > 0) {
14291
- const ownIds = new Set(own.map((e) => e.id));
14292
- unresolved = unresolved.filter((c) => !ownIds.has(c.entity_id));
14293
- const again = await pushOneBatch(target, own, byType, `batch ${i} own-version retry`);
14403
+ const ownIds = new Set(own.map((e) => e.id));
14404
+ const basesBefore = opts.adopt ? await readCloudBase(target.projectRoot) : {};
14405
+ const fromBundles = await rowsToAdopt(target.projectRoot, batch, unresolved.filter((c) => !ownIds.has(c.entity_id)), opts.adopt);
14406
+ const resend = [...own, ...fromBundles.map((a) => a.entity)];
14407
+ if (resend.length > 0) {
14408
+ const resendIds = new Set(resend.map((e) => e.id));
14409
+ unresolved = unresolved.filter((c) => !resendIds.has(c.entity_id));
14410
+ const again = await pushOneBatch(target, resend, byType, `batch ${i} own-version retry`);
14294
14411
  absorb(again);
14295
14412
  conflicts.push(...again.conflicts);
14296
- selfResolved.push(...again.landed);
14413
+ const landed2 = new Set(again.landed);
14414
+ selfResolved.push(...own.filter((e) => landed2.has(e.id)).map((e) => e.id));
14415
+ for (const a of fromBundles) {
14416
+ if (landed2.has(a.entity.id)) {
14417
+ adopted.push({
14418
+ slug: a.entity.slug,
14419
+ kind: a.kind,
14420
+ restamped: a.restamped,
14421
+ serverClock: opts.adopt?.serverClock ?? false
14422
+ });
14423
+ continue;
14424
+ }
14425
+ const prior = basesBefore[a.entity.id];
14426
+ if (prior === void 0) {
14427
+ await forgetCloudBase(target.projectRoot, a.entity.id);
14428
+ } else {
14429
+ await recordCloudBase(target.projectRoot, a.entity.id, prior);
14430
+ }
14431
+ }
14297
14432
  }
14298
14433
  if (!opts.keepMine || unresolved.length === 0) {
14299
14434
  conflicts.push(...unresolved);
@@ -14346,8 +14481,17 @@ async function pushBatches(target, batches, byType, opts = {}) {
14346
14481
  }
14347
14482
  await forgetQueuedUpserts(target.projectRoot, revoked);
14348
14483
  }
14349
- return { pushed, enqueued, conflicts, overwritten, errors, selfResolved };
14484
+ return {
14485
+ pushed,
14486
+ enqueued,
14487
+ conflicts,
14488
+ overwritten,
14489
+ errors,
14490
+ selfResolved,
14491
+ adopted
14492
+ };
14350
14493
  }
14494
+ var ADOPT_CLOCK_SLACK_MS;
14351
14495
  var init_push = __esm({
14352
14496
  "../../packages/local-store/dist/sync/push.js"() {
14353
14497
  "use strict";
@@ -14355,6 +14499,7 @@ var init_push = __esm({
14355
14499
  init_server_reason();
14356
14500
  init_sync_tracking();
14357
14501
  init_types();
14502
+ ADOPT_CLOCK_SLACK_MS = 5e3;
14358
14503
  }
14359
14504
  });
14360
14505
 
@@ -14749,15 +14894,33 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14749
14894
  const hold = deps.capabilities === null;
14750
14895
  let held = 0;
14751
14896
  const recordedFindings = /* @__PURE__ */ new Set();
14897
+ const pushedRuns = /* @__PURE__ */ new Set();
14898
+ let serverClockOffsetMs = null;
14752
14899
  let runIds;
14753
14900
  try {
14754
14901
  runIds = await listRuns(projectRoot);
14755
14902
  } catch (err) {
14756
14903
  errors.push(`walkthrough scan failed: ${err instanceof Error ? err.message : String(err)} \u2014 check that iamu/.runs/ exists and is readable`);
14757
- return { summary, errors, withheldRuns, held, recordedFindings };
14904
+ return {
14905
+ summary,
14906
+ errors,
14907
+ withheldRuns,
14908
+ held,
14909
+ recordedFindings,
14910
+ pushedRuns,
14911
+ serverClockOffsetMs
14912
+ };
14758
14913
  }
14759
14914
  if (runIds.length === 0)
14760
- return { summary, errors, withheldRuns, held, recordedFindings };
14915
+ return {
14916
+ summary,
14917
+ errors,
14918
+ withheldRuns,
14919
+ held,
14920
+ recordedFindings,
14921
+ pushedRuns,
14922
+ serverClockOffsetMs
14923
+ };
14761
14924
  const lastPush = await readLastPush(projectRoot);
14762
14925
  const pendingWalkthroughQueueEntries = /* @__PURE__ */ new Set();
14763
14926
  const retryQueue = await readRetryQueue(projectRoot);
@@ -14824,6 +14987,9 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14824
14987
  });
14825
14988
  if (result.status === "pushed" || result.status === "already_pushed") {
14826
14989
  summary.pushed++;
14990
+ if (result.status === "pushed")
14991
+ pushedRuns.add(runId);
14992
+ serverClockOffsetMs ??= result.serverClockOffsetMs ?? null;
14827
14993
  for (const id of result.recordedFindings ?? [])
14828
14994
  recordedFindings.add(id);
14829
14995
  } else if (result.skippedReason === "bundle_too_large" || result.skippedReason === "unsupported" || result.skippedReason === "missing_run" || result.skippedReason === "cloud_conflict") {
@@ -14846,7 +15012,15 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14846
15012
  }
14847
15013
  }
14848
15014
  }
14849
- return { summary, errors, withheldRuns, held, recordedFindings };
15015
+ return {
15016
+ summary,
15017
+ errors,
15018
+ withheldRuns,
15019
+ held,
15020
+ recordedFindings,
15021
+ pushedRuns,
15022
+ serverClockOffsetMs
15023
+ };
14850
15024
  }
14851
15025
  var HOLD_SHAPES, WALKTHROUGH_SKIP_HINTS;
14852
15026
  var init_walkthroughs = __esm({
@@ -14896,6 +15070,7 @@ function mergePushProgress(target, outcome) {
14896
15070
  if (!outcome)
14897
15071
  return;
14898
15072
  target.selfResolved.push(...outcome.selfResolved ?? []);
15073
+ target.adopted.push(...outcome.adopted ?? []);
14899
15074
  target.pushed += outcome.pushed;
14900
15075
  target.enqueued += outcome.enqueued;
14901
15076
  target.conflicts.push(...outcome.conflicts);
@@ -14942,6 +15117,7 @@ async function pushDeferredFindings(projectRoot, capabilities, deferredFindings,
14942
15117
  return pushBatches(target, toBatches(retryFindings, batchSize), byType, pushOpts);
14943
15118
  }
14944
15119
  async function runSync(projectRoot, deps) {
15120
+ const startedAt = Date.now();
14945
15121
  const config = await readConfig(projectRoot);
14946
15122
  if (!config.cloud_linked) {
14947
15123
  return emptySyncResult([
@@ -14956,11 +15132,21 @@ async function runSync(projectRoot, deps) {
14956
15132
  const apiKey = deps.apiKey;
14957
15133
  const fetchFn = deps.fetch ?? globalThis.fetch;
14958
15134
  let capabilitiesFailure = "";
15135
+ let serverClockOffsetMs = null;
14959
15136
  const capabilities = deps.capabilities !== void 0 ? deps.capabilities : await fetchServerCapabilities(config.cloud_api_url, apiKey, fetchFn, deps.timeoutMs ?? DEFAULT_TIMEOUT_MS4, (detail) => {
14960
15137
  capabilitiesFailure = detail;
15138
+ }, (offset) => {
15139
+ serverClockOffsetMs = offset;
14961
15140
  });
14962
15141
  deps = { ...deps, capabilities };
14963
- const drain = await drainRetryQueue(projectRoot, deps);
15142
+ const { pushedRuns: drainedRuns = [], ...drain } = await drainRetryQueue(projectRoot, deps);
15143
+ const adoptFrom = (runs) => ({
15144
+ runs: /* @__PURE__ */ new Set([...drainedRuns, ...runs]),
15145
+ since: startedAt + (serverClockOffsetMs ?? 0),
15146
+ serverClock: serverClockOffsetMs !== null,
15147
+ humanTokens: cloudDistinguishesHumanEdits(capabilities),
15148
+ identity: deps.identity ?? null
15149
+ });
14964
15150
  const scanned = await scanPending(projectRoot);
14965
15151
  if (scanned.errors.length === 0) {
14966
15152
  await pruneSentHashes(projectRoot, scanned.seenIds);
@@ -14984,14 +15170,15 @@ async function runSync(projectRoot, deps) {
14984
15170
  const pushOpts = { keepMine: deps.keepMine };
14985
15171
  const guidance = scan.pending.filter((e) => e.type === "guidance");
14986
15172
  const nonGuidance = scan.pending.filter((e) => e.type !== "guidance");
14987
- const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, pushOpts);
15173
+ const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, { ...pushOpts, adopt: adoptFrom([]) });
14988
15174
  const progress = {
14989
15175
  pushed: phase1.pushed,
14990
15176
  enqueued: phase1.enqueued,
14991
15177
  conflicts: [...phase1.conflicts],
14992
15178
  overwritten: [...phase1.overwritten],
14993
15179
  errors,
14994
- selfResolved: [...phase1.selfResolved]
15180
+ selfResolved: [...phase1.selfResolved],
15181
+ adopted: [...phase1.adopted]
14995
15182
  };
14996
15183
  progress.errors.push(...phase1.errors);
14997
15184
  const notices = [];
@@ -15001,10 +15188,11 @@ async function runSync(projectRoot, deps) {
15001
15188
  const guidanceResult = await pushGuidanceBatch(target, guidance, batchSize, byType, { ...pushOpts, detectUnsupported: !cloudSupportsGuidance(capabilities) }, notices);
15002
15189
  mergePushProgress(progress, guidanceResult);
15003
15190
  const walkthroughs = await syncWalkthroughs(projectRoot, deps, scan.unpushableScenarios);
15191
+ serverClockOffsetMs ??= walkthroughs.serverClockOffsetMs;
15004
15192
  if (walkthroughs.errors.length > 0) {
15005
15193
  progress.errors.push(...walkthroughs.errors);
15006
15194
  }
15007
- const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, pushOpts, reportedRefErrors, progress.errors);
15195
+ const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, { ...pushOpts, adopt: adoptFrom(walkthroughs.pushedRuns) }, reportedRefErrors, progress.errors);
15008
15196
  mergePushProgress(progress, phase3);
15009
15197
  if (capabilities === null) {
15010
15198
  const heldFindings = shaped.heldFindings + deferredFindings.size;
@@ -15025,6 +15213,15 @@ async function runSync(projectRoot, deps) {
15025
15213
  const deletion = await syncDeletions(projectRoot, config, deps);
15026
15214
  progress.errors.push(...deletion.errors);
15027
15215
  notices.push(...deletion.notices);
15216
+ for (const a of progress.adopted) {
15217
+ if (a.kind === "projection") {
15218
+ notices.push(`${a.slug}: kept the local version over the cloud's projected triage (automated, from a run's outcome)`);
15219
+ continue;
15220
+ }
15221
+ 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)";
15222
+ const clock = a.serverClock ? "" : "; the server sent no clock, so only rows stamped after this sync began by this machine's clock counted";
15223
+ notices.push(`${a.slug}: ${what}${clock}`);
15224
+ }
15028
15225
  if (progress.selfResolved.length > 0) {
15029
15226
  const n = progress.selfResolved.length;
15030
15227
  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`);
@@ -19274,7 +19471,14 @@ async function buildRunDesignerInput(projectRoot, runId) {
19274
19471
  await chooseKeyFrames(rd, grouped.candidates);
19275
19472
  const screens = grouped.screens;
19276
19473
  const screenKeys = new Set(screens.map((s) => s.key));
19277
- const earlier = await designerFindingsOn(projectRoot, screenKeys, manifest?.created_at ?? null, target);
19474
+ const earlier = await designerFindingsOn(
19475
+ projectRoot,
19476
+ screenKeys,
19477
+ manifest?.created_at ?? null,
19478
+ target,
19479
+ // A fix a designer's review already verified is not asked about again.
19480
+ await verifiedDesignerFixes(projectRoot, manifest?.created_at ?? null)
19481
+ );
19278
19482
  for (const o of earlier.open)
19279
19483
  refs2[o.ref] = o.id;
19280
19484
  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));
@@ -19430,6 +19634,7 @@ var init_designer_input = __esm({
19430
19634
  init_designer_files();
19431
19635
  init_designer_ignored();
19432
19636
  init_designer_ledger();
19637
+ init_fix_state();
19433
19638
  init_walkthrough_canonical2();
19434
19639
  init_dist();
19435
19640
  LONG_VISIT = 6;
@@ -22382,6 +22587,69 @@ function stepNumberOf(s) {
22382
22587
  const m = /^(?:step-)?(\d+)$/i.exec(s.trim());
22383
22588
  return m ? Number(m[1]) : null;
22384
22589
  }
22590
+ async function writeDesignerFixStatus(projectRoot, runId, verification, checks) {
22591
+ const verified = [];
22592
+ const untriaged = [];
22593
+ const warnings = [];
22594
+ const out2 = { verified, untriaged, warnings };
22595
+ const resolved = new Set(verification.resolved);
22596
+ const persists = new Set(verification.persists);
22597
+ if (resolved.size + persists.size === 0)
22598
+ return out2;
22599
+ const notWritten = (what, err) => warnings.push(`could not update ${what}: ${err instanceof Error ? err.message : String(err)}; designer.json holds the verification`);
22600
+ let files;
22601
+ try {
22602
+ 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)));
22603
+ } catch (err) {
22604
+ notWritten("the checked findings' fix_status", err);
22605
+ return out2;
22606
+ }
22607
+ const checkOf = new Map(checks.map((c) => [c.canonical_id, c]));
22608
+ for (const cid of verification.resolved) {
22609
+ const mine = files.filter((fm) => fm.canonical_id === cid);
22610
+ if (mine.some((fm) => fm.triage_status === "addressed"))
22611
+ continue;
22612
+ const latest = mine.filter((fm) => fm.triage_status !== "dismissed").sort((a, b) => String(a.created_at ?? "").localeCompare(String(b.created_at ?? ""))).at(-1);
22613
+ if (latest) {
22614
+ untriaged.push({
22615
+ ref: checkOf.get(cid)?.ref ?? null,
22616
+ finding: latest.slug
22617
+ });
22618
+ }
22619
+ }
22620
+ let states;
22621
+ try {
22622
+ states = deriveFixStates(files, await readRunEvidence(projectRoot));
22623
+ } catch (err) {
22624
+ notWritten("the checked findings' fix_status", err);
22625
+ return out2;
22626
+ }
22627
+ const decidedHere = (slug, state) => {
22628
+ const s = states.get(slug);
22629
+ return s?.fix_state === state && s.decided_by === runId;
22630
+ };
22631
+ for (const fm of files) {
22632
+ if (fm.triage_status !== "addressed" || !fm.canonical_id)
22633
+ continue;
22634
+ if (resolved.has(fm.canonical_id) && decidedHere(fm.slug, "verified")) {
22635
+ const [failed] = await markAddressedResolved([
22636
+ {
22637
+ file: entityFilePath(projectRoot, "finding", fm.slug),
22638
+ triage_status: fm.triage_status
22639
+ }
22640
+ ], checkOf.get(fm.canonical_id)?.note ?? void 0);
22641
+ if (failed)
22642
+ notWritten(fm.slug, failed.error);
22643
+ else
22644
+ verified.push(fm.slug);
22645
+ } else if (persists.has(fm.canonical_id) && decidedHere(fm.slug, "recurred")) {
22646
+ const error = await updateFixStatus(projectRoot, fm.slug, "verified_persists");
22647
+ if (error)
22648
+ notWritten(fm.slug, error);
22649
+ }
22650
+ }
22651
+ return out2;
22652
+ }
22385
22653
  async function readRefs2(rd) {
22386
22654
  try {
22387
22655
  return JSON.parse(await fs49.readFile(designerRefsFile(rd), "utf8"));
@@ -22702,6 +22970,11 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22702
22970
  };
22703
22971
  await atomicWriteFile(file, `${JSON.stringify(record, null, 2)}
22704
22972
  `);
22973
+ const fixes = await writeDesignerFixStatus(projectRoot, runId, verification, checks);
22974
+ warnings.push(...fixes.warnings);
22975
+ for (const u of fixes.untriaged) {
22976
+ warnings.push(`checked ${u.ref ?? "?"}: ${u.finding} is gone but was never marked addressed; its fix_status is left alone (checked_but_untriaged)`);
22977
+ }
22705
22978
  const bySeverity = {};
22706
22979
  const ruleCount = groupDesignerRules(rules);
22707
22980
  for (const i of issues)
@@ -22715,7 +22988,9 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22715
22988
  proposals: proposals.written,
22716
22989
  rejected,
22717
22990
  warnings,
22718
- ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {}
22991
+ ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {},
22992
+ ...fixes.verified.length > 0 ? { verified: fixes.verified } : {},
22993
+ ...fixes.untriaged.length > 0 ? { checked_but_untriaged: fixes.untriaged } : {}
22719
22994
  };
22720
22995
  }
22721
22996
  var DESIGNER_CATEGORIES, steps, refs, designerAnswerSchema;
@@ -22725,13 +23000,14 @@ var init_designer_record = __esm({
22725
23000
  init_atomic();
22726
23001
  init_design_proposals();
22727
23002
  init_design_review();
22728
- init_paths();
22729
23003
  init_url_path();
22730
23004
  init_review_issues();
22731
23005
  init_designer_files();
22732
23006
  init_designer_input();
22733
23007
  init_designer_ledger();
22734
23008
  init_entity();
23009
+ init_fix_state();
23010
+ init_paths();
22735
23011
  init_walkthrough_canonical2();
22736
23012
  DESIGNER_CATEGORIES = [
22737
23013
  "hierarchy",
@@ -23774,15 +24050,15 @@ function resolveApiUrl(configured) {
23774
24050
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
23775
24051
  return url;
23776
24052
  }
23777
- function parseStore(raw, path77) {
24053
+ function parseStore(raw, path78) {
23778
24054
  let parsed;
23779
24055
  try {
23780
24056
  parsed = JSON.parse(raw);
23781
24057
  } catch (err) {
23782
- throw new CredentialsFileError(path77, "not valid JSON", err);
24058
+ throw new CredentialsFileError(path78, "not valid JSON", err);
23783
24059
  }
23784
24060
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
23785
- throw new CredentialsFileError(path77, "expected a JSON object");
24061
+ throw new CredentialsFileError(path78, "expected a JSON object");
23786
24062
  }
23787
24063
  return parsed;
23788
24064
  }
@@ -23790,26 +24066,26 @@ function isNotFound(err) {
23790
24066
  return typeof err === "object" && err !== null && err.code === "ENOENT";
23791
24067
  }
23792
24068
  async function readCredentialsStore() {
23793
- const path77 = getCredentialsPath();
24069
+ const path78 = getCredentialsPath();
23794
24070
  let raw;
23795
24071
  try {
23796
- raw = await readFile(path77, "utf-8");
24072
+ raw = await readFile(path78, "utf-8");
23797
24073
  } catch (err) {
23798
24074
  if (isNotFound(err)) return {};
23799
24075
  warnUnreadableStoreOnce(err);
23800
24076
  throw err;
23801
24077
  }
23802
24078
  try {
23803
- return parseStore(raw, path77);
24079
+ return parseStore(raw, path78);
23804
24080
  } catch (err) {
23805
24081
  warnUnreadableStoreOnce(err);
23806
24082
  throw err;
23807
24083
  }
23808
24084
  }
23809
24085
  async function writeCredentialsStore(store) {
23810
- const path77 = getCredentialsPath();
23811
- await mkdir(dirname(path77), { recursive: true, mode: 448 });
23812
- await atomicWriteFile(path77, `${JSON.stringify(store, null, 2)}
24086
+ const path78 = getCredentialsPath();
24087
+ await mkdir(dirname(path78), { recursive: true, mode: 448 });
24088
+ await atomicWriteFile(path78, `${JSON.stringify(store, null, 2)}
23813
24089
  `, {
23814
24090
  mode: 384
23815
24091
  });
@@ -23837,16 +24113,16 @@ function resolveApiKey(apiUrl) {
23837
24113
  }
23838
24114
  }
23839
24115
  const normalized = url.replace(/\/+$/, "");
23840
- for (const path77 of candidatePaths) {
24116
+ for (const path78 of candidatePaths) {
23841
24117
  let raw;
23842
24118
  try {
23843
- raw = readFileSync3(path77, "utf-8");
24119
+ raw = readFileSync3(path78, "utf-8");
23844
24120
  } catch (err) {
23845
24121
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
23846
24122
  continue;
23847
24123
  }
23848
24124
  try {
23849
- const store = parseStore(raw, path77);
24125
+ const store = parseStore(raw, path78);
23850
24126
  const entry = store[url] ?? store[normalized] ?? store[`${normalized}/`];
23851
24127
  if (typeof entry !== "object" || entry === null) continue;
23852
24128
  const token = entry.token;
@@ -23887,12 +24163,12 @@ var init_credentials = __esm({
23887
24163
  * @param reason - What is wrong with it.
23888
24164
  * @param cause - Underlying parse error, if any.
23889
24165
  */
23890
- constructor(path77, reason, cause) {
24166
+ constructor(path78, reason, cause) {
23891
24167
  super(
23892
- `Credentials file ${path77} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
24168
+ `Credentials file ${path78} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
23893
24169
  {
23894
24170
  cause,
23895
- suggestedAction: `Delete ${path77} and run \`iamu auth login\`, or set IAMU_TOKEN.`
24171
+ suggestedAction: `Delete ${path78} and run \`iamu auth login\`, or set IAMU_TOKEN.`
23896
24172
  }
23897
24173
  );
23898
24174
  }
@@ -24832,8 +25108,8 @@ import { dirname as dirname3, join as join5, relative, resolve as resolve2 } fro
24832
25108
  import { fileURLToPath as fileURLToPath2 } from "url";
24833
25109
  import chalk6 from "chalk";
24834
25110
  function getVersion() {
24835
- if ("0.8.0-beta.5") {
24836
- return "0.8.0-beta.5";
25111
+ if ("0.8.0-beta.7") {
25112
+ return "0.8.0-beta.7";
24837
25113
  }
24838
25114
  try {
24839
25115
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -24956,10 +25232,10 @@ function hasGeneratedByMarker(content) {
24956
25232
  function withoutMarker(content) {
24957
25233
  return content.replace(/^generatedBy:[^\r\n]*\r?\n/m, "");
24958
25234
  }
24959
- function cliSkillState(path77, template) {
25235
+ function cliSkillState(path78, template) {
24960
25236
  let content;
24961
25237
  try {
24962
- content = readFileSync7(path77, "utf-8");
25238
+ content = readFileSync7(path78, "utf-8");
24963
25239
  } catch {
24964
25240
  return null;
24965
25241
  }
@@ -24967,9 +25243,9 @@ function cliSkillState(path77, template) {
24967
25243
  if (template === void 0) return "edited";
24968
25244
  return withoutMarker(content) === withoutMarker(template) ? "clean" : "edited";
24969
25245
  }
24970
- function isCliWrittenPrompt(path77) {
25246
+ function isCliWrittenPrompt(path78) {
24971
25247
  try {
24972
- const content = readFileSync7(path77, "utf-8");
25248
+ const content = readFileSync7(path78, "utf-8");
24973
25249
  return hasGeneratedByMarker(content) || /^#\s+iamu:/m.test(content) || /^description:\s*.*iamu/im.test(content);
24974
25250
  } catch {
24975
25251
  return false;
@@ -24979,10 +25255,10 @@ function planSkillFiles(hosts, projectRoot, version2) {
24979
25255
  const skillTemplates = loadSkillTemplates(version2);
24980
25256
  const files = [];
24981
25257
  const keptEdited = [];
24982
- const removeIfClean = (host, path77, name) => {
24983
- const state = cliSkillState(path77, skillTemplates.get(name));
24984
- if (state === "clean") files.push({ host, path: path77, content: null });
24985
- else if (state === "edited") keptEdited.push({ host, path: path77 });
25258
+ const removeIfClean = (host, path78, name) => {
25259
+ const state = cliSkillState(path78, skillTemplates.get(name));
25260
+ if (state === "clean") files.push({ host, path: path78, content: null });
25261
+ else if (state === "edited") keptEdited.push({ host, path: path78 });
24986
25262
  };
24987
25263
  for (const host of hosts) {
24988
25264
  if (pluginProvidesSkills(host, projectRoot)) {
@@ -25079,9 +25355,9 @@ async function findStaleSkills(projectRoot) {
25079
25355
  }
25080
25356
  return stale;
25081
25357
  }
25082
- async function removeSkillFile(path77) {
25083
- await rm(path77, { force: true });
25084
- await rmdir(dirname3(path77)).catch(() => {
25358
+ async function removeSkillFile(path78) {
25359
+ await rm(path78, { force: true });
25360
+ await rmdir(dirname3(path78)).catch(() => {
25085
25361
  });
25086
25362
  }
25087
25363
  async function installSkillsForHosts(hosts, projectRoot, version2, opts) {
@@ -25883,29 +26159,29 @@ async function writeSyncMcpEntry(mcpPath) {
25883
26159
  return { removedLegacy };
25884
26160
  }
25885
26161
  async function registerSyncMcpForHost(host, projectRoot) {
25886
- const path77 = host.getMcpConfigPath(projectRoot);
25887
- if (path77 === null) {
26162
+ const path78 = host.getMcpConfigPath(projectRoot);
26163
+ if (path78 === null) {
25888
26164
  return {
25889
26165
  status: "manual",
25890
26166
  instruction: host.manualMcpSetup ?? `add an "iamu-sync" server that runs \`${SYNC_SERVER_COMMAND.join(" ")}\``
25891
26167
  };
25892
26168
  }
25893
26169
  try {
25894
- const { removedLegacy } = await writeSyncMcpEntry(path77);
25895
- return { status: "written", path: path77, removedLegacy };
26170
+ const { removedLegacy } = await writeSyncMcpEntry(path78);
26171
+ return { status: "written", path: path78, removedLegacy };
25896
26172
  } catch (err) {
25897
26173
  return {
25898
26174
  status: "failed",
25899
- path: path77,
26175
+ path: path78,
25900
26176
  message: err instanceof Error ? err.message : String(err)
25901
26177
  };
25902
26178
  }
25903
26179
  }
25904
26180
  async function removeLegacyMcpForHost(host, projectRoot) {
25905
- const path77 = host.getMcpConfigPath(projectRoot);
25906
- if (path77 === null) return null;
26181
+ const path78 = host.getMcpConfigPath(projectRoot);
26182
+ if (path78 === null) return null;
25907
26183
  try {
25908
- return await removeLegacyIamuEntry(path77) ? { status: "legacy_removed", path: path77, removedLegacy: true } : null;
26184
+ return await removeLegacyIamuEntry(path78) ? { status: "legacy_removed", path: path78, removedLegacy: true } : null;
25909
26185
  } catch {
25910
26186
  return null;
25911
26187
  }
@@ -33483,6 +33759,7 @@ __export(triage_exports, {
33483
33759
  triage: () => triage
33484
33760
  });
33485
33761
  import { promises as fs65 } from "fs";
33762
+ import path62 from "path";
33486
33763
  import chalk29 from "chalk";
33487
33764
  async function awaitingSteps(projectRoot, findings2) {
33488
33765
  const scenarios = /* @__PURE__ */ new Set();
@@ -33621,14 +33898,53 @@ async function triage(refs2, opts) {
33621
33898
  );
33622
33899
  return;
33623
33900
  }
33901
+ let note = opts.note;
33902
+ if (opts.noteFile !== void 0) {
33903
+ if (opts.note !== void 0) {
33904
+ commandError("Pass --note or --note-file, not both.", {
33905
+ ...opts,
33906
+ code: "invalid_field_combination",
33907
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note-file <path>`
33908
+ });
33909
+ return;
33910
+ }
33911
+ try {
33912
+ const text = opts.noteFile === "-" ? await readStdinText() : await fs65.readFile(
33913
+ path62.resolve(process.cwd(), opts.noteFile),
33914
+ "utf8"
33915
+ );
33916
+ note = text.trim();
33917
+ } catch (err) {
33918
+ commandError(
33919
+ `Could not read --note-file ${opts.noteFile}: ${err instanceof Error ? err.message : String(err)}`,
33920
+ {
33921
+ ...opts,
33922
+ code: "note_read_error",
33923
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33924
+ }
33925
+ );
33926
+ return;
33927
+ }
33928
+ if (!note) {
33929
+ commandError(`--note-file ${opts.noteFile} is empty`, {
33930
+ ...opts,
33931
+ code: "note_empty",
33932
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33933
+ });
33934
+ return;
33935
+ }
33936
+ }
33624
33937
  let reason = null;
33625
33938
  if (opts.reason !== void 0) {
33626
33939
  if (status2.data !== "dismissed") {
33627
- commandError("--reason applies only to --status dismissed", {
33628
- ...opts,
33629
- code: "invalid_reason_usage",
33630
- fix: `iamu triage ${refs2.join(" ")} --status dismissed --reason ${opts.reason}`
33631
- });
33940
+ commandError(
33941
+ "--reason applies only to --status dismissed \u2014 to record why or what changed, use --note",
33942
+ {
33943
+ ...opts,
33944
+ code: "invalid_reason_usage",
33945
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33946
+ }
33947
+ );
33632
33948
  return;
33633
33949
  }
33634
33950
  if (!DISMISS_REASONS.includes(opts.reason)) {
@@ -33820,7 +34136,7 @@ async function triage(refs2, opts) {
33820
34136
  const rule = await createGuidanceRule(projectRoot, {
33821
34137
  kind: effectiveReason,
33822
34138
  rule: ruleText,
33823
- reason: opts.note,
34139
+ reason: note,
33824
34140
  scope,
33825
34141
  sourceCanonicalIds: allTargets.map((f) => f.frontmatter.canonical_id).filter((id) => Boolean(id && UUID_RE4.test(id)))
33826
34142
  });
@@ -33831,7 +34147,7 @@ async function triage(refs2, opts) {
33831
34147
  status: status2.data,
33832
34148
  reason: effectiveReason,
33833
34149
  duplicateOf: duplicateTarget,
33834
- ...opts.note !== void 0 ? { note: opts.note } : {},
34150
+ ...note !== void 0 ? { note } : {},
33835
34151
  by: "triage",
33836
34152
  cascade: true,
33837
34153
  guidance: guidance ? { id: guidance.id, all: isSweep } : null,
@@ -33916,6 +34232,7 @@ var init_triage = __esm({
33916
34232
  init_dist2();
33917
34233
  init_dist();
33918
34234
  init_agent_commands();
34235
+ init_answer_input();
33919
34236
  init_complete();
33920
34237
  init_context4();
33921
34238
  SCOPES = ["project", "outline", "capability"];
@@ -34383,7 +34700,7 @@ var init_browse_driver = __esm({
34383
34700
 
34384
34701
  // src/commands/browse-actions.ts
34385
34702
  import { existsSync as existsSync15 } from "fs";
34386
- import path62 from "path";
34703
+ import path63 from "path";
34387
34704
  function byRole(page, role, name, opts) {
34388
34705
  const loc = page.getByRole(role, { name, exact: false });
34389
34706
  if (opts.nth !== void 0) {
@@ -34684,7 +35001,7 @@ async function fillIndexedElement(page, index, text, submit) {
34684
35001
  await fillOrSelect(loc.first(), text, submit);
34685
35002
  }
34686
35003
  async function setFilesOnElement(el, filePath) {
34687
- const resolved = path62.resolve(filePath);
35004
+ const resolved = path63.resolve(filePath);
34688
35005
  if (SENSITIVE_UPLOAD_PATH.test(resolved)) {
34689
35006
  throw new Error(
34690
35007
  `Refusing to upload sensitive path: "${resolved}". Choose a test fixture file inside the project or temp directory.`
@@ -34917,7 +35234,7 @@ var init_browse_actions = __esm({
34917
35234
 
34918
35235
  // src/commands/step-numbering.ts
34919
35236
  import { open, readFile as readFile6, readdir, stat as stat2, unlink } from "fs/promises";
34920
- import path63 from "path";
35237
+ import path64 from "path";
34921
35238
  function reservationMarker() {
34922
35239
  const marker = {
34923
35240
  reservation: { pid: process.pid, ts: Date.now() }
@@ -34959,7 +35276,7 @@ async function peekNextStepNumber(stepsDir) {
34959
35276
  for (const f of files) {
34960
35277
  const m = /^(\d+)\.json$/.exec(f);
34961
35278
  if (!m) continue;
34962
- const fullPath = path63.join(stepsDir, f);
35279
+ const fullPath = path64.join(stepsDir, f);
34963
35280
  const st = await stat2(fullPath).catch(() => null);
34964
35281
  if (!st) continue;
34965
35282
  if (await isAbandonedReservation(fullPath, st)) {
@@ -34977,7 +35294,7 @@ async function reserveStepFile(stepsDir, limit) {
34977
35294
  if (candidate > limit.maxSteps) {
34978
35295
  throw new Error(stepLimitMessage(limit.maxSteps, limit.runId));
34979
35296
  }
34980
- const stepPath = path63.join(stepsDir, stepFileName(candidate));
35297
+ const stepPath = path64.join(stepsDir, stepFileName(candidate));
34981
35298
  try {
34982
35299
  const handle = await open(stepPath, "wx");
34983
35300
  try {
@@ -35023,7 +35340,7 @@ __export(step_exports, {
35023
35340
  });
35024
35341
  import { existsSync as existsSync16 } from "fs";
35025
35342
  import { copyFile, mkdir as mkdir4, rename, stat as stat3 } from "fs/promises";
35026
- import path64 from "path";
35343
+ import path65 from "path";
35027
35344
  import chalk31 from "chalk";
35028
35345
  function parseMaxSteps(val) {
35029
35346
  if (typeof val === "number" && Number.isFinite(val) && val > 0) return val;
@@ -35039,11 +35356,11 @@ function parseMaxSteps(val) {
35039
35356
  }
35040
35357
  async function recordStep(input) {
35041
35358
  const runDirPath = runDir(input.projectRoot, input.runId);
35042
- if (!existsSync16(path64.join(runDirPath, "walkthrough.json"))) {
35359
+ if (!existsSync16(path65.join(runDirPath, "walkthrough.json"))) {
35043
35360
  throw new Error(`Run not found: ${input.runId}`);
35044
35361
  }
35045
- const stepsDir = path64.join(runDirPath, "steps");
35046
- const screenshotsDir = path64.join(runDirPath, "screenshots");
35362
+ const stepsDir = path65.join(runDirPath, "steps");
35363
+ const screenshotsDir = path65.join(runDirPath, "screenshots");
35047
35364
  await mkdir4(stepsDir, { recursive: true });
35048
35365
  await mkdir4(screenshotsDir, { recursive: true });
35049
35366
  const shotSource = input.shot ? await existingShot(input.shot) : null;
@@ -35058,9 +35375,9 @@ async function recordStep(input) {
35058
35375
  let screenshotRef = null;
35059
35376
  if (shotSource) {
35060
35377
  const destName = `step-${stepNumber2}.png`;
35061
- const destPath = path64.join(screenshotsDir, destName);
35378
+ const destPath = path65.join(screenshotsDir, destName);
35062
35379
  finalScreenshotPath = destPath;
35063
- if (path64.resolve(destPath) !== shotSource) {
35380
+ if (path65.resolve(destPath) !== shotSource) {
35064
35381
  if (input.moveShot) {
35065
35382
  await rename(shotSource, destPath).catch(
35066
35383
  () => copyFile(shotSource, destPath)
@@ -35069,16 +35386,16 @@ async function recordStep(input) {
35069
35386
  await copyFile(shotSource, destPath);
35070
35387
  }
35071
35388
  }
35072
- screenshotRef = path64.posix.join("screenshots", destName);
35389
+ screenshotRef = path65.posix.join("screenshots", destName);
35073
35390
  }
35074
35391
  let fullRef = null;
35075
35392
  if (input.fullShot && existsSync16(input.fullShot)) {
35076
35393
  const fullName = `step-${stepNumber2}.full.png`;
35077
35394
  const moved = await rename(
35078
35395
  input.fullShot,
35079
- path64.join(screenshotsDir, fullName)
35396
+ path65.join(screenshotsDir, fullName)
35080
35397
  ).then(() => true).catch(() => false);
35081
- if (moved) fullRef = path64.posix.join("screenshots", fullName);
35398
+ if (moved) fullRef = path65.posix.join("screenshots", fullName);
35082
35399
  }
35083
35400
  parsedStep = runStepSchema.parse({
35084
35401
  step_number: stepNumber2,
@@ -35125,7 +35442,7 @@ async function recordStep(input) {
35125
35442
  };
35126
35443
  }
35127
35444
  async function existingShot(shot) {
35128
- const resolved = path64.resolve(shot);
35445
+ const resolved = path65.resolve(shot);
35129
35446
  const isFile = await stat3(resolved).then((s) => s.isFile()).catch(() => false);
35130
35447
  if (!isFile) {
35131
35448
  throw new Error(
@@ -35186,7 +35503,7 @@ async function stepCommand(runArg, opts) {
35186
35503
  {
35187
35504
  step_number: res.step.step_number,
35188
35505
  cancelled: true,
35189
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35506
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35190
35507
  action: res.step.action,
35191
35508
  note: res.step.note,
35192
35509
  url: res.step.url,
@@ -35214,7 +35531,7 @@ async function stepCommand(runArg, opts) {
35214
35531
  JSON.stringify(
35215
35532
  {
35216
35533
  step_number: res.step.step_number,
35217
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35534
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35218
35535
  action: res.step.action,
35219
35536
  note: res.step.note,
35220
35537
  url: res.step.url,
@@ -35243,7 +35560,7 @@ async function stepCommand(runArg, opts) {
35243
35560
  console.log(` ${chalk31.dim("shot:")} ${res.step.screenshot}`);
35244
35561
  }
35245
35562
  console.log(
35246
- ` ${chalk31.dim("file:")} ${path64.relative(projectRoot, res.stepPath)}`
35563
+ ` ${chalk31.dim("file:")} ${path65.relative(projectRoot, res.stepPath)}`
35247
35564
  );
35248
35565
  console.log();
35249
35566
  } else {
@@ -35315,16 +35632,16 @@ import {
35315
35632
  import { rm as rm2 } from "fs/promises";
35316
35633
  import { createServer } from "net";
35317
35634
  import { homedir as homedir3 } from "os";
35318
- import path65 from "path";
35635
+ import path66 from "path";
35319
35636
  import chalk32 from "chalk";
35320
35637
  function stateDir() {
35321
- return path65.join(
35322
- process.env.IAMU_HOME ?? path65.join(homedir3(), ".iamu"),
35638
+ return path66.join(
35639
+ process.env.IAMU_HOME ?? path66.join(homedir3(), ".iamu"),
35323
35640
  "browse"
35324
35641
  );
35325
35642
  }
35326
35643
  function profileDir() {
35327
- return path65.join(stateDir(), "profile");
35644
+ return path66.join(stateDir(), "profile");
35328
35645
  }
35329
35646
  function privateDir(dir) {
35330
35647
  mkdirSync3(dir, { recursive: true, mode: 448 });
@@ -35335,7 +35652,7 @@ function privateDir(dir) {
35335
35652
  }
35336
35653
  function stateFile(run) {
35337
35654
  const key = run && STATE_KEY.test(run) && `${run}.json` !== SHARED_STATE ? `${run}.json` : SHARED_STATE;
35338
- return path65.join(stateDir(), key);
35655
+ return path66.join(stateDir(), key);
35339
35656
  }
35340
35657
  function loadState(file) {
35341
35658
  try {
@@ -35376,7 +35693,7 @@ function storedStates() {
35376
35693
  }
35377
35694
  const out2 = [];
35378
35695
  for (const name of names2.sort()) {
35379
- const file = path65.join(stateDir(), name);
35696
+ const file = path66.join(stateDir(), name);
35380
35697
  const state = loadState(file);
35381
35698
  if (state) out2.push({ state, file });
35382
35699
  }
@@ -35394,16 +35711,16 @@ function removeState(file) {
35394
35711
  }
35395
35712
  function runStillOpen(state, now = Date.now()) {
35396
35713
  if (!state.run || !state.project) return false;
35397
- const dir = path65.join(state.project, "iamu", ".runs", state.run);
35714
+ const dir = path66.join(state.project, "iamu", ".runs", state.run);
35398
35715
  try {
35399
- const manifestPath = path65.join(dir, "walkthrough.json");
35716
+ const manifestPath = path66.join(dir, "walkthrough.json");
35400
35717
  const manifest = JSON.parse(readFileSync10(manifestPath, "utf8"));
35401
35718
  if (typeof manifest.status !== "string" || !OPEN_RUN_STATUSES.has(manifest.status)) {
35402
35719
  return false;
35403
35720
  }
35404
35721
  let last = statSync3(manifestPath).mtimeMs;
35405
35722
  try {
35406
- last = Math.max(last, statSync3(path65.join(dir, "steps")).mtimeMs);
35723
+ last = Math.max(last, statSync3(path66.join(dir, "steps")).mtimeMs);
35407
35724
  } catch {
35408
35725
  }
35409
35726
  return now - last < RUN_IDLE_MS;
@@ -35502,7 +35819,7 @@ async function requestedViewport(opts) {
35502
35819
  let manifest;
35503
35820
  try {
35504
35821
  manifest = JSON.parse(
35505
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
35822
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
35506
35823
  );
35507
35824
  } catch {
35508
35825
  fail(
@@ -35681,7 +35998,7 @@ function computePageFingerprint(url, bodySnippet) {
35681
35998
  return createHash6("sha256").update(`${url}|${bodySnippet.trim().replace(/\s+/g, " ")}`).digest("hex").slice(0, 16);
35682
35999
  }
35683
36000
  function checkAndRecordStagnation(runDirPath, fingerprint, action) {
35684
- const cacheFile = path65.join(runDirPath, ".stagnation.json");
36001
+ const cacheFile = path66.join(runDirPath, ".stagnation.json");
35685
36002
  let previous = null;
35686
36003
  try {
35687
36004
  if (existsSync17(cacheFile)) {
@@ -35962,8 +36279,8 @@ async function actOnPage(opts, attached, state, fn, onPostShot) {
35962
36279
  let contentSnippet;
35963
36280
  if (opts.shot) {
35964
36281
  await waitForVisualStability(page);
35965
- screenshot = path65.resolve(opts.shot);
35966
- mkdirSync3(path65.dirname(screenshot), { recursive: true });
36282
+ screenshot = path66.resolve(opts.shot);
36283
+ mkdirSync3(path66.dirname(screenshot), { recursive: true });
35967
36284
  await page.screenshot({
35968
36285
  path: screenshot,
35969
36286
  fullPage: Boolean(opts.full),
@@ -36173,7 +36490,7 @@ function httpOrigin(raw) {
36173
36490
  function readTargetUrl(runDirPath) {
36174
36491
  try {
36175
36492
  const m = JSON.parse(
36176
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36493
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36177
36494
  );
36178
36495
  return typeof m.target_url === "string" ? m.target_url : null;
36179
36496
  } catch {
@@ -36185,7 +36502,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36185
36502
  let manifest = {};
36186
36503
  try {
36187
36504
  manifest = JSON.parse(
36188
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36505
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36189
36506
  );
36190
36507
  } catch {
36191
36508
  }
@@ -36196,7 +36513,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36196
36513
  if (designer2.includes(key) || scrolled) {
36197
36514
  return { take: true, lastHeight: null };
36198
36515
  }
36199
- const stepsDir = path65.join(runDirPath, "steps");
36516
+ const stepsDir = path66.join(runDirPath, "steps");
36200
36517
  let names2 = [];
36201
36518
  try {
36202
36519
  names2 = readdirSync3(stepsDir).filter((n) => /^\d+\.json$/.test(n)).sort();
@@ -36207,7 +36524,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36207
36524
  for (const name of names2) {
36208
36525
  try {
36209
36526
  const earlier = JSON.parse(
36210
- readFileSync10(path65.join(stepsDir, name), "utf8")
36527
+ readFileSync10(path66.join(stepsDir, name), "utf8")
36211
36528
  );
36212
36529
  if (typeof earlier.url === "string" && stepScreenKey(earlier.url, target) === key) {
36213
36530
  seen = true;
@@ -36274,7 +36591,7 @@ function printStep(opts, record, projectRoot, stagnationWarning, challengeWarnin
36274
36591
  if (opts.json) {
36275
36592
  const doc = {
36276
36593
  step_number: step.step_number,
36277
- path: path65.relative(projectRoot, record.stepPath).split(path65.sep).join("/"),
36594
+ path: path66.relative(projectRoot, record.stepPath).split(path66.sep).join("/"),
36278
36595
  action: step.action,
36279
36596
  note: step.note,
36280
36597
  url: step.url,
@@ -36392,7 +36709,7 @@ async function clearStorage(opts, driver) {
36392
36709
  try {
36393
36710
  if (dir) {
36394
36711
  target = JSON.parse(
36395
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
36712
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
36396
36713
  ).target_url;
36397
36714
  }
36398
36715
  } catch {
@@ -36579,15 +36896,15 @@ async function browseStep(args2, opts, driver) {
36579
36896
  ))
36580
36897
  return;
36581
36898
  const maxSteps = parseMaxSteps(opts.maxSteps);
36582
- if (await peekNextStepNumber(path65.join(runDirPath, "steps")) > maxSteps) {
36899
+ if (await peekNextStepNumber(path66.join(runDirPath, "steps")) > maxSteps) {
36583
36900
  return fail(stepLimitMessage(maxSteps, opts.run), opts);
36584
36901
  }
36585
- const pendingShot = path65.join(
36902
+ const pendingShot = path66.join(
36586
36903
  runDirPath,
36587
36904
  "screenshots",
36588
36905
  `.pending-${randomUUID11()}.png`
36589
36906
  );
36590
- const pendingFull = path65.join(
36907
+ const pendingFull = path66.join(
36591
36908
  runDirPath,
36592
36909
  "screenshots",
36593
36910
  `.pending-${randomUUID11()}.full.png`
@@ -38470,18 +38787,18 @@ __export(preflight_exports, {
38470
38787
  });
38471
38788
  import { spawnSync } from "child_process";
38472
38789
  import { existsSync as existsSync20, promises as fs67 } from "fs";
38473
- import path66 from "path";
38790
+ import path67 from "path";
38474
38791
  import chalk36 from "chalk";
38475
38792
  function detectPackageManager(targetDir) {
38476
- if (existsSync20(path66.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
38477
- if (existsSync20(path66.join(targetDir, "yarn.lock"))) return "yarn";
38478
- if (existsSync20(path66.join(targetDir, "bun.lockb")) || existsSync20(path66.join(targetDir, "bun.lock"))) {
38793
+ if (existsSync20(path67.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
38794
+ if (existsSync20(path67.join(targetDir, "yarn.lock"))) return "yarn";
38795
+ if (existsSync20(path67.join(targetDir, "bun.lockb")) || existsSync20(path67.join(targetDir, "bun.lock"))) {
38479
38796
  return "bun";
38480
38797
  }
38481
38798
  return "npm";
38482
38799
  }
38483
38800
  async function detectPreflightCommand(targetDir) {
38484
- const pkgPath = path66.join(targetDir, "package.json");
38801
+ const pkgPath = path67.join(targetDir, "package.json");
38485
38802
  if (existsSync20(pkgPath)) {
38486
38803
  try {
38487
38804
  const raw = await fs67.readFile(pkgPath, "utf8");
@@ -38494,7 +38811,7 @@ async function detectPreflightCommand(targetDir) {
38494
38811
  } catch {
38495
38812
  }
38496
38813
  }
38497
- const tsconfigPath = path66.join(targetDir, "tsconfig.json");
38814
+ const tsconfigPath = path67.join(targetDir, "tsconfig.json");
38498
38815
  if (existsSync20(tsconfigPath)) {
38499
38816
  return { command: "npx", args: ["--no-install", "tsc", "--noEmit"] };
38500
38817
  }
@@ -38533,7 +38850,7 @@ async function runPreflightCheck(targetDir) {
38533
38850
  };
38534
38851
  }
38535
38852
  async function preflight(opts = {}) {
38536
- const targetDir = opts.cwd ? path66.resolve(opts.cwd) : process.cwd();
38853
+ const targetDir = opts.cwd ? path67.resolve(opts.cwd) : process.cwd();
38537
38854
  const result = await runPreflightCheck(targetDir);
38538
38855
  if (result.ok) {
38539
38856
  if (opts.json) {
@@ -39042,12 +39359,12 @@ __export(validate_exports, {
39042
39359
  });
39043
39360
  import { randomUUID as randomUUID12 } from "crypto";
39044
39361
  import { promises as fs68 } from "fs";
39045
- import path67 from "path";
39362
+ import path68 from "path";
39046
39363
  import chalk38 from "chalk";
39047
39364
  function inferTypeFromPath(filePath, projectRoot) {
39048
- const rel = path67.relative(path67.join(projectRoot, "iamu"), filePath);
39049
- if (rel.startsWith("..") || path67.isAbsolute(rel)) return null;
39050
- const segments = rel.split(path67.sep);
39365
+ const rel = path68.relative(path68.join(projectRoot, "iamu"), filePath);
39366
+ if (rel.startsWith("..") || path68.isAbsolute(rel)) return null;
39367
+ const segments = rel.split(path68.sep);
39051
39368
  if (segments.length < 2) return null;
39052
39369
  return TYPE_BY_ALIAS.get(segments[0]) ?? null;
39053
39370
  }
@@ -39064,7 +39381,7 @@ function applyZodValidation(schema2, data, result) {
39064
39381
  }
39065
39382
  function validateFile(filePath, raw, projectRoot) {
39066
39383
  const result = { path: filePath, valid: true, errors: [] };
39067
- if (path67.basename(filePath) === "config.json") {
39384
+ if (path68.basename(filePath) === "config.json") {
39068
39385
  let parsedJson;
39069
39386
  try {
39070
39387
  parsedJson = JSON.parse(raw);
@@ -39091,11 +39408,11 @@ function validateFile(filePath, raw, projectRoot) {
39091
39408
  );
39092
39409
  return result;
39093
39410
  }
39094
- if (path67.basename(filePath) === "testability.md") {
39411
+ if (path68.basename(filePath) === "testability.md") {
39095
39412
  applyZodValidation(testabilityFrontmatterSchema, frontmatter, result);
39096
39413
  return result;
39097
39414
  }
39098
- if (path67.basename(filePath) === "context.yml") {
39415
+ if (path68.basename(filePath) === "context.yml") {
39099
39416
  applyZodValidation(contextFrontmatterSchema, frontmatter, result);
39100
39417
  const warnings = [];
39101
39418
  const hasNotes = Boolean(
@@ -39157,7 +39474,7 @@ async function buildRepairLookups(projectRoot) {
39157
39474
  }
39158
39475
  }
39159
39476
  const runScenario = /* @__PURE__ */ new Map();
39160
- const runsDir2 = path67.join(projectRoot, "iamu", ".runs");
39477
+ const runsDir2 = path68.join(projectRoot, "iamu", ".runs");
39161
39478
  let runIds = [];
39162
39479
  try {
39163
39480
  runIds = await fs68.readdir(runsDir2);
@@ -39167,7 +39484,7 @@ async function buildRepairLookups(projectRoot) {
39167
39484
  try {
39168
39485
  const manifest = JSON.parse(
39169
39486
  await fs68.readFile(
39170
- path67.join(runsDir2, runId, "walkthrough.json"),
39487
+ path68.join(runsDir2, runId, "walkthrough.json"),
39171
39488
  "utf8"
39172
39489
  )
39173
39490
  );
@@ -39198,7 +39515,7 @@ async function fixFindings(projectRoot, results, dryRun) {
39198
39515
  parsed.frontmatter,
39199
39516
  {
39200
39517
  ...lookups,
39201
- fileSlug: path67.basename(result.path, ".md"),
39518
+ fileSlug: path68.basename(result.path, ".md"),
39202
39519
  newId: randomUUID12,
39203
39520
  body: parsed.body
39204
39521
  }
@@ -39240,7 +39557,7 @@ async function validate(filePath, opts) {
39240
39557
  if (process.exitCode === 1) return;
39241
39558
  const results = [];
39242
39559
  if (filePath) {
39243
- const abs = path67.isAbsolute(filePath) ? filePath : path67.resolve(process.cwd(), filePath);
39560
+ const abs = path68.isAbsolute(filePath) ? filePath : path68.resolve(process.cwd(), filePath);
39244
39561
  try {
39245
39562
  const raw = await fs68.readFile(abs, "utf8");
39246
39563
  results.push(validateFile(abs, raw, projectRoot));
@@ -39256,7 +39573,7 @@ async function validate(filePath, opts) {
39256
39573
  return;
39257
39574
  }
39258
39575
  } else if (opts.run) {
39259
- const findingsDir = path67.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39576
+ const findingsDir = path68.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39260
39577
  let names2 = [];
39261
39578
  try {
39262
39579
  names2 = await fs68.readdir(findingsDir);
@@ -39264,7 +39581,7 @@ async function validate(filePath, opts) {
39264
39581
  }
39265
39582
  for (const name of names2) {
39266
39583
  if (!name.endsWith(".md")) continue;
39267
- const abs = path67.join(findingsDir, name);
39584
+ const abs = path68.join(findingsDir, name);
39268
39585
  try {
39269
39586
  const raw = await fs68.readFile(abs, "utf8");
39270
39587
  const { frontmatter } = parseFrontmatter(raw);
@@ -39273,7 +39590,7 @@ async function validate(filePath, opts) {
39273
39590
  }
39274
39591
  } catch {
39275
39592
  results.push({
39276
- path: path67.relative(projectRoot, abs),
39593
+ path: path68.relative(projectRoot, abs),
39277
39594
  valid: false,
39278
39595
  errors: ["Cannot read file or parse frontmatter"]
39279
39596
  });
@@ -39306,7 +39623,7 @@ async function validate(filePath, opts) {
39306
39623
  } else {
39307
39624
  const typesToCheck = typeFilter ? ENTITY_TYPES.filter((t) => typeFilter.has(t)) : ENTITY_TYPES;
39308
39625
  for (const type2 of typesToCheck) {
39309
- const iamuTypeDir = path67.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39626
+ const iamuTypeDir = path68.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39310
39627
  let names2;
39311
39628
  try {
39312
39629
  names2 = await fs68.readdir(iamuTypeDir);
@@ -39315,13 +39632,13 @@ async function validate(filePath, opts) {
39315
39632
  }
39316
39633
  for (const name of names2) {
39317
39634
  if (!name.endsWith(".md")) continue;
39318
- const abs = path67.join(iamuTypeDir, name);
39635
+ const abs = path68.join(iamuTypeDir, name);
39319
39636
  try {
39320
39637
  const raw = await fs68.readFile(abs, "utf8");
39321
39638
  results.push(validateFile(abs, raw, projectRoot));
39322
39639
  } catch {
39323
39640
  results.push({
39324
- path: path67.relative(projectRoot, abs),
39641
+ path: path68.relative(projectRoot, abs),
39325
39642
  valid: false,
39326
39643
  errors: ["Cannot read file"]
39327
39644
  });
@@ -39329,7 +39646,7 @@ async function validate(filePath, opts) {
39329
39646
  }
39330
39647
  }
39331
39648
  for (const extraFile of ["context.yml", "testability.md", "config.json"]) {
39332
- const extraPath = path67.join(projectRoot, "iamu", extraFile);
39649
+ const extraPath = path68.join(projectRoot, "iamu", extraFile);
39333
39650
  try {
39334
39651
  const raw = await fs68.readFile(extraPath, "utf8");
39335
39652
  results.push(validateFile(extraPath, raw, projectRoot));
@@ -39355,19 +39672,19 @@ async function validate(filePath, opts) {
39355
39672
  valid,
39356
39673
  errors: results.filter((r) => !r.valid).flatMap(
39357
39674
  (r) => r.errors.map((e) => ({
39358
- path: path67.relative(projectRoot, r.path),
39675
+ path: path68.relative(projectRoot, r.path),
39359
39676
  message: e
39360
39677
  }))
39361
39678
  ),
39362
39679
  warnings: results.filter((r) => r.warnings && r.warnings.length > 0).flatMap(
39363
39680
  (r) => (r.warnings ?? []).map((w) => ({
39364
- path: path67.relative(projectRoot, r.path),
39681
+ path: path68.relative(projectRoot, r.path),
39365
39682
  message: w
39366
39683
  }))
39367
39684
  ),
39368
39685
  ...opts.fix ? {
39369
39686
  repaired: repaired.map((r) => ({
39370
- path: path67.relative(projectRoot, r.path),
39687
+ path: path68.relative(projectRoot, r.path),
39371
39688
  changes: r.repairs,
39372
39689
  valid: r.valid
39373
39690
  }))
@@ -39389,7 +39706,7 @@ async function validate(filePath, opts) {
39389
39706
  }
39390
39707
  let hasErrors = false;
39391
39708
  for (const r of results) {
39392
- const relPath = path67.relative(projectRoot, r.path);
39709
+ const relPath = path68.relative(projectRoot, r.path);
39393
39710
  if (r.valid) {
39394
39711
  if (r.warnings && r.warnings.length > 0) {
39395
39712
  console.log(` ${symbols.warning} ` + chalk38.yellow(relPath));
@@ -39584,7 +39901,7 @@ __export(new_entity_exports, {
39584
39901
  });
39585
39902
  import { randomUUID as randomUUID13 } from "crypto";
39586
39903
  import { readFile as readFile8 } from "fs/promises";
39587
- import path68 from "path";
39904
+ import path69 from "path";
39588
39905
  import chalk40 from "chalk";
39589
39906
  async function resolveBodyInput(opts, command, flag = "--body") {
39590
39907
  if (opts.bodyFile !== void 0) {
@@ -39597,7 +39914,7 @@ async function resolveBodyInput(opts, command, flag = "--body") {
39597
39914
  return null;
39598
39915
  }
39599
39916
  try {
39600
- const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path68.resolve(process.cwd(), opts.bodyFile), "utf8");
39917
+ const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path69.resolve(process.cwd(), opts.bodyFile), "utf8");
39601
39918
  return { body };
39602
39919
  } catch (err) {
39603
39920
  commandError(
@@ -39847,7 +40164,7 @@ __export(set_entity_exports, {
39847
40164
  setEntity: () => setEntity
39848
40165
  });
39849
40166
  import { promises as fs69 } from "fs";
39850
- import path69 from "path";
40167
+ import path70 from "path";
39851
40168
  import chalk41 from "chalk";
39852
40169
  import { ZodError as ZodError2 } from "zod";
39853
40170
  function normalizeAuthStrategy(raw) {
@@ -40038,7 +40355,7 @@ async function setEntity(type2, slug, opts) {
40038
40355
  }
40039
40356
  raw = Buffer.concat(chunks).toString("utf8");
40040
40357
  } else {
40041
- raw = await fs69.readFile(path69.resolve(projectRoot, opts.file), "utf8");
40358
+ raw = await fs69.readFile(path70.resolve(projectRoot, opts.file), "utf8");
40042
40359
  }
40043
40360
  } catch (err) {
40044
40361
  commandError(
@@ -40624,7 +40941,7 @@ __export(edit_entity_exports, {
40624
40941
  });
40625
40942
  import { spawn as spawn2 } from "child_process";
40626
40943
  import { promises as fs70 } from "fs";
40627
- import path70 from "path";
40944
+ import path71 from "path";
40628
40945
  import chalk42 from "chalk";
40629
40946
  async function editEntity(type2, slug, opts = {}) {
40630
40947
  const ctx = await resolveProject(opts);
@@ -40648,7 +40965,7 @@ async function editEntity(type2, slug, opts = {}) {
40648
40965
  }
40649
40966
  const effectiveSlug = isSpecial ? type2 : slug;
40650
40967
  const resolvedPath = isSpecial ? type2 === "testability" ? testabilityPath(projectRoot) : contextPath(projectRoot) : await resolveEntityPath(projectRoot, type2, effectiveSlug);
40651
- const filePath = resolvedPath ?? path70.join(
40968
+ const filePath = resolvedPath ?? path71.join(
40652
40969
  projectRoot,
40653
40970
  "iamu",
40654
40971
  ENTITY_DIRS[type2],
@@ -40659,7 +40976,7 @@ async function editEntity(type2, slug, opts = {}) {
40659
40976
  await fs70.access(filePath);
40660
40977
  } catch {
40661
40978
  commandError(
40662
- `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path70.relative(projectRoot, filePath)}.`,
40979
+ `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path71.relative(projectRoot, filePath)}.`,
40663
40980
  {
40664
40981
  ...opts,
40665
40982
  code: "entity_not_found",
@@ -40670,7 +40987,7 @@ async function editEntity(type2, slug, opts = {}) {
40670
40987
  }
40671
40988
  if (!process.stdin.isTTY || process.env.CI) {
40672
40989
  commandError(
40673
- `Cannot open editor in a non-interactive environment. Edit the file directly: ${path70.relative(projectRoot, filePath)}`,
40990
+ `Cannot open editor in a non-interactive environment. Edit the file directly: ${path71.relative(projectRoot, filePath)}`,
40674
40991
  {
40675
40992
  ...opts,
40676
40993
  code: "non_interactive_environment",
@@ -40746,7 +41063,7 @@ async function editEntity(type2, slug, opts = {}) {
40746
41063
  console.log();
40747
41064
  console.log(
40748
41065
  chalk42.dim(
40749
- ` Fix the file and run: iamu validate ${path70.relative(projectRoot, filePath)}`
41066
+ ` Fix the file and run: iamu validate ${path71.relative(projectRoot, filePath)}`
40750
41067
  )
40751
41068
  );
40752
41069
  console.log();
@@ -40776,7 +41093,7 @@ async function editEntity(type2, slug, opts = {}) {
40776
41093
  {
40777
41094
  ...opts,
40778
41095
  code: "validation_error",
40779
- fix: `iamu validate ${path70.relative(projectRoot, filePath)}`
41096
+ fix: `iamu validate ${path71.relative(projectRoot, filePath)}`
40780
41097
  }
40781
41098
  );
40782
41099
  }
@@ -40945,7 +41262,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40945
41262
  const byKey = /* @__PURE__ */ new Map();
40946
41263
  const byPlaceView = /* @__PURE__ */ new Map();
40947
41264
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
40948
- function addSurface(state, place, view, path77, depth) {
41265
+ function addSurface(state, place, view, path78, depth) {
40949
41266
  const key = exploreSurfaceKey(place, view);
40950
41267
  const surface = {
40951
41268
  key,
@@ -40957,7 +41274,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40957
41274
  headings: state.headings,
40958
41275
  fields: state.fields,
40959
41276
  depth,
40960
- path: path77,
41277
+ path: path78,
40961
41278
  actions: []
40962
41279
  };
40963
41280
  surfaces.push(surface);
@@ -40975,9 +41292,9 @@ ${surface.signature}`, key);
40975
41292
  });
40976
41293
  await settle(page, settleMs);
40977
41294
  }
40978
- async function replay(path77) {
40979
- if (!("goto" in (path77[0] ?? {}))) await goto(opts.startUrl);
40980
- for (const step of path77) {
41295
+ async function replay(path78) {
41296
+ if (!("goto" in (path78[0] ?? {}))) await goto(opts.startUrl);
41297
+ for (const step of path78) {
40981
41298
  if ("goto" in step) {
40982
41299
  await goto(step.goto);
40983
41300
  } else {
@@ -41104,8 +41421,8 @@ ${sig}`, key);
41104
41421
  if (stats.surfaces >= budget.max_surfaces) {
41105
41422
  stats.truncated = true;
41106
41423
  } else {
41107
- const path77 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41108
- addSurface(after, place, view, path77, surface.depth + 1);
41424
+ const path78 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41425
+ addSurface(after, place, view, path78, surface.depth + 1);
41109
41426
  }
41110
41427
  }
41111
41428
  }
@@ -41375,7 +41692,7 @@ __export(explore_exports, {
41375
41692
  exploreList: () => exploreList,
41376
41693
  exploreRun: () => exploreRun
41377
41694
  });
41378
- import path71 from "path";
41695
+ import path72 from "path";
41379
41696
  import chalk43 from "chalk";
41380
41697
  function fail2(message, json2) {
41381
41698
  if (json2) console.error(JSON.stringify({ error: message }));
@@ -41515,7 +41832,7 @@ async function exploreRun(url, opts) {
41515
41832
  surfaces: result.surfaces
41516
41833
  };
41517
41834
  const file = await writeExploreMap(ctx.projectRoot, map2);
41518
- const rel = path71.relative(process.cwd(), file);
41835
+ const rel = path72.relative(process.cwd(), file);
41519
41836
  if (opts.json) {
41520
41837
  console.log(JSON.stringify({ id: map2.id, path: rel, stats: map2.stats }));
41521
41838
  return;
@@ -42711,7 +43028,7 @@ __export(new_finding_exports, {
42711
43028
  });
42712
43029
  import { randomUUID as randomUUID14 } from "crypto";
42713
43030
  import { access, readFile as readFile9 } from "fs/promises";
42714
- import path72 from "path";
43031
+ import path73 from "path";
42715
43032
  import chalk46 from "chalk";
42716
43033
  async function newFinding(opts) {
42717
43034
  const ctx = await resolveProject(opts);
@@ -42799,7 +43116,7 @@ async function newFinding(opts) {
42799
43116
  }
42800
43117
  try {
42801
43118
  await access(
42802
- path72.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
43119
+ path73.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
42803
43120
  );
42804
43121
  } catch {
42805
43122
  commandError(
@@ -42818,7 +43135,7 @@ async function newFinding(opts) {
42818
43135
  if (!scenarioSlug) {
42819
43136
  try {
42820
43137
  const manifestRaw = await readFile9(
42821
- path72.join(runDirPath, "walkthrough.json"),
43138
+ path73.join(runDirPath, "walkthrough.json"),
42822
43139
  "utf8"
42823
43140
  );
42824
43141
  const manifest = JSON.parse(manifestRaw);
@@ -42965,7 +43282,7 @@ __export(summary_exports, {
42965
43282
  summaryCommand: () => summaryCommand
42966
43283
  });
42967
43284
  import { promises as fs71 } from "fs";
42968
- import path73 from "path";
43285
+ import path74 from "path";
42969
43286
  import chalk47 from "chalk";
42970
43287
  function endedFrom(value) {
42971
43288
  if (!value || typeof value !== "object") return void 0;
@@ -42996,7 +43313,7 @@ async function summaryCommand(runIdArg, opts) {
42996
43313
  let openQuestions = [];
42997
43314
  let setupNotes = [];
42998
43315
  let model = opts.model?.trim() || void 0;
42999
- const summaryPath = path73.join(runDirPath, "summary.json");
43316
+ const summaryPath = path74.join(runDirPath, "summary.json");
43000
43317
  let blocked;
43001
43318
  let ended;
43002
43319
  try {
@@ -43016,7 +43333,7 @@ async function summaryCommand(runIdArg, opts) {
43016
43333
  if (opts.file === "-") {
43017
43334
  raw = await readStdinText();
43018
43335
  } else {
43019
- raw = await fs71.readFile(path73.resolve(projectRoot, opts.file), "utf8");
43336
+ raw = await fs71.readFile(path74.resolve(projectRoot, opts.file), "utf8");
43020
43337
  }
43021
43338
  const parsed = JSON.parse(raw);
43022
43339
  if (Array.isArray(parsed.what_worked)) {
@@ -43093,7 +43410,7 @@ async function summaryCommand(runIdArg, opts) {
43093
43410
  JSON.stringify(
43094
43411
  {
43095
43412
  run_id: runId,
43096
- path: path73.relative(projectRoot, summaryPath),
43413
+ path: path74.relative(projectRoot, summaryPath),
43097
43414
  summary: data
43098
43415
  },
43099
43416
  null,
@@ -43155,7 +43472,7 @@ __export(advise_exports, {
43155
43472
  readAdvisePrompt: () => readAdvisePrompt
43156
43473
  });
43157
43474
  import { existsSync as existsSync23, promises as fs72 } from "fs";
43158
- import path74 from "path";
43475
+ import path75 from "path";
43159
43476
  import chalk48 from "chalk";
43160
43477
  function readAdvisePrompt(projectRoot) {
43161
43478
  return readInstalledPrompt(projectRoot, "advise.md");
@@ -43213,7 +43530,7 @@ async function advise(runIdArg, opts = {}) {
43213
43530
  findings2 = input.findings.length;
43214
43531
  needed = findings2 >= 2;
43215
43532
  if (!needed) reason = "fewer than two open findings in the project";
43216
- promptPath = opts.promptFile ?? path74.join(analysisDir(projectRoot), "advise_prompt.md");
43533
+ promptPath = opts.promptFile ?? path75.join(analysisDir(projectRoot), "advise_prompt.md");
43217
43534
  } else {
43218
43535
  const runId = resolveRunId(projectRoot, runIdArg ?? opts.run);
43219
43536
  if (!requireRunId(runId, opts, "iamu advise <run-id>")) return;
@@ -43224,12 +43541,12 @@ async function advise(runIdArg, opts = {}) {
43224
43541
  input = await buildAdviseInput(projectRoot, runId, manifest.scenario_slug);
43225
43542
  refsDir = runDirPath;
43226
43543
  findings2 = input.findings.length;
43227
- const advised = existsSync23(path74.join(runDirPath, ADVICE_FILE));
43544
+ const advised = existsSync23(path75.join(runDirPath, ADVICE_FILE));
43228
43545
  needed = !manifest.blocked && findings2 > 0 && !advised;
43229
43546
  if (manifest.blocked) reason = "the run was blocked";
43230
43547
  else if (advised) reason = "the run is already advised";
43231
43548
  else if (findings2 === 0) reason = "no open findings to advise";
43232
- promptPath = opts.promptFile ?? path74.join(runDirPath, "advise_prompt.md");
43549
+ promptPath = opts.promptFile ?? path75.join(runDirPath, "advise_prompt.md");
43233
43550
  }
43234
43551
  if (needed) input = await withCodeIndex(projectRoot, input);
43235
43552
  const { bundle, refs: refs2 } = promptBundle(input);
@@ -43239,11 +43556,11 @@ async function advise(runIdArg, opts = {}) {
43239
43556
  promptSizeWarning(prompt)
43240
43557
  ].filter((w) => w !== null) : [];
43241
43558
  if (prompt) {
43242
- await fs72.mkdir(path74.dirname(promptPath), { recursive: true });
43559
+ await fs72.mkdir(path75.dirname(promptPath), { recursive: true });
43243
43560
  await fs72.writeFile(promptPath, prompt, "utf8");
43244
43561
  await fs72.mkdir(refsDir, { recursive: true });
43245
43562
  await fs72.writeFile(
43246
- path74.join(refsDir, ADVISE_REFS_FILE),
43563
+ path75.join(refsDir, ADVISE_REFS_FILE),
43247
43564
  `${JSON.stringify(refs2, null, 2)}
43248
43565
  `,
43249
43566
  "utf8"
@@ -43258,8 +43575,8 @@ async function advise(runIdArg, opts = {}) {
43258
43575
  ...reason ? { reason } : {},
43259
43576
  mode: opts.project ? "project" : "run",
43260
43577
  findings: findings2,
43261
- prompt_file: prompt ? path74.relative(projectRoot, promptPath).split(path74.sep).join("/") : null,
43262
- prompt_file_abs: prompt ? path74.resolve(promptPath) : null,
43578
+ prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43579
+ prompt_file_abs: prompt ? path75.resolve(promptPath) : null,
43263
43580
  prompt_chars: prompt?.length ?? 0,
43264
43581
  warnings,
43265
43582
  advise_prompt: prompt
@@ -43335,7 +43652,7 @@ async function adviseRecord(runIdArg, opts) {
43335
43652
  const { projectRoot } = ctx;
43336
43653
  if (opts.project) {
43337
43654
  const input2 = await readAnswerInput(opts, "advice", {
43338
- defaultPath: path74.join(analysisDir(projectRoot), "answer.json"),
43655
+ defaultPath: path75.join(analysisDir(projectRoot), "answer.json"),
43339
43656
  code: "missing_advice_input",
43340
43657
  fix: "iamu advise-record --project --data '<json>'"
43341
43658
  });
@@ -43367,7 +43684,7 @@ async function adviseRecord(runIdArg, opts) {
43367
43684
  if (!manifest) return;
43368
43685
  await logTiming(runDirPath, "advise-record", "start");
43369
43686
  const input = await readAnswerInput(opts, "advice", {
43370
- defaultPath: path74.join(runDir(projectRoot, runId), "advise_answer.json"),
43687
+ defaultPath: path75.join(runDir(projectRoot, runId), "advise_answer.json"),
43371
43688
  code: "missing_advice_input",
43372
43689
  fix: `iamu advise-record ${runId} --data '<json>'`
43373
43690
  });
@@ -43417,7 +43734,7 @@ async function directionsList(opts) {
43417
43734
  problem: d.texts.problem,
43418
43735
  change: d.texts.change,
43419
43736
  leading_practice: d.texts.leading_practice,
43420
- path: path74.relative(ctx.projectRoot, d.path)
43737
+ path: path75.relative(ctx.projectRoot, d.path)
43421
43738
  })),
43422
43739
  null,
43423
43740
  2
@@ -43566,7 +43883,7 @@ __export(designer_exports, {
43566
43883
  designerRecord: () => designerRecord
43567
43884
  });
43568
43885
  import { promises as fs73 } from "fs";
43569
- import path75 from "path";
43886
+ import path76 from "path";
43570
43887
  import chalk49 from "chalk";
43571
43888
  import { ZodError as ZodError3 } from "zod";
43572
43889
  function describeError(err) {
@@ -43586,7 +43903,7 @@ async function designer(runId, opts = {}) {
43586
43903
  const { projectRoot, runDirPath } = ctx;
43587
43904
  if (!await completedManifest2(runDirPath, runId, opts)) return;
43588
43905
  const input = await buildRunDesignerInput(projectRoot, runId);
43589
- const promptPath = path75.join(runDirPath, DESIGNER_PROMPT_FILE);
43906
+ const promptPath = path76.join(runDirPath, DESIGNER_PROMPT_FILE);
43590
43907
  const prompt = input.needed ? composeDesignPrompt(
43591
43908
  projectRoot,
43592
43909
  "designer.md",
@@ -43595,7 +43912,7 @@ async function designer(runId, opts = {}) {
43595
43912
  if (prompt) {
43596
43913
  await fs73.writeFile(promptPath, prompt, "utf8");
43597
43914
  await fs73.writeFile(
43598
- path75.join(runDirPath, DESIGNER_REFS_FILE),
43915
+ path76.join(runDirPath, DESIGNER_REFS_FILE),
43599
43916
  `${JSON.stringify(input.refs, null, 2)}
43600
43917
  `,
43601
43918
  "utf8"
@@ -43606,7 +43923,7 @@ async function designer(runId, opts = {}) {
43606
43923
  installedPromptWarning(projectRoot, "designer.md"),
43607
43924
  ...input.warnings
43608
43925
  ].filter((w) => w !== null) : [];
43609
- const answer = path75.join(runDirPath, DESIGNER_ANSWER_FILE);
43926
+ const answer = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
43610
43927
  const result = {
43611
43928
  needed: input.needed,
43612
43929
  reason: input.reason,
@@ -43622,11 +43939,11 @@ async function designer(runId, opts = {}) {
43622
43939
  title: f.title
43623
43940
  })),
43624
43941
  design_files: (input.bundle.design?.files ?? []).map((f) => f.entry),
43625
- prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43942
+ prompt_file: prompt ? path76.relative(projectRoot, promptPath).split(path76.sep).join("/") : null,
43626
43943
  prompt_file_abs: prompt ? promptPath : null,
43627
43944
  prompt_chars: prompt?.length ?? 0,
43628
43945
  answer_file_abs: prompt ? answer : null,
43629
- 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,
43946
+ 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,
43630
43947
  hint: input.judged_against === "general-craft" ? await designDocsHint(projectRoot) : null,
43631
43948
  // No design doc recorded or found, and the template not declined:
43632
43949
  // the run summary offers to start one (iamu-design).
@@ -43672,6 +43989,9 @@ function printDesignerRecord(res) {
43672
43989
  ` ${chalk49.cyan("\u25C6")} Proposed doc edit ${p.outcome}: ${p.slug} (iamu design proposals)`
43673
43990
  );
43674
43991
  }
43992
+ for (const slug of res.verified ?? []) {
43993
+ console.log(` ${chalk49.green("\u2713")} Fix verified: ${slug}`);
43994
+ }
43675
43995
  for (const r of res.rejected) {
43676
43996
  console.log(` ${chalk49.yellow("!")} Dropped ${r.id}: ${r.reason}`);
43677
43997
  }
@@ -43757,7 +44077,7 @@ async function designerRecordLocked(projectRoot, runDirPath, runId, manifest, op
43757
44077
  return;
43758
44078
  }
43759
44079
  const input = await readAnswerInput(opts, "designer's review", {
43760
- defaultPath: path75.join(runDirPath, DESIGNER_ANSWER_FILE),
44080
+ defaultPath: path76.join(runDirPath, DESIGNER_ANSWER_FILE),
43761
44081
  code: "missing_designer_answer",
43762
44082
  fix: `iamu designer-record ${runId} --data '<json>'`
43763
44083
  });
@@ -43889,7 +44209,7 @@ __export(post_walk_exports, {
43889
44209
  postWalk: () => postWalk
43890
44210
  });
43891
44211
  import { existsSync as existsSync24, promises as fs74 } from "fs";
43892
- import path76 from "path";
44212
+ import path77 from "path";
43893
44213
  import chalk50 from "chalk";
43894
44214
  async function captureStageJson(fn) {
43895
44215
  const captured = [];
@@ -43932,7 +44252,7 @@ async function postWalk(runIdArg, opts = {}) {
43932
44252
  }
43933
44253
  const runDirPath = requireRun(projectRoot, runId, opts);
43934
44254
  if (!runDirPath) return;
43935
- const manifestPath = path76.join(runDirPath, "walkthrough.json");
44255
+ const manifestPath = path77.join(runDirPath, "walkthrough.json");
43936
44256
  let manifest;
43937
44257
  try {
43938
44258
  manifest = JSON.parse(
@@ -43973,7 +44293,7 @@ async function postWalk(runIdArg, opts = {}) {
43973
44293
  return;
43974
44294
  }
43975
44295
  const recordedNow = [];
43976
- const matchesAnswerPath = path76.join(runDirPath, MATCH_ANSWER_FILE);
44296
+ const matchesAnswerPath = path77.join(runDirPath, MATCH_ANSWER_FILE);
43977
44297
  if (existsSync24(matchesAnswerPath)) {
43978
44298
  const res = await captureStageJson(
43979
44299
  () => matchRecord(runId, { file: matchesAnswerPath, json: true })
@@ -43984,7 +44304,7 @@ async function postWalk(runIdArg, opts = {}) {
43984
44304
  });
43985
44305
  }
43986
44306
  }
43987
- const judgeAnswerPath = path76.join(runDirPath, "judge_answer.json");
44307
+ const judgeAnswerPath = path77.join(runDirPath, "judge_answer.json");
43988
44308
  if (existsSync24(judgeAnswerPath)) {
43989
44309
  const res = await captureStageJson(
43990
44310
  () => evaluateRecord(runId, { file: judgeAnswerPath, json: true })
@@ -43995,8 +44315,8 @@ async function postWalk(runIdArg, opts = {}) {
43995
44315
  });
43996
44316
  }
43997
44317
  }
43998
- const adviseAnswerPath = path76.join(runDirPath, "advise_answer.json");
43999
- if (!opts.critic && !existsSync24(path76.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
44318
+ const adviseAnswerPath = path77.join(runDirPath, "advise_answer.json");
44319
+ if (!opts.critic && !existsSync24(path77.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
44000
44320
  const res = await captureStageJson(
44001
44321
  () => adviseRecord(runId, { file: adviseAnswerPath, json: true })
44002
44322
  );
@@ -44006,7 +44326,7 @@ async function postWalk(runIdArg, opts = {}) {
44006
44326
  });
44007
44327
  }
44008
44328
  }
44009
- const designerAnswerPath = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
44329
+ const designerAnswerPath = path77.join(runDirPath, DESIGNER_ANSWER_FILE);
44010
44330
  let designerWarnings = [];
44011
44331
  let designerInvalidChecked = [];
44012
44332
  if (!existingDesignerFile(runDirPath)) {
@@ -44038,9 +44358,9 @@ async function postWalk(runIdArg, opts = {}) {
44038
44358
  }
44039
44359
  }
44040
44360
  const matchInfo = await captureStageJson(() => match(runId, { json: true }));
44041
- const evalRecorded = existsSync24(path76.join(runDirPath, "verdicts.json"));
44361
+ const evalRecorded = existsSync24(path77.join(runDirPath, "verdicts.json"));
44042
44362
  const evalInfo = evalRecorded ? null : await captureStageJson(() => evaluate(runId, { json: true }));
44043
- const adviseRecorded = existsSync24(path76.join(runDirPath, ADVICE_FILE));
44363
+ const adviseRecorded = existsSync24(path77.join(runDirPath, ADVICE_FILE));
44044
44364
  const awaitingJudge = !opts.critic && !adviseRecorded && !evalRecorded;
44045
44365
  const adviseInfo = opts.critic || adviseRecorded || awaitingJudge ? null : await captureStageJson(() => advise(runId, { json: true }));
44046
44366
  const designerRecorded = Boolean(existingDesignerFile(runDirPath));
@@ -44547,7 +44867,10 @@ function registerFindingsCommands(program2) {
44547
44867
  program2.command("triage [findings...]").helpGroup("Findings:").description("Set findings' triage status").requiredOption(
44548
44868
  "--status <status>",
44549
44869
  "open, addressed, dismissed, deferred or wont_fix"
44550
- ).option("--note <text>", "Optional triage note or reason").option(
44870
+ ).option(
44871
+ "--note <text>",
44872
+ "Triage note, any status. For addressed: one line on what changed on screen, so the next run's matcher can confirm the fix"
44873
+ ).option("--note-file <path>", "Read the note from a file (- reads stdin)").option(
44551
44874
  "--reason <reason>",
44552
44875
  "Dismiss reason: not_relevant (default), walker_error, out_of_scope or duplicate"
44553
44876
  ).option(
@@ -44564,7 +44887,7 @@ function registerFindingsCommands(program2) {
44564
44887
  "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"
44565
44888
  ).option("--no-guidance", "Dismiss without writing a tester guidance rule").option("--json", "Output as JSON").addHelpText(
44566
44889
  "after",
44567
- "\nDismissing as not_relevant, walker_error, or out_of_scope teaches the walker: it writes a\ntester guidance rule (skip it with --no-guidance)."
44890
+ '\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>"'
44568
44891
  ).action(
44569
44892
  async (refs2, opts) => {
44570
44893
  const { triage: triage2 } = await Promise.resolve().then(() => (init_triage(), triage_exports));
@@ -44663,9 +44986,9 @@ function registerProjectCommands(program2) {
44663
44986
  "after",
44664
44987
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
44665
44988
  ).action(
44666
- async (path77, opts) => {
44989
+ async (path78, opts) => {
44667
44990
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
44668
- await validate2(path77, opts);
44991
+ await validate2(path78, opts);
44669
44992
  }
44670
44993
  );
44671
44994
  program2.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(