@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/index.js CHANGED
@@ -6684,10 +6684,13 @@ var init_wire_compat = __esm({
6684
6684
  });
6685
6685
 
6686
6686
  // ../../packages/local-store/dist/sync/server-meta.js
6687
+ function cloudDistinguishesHumanEdits(capabilities) {
6688
+ return Boolean(capabilities?.includes(SYNC_TOKEN_CAPABILITY));
6689
+ }
6687
6690
  function cloudSupportsGuidance(capabilities) {
6688
6691
  return Boolean(capabilities?.includes(GUIDANCE_CAPABILITY) || capabilities?.includes(DESIGNER_FINDINGS_CAPABILITY));
6689
6692
  }
6690
- async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure) {
6693
+ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFailure, onServerClock) {
6691
6694
  const controller = new AbortController();
6692
6695
  const timer = setTimeout(() => controller.abort(), timeoutMs);
6693
6696
  try {
@@ -6697,6 +6700,9 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6697
6700
  signal: controller.signal
6698
6701
  };
6699
6702
  const res = await fetchFn(`${apiUrl.replace(/\/+$/, "")}/api/meta`, init2);
6703
+ const offset = serverClockOffset(res);
6704
+ if (offset !== null)
6705
+ onServerClock?.(offset);
6700
6706
  if (res.status === 404)
6701
6707
  return [];
6702
6708
  if (!res.ok) {
@@ -6718,6 +6724,16 @@ async function fetchServerCapabilities(apiUrl, apiKey, fetchFn, timeoutMs, onFai
6718
6724
  clearTimeout(timer);
6719
6725
  }
6720
6726
  }
6727
+ function serverClockOffset(res) {
6728
+ const headers = res?.headers;
6729
+ if (!headers || typeof headers.get !== "function")
6730
+ return null;
6731
+ const raw = headers.get.call(headers, "date");
6732
+ if (typeof raw !== "string")
6733
+ return null;
6734
+ const at = Date.parse(raw);
6735
+ return Number.isNaN(at) ? null : at - Date.now();
6736
+ }
6721
6737
  function shapeForServer(pending, capabilities) {
6722
6738
  const knows = capabilities.includes(DESIGNER_FINDINGS_CAPABILITY);
6723
6739
  const knowsProvenance = capabilities.includes(FINDING_PROVENANCE_CAPABILITY);
@@ -6739,7 +6755,7 @@ function shapeForServer(pending, capabilities) {
6739
6755
  }
6740
6756
  return { pending: out2, heldDesigner };
6741
6757
  }
6742
- var DESIGNER_FINDINGS_CAPABILITY, GUIDANCE_CAPABILITY, WALKTHROUGH_DELETE_CAPABILITY, FINDING_PROVENANCE_CAPABILITY, BUNDLE_FINDING_HASH_CAPABILITY, RUN_BLOCKED_CAPABILITY;
6758
+ var DESIGNER_FINDINGS_CAPABILITY, GUIDANCE_CAPABILITY, WALKTHROUGH_DELETE_CAPABILITY, FINDING_PROVENANCE_CAPABILITY, BUNDLE_FINDING_HASH_CAPABILITY, RUN_BLOCKED_CAPABILITY, SYNC_TOKEN_CAPABILITY;
6743
6759
  var init_server_meta = __esm({
6744
6760
  "../../packages/local-store/dist/sync/server-meta.js"() {
6745
6761
  "use strict";
@@ -6751,6 +6767,7 @@ var init_server_meta = __esm({
6751
6767
  FINDING_PROVENANCE_CAPABILITY = "finding-provenance";
6752
6768
  BUNDLE_FINDING_HASH_CAPABILITY = "bundle-finding-hash";
6753
6769
  RUN_BLOCKED_CAPABILITY = "run-blocked";
6770
+ SYNC_TOKEN_CAPABILITY = "sync-token";
6754
6771
  }
6755
6772
  });
6756
6773
 
@@ -10687,7 +10704,7 @@ function compareIso(a, b) {
10687
10704
  return ta - tb;
10688
10705
  return String(a ?? "").localeCompare(String(b ?? ""));
10689
10706
  }
10690
- async function designerFindingsOn(projectRoot, screens, before, targetUrl = null) {
10707
+ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null, settled = /* @__PURE__ */ new Set()) {
10691
10708
  const latest = /* @__PURE__ */ new Map();
10692
10709
  const cache = /* @__PURE__ */ new Map();
10693
10710
  const wanted = new Set([...screens].map((k) => normalizeScreenKey(k, targetUrl)));
@@ -10711,7 +10728,7 @@ async function designerFindingsOn(projectRoot, screens, before, targetUrl = null
10711
10728
  }
10712
10729
  const all = [...latest.values()].sort((a, b) => compareIso(a.created_at, b.created_at));
10713
10730
  return {
10714
- open: all.filter(unsettled).map((fm, n) => ({
10731
+ open: all.filter((fm) => unsettled(fm) && !settled.has(fm.id)).map((fm, n) => ({
10715
10732
  ref: `P${n + 1}`,
10716
10733
  id: fm.id,
10717
10734
  title: fm.title ?? fm.slug,
@@ -11204,6 +11221,26 @@ async function readReviewEvidence(projectRoot) {
11204
11221
  }
11205
11222
  return out2;
11206
11223
  }
11224
+ async function verifiedDesignerFixes(projectRoot, before) {
11225
+ const addressed = (await listDesignerFindings(projectRoot)).map((f) => f.frontmatter).filter((fm) => fm.triage_status === "addressed" && !fm.design_cause_of);
11226
+ const out2 = /* @__PURE__ */ new Set();
11227
+ if (addressed.length === 0)
11228
+ return out2;
11229
+ const cutoff = toMillis(before);
11230
+ const runs = (await readRunEvidence(projectRoot)).filter((r) => {
11231
+ if (cutoff === null)
11232
+ return true;
11233
+ const start2 = toMillis(r.started_at);
11234
+ return start2 !== null && start2 < cutoff;
11235
+ });
11236
+ const states = deriveFixStates(addressed, runs);
11237
+ for (const fm of addressed) {
11238
+ if (fm.fix_status === "verified_resolved" || states.get(fm.slug)?.fix_state === "verified") {
11239
+ out2.add(fm.id);
11240
+ }
11241
+ }
11242
+ return out2;
11243
+ }
11207
11244
  async function computeFixStates(projectRoot) {
11208
11245
  const { entities } = await listEntities(projectRoot, "finding");
11209
11246
  const findings2 = entities.map((e) => e.frontmatter);
@@ -12265,6 +12302,7 @@ async function canonicalFindingFiles(projectRoot, scenarioSlug, canonicalId) {
12265
12302
  return out2;
12266
12303
  }
12267
12304
  async function markAddressedResolved(files, note) {
12305
+ const failed = [];
12268
12306
  const trimmedNote = note?.trim();
12269
12307
  for (const f of files) {
12270
12308
  if (f.triage_status !== "addressed")
@@ -12280,9 +12318,14 @@ async function markAddressedResolved(files, note) {
12280
12318
  if (updated !== raw)
12281
12319
  await atomicWriteFile(f.file, updated);
12282
12320
  });
12283
- } catch {
12321
+ } catch (err) {
12322
+ failed.push({
12323
+ file: f.file,
12324
+ error: err instanceof Error ? err.message : String(err)
12325
+ });
12284
12326
  }
12285
12327
  }
12328
+ return failed;
12286
12329
  }
12287
12330
  function originOf(url) {
12288
12331
  if (!url)
@@ -12465,7 +12508,9 @@ async function updateFixStatus(projectRoot, slug, status2) {
12465
12508
  await atomicWriteFile(filePath, updated);
12466
12509
  }
12467
12510
  });
12468
- } catch {
12511
+ return null;
12512
+ } catch (err) {
12513
+ return err instanceof Error ? err.message : String(err);
12469
12514
  }
12470
12515
  }
12471
12516
  async function listRunsInternal(projectRoot) {
@@ -12915,6 +12960,7 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12915
12960
  body: form,
12916
12961
  signal: controller.signal
12917
12962
  });
12963
+ const clockOffset = serverClockOffset(response);
12918
12964
  if (response.ok) {
12919
12965
  let body = null;
12920
12966
  try {
@@ -12960,7 +13006,8 @@ async function mirrorLocalWalkthrough(projectRoot, runId, deps) {
12960
13006
  runCloudId: parsed.data.cloud_walkthrough_id,
12961
13007
  contentHash,
12962
13008
  cloudOutcome: parsed.data.status,
12963
- ...recordedFindings.length > 0 ? { recordedFindings } : {}
13009
+ ...recordedFindings.length > 0 ? { recordedFindings } : {},
13010
+ ...clockOffset !== null ? { serverClockOffsetMs: clockOffset } : {}
12964
13011
  };
12965
13012
  }
12966
13013
  if (response.status === 409) {
@@ -13607,6 +13654,7 @@ async function drainRetryQueue(projectRoot, deps) {
13607
13654
  const deleteUrl = projectEndpoint(config.cloud_api_url, config.cloud_project_id, "delete");
13608
13655
  const remaining = [];
13609
13656
  const failures = [];
13657
+ const pushedRuns = [];
13610
13658
  let drained = 0;
13611
13659
  let dropped = 0;
13612
13660
  let notAttempted = 0;
@@ -13718,6 +13766,8 @@ async function drainRetryQueue(projectRoot, deps) {
13718
13766
  });
13719
13767
  if (result.mirrored || result.skippedReason === "bundle_too_large") {
13720
13768
  drained++;
13769
+ if (result.status === "pushed")
13770
+ pushedRuns.push(entry.entity_id);
13721
13771
  } else if (result.skippedReason === "missing_run" || result.skippedReason === "unsupported") {
13722
13772
  drained++;
13723
13773
  } else if (result.skippedReason === "cloud_conflict") {
@@ -13809,7 +13859,8 @@ async function drainRetryQueue(projectRoot, deps) {
13809
13859
  dropped,
13810
13860
  remaining: after.length,
13811
13861
  failures,
13812
- warnings
13862
+ warnings,
13863
+ ...pushedRuns.length > 0 ? { pushedRuns } : {}
13813
13864
  };
13814
13865
  }
13815
13866
  function unreadableBody(text, isDelete) {
@@ -14233,6 +14284,18 @@ async function pushOneBatch(target, batch, byType, label, opts = {}) {
14233
14284
  out2.errors.push(failure.message);
14234
14285
  return out2;
14235
14286
  }
14287
+ function cloudTokenOrigin(hash, humanTokens) {
14288
+ if (hash.startsWith("cloud-sync-"))
14289
+ return "projection";
14290
+ if (hash.startsWith("cloud-"))
14291
+ return humanTokens ? "person" : "unknown";
14292
+ if (hash.startsWith("cloud:"))
14293
+ return "unknown";
14294
+ return null;
14295
+ }
14296
+ function isCloudToken(hash) {
14297
+ return cloudTokenOrigin(hash, false) !== null;
14298
+ }
14236
14299
  async function ownEarlierWrites(projectRoot, batch, conflicts) {
14237
14300
  if (conflicts.length === 0)
14238
14301
  return [];
@@ -14246,7 +14309,7 @@ async function ownEarlierWrites(projectRoot, batch, conflicts) {
14246
14309
  const entity = byId.get(c.entity_id);
14247
14310
  if (!entity || c.base_hash || bases[c.entity_id])
14248
14311
  continue;
14249
- if (!c.cloud_hash || c.cloud_hash.startsWith("cloud:"))
14312
+ if (!c.cloud_hash || isCloudToken(c.cloud_hash))
14250
14313
  continue;
14251
14314
  if (!sent[c.entity_id]?.includes(c.cloud_hash))
14252
14315
  continue;
@@ -14255,10 +14318,59 @@ async function ownEarlierWrites(projectRoot, batch, conflicts) {
14255
14318
  }
14256
14319
  return out2;
14257
14320
  }
14321
+ function findingRuns(frontmatter) {
14322
+ const out2 = /* @__PURE__ */ new Set();
14323
+ if (typeof frontmatter.run === "string" && frontmatter.run) {
14324
+ out2.add(frontmatter.run);
14325
+ }
14326
+ const seen = frontmatter.seen_in;
14327
+ if (typeof seen === "string") {
14328
+ const m = /^(?:ux|designer):(.+)$/.exec(seen);
14329
+ if (m?.[1])
14330
+ out2.add(m[1]);
14331
+ }
14332
+ return out2;
14333
+ }
14334
+ async function rowsToAdopt(projectRoot, batch, conflicts, adopt) {
14335
+ if (!adopt || conflicts.length === 0)
14336
+ return [];
14337
+ const bases = await readBaseHashes(projectRoot);
14338
+ const byId = new Map(batch.map((e) => [e.id, e]));
14339
+ const out2 = [];
14340
+ for (const c of conflicts) {
14341
+ const entity = byId.get(c.entity_id);
14342
+ if (!entity || entity.type !== "finding" || !c.cloud_hash)
14343
+ continue;
14344
+ const origin = cloudTokenOrigin(c.cloud_hash, adopt.humanTokens);
14345
+ if (origin === null || origin === "person")
14346
+ continue;
14347
+ if (origin === "unknown" && !stampedByThisPass(c, entity, adopt)) {
14348
+ continue;
14349
+ }
14350
+ await recordCloudBase(projectRoot, c.entity_id, c.cloud_hash);
14351
+ out2.push(origin === "projection" ? { entity, restamped: false, kind: "projection" } : {
14352
+ entity,
14353
+ restamped: Boolean(c.base_hash || bases[c.entity_id]),
14354
+ kind: "bundle"
14355
+ });
14356
+ }
14357
+ return out2;
14358
+ }
14359
+ function stampedByThisPass(c, entity, adopt) {
14360
+ const runs = findingRuns(entity.frontmatter);
14361
+ if (![...runs].some((r) => adopt.runs.has(r)))
14362
+ return false;
14363
+ const at = c.cloud_updated_at ? Date.parse(c.cloud_updated_at) : Number.NaN;
14364
+ const slack = adopt.serverClock ? ADOPT_CLOCK_SLACK_MS : 0;
14365
+ if (Number.isNaN(at) || at < adopt.since - slack)
14366
+ return false;
14367
+ return !adopt.identity || c.cloud_changed_by === adopt.identity;
14368
+ }
14258
14369
  async function pushBatches(target, batches, byType, opts = {}) {
14259
14370
  let pushed = 0;
14260
14371
  let enqueued = 0;
14261
14372
  const selfResolved = [];
14373
+ const adopted = [];
14262
14374
  const conflicts = [];
14263
14375
  const overwritten = [];
14264
14376
  const errors = [];
@@ -14280,19 +14392,42 @@ async function pushBatches(target, batches, byType, opts = {}) {
14280
14392
  conflicts,
14281
14393
  overwritten,
14282
14394
  errors,
14283
- selfResolved
14395
+ selfResolved,
14396
+ adopted
14284
14397
  };
14285
14398
  }
14286
14399
  absorb(first);
14287
14400
  let unresolved = first.conflicts;
14288
14401
  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`);
14402
+ const ownIds = new Set(own.map((e) => e.id));
14403
+ const basesBefore = opts.adopt ? await readCloudBase(target.projectRoot) : {};
14404
+ const fromBundles = await rowsToAdopt(target.projectRoot, batch, unresolved.filter((c) => !ownIds.has(c.entity_id)), opts.adopt);
14405
+ const resend = [...own, ...fromBundles.map((a) => a.entity)];
14406
+ if (resend.length > 0) {
14407
+ const resendIds = new Set(resend.map((e) => e.id));
14408
+ unresolved = unresolved.filter((c) => !resendIds.has(c.entity_id));
14409
+ const again = await pushOneBatch(target, resend, byType, `batch ${i} own-version retry`);
14293
14410
  absorb(again);
14294
14411
  conflicts.push(...again.conflicts);
14295
- selfResolved.push(...again.landed);
14412
+ const landed2 = new Set(again.landed);
14413
+ selfResolved.push(...own.filter((e) => landed2.has(e.id)).map((e) => e.id));
14414
+ for (const a of fromBundles) {
14415
+ if (landed2.has(a.entity.id)) {
14416
+ adopted.push({
14417
+ slug: a.entity.slug,
14418
+ kind: a.kind,
14419
+ restamped: a.restamped,
14420
+ serverClock: opts.adopt?.serverClock ?? false
14421
+ });
14422
+ continue;
14423
+ }
14424
+ const prior = basesBefore[a.entity.id];
14425
+ if (prior === void 0) {
14426
+ await forgetCloudBase(target.projectRoot, a.entity.id);
14427
+ } else {
14428
+ await recordCloudBase(target.projectRoot, a.entity.id, prior);
14429
+ }
14430
+ }
14296
14431
  }
14297
14432
  if (!opts.keepMine || unresolved.length === 0) {
14298
14433
  conflicts.push(...unresolved);
@@ -14345,8 +14480,17 @@ async function pushBatches(target, batches, byType, opts = {}) {
14345
14480
  }
14346
14481
  await forgetQueuedUpserts(target.projectRoot, revoked);
14347
14482
  }
14348
- return { pushed, enqueued, conflicts, overwritten, errors, selfResolved };
14483
+ return {
14484
+ pushed,
14485
+ enqueued,
14486
+ conflicts,
14487
+ overwritten,
14488
+ errors,
14489
+ selfResolved,
14490
+ adopted
14491
+ };
14349
14492
  }
14493
+ var ADOPT_CLOCK_SLACK_MS;
14350
14494
  var init_push = __esm({
14351
14495
  "../../packages/local-store/dist/sync/push.js"() {
14352
14496
  "use strict";
@@ -14354,6 +14498,7 @@ var init_push = __esm({
14354
14498
  init_server_reason();
14355
14499
  init_sync_tracking();
14356
14500
  init_types();
14501
+ ADOPT_CLOCK_SLACK_MS = 5e3;
14357
14502
  }
14358
14503
  });
14359
14504
 
@@ -14748,15 +14893,33 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14748
14893
  const hold = deps.capabilities === null;
14749
14894
  let held = 0;
14750
14895
  const recordedFindings = /* @__PURE__ */ new Set();
14896
+ const pushedRuns = /* @__PURE__ */ new Set();
14897
+ let serverClockOffsetMs = null;
14751
14898
  let runIds;
14752
14899
  try {
14753
14900
  runIds = await listRuns(projectRoot);
14754
14901
  } catch (err) {
14755
14902
  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 };
14903
+ return {
14904
+ summary,
14905
+ errors,
14906
+ withheldRuns,
14907
+ held,
14908
+ recordedFindings,
14909
+ pushedRuns,
14910
+ serverClockOffsetMs
14911
+ };
14757
14912
  }
14758
14913
  if (runIds.length === 0)
14759
- return { summary, errors, withheldRuns, held, recordedFindings };
14914
+ return {
14915
+ summary,
14916
+ errors,
14917
+ withheldRuns,
14918
+ held,
14919
+ recordedFindings,
14920
+ pushedRuns,
14921
+ serverClockOffsetMs
14922
+ };
14760
14923
  const lastPush = await readLastPush(projectRoot);
14761
14924
  const pendingWalkthroughQueueEntries = /* @__PURE__ */ new Set();
14762
14925
  const retryQueue = await readRetryQueue(projectRoot);
@@ -14823,6 +14986,9 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14823
14986
  });
14824
14987
  if (result.status === "pushed" || result.status === "already_pushed") {
14825
14988
  summary.pushed++;
14989
+ if (result.status === "pushed")
14990
+ pushedRuns.add(runId);
14991
+ serverClockOffsetMs ??= result.serverClockOffsetMs ?? null;
14826
14992
  for (const id of result.recordedFindings ?? [])
14827
14993
  recordedFindings.add(id);
14828
14994
  } else if (result.skippedReason === "bundle_too_large" || result.skippedReason === "unsupported" || result.skippedReason === "missing_run" || result.skippedReason === "cloud_conflict") {
@@ -14845,7 +15011,15 @@ async function syncWalkthroughs(projectRoot, deps, unpushableScenarios = /* @__P
14845
15011
  }
14846
15012
  }
14847
15013
  }
14848
- return { summary, errors, withheldRuns, held, recordedFindings };
15014
+ return {
15015
+ summary,
15016
+ errors,
15017
+ withheldRuns,
15018
+ held,
15019
+ recordedFindings,
15020
+ pushedRuns,
15021
+ serverClockOffsetMs
15022
+ };
14849
15023
  }
14850
15024
  var HOLD_SHAPES, WALKTHROUGH_SKIP_HINTS;
14851
15025
  var init_walkthroughs = __esm({
@@ -14895,6 +15069,7 @@ function mergePushProgress(target, outcome) {
14895
15069
  if (!outcome)
14896
15070
  return;
14897
15071
  target.selfResolved.push(...outcome.selfResolved ?? []);
15072
+ target.adopted.push(...outcome.adopted ?? []);
14898
15073
  target.pushed += outcome.pushed;
14899
15074
  target.enqueued += outcome.enqueued;
14900
15075
  target.conflicts.push(...outcome.conflicts);
@@ -14941,6 +15116,7 @@ async function pushDeferredFindings(projectRoot, capabilities, deferredFindings,
14941
15116
  return pushBatches(target, toBatches(retryFindings, batchSize), byType, pushOpts);
14942
15117
  }
14943
15118
  async function runSync(projectRoot, deps) {
15119
+ const startedAt = Date.now();
14944
15120
  const config = await readConfig(projectRoot);
14945
15121
  if (!config.cloud_linked) {
14946
15122
  return emptySyncResult([
@@ -14955,11 +15131,21 @@ async function runSync(projectRoot, deps) {
14955
15131
  const apiKey = deps.apiKey;
14956
15132
  const fetchFn = deps.fetch ?? globalThis.fetch;
14957
15133
  let capabilitiesFailure = "";
15134
+ let serverClockOffsetMs = null;
14958
15135
  const capabilities = deps.capabilities !== void 0 ? deps.capabilities : await fetchServerCapabilities(config.cloud_api_url, apiKey, fetchFn, deps.timeoutMs ?? DEFAULT_TIMEOUT_MS4, (detail) => {
14959
15136
  capabilitiesFailure = detail;
15137
+ }, (offset) => {
15138
+ serverClockOffsetMs = offset;
14960
15139
  });
14961
15140
  deps = { ...deps, capabilities };
14962
- const drain = await drainRetryQueue(projectRoot, deps);
15141
+ const { pushedRuns: drainedRuns = [], ...drain } = await drainRetryQueue(projectRoot, deps);
15142
+ const adoptFrom = (runs) => ({
15143
+ runs: /* @__PURE__ */ new Set([...drainedRuns, ...runs]),
15144
+ since: startedAt + (serverClockOffsetMs ?? 0),
15145
+ serverClock: serverClockOffsetMs !== null,
15146
+ humanTokens: cloudDistinguishesHumanEdits(capabilities),
15147
+ identity: deps.identity ?? null
15148
+ });
14963
15149
  const scanned = await scanPending(projectRoot);
14964
15150
  if (scanned.errors.length === 0) {
14965
15151
  await pruneSentHashes(projectRoot, scanned.seenIds);
@@ -14983,14 +15169,15 @@ async function runSync(projectRoot, deps) {
14983
15169
  const pushOpts = { keepMine: deps.keepMine };
14984
15170
  const guidance = scan.pending.filter((e) => e.type === "guidance");
14985
15171
  const nonGuidance = scan.pending.filter((e) => e.type !== "guidance");
14986
- const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, pushOpts);
15172
+ const phase1 = await pushBatches(target, toBatches(nonGuidance, batchSize), byType, { ...pushOpts, adopt: adoptFrom([]) });
14987
15173
  const progress = {
14988
15174
  pushed: phase1.pushed,
14989
15175
  enqueued: phase1.enqueued,
14990
15176
  conflicts: [...phase1.conflicts],
14991
15177
  overwritten: [...phase1.overwritten],
14992
15178
  errors,
14993
- selfResolved: [...phase1.selfResolved]
15179
+ selfResolved: [...phase1.selfResolved],
15180
+ adopted: [...phase1.adopted]
14994
15181
  };
14995
15182
  progress.errors.push(...phase1.errors);
14996
15183
  const notices = [];
@@ -15000,10 +15187,11 @@ async function runSync(projectRoot, deps) {
15000
15187
  const guidanceResult = await pushGuidanceBatch(target, guidance, batchSize, byType, { ...pushOpts, detectUnsupported: !cloudSupportsGuidance(capabilities) }, notices);
15001
15188
  mergePushProgress(progress, guidanceResult);
15002
15189
  const walkthroughs = await syncWalkthroughs(projectRoot, deps, scan.unpushableScenarios);
15190
+ serverClockOffsetMs ??= walkthroughs.serverClockOffsetMs;
15003
15191
  if (walkthroughs.errors.length > 0) {
15004
15192
  progress.errors.push(...walkthroughs.errors);
15005
15193
  }
15006
- const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, pushOpts, reportedRefErrors, progress.errors);
15194
+ const phase3 = capabilities === null ? null : await pushDeferredFindings(projectRoot, capabilities, deferredFindings, walkthroughs, target, batchSize, byType, { ...pushOpts, adopt: adoptFrom(walkthroughs.pushedRuns) }, reportedRefErrors, progress.errors);
15007
15195
  mergePushProgress(progress, phase3);
15008
15196
  if (capabilities === null) {
15009
15197
  const heldFindings = shaped.heldFindings + deferredFindings.size;
@@ -15024,6 +15212,15 @@ async function runSync(projectRoot, deps) {
15024
15212
  const deletion = await syncDeletions(projectRoot, config, deps);
15025
15213
  progress.errors.push(...deletion.errors);
15026
15214
  notices.push(...deletion.notices);
15215
+ for (const a of progress.adopted) {
15216
+ if (a.kind === "projection") {
15217
+ notices.push(`${a.slug}: kept the local version over the cloud's projected triage (automated, from a run's outcome)`);
15218
+ continue;
15219
+ }
15220
+ 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)";
15221
+ const clock = a.serverClock ? "" : "; the server sent no clock, so only rows stamped after this sync began by this machine's clock counted";
15222
+ notices.push(`${a.slug}: ${what}${clock}`);
15223
+ }
15027
15224
  if (progress.selfResolved.length > 0) {
15028
15225
  const n = progress.selfResolved.length;
15029
15226
  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 +19470,14 @@ async function buildRunDesignerInput(projectRoot, runId) {
19273
19470
  await chooseKeyFrames(rd, grouped.candidates);
19274
19471
  const screens = grouped.screens;
19275
19472
  const screenKeys = new Set(screens.map((s) => s.key));
19276
- const earlier = await designerFindingsOn(projectRoot, screenKeys, manifest?.created_at ?? null, target);
19473
+ const earlier = await designerFindingsOn(
19474
+ projectRoot,
19475
+ screenKeys,
19476
+ manifest?.created_at ?? null,
19477
+ target,
19478
+ // A fix a designer's review already verified is not asked about again.
19479
+ await verifiedDesignerFixes(projectRoot, manifest?.created_at ?? null)
19480
+ );
19277
19481
  for (const o of earlier.open)
19278
19482
  refs2[o.ref] = o.id;
19279
19483
  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 +19633,7 @@ var init_designer_input = __esm({
19429
19633
  init_designer_files();
19430
19634
  init_designer_ignored();
19431
19635
  init_designer_ledger();
19636
+ init_fix_state();
19432
19637
  init_walkthrough_canonical2();
19433
19638
  init_dist();
19434
19639
  LONG_VISIT = 6;
@@ -22381,6 +22586,69 @@ function stepNumberOf(s) {
22381
22586
  const m = /^(?:step-)?(\d+)$/i.exec(s.trim());
22382
22587
  return m ? Number(m[1]) : null;
22383
22588
  }
22589
+ async function writeDesignerFixStatus(projectRoot, runId, verification, checks) {
22590
+ const verified = [];
22591
+ const untriaged = [];
22592
+ const warnings = [];
22593
+ const out2 = { verified, untriaged, warnings };
22594
+ const resolved = new Set(verification.resolved);
22595
+ const persists = new Set(verification.persists);
22596
+ if (resolved.size + persists.size === 0)
22597
+ return out2;
22598
+ const notWritten = (what, err) => warnings.push(`could not update ${what}: ${err instanceof Error ? err.message : String(err)}; designer.json holds the verification`);
22599
+ let files;
22600
+ try {
22601
+ 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)));
22602
+ } catch (err) {
22603
+ notWritten("the checked findings' fix_status", err);
22604
+ return out2;
22605
+ }
22606
+ const checkOf = new Map(checks.map((c) => [c.canonical_id, c]));
22607
+ for (const cid of verification.resolved) {
22608
+ const mine = files.filter((fm) => fm.canonical_id === cid);
22609
+ if (mine.some((fm) => fm.triage_status === "addressed"))
22610
+ continue;
22611
+ const latest = mine.filter((fm) => fm.triage_status !== "dismissed").sort((a, b) => String(a.created_at ?? "").localeCompare(String(b.created_at ?? ""))).at(-1);
22612
+ if (latest) {
22613
+ untriaged.push({
22614
+ ref: checkOf.get(cid)?.ref ?? null,
22615
+ finding: latest.slug
22616
+ });
22617
+ }
22618
+ }
22619
+ let states;
22620
+ try {
22621
+ states = deriveFixStates(files, await readRunEvidence(projectRoot));
22622
+ } catch (err) {
22623
+ notWritten("the checked findings' fix_status", err);
22624
+ return out2;
22625
+ }
22626
+ const decidedHere = (slug, state) => {
22627
+ const s = states.get(slug);
22628
+ return s?.fix_state === state && s.decided_by === runId;
22629
+ };
22630
+ for (const fm of files) {
22631
+ if (fm.triage_status !== "addressed" || !fm.canonical_id)
22632
+ continue;
22633
+ if (resolved.has(fm.canonical_id) && decidedHere(fm.slug, "verified")) {
22634
+ const [failed] = await markAddressedResolved([
22635
+ {
22636
+ file: entityFilePath(projectRoot, "finding", fm.slug),
22637
+ triage_status: fm.triage_status
22638
+ }
22639
+ ], checkOf.get(fm.canonical_id)?.note ?? void 0);
22640
+ if (failed)
22641
+ notWritten(fm.slug, failed.error);
22642
+ else
22643
+ verified.push(fm.slug);
22644
+ } else if (persists.has(fm.canonical_id) && decidedHere(fm.slug, "recurred")) {
22645
+ const error = await updateFixStatus(projectRoot, fm.slug, "verified_persists");
22646
+ if (error)
22647
+ notWritten(fm.slug, error);
22648
+ }
22649
+ }
22650
+ return out2;
22651
+ }
22384
22652
  async function readRefs2(rd) {
22385
22653
  try {
22386
22654
  return JSON.parse(await fs49.readFile(designerRefsFile(rd), "utf8"));
@@ -22701,6 +22969,11 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22701
22969
  };
22702
22970
  await atomicWriteFile(file, `${JSON.stringify(record, null, 2)}
22703
22971
  `);
22972
+ const fixes = await writeDesignerFixStatus(projectRoot, runId, verification, checks);
22973
+ warnings.push(...fixes.warnings);
22974
+ for (const u of fixes.untriaged) {
22975
+ warnings.push(`checked ${u.ref ?? "?"}: ${u.finding} is gone but was never marked addressed; its fix_status is left alone (checked_but_untriaged)`);
22976
+ }
22704
22977
  const bySeverity = {};
22705
22978
  const ruleCount = groupDesignerRules(rules);
22706
22979
  for (const i of issues)
@@ -22714,7 +22987,9 @@ async function recordDesigner(projectRoot, runId, answer, opts) {
22714
22987
  proposals: proposals.written,
22715
22988
  rejected,
22716
22989
  warnings,
22717
- ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {}
22990
+ ...invalidChecked.length > 0 ? { invalid_checked: invalidChecked } : {},
22991
+ ...fixes.verified.length > 0 ? { verified: fixes.verified } : {},
22992
+ ...fixes.untriaged.length > 0 ? { checked_but_untriaged: fixes.untriaged } : {}
22718
22993
  };
22719
22994
  }
22720
22995
  var DESIGNER_CATEGORIES, steps, refs, designerAnswerSchema;
@@ -22724,13 +22999,14 @@ var init_designer_record = __esm({
22724
22999
  init_atomic();
22725
23000
  init_design_proposals();
22726
23001
  init_design_review();
22727
- init_paths();
22728
23002
  init_url_path();
22729
23003
  init_review_issues();
22730
23004
  init_designer_files();
22731
23005
  init_designer_input();
22732
23006
  init_designer_ledger();
22733
23007
  init_entity();
23008
+ init_fix_state();
23009
+ init_paths();
22734
23010
  init_walkthrough_canonical2();
22735
23011
  DESIGNER_CATEGORIES = [
22736
23012
  "hierarchy",
@@ -23847,15 +24123,15 @@ function resolveApiUrl(configured) {
23847
24123
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
23848
24124
  return url;
23849
24125
  }
23850
- function parseStore(raw, path77) {
24126
+ function parseStore(raw, path78) {
23851
24127
  let parsed;
23852
24128
  try {
23853
24129
  parsed = JSON.parse(raw);
23854
24130
  } catch (err) {
23855
- throw new CredentialsFileError(path77, "not valid JSON", err);
24131
+ throw new CredentialsFileError(path78, "not valid JSON", err);
23856
24132
  }
23857
24133
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
23858
- throw new CredentialsFileError(path77, "expected a JSON object");
24134
+ throw new CredentialsFileError(path78, "expected a JSON object");
23859
24135
  }
23860
24136
  return parsed;
23861
24137
  }
@@ -23863,26 +24139,26 @@ function isNotFound(err) {
23863
24139
  return typeof err === "object" && err !== null && err.code === "ENOENT";
23864
24140
  }
23865
24141
  async function readCredentialsStore() {
23866
- const path77 = getCredentialsPath();
24142
+ const path78 = getCredentialsPath();
23867
24143
  let raw;
23868
24144
  try {
23869
- raw = await readFile(path77, "utf-8");
24145
+ raw = await readFile(path78, "utf-8");
23870
24146
  } catch (err) {
23871
24147
  if (isNotFound(err)) return {};
23872
24148
  warnUnreadableStoreOnce(err);
23873
24149
  throw err;
23874
24150
  }
23875
24151
  try {
23876
- return parseStore(raw, path77);
24152
+ return parseStore(raw, path78);
23877
24153
  } catch (err) {
23878
24154
  warnUnreadableStoreOnce(err);
23879
24155
  throw err;
23880
24156
  }
23881
24157
  }
23882
24158
  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)}
24159
+ const path78 = getCredentialsPath();
24160
+ await mkdir(dirname(path78), { recursive: true, mode: 448 });
24161
+ await atomicWriteFile(path78, `${JSON.stringify(store, null, 2)}
23886
24162
  `, {
23887
24163
  mode: 384
23888
24164
  });
@@ -23910,16 +24186,16 @@ function resolveApiKey(apiUrl) {
23910
24186
  }
23911
24187
  }
23912
24188
  const normalized = url.replace(/\/+$/, "");
23913
- for (const path77 of candidatePaths) {
24189
+ for (const path78 of candidatePaths) {
23914
24190
  let raw;
23915
24191
  try {
23916
- raw = readFileSync3(path77, "utf-8");
24192
+ raw = readFileSync3(path78, "utf-8");
23917
24193
  } catch (err) {
23918
24194
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
23919
24195
  continue;
23920
24196
  }
23921
24197
  try {
23922
- const store = parseStore(raw, path77);
24198
+ const store = parseStore(raw, path78);
23923
24199
  const entry = store[url] ?? store[normalized] ?? store[`${normalized}/`];
23924
24200
  if (typeof entry !== "object" || entry === null) continue;
23925
24201
  const token = entry.token;
@@ -23960,12 +24236,12 @@ var init_credentials = __esm({
23960
24236
  * @param reason - What is wrong with it.
23961
24237
  * @param cause - Underlying parse error, if any.
23962
24238
  */
23963
- constructor(path77, reason, cause) {
24239
+ constructor(path78, reason, cause) {
23964
24240
  super(
23965
- `Credentials file ${path77} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
24241
+ `Credentials file ${path78} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
23966
24242
  {
23967
24243
  cause,
23968
- suggestedAction: `Delete ${path77} and run \`iamu auth login\`, or set IAMU_TOKEN.`
24244
+ suggestedAction: `Delete ${path78} and run \`iamu auth login\`, or set IAMU_TOKEN.`
23969
24245
  }
23970
24246
  );
23971
24247
  }
@@ -24831,8 +25107,8 @@ import { dirname as dirname3, join as join5, relative, resolve as resolve2 } fro
24831
25107
  import { fileURLToPath as fileURLToPath2 } from "url";
24832
25108
  import chalk6 from "chalk";
24833
25109
  function getVersion() {
24834
- if ("0.8.0-beta.5") {
24835
- return "0.8.0-beta.5";
25110
+ if ("0.8.0-beta.7") {
25111
+ return "0.8.0-beta.7";
24836
25112
  }
24837
25113
  try {
24838
25114
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -24955,10 +25231,10 @@ function hasGeneratedByMarker(content) {
24955
25231
  function withoutMarker(content) {
24956
25232
  return content.replace(/^generatedBy:[^\r\n]*\r?\n/m, "");
24957
25233
  }
24958
- function cliSkillState(path77, template) {
25234
+ function cliSkillState(path78, template) {
24959
25235
  let content;
24960
25236
  try {
24961
- content = readFileSync7(path77, "utf-8");
25237
+ content = readFileSync7(path78, "utf-8");
24962
25238
  } catch {
24963
25239
  return null;
24964
25240
  }
@@ -24966,9 +25242,9 @@ function cliSkillState(path77, template) {
24966
25242
  if (template === void 0) return "edited";
24967
25243
  return withoutMarker(content) === withoutMarker(template) ? "clean" : "edited";
24968
25244
  }
24969
- function isCliWrittenPrompt(path77) {
25245
+ function isCliWrittenPrompt(path78) {
24970
25246
  try {
24971
- const content = readFileSync7(path77, "utf-8");
25247
+ const content = readFileSync7(path78, "utf-8");
24972
25248
  return hasGeneratedByMarker(content) || /^#\s+iamu:/m.test(content) || /^description:\s*.*iamu/im.test(content);
24973
25249
  } catch {
24974
25250
  return false;
@@ -24978,10 +25254,10 @@ function planSkillFiles(hosts, projectRoot, version) {
24978
25254
  const skillTemplates = loadSkillTemplates(version);
24979
25255
  const files = [];
24980
25256
  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 });
25257
+ const removeIfClean = (host, path78, name) => {
25258
+ const state = cliSkillState(path78, skillTemplates.get(name));
25259
+ if (state === "clean") files.push({ host, path: path78, content: null });
25260
+ else if (state === "edited") keptEdited.push({ host, path: path78 });
24985
25261
  };
24986
25262
  for (const host of hosts) {
24987
25263
  if (pluginProvidesSkills(host, projectRoot)) {
@@ -25078,9 +25354,9 @@ async function findStaleSkills(projectRoot) {
25078
25354
  }
25079
25355
  return stale;
25080
25356
  }
25081
- async function removeSkillFile(path77) {
25082
- await rm(path77, { force: true });
25083
- await rmdir(dirname3(path77)).catch(() => {
25357
+ async function removeSkillFile(path78) {
25358
+ await rm(path78, { force: true });
25359
+ await rmdir(dirname3(path78)).catch(() => {
25084
25360
  });
25085
25361
  }
25086
25362
  async function installSkillsForHosts(hosts, projectRoot, version, opts) {
@@ -25882,29 +26158,29 @@ async function writeSyncMcpEntry(mcpPath) {
25882
26158
  return { removedLegacy };
25883
26159
  }
25884
26160
  async function registerSyncMcpForHost(host, projectRoot) {
25885
- const path77 = host.getMcpConfigPath(projectRoot);
25886
- if (path77 === null) {
26161
+ const path78 = host.getMcpConfigPath(projectRoot);
26162
+ if (path78 === null) {
25887
26163
  return {
25888
26164
  status: "manual",
25889
26165
  instruction: host.manualMcpSetup ?? `add an "iamu-sync" server that runs \`${SYNC_SERVER_COMMAND.join(" ")}\``
25890
26166
  };
25891
26167
  }
25892
26168
  try {
25893
- const { removedLegacy } = await writeSyncMcpEntry(path77);
25894
- return { status: "written", path: path77, removedLegacy };
26169
+ const { removedLegacy } = await writeSyncMcpEntry(path78);
26170
+ return { status: "written", path: path78, removedLegacy };
25895
26171
  } catch (err) {
25896
26172
  return {
25897
26173
  status: "failed",
25898
- path: path77,
26174
+ path: path78,
25899
26175
  message: err instanceof Error ? err.message : String(err)
25900
26176
  };
25901
26177
  }
25902
26178
  }
25903
26179
  async function removeLegacyMcpForHost(host, projectRoot) {
25904
- const path77 = host.getMcpConfigPath(projectRoot);
25905
- if (path77 === null) return null;
26180
+ const path78 = host.getMcpConfigPath(projectRoot);
26181
+ if (path78 === null) return null;
25906
26182
  try {
25907
- return await removeLegacyIamuEntry(path77) ? { status: "legacy_removed", path: path77, removedLegacy: true } : null;
26183
+ return await removeLegacyIamuEntry(path78) ? { status: "legacy_removed", path: path78, removedLegacy: true } : null;
25908
26184
  } catch {
25909
26185
  return null;
25910
26186
  }
@@ -33482,6 +33758,7 @@ __export(triage_exports, {
33482
33758
  triage: () => triage
33483
33759
  });
33484
33760
  import { promises as fs65 } from "fs";
33761
+ import path62 from "path";
33485
33762
  import chalk29 from "chalk";
33486
33763
  async function awaitingSteps(projectRoot, findings2) {
33487
33764
  const scenarios = /* @__PURE__ */ new Set();
@@ -33620,14 +33897,53 @@ async function triage(refs2, opts) {
33620
33897
  );
33621
33898
  return;
33622
33899
  }
33900
+ let note = opts.note;
33901
+ if (opts.noteFile !== void 0) {
33902
+ if (opts.note !== void 0) {
33903
+ commandError("Pass --note or --note-file, not both.", {
33904
+ ...opts,
33905
+ code: "invalid_field_combination",
33906
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note-file <path>`
33907
+ });
33908
+ return;
33909
+ }
33910
+ try {
33911
+ const text = opts.noteFile === "-" ? await readStdinText() : await fs65.readFile(
33912
+ path62.resolve(process.cwd(), opts.noteFile),
33913
+ "utf8"
33914
+ );
33915
+ note = text.trim();
33916
+ } catch (err) {
33917
+ commandError(
33918
+ `Could not read --note-file ${opts.noteFile}: ${err instanceof Error ? err.message : String(err)}`,
33919
+ {
33920
+ ...opts,
33921
+ code: "note_read_error",
33922
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33923
+ }
33924
+ );
33925
+ return;
33926
+ }
33927
+ if (!note) {
33928
+ commandError(`--note-file ${opts.noteFile} is empty`, {
33929
+ ...opts,
33930
+ code: "note_empty",
33931
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33932
+ });
33933
+ return;
33934
+ }
33935
+ }
33623
33936
  let reason = null;
33624
33937
  if (opts.reason !== void 0) {
33625
33938
  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
- });
33939
+ commandError(
33940
+ "--reason applies only to --status dismissed \u2014 to record why or what changed, use --note",
33941
+ {
33942
+ ...opts,
33943
+ code: "invalid_reason_usage",
33944
+ fix: `iamu triage ${refs2.join(" ")} --status ${status2.data} --note "<what changed>"`
33945
+ }
33946
+ );
33631
33947
  return;
33632
33948
  }
33633
33949
  if (!DISMISS_REASONS.includes(opts.reason)) {
@@ -33819,7 +34135,7 @@ async function triage(refs2, opts) {
33819
34135
  const rule = await createGuidanceRule(projectRoot, {
33820
34136
  kind: effectiveReason,
33821
34137
  rule: ruleText,
33822
- reason: opts.note,
34138
+ reason: note,
33823
34139
  scope,
33824
34140
  sourceCanonicalIds: allTargets.map((f) => f.frontmatter.canonical_id).filter((id) => Boolean(id && UUID_RE4.test(id)))
33825
34141
  });
@@ -33830,7 +34146,7 @@ async function triage(refs2, opts) {
33830
34146
  status: status2.data,
33831
34147
  reason: effectiveReason,
33832
34148
  duplicateOf: duplicateTarget,
33833
- ...opts.note !== void 0 ? { note: opts.note } : {},
34149
+ ...note !== void 0 ? { note } : {},
33834
34150
  by: "triage",
33835
34151
  cascade: true,
33836
34152
  guidance: guidance ? { id: guidance.id, all: isSweep } : null,
@@ -33915,6 +34231,7 @@ var init_triage = __esm({
33915
34231
  init_dist2();
33916
34232
  init_dist();
33917
34233
  init_agent_commands();
34234
+ init_answer_input();
33918
34235
  init_complete();
33919
34236
  init_context4();
33920
34237
  SCOPES = ["project", "outline", "capability"];
@@ -34382,7 +34699,7 @@ var init_browse_driver = __esm({
34382
34699
 
34383
34700
  // src/commands/browse-actions.ts
34384
34701
  import { existsSync as existsSync15 } from "fs";
34385
- import path62 from "path";
34702
+ import path63 from "path";
34386
34703
  function byRole(page, role, name, opts) {
34387
34704
  const loc = page.getByRole(role, { name, exact: false });
34388
34705
  if (opts.nth !== void 0) {
@@ -34683,7 +35000,7 @@ async function fillIndexedElement(page, index, text, submit) {
34683
35000
  await fillOrSelect(loc.first(), text, submit);
34684
35001
  }
34685
35002
  async function setFilesOnElement(el, filePath) {
34686
- const resolved = path62.resolve(filePath);
35003
+ const resolved = path63.resolve(filePath);
34687
35004
  if (SENSITIVE_UPLOAD_PATH.test(resolved)) {
34688
35005
  throw new Error(
34689
35006
  `Refusing to upload sensitive path: "${resolved}". Choose a test fixture file inside the project or temp directory.`
@@ -34916,7 +35233,7 @@ var init_browse_actions = __esm({
34916
35233
 
34917
35234
  // src/commands/step-numbering.ts
34918
35235
  import { open, readFile as readFile6, readdir, stat as stat2, unlink } from "fs/promises";
34919
- import path63 from "path";
35236
+ import path64 from "path";
34920
35237
  function reservationMarker() {
34921
35238
  const marker = {
34922
35239
  reservation: { pid: process.pid, ts: Date.now() }
@@ -34958,7 +35275,7 @@ async function peekNextStepNumber(stepsDir) {
34958
35275
  for (const f of files) {
34959
35276
  const m = /^(\d+)\.json$/.exec(f);
34960
35277
  if (!m) continue;
34961
- const fullPath = path63.join(stepsDir, f);
35278
+ const fullPath = path64.join(stepsDir, f);
34962
35279
  const st = await stat2(fullPath).catch(() => null);
34963
35280
  if (!st) continue;
34964
35281
  if (await isAbandonedReservation(fullPath, st)) {
@@ -34976,7 +35293,7 @@ async function reserveStepFile(stepsDir, limit) {
34976
35293
  if (candidate > limit.maxSteps) {
34977
35294
  throw new Error(stepLimitMessage(limit.maxSteps, limit.runId));
34978
35295
  }
34979
- const stepPath = path63.join(stepsDir, stepFileName(candidate));
35296
+ const stepPath = path64.join(stepsDir, stepFileName(candidate));
34980
35297
  try {
34981
35298
  const handle = await open(stepPath, "wx");
34982
35299
  try {
@@ -35022,7 +35339,7 @@ __export(step_exports, {
35022
35339
  });
35023
35340
  import { existsSync as existsSync16 } from "fs";
35024
35341
  import { copyFile, mkdir as mkdir4, rename, stat as stat3 } from "fs/promises";
35025
- import path64 from "path";
35342
+ import path65 from "path";
35026
35343
  import chalk31 from "chalk";
35027
35344
  function parseMaxSteps(val) {
35028
35345
  if (typeof val === "number" && Number.isFinite(val) && val > 0) return val;
@@ -35038,11 +35355,11 @@ function parseMaxSteps(val) {
35038
35355
  }
35039
35356
  async function recordStep(input) {
35040
35357
  const runDirPath = runDir(input.projectRoot, input.runId);
35041
- if (!existsSync16(path64.join(runDirPath, "walkthrough.json"))) {
35358
+ if (!existsSync16(path65.join(runDirPath, "walkthrough.json"))) {
35042
35359
  throw new Error(`Run not found: ${input.runId}`);
35043
35360
  }
35044
- const stepsDir = path64.join(runDirPath, "steps");
35045
- const screenshotsDir = path64.join(runDirPath, "screenshots");
35361
+ const stepsDir = path65.join(runDirPath, "steps");
35362
+ const screenshotsDir = path65.join(runDirPath, "screenshots");
35046
35363
  await mkdir4(stepsDir, { recursive: true });
35047
35364
  await mkdir4(screenshotsDir, { recursive: true });
35048
35365
  const shotSource = input.shot ? await existingShot(input.shot) : null;
@@ -35057,9 +35374,9 @@ async function recordStep(input) {
35057
35374
  let screenshotRef = null;
35058
35375
  if (shotSource) {
35059
35376
  const destName = `step-${stepNumber2}.png`;
35060
- const destPath = path64.join(screenshotsDir, destName);
35377
+ const destPath = path65.join(screenshotsDir, destName);
35061
35378
  finalScreenshotPath = destPath;
35062
- if (path64.resolve(destPath) !== shotSource) {
35379
+ if (path65.resolve(destPath) !== shotSource) {
35063
35380
  if (input.moveShot) {
35064
35381
  await rename(shotSource, destPath).catch(
35065
35382
  () => copyFile(shotSource, destPath)
@@ -35068,16 +35385,16 @@ async function recordStep(input) {
35068
35385
  await copyFile(shotSource, destPath);
35069
35386
  }
35070
35387
  }
35071
- screenshotRef = path64.posix.join("screenshots", destName);
35388
+ screenshotRef = path65.posix.join("screenshots", destName);
35072
35389
  }
35073
35390
  let fullRef = null;
35074
35391
  if (input.fullShot && existsSync16(input.fullShot)) {
35075
35392
  const fullName = `step-${stepNumber2}.full.png`;
35076
35393
  const moved = await rename(
35077
35394
  input.fullShot,
35078
- path64.join(screenshotsDir, fullName)
35395
+ path65.join(screenshotsDir, fullName)
35079
35396
  ).then(() => true).catch(() => false);
35080
- if (moved) fullRef = path64.posix.join("screenshots", fullName);
35397
+ if (moved) fullRef = path65.posix.join("screenshots", fullName);
35081
35398
  }
35082
35399
  parsedStep = runStepSchema.parse({
35083
35400
  step_number: stepNumber2,
@@ -35124,7 +35441,7 @@ async function recordStep(input) {
35124
35441
  };
35125
35442
  }
35126
35443
  async function existingShot(shot) {
35127
- const resolved = path64.resolve(shot);
35444
+ const resolved = path65.resolve(shot);
35128
35445
  const isFile = await stat3(resolved).then((s) => s.isFile()).catch(() => false);
35129
35446
  if (!isFile) {
35130
35447
  throw new Error(
@@ -35185,7 +35502,7 @@ async function stepCommand(runArg, opts) {
35185
35502
  {
35186
35503
  step_number: res.step.step_number,
35187
35504
  cancelled: true,
35188
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35505
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35189
35506
  action: res.step.action,
35190
35507
  note: res.step.note,
35191
35508
  url: res.step.url,
@@ -35213,7 +35530,7 @@ async function stepCommand(runArg, opts) {
35213
35530
  JSON.stringify(
35214
35531
  {
35215
35532
  step_number: res.step.step_number,
35216
- path: path64.relative(projectRoot, res.stepPath).split(path64.sep).join("/"),
35533
+ path: path65.relative(projectRoot, res.stepPath).split(path65.sep).join("/"),
35217
35534
  action: res.step.action,
35218
35535
  note: res.step.note,
35219
35536
  url: res.step.url,
@@ -35242,7 +35559,7 @@ async function stepCommand(runArg, opts) {
35242
35559
  console.log(` ${chalk31.dim("shot:")} ${res.step.screenshot}`);
35243
35560
  }
35244
35561
  console.log(
35245
- ` ${chalk31.dim("file:")} ${path64.relative(projectRoot, res.stepPath)}`
35562
+ ` ${chalk31.dim("file:")} ${path65.relative(projectRoot, res.stepPath)}`
35246
35563
  );
35247
35564
  console.log();
35248
35565
  } else {
@@ -35314,16 +35631,16 @@ import {
35314
35631
  import { rm as rm2 } from "fs/promises";
35315
35632
  import { createServer } from "net";
35316
35633
  import { homedir as homedir3 } from "os";
35317
- import path65 from "path";
35634
+ import path66 from "path";
35318
35635
  import chalk32 from "chalk";
35319
35636
  function stateDir() {
35320
- return path65.join(
35321
- process.env.IAMU_HOME ?? path65.join(homedir3(), ".iamu"),
35637
+ return path66.join(
35638
+ process.env.IAMU_HOME ?? path66.join(homedir3(), ".iamu"),
35322
35639
  "browse"
35323
35640
  );
35324
35641
  }
35325
35642
  function profileDir() {
35326
- return path65.join(stateDir(), "profile");
35643
+ return path66.join(stateDir(), "profile");
35327
35644
  }
35328
35645
  function privateDir(dir) {
35329
35646
  mkdirSync3(dir, { recursive: true, mode: 448 });
@@ -35334,7 +35651,7 @@ function privateDir(dir) {
35334
35651
  }
35335
35652
  function stateFile(run) {
35336
35653
  const key = run && STATE_KEY.test(run) && `${run}.json` !== SHARED_STATE ? `${run}.json` : SHARED_STATE;
35337
- return path65.join(stateDir(), key);
35654
+ return path66.join(stateDir(), key);
35338
35655
  }
35339
35656
  function loadState(file) {
35340
35657
  try {
@@ -35375,7 +35692,7 @@ function storedStates() {
35375
35692
  }
35376
35693
  const out2 = [];
35377
35694
  for (const name of names2.sort()) {
35378
- const file = path65.join(stateDir(), name);
35695
+ const file = path66.join(stateDir(), name);
35379
35696
  const state = loadState(file);
35380
35697
  if (state) out2.push({ state, file });
35381
35698
  }
@@ -35393,16 +35710,16 @@ function removeState(file) {
35393
35710
  }
35394
35711
  function runStillOpen(state, now = Date.now()) {
35395
35712
  if (!state.run || !state.project) return false;
35396
- const dir = path65.join(state.project, "iamu", ".runs", state.run);
35713
+ const dir = path66.join(state.project, "iamu", ".runs", state.run);
35397
35714
  try {
35398
- const manifestPath = path65.join(dir, "walkthrough.json");
35715
+ const manifestPath = path66.join(dir, "walkthrough.json");
35399
35716
  const manifest = JSON.parse(readFileSync10(manifestPath, "utf8"));
35400
35717
  if (typeof manifest.status !== "string" || !OPEN_RUN_STATUSES.has(manifest.status)) {
35401
35718
  return false;
35402
35719
  }
35403
35720
  let last = statSync3(manifestPath).mtimeMs;
35404
35721
  try {
35405
- last = Math.max(last, statSync3(path65.join(dir, "steps")).mtimeMs);
35722
+ last = Math.max(last, statSync3(path66.join(dir, "steps")).mtimeMs);
35406
35723
  } catch {
35407
35724
  }
35408
35725
  return now - last < RUN_IDLE_MS;
@@ -35501,7 +35818,7 @@ async function requestedViewport(opts) {
35501
35818
  let manifest;
35502
35819
  try {
35503
35820
  manifest = JSON.parse(
35504
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
35821
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
35505
35822
  );
35506
35823
  } catch {
35507
35824
  fail(
@@ -35680,7 +35997,7 @@ function computePageFingerprint(url, bodySnippet) {
35680
35997
  return createHash6("sha256").update(`${url}|${bodySnippet.trim().replace(/\s+/g, " ")}`).digest("hex").slice(0, 16);
35681
35998
  }
35682
35999
  function checkAndRecordStagnation(runDirPath, fingerprint, action) {
35683
- const cacheFile = path65.join(runDirPath, ".stagnation.json");
36000
+ const cacheFile = path66.join(runDirPath, ".stagnation.json");
35684
36001
  let previous = null;
35685
36002
  try {
35686
36003
  if (existsSync17(cacheFile)) {
@@ -35961,8 +36278,8 @@ async function actOnPage(opts, attached, state, fn, onPostShot) {
35961
36278
  let contentSnippet;
35962
36279
  if (opts.shot) {
35963
36280
  await waitForVisualStability(page);
35964
- screenshot = path65.resolve(opts.shot);
35965
- mkdirSync3(path65.dirname(screenshot), { recursive: true });
36281
+ screenshot = path66.resolve(opts.shot);
36282
+ mkdirSync3(path66.dirname(screenshot), { recursive: true });
35966
36283
  await page.screenshot({
35967
36284
  path: screenshot,
35968
36285
  fullPage: Boolean(opts.full),
@@ -36172,7 +36489,7 @@ function httpOrigin(raw) {
36172
36489
  function readTargetUrl(runDirPath) {
36173
36490
  try {
36174
36491
  const m = JSON.parse(
36175
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36492
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36176
36493
  );
36177
36494
  return typeof m.target_url === "string" ? m.target_url : null;
36178
36495
  } catch {
@@ -36184,7 +36501,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36184
36501
  let manifest = {};
36185
36502
  try {
36186
36503
  manifest = JSON.parse(
36187
- readFileSync10(path65.join(runDirPath, "walkthrough.json"), "utf8")
36504
+ readFileSync10(path66.join(runDirPath, "walkthrough.json"), "utf8")
36188
36505
  );
36189
36506
  } catch {
36190
36507
  }
@@ -36195,7 +36512,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36195
36512
  if (designer2.includes(key) || scrolled) {
36196
36513
  return { take: true, lastHeight: null };
36197
36514
  }
36198
- const stepsDir = path65.join(runDirPath, "steps");
36515
+ const stepsDir = path66.join(runDirPath, "steps");
36199
36516
  let names2 = [];
36200
36517
  try {
36201
36518
  names2 = readdirSync3(stepsDir).filter((n) => /^\d+\.json$/.test(n)).sort();
@@ -36206,7 +36523,7 @@ function fullFramePlan(runDirPath, url, scrolled) {
36206
36523
  for (const name of names2) {
36207
36524
  try {
36208
36525
  const earlier = JSON.parse(
36209
- readFileSync10(path65.join(stepsDir, name), "utf8")
36526
+ readFileSync10(path66.join(stepsDir, name), "utf8")
36210
36527
  );
36211
36528
  if (typeof earlier.url === "string" && stepScreenKey(earlier.url, target) === key) {
36212
36529
  seen = true;
@@ -36273,7 +36590,7 @@ function printStep(opts, record, projectRoot, stagnationWarning, challengeWarnin
36273
36590
  if (opts.json) {
36274
36591
  const doc = {
36275
36592
  step_number: step.step_number,
36276
- path: path65.relative(projectRoot, record.stepPath).split(path65.sep).join("/"),
36593
+ path: path66.relative(projectRoot, record.stepPath).split(path66.sep).join("/"),
36277
36594
  action: step.action,
36278
36595
  note: step.note,
36279
36596
  url: step.url,
@@ -36391,7 +36708,7 @@ async function clearStorage(opts, driver) {
36391
36708
  try {
36392
36709
  if (dir) {
36393
36710
  target = JSON.parse(
36394
- readFileSync10(path65.join(dir, "walkthrough.json"), "utf8")
36711
+ readFileSync10(path66.join(dir, "walkthrough.json"), "utf8")
36395
36712
  ).target_url;
36396
36713
  }
36397
36714
  } catch {
@@ -36578,15 +36895,15 @@ async function browseStep(args2, opts, driver) {
36578
36895
  ))
36579
36896
  return;
36580
36897
  const maxSteps = parseMaxSteps(opts.maxSteps);
36581
- if (await peekNextStepNumber(path65.join(runDirPath, "steps")) > maxSteps) {
36898
+ if (await peekNextStepNumber(path66.join(runDirPath, "steps")) > maxSteps) {
36582
36899
  return fail(stepLimitMessage(maxSteps, opts.run), opts);
36583
36900
  }
36584
- const pendingShot = path65.join(
36901
+ const pendingShot = path66.join(
36585
36902
  runDirPath,
36586
36903
  "screenshots",
36587
36904
  `.pending-${randomUUID11()}.png`
36588
36905
  );
36589
- const pendingFull = path65.join(
36906
+ const pendingFull = path66.join(
36590
36907
  runDirPath,
36591
36908
  "screenshots",
36592
36909
  `.pending-${randomUUID11()}.full.png`
@@ -38469,18 +38786,18 @@ __export(preflight_exports, {
38469
38786
  });
38470
38787
  import { spawnSync } from "child_process";
38471
38788
  import { existsSync as existsSync20, promises as fs67 } from "fs";
38472
- import path66 from "path";
38789
+ import path67 from "path";
38473
38790
  import chalk36 from "chalk";
38474
38791
  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"))) {
38792
+ if (existsSync20(path67.join(targetDir, "pnpm-lock.yaml"))) return "pnpm";
38793
+ if (existsSync20(path67.join(targetDir, "yarn.lock"))) return "yarn";
38794
+ if (existsSync20(path67.join(targetDir, "bun.lockb")) || existsSync20(path67.join(targetDir, "bun.lock"))) {
38478
38795
  return "bun";
38479
38796
  }
38480
38797
  return "npm";
38481
38798
  }
38482
38799
  async function detectPreflightCommand(targetDir) {
38483
- const pkgPath = path66.join(targetDir, "package.json");
38800
+ const pkgPath = path67.join(targetDir, "package.json");
38484
38801
  if (existsSync20(pkgPath)) {
38485
38802
  try {
38486
38803
  const raw = await fs67.readFile(pkgPath, "utf8");
@@ -38493,7 +38810,7 @@ async function detectPreflightCommand(targetDir) {
38493
38810
  } catch {
38494
38811
  }
38495
38812
  }
38496
- const tsconfigPath = path66.join(targetDir, "tsconfig.json");
38813
+ const tsconfigPath = path67.join(targetDir, "tsconfig.json");
38497
38814
  if (existsSync20(tsconfigPath)) {
38498
38815
  return { command: "npx", args: ["--no-install", "tsc", "--noEmit"] };
38499
38816
  }
@@ -38532,7 +38849,7 @@ async function runPreflightCheck(targetDir) {
38532
38849
  };
38533
38850
  }
38534
38851
  async function preflight(opts = {}) {
38535
- const targetDir = opts.cwd ? path66.resolve(opts.cwd) : process.cwd();
38852
+ const targetDir = opts.cwd ? path67.resolve(opts.cwd) : process.cwd();
38536
38853
  const result = await runPreflightCheck(targetDir);
38537
38854
  if (result.ok) {
38538
38855
  if (opts.json) {
@@ -39041,12 +39358,12 @@ __export(validate_exports, {
39041
39358
  });
39042
39359
  import { randomUUID as randomUUID12 } from "crypto";
39043
39360
  import { promises as fs68 } from "fs";
39044
- import path67 from "path";
39361
+ import path68 from "path";
39045
39362
  import chalk38 from "chalk";
39046
39363
  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);
39364
+ const rel = path68.relative(path68.join(projectRoot, "iamu"), filePath);
39365
+ if (rel.startsWith("..") || path68.isAbsolute(rel)) return null;
39366
+ const segments = rel.split(path68.sep);
39050
39367
  if (segments.length < 2) return null;
39051
39368
  return TYPE_BY_ALIAS.get(segments[0]) ?? null;
39052
39369
  }
@@ -39063,7 +39380,7 @@ function applyZodValidation(schema2, data, result) {
39063
39380
  }
39064
39381
  function validateFile(filePath, raw, projectRoot) {
39065
39382
  const result = { path: filePath, valid: true, errors: [] };
39066
- if (path67.basename(filePath) === "config.json") {
39383
+ if (path68.basename(filePath) === "config.json") {
39067
39384
  let parsedJson;
39068
39385
  try {
39069
39386
  parsedJson = JSON.parse(raw);
@@ -39090,11 +39407,11 @@ function validateFile(filePath, raw, projectRoot) {
39090
39407
  );
39091
39408
  return result;
39092
39409
  }
39093
- if (path67.basename(filePath) === "testability.md") {
39410
+ if (path68.basename(filePath) === "testability.md") {
39094
39411
  applyZodValidation(testabilityFrontmatterSchema, frontmatter, result);
39095
39412
  return result;
39096
39413
  }
39097
- if (path67.basename(filePath) === "context.yml") {
39414
+ if (path68.basename(filePath) === "context.yml") {
39098
39415
  applyZodValidation(contextFrontmatterSchema, frontmatter, result);
39099
39416
  const warnings = [];
39100
39417
  const hasNotes = Boolean(
@@ -39156,7 +39473,7 @@ async function buildRepairLookups(projectRoot) {
39156
39473
  }
39157
39474
  }
39158
39475
  const runScenario = /* @__PURE__ */ new Map();
39159
- const runsDir2 = path67.join(projectRoot, "iamu", ".runs");
39476
+ const runsDir2 = path68.join(projectRoot, "iamu", ".runs");
39160
39477
  let runIds = [];
39161
39478
  try {
39162
39479
  runIds = await fs68.readdir(runsDir2);
@@ -39166,7 +39483,7 @@ async function buildRepairLookups(projectRoot) {
39166
39483
  try {
39167
39484
  const manifest = JSON.parse(
39168
39485
  await fs68.readFile(
39169
- path67.join(runsDir2, runId, "walkthrough.json"),
39486
+ path68.join(runsDir2, runId, "walkthrough.json"),
39170
39487
  "utf8"
39171
39488
  )
39172
39489
  );
@@ -39197,7 +39514,7 @@ async function fixFindings(projectRoot, results, dryRun) {
39197
39514
  parsed.frontmatter,
39198
39515
  {
39199
39516
  ...lookups,
39200
- fileSlug: path67.basename(result.path, ".md"),
39517
+ fileSlug: path68.basename(result.path, ".md"),
39201
39518
  newId: randomUUID12,
39202
39519
  body: parsed.body
39203
39520
  }
@@ -39239,7 +39556,7 @@ async function validate(filePath, opts) {
39239
39556
  if (process.exitCode === 1) return;
39240
39557
  const results = [];
39241
39558
  if (filePath) {
39242
- const abs = path67.isAbsolute(filePath) ? filePath : path67.resolve(process.cwd(), filePath);
39559
+ const abs = path68.isAbsolute(filePath) ? filePath : path68.resolve(process.cwd(), filePath);
39243
39560
  try {
39244
39561
  const raw = await fs68.readFile(abs, "utf8");
39245
39562
  results.push(validateFile(abs, raw, projectRoot));
@@ -39255,7 +39572,7 @@ async function validate(filePath, opts) {
39255
39572
  return;
39256
39573
  }
39257
39574
  } else if (opts.run) {
39258
- const findingsDir = path67.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39575
+ const findingsDir = path68.join(projectRoot, "iamu", ENTITY_DIRS.finding);
39259
39576
  let names2 = [];
39260
39577
  try {
39261
39578
  names2 = await fs68.readdir(findingsDir);
@@ -39263,7 +39580,7 @@ async function validate(filePath, opts) {
39263
39580
  }
39264
39581
  for (const name of names2) {
39265
39582
  if (!name.endsWith(".md")) continue;
39266
- const abs = path67.join(findingsDir, name);
39583
+ const abs = path68.join(findingsDir, name);
39267
39584
  try {
39268
39585
  const raw = await fs68.readFile(abs, "utf8");
39269
39586
  const { frontmatter } = parseFrontmatter(raw);
@@ -39272,7 +39589,7 @@ async function validate(filePath, opts) {
39272
39589
  }
39273
39590
  } catch {
39274
39591
  results.push({
39275
- path: path67.relative(projectRoot, abs),
39592
+ path: path68.relative(projectRoot, abs),
39276
39593
  valid: false,
39277
39594
  errors: ["Cannot read file or parse frontmatter"]
39278
39595
  });
@@ -39305,7 +39622,7 @@ async function validate(filePath, opts) {
39305
39622
  } else {
39306
39623
  const typesToCheck = typeFilter ? ENTITY_TYPES.filter((t) => typeFilter.has(t)) : ENTITY_TYPES;
39307
39624
  for (const type2 of typesToCheck) {
39308
- const iamuTypeDir = path67.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39625
+ const iamuTypeDir = path68.join(projectRoot, "iamu", ENTITY_DIRS[type2]);
39309
39626
  let names2;
39310
39627
  try {
39311
39628
  names2 = await fs68.readdir(iamuTypeDir);
@@ -39314,13 +39631,13 @@ async function validate(filePath, opts) {
39314
39631
  }
39315
39632
  for (const name of names2) {
39316
39633
  if (!name.endsWith(".md")) continue;
39317
- const abs = path67.join(iamuTypeDir, name);
39634
+ const abs = path68.join(iamuTypeDir, name);
39318
39635
  try {
39319
39636
  const raw = await fs68.readFile(abs, "utf8");
39320
39637
  results.push(validateFile(abs, raw, projectRoot));
39321
39638
  } catch {
39322
39639
  results.push({
39323
- path: path67.relative(projectRoot, abs),
39640
+ path: path68.relative(projectRoot, abs),
39324
39641
  valid: false,
39325
39642
  errors: ["Cannot read file"]
39326
39643
  });
@@ -39328,7 +39645,7 @@ async function validate(filePath, opts) {
39328
39645
  }
39329
39646
  }
39330
39647
  for (const extraFile of ["context.yml", "testability.md", "config.json"]) {
39331
- const extraPath = path67.join(projectRoot, "iamu", extraFile);
39648
+ const extraPath = path68.join(projectRoot, "iamu", extraFile);
39332
39649
  try {
39333
39650
  const raw = await fs68.readFile(extraPath, "utf8");
39334
39651
  results.push(validateFile(extraPath, raw, projectRoot));
@@ -39354,19 +39671,19 @@ async function validate(filePath, opts) {
39354
39671
  valid,
39355
39672
  errors: results.filter((r) => !r.valid).flatMap(
39356
39673
  (r) => r.errors.map((e) => ({
39357
- path: path67.relative(projectRoot, r.path),
39674
+ path: path68.relative(projectRoot, r.path),
39358
39675
  message: e
39359
39676
  }))
39360
39677
  ),
39361
39678
  warnings: results.filter((r) => r.warnings && r.warnings.length > 0).flatMap(
39362
39679
  (r) => (r.warnings ?? []).map((w) => ({
39363
- path: path67.relative(projectRoot, r.path),
39680
+ path: path68.relative(projectRoot, r.path),
39364
39681
  message: w
39365
39682
  }))
39366
39683
  ),
39367
39684
  ...opts.fix ? {
39368
39685
  repaired: repaired.map((r) => ({
39369
- path: path67.relative(projectRoot, r.path),
39686
+ path: path68.relative(projectRoot, r.path),
39370
39687
  changes: r.repairs,
39371
39688
  valid: r.valid
39372
39689
  }))
@@ -39388,7 +39705,7 @@ async function validate(filePath, opts) {
39388
39705
  }
39389
39706
  let hasErrors = false;
39390
39707
  for (const r of results) {
39391
- const relPath = path67.relative(projectRoot, r.path);
39708
+ const relPath = path68.relative(projectRoot, r.path);
39392
39709
  if (r.valid) {
39393
39710
  if (r.warnings && r.warnings.length > 0) {
39394
39711
  console.log(` ${symbols.warning} ` + chalk38.yellow(relPath));
@@ -39583,7 +39900,7 @@ __export(new_entity_exports, {
39583
39900
  });
39584
39901
  import { randomUUID as randomUUID13 } from "crypto";
39585
39902
  import { readFile as readFile8 } from "fs/promises";
39586
- import path68 from "path";
39903
+ import path69 from "path";
39587
39904
  import chalk40 from "chalk";
39588
39905
  async function resolveBodyInput(opts, command, flag = "--body") {
39589
39906
  if (opts.bodyFile !== void 0) {
@@ -39596,7 +39913,7 @@ async function resolveBodyInput(opts, command, flag = "--body") {
39596
39913
  return null;
39597
39914
  }
39598
39915
  try {
39599
- const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path68.resolve(process.cwd(), opts.bodyFile), "utf8");
39916
+ const body = opts.bodyFile === "-" ? await readStdinText() : await readFile8(path69.resolve(process.cwd(), opts.bodyFile), "utf8");
39600
39917
  return { body };
39601
39918
  } catch (err) {
39602
39919
  commandError(
@@ -39846,7 +40163,7 @@ __export(set_entity_exports, {
39846
40163
  setEntity: () => setEntity
39847
40164
  });
39848
40165
  import { promises as fs69 } from "fs";
39849
- import path69 from "path";
40166
+ import path70 from "path";
39850
40167
  import chalk41 from "chalk";
39851
40168
  import { ZodError as ZodError2 } from "zod";
39852
40169
  function normalizeAuthStrategy(raw) {
@@ -40037,7 +40354,7 @@ async function setEntity(type2, slug, opts) {
40037
40354
  }
40038
40355
  raw = Buffer.concat(chunks).toString("utf8");
40039
40356
  } else {
40040
- raw = await fs69.readFile(path69.resolve(projectRoot, opts.file), "utf8");
40357
+ raw = await fs69.readFile(path70.resolve(projectRoot, opts.file), "utf8");
40041
40358
  }
40042
40359
  } catch (err) {
40043
40360
  commandError(
@@ -40623,7 +40940,7 @@ __export(edit_entity_exports, {
40623
40940
  });
40624
40941
  import { spawn as spawn2 } from "child_process";
40625
40942
  import { promises as fs70 } from "fs";
40626
- import path70 from "path";
40943
+ import path71 from "path";
40627
40944
  import chalk42 from "chalk";
40628
40945
  async function editEntity(type2, slug, opts = {}) {
40629
40946
  const ctx = await resolveProject(opts);
@@ -40647,7 +40964,7 @@ async function editEntity(type2, slug, opts = {}) {
40647
40964
  }
40648
40965
  const effectiveSlug = isSpecial ? type2 : slug;
40649
40966
  const resolvedPath = isSpecial ? type2 === "testability" ? testabilityPath(projectRoot) : contextPath(projectRoot) : await resolveEntityPath(projectRoot, type2, effectiveSlug);
40650
- const filePath = resolvedPath ?? path70.join(
40967
+ const filePath = resolvedPath ?? path71.join(
40651
40968
  projectRoot,
40652
40969
  "iamu",
40653
40970
  ENTITY_DIRS[type2],
@@ -40658,7 +40975,7 @@ async function editEntity(type2, slug, opts = {}) {
40658
40975
  await fs70.access(filePath);
40659
40976
  } catch {
40660
40977
  commandError(
40661
- `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path70.relative(projectRoot, filePath)}.`,
40978
+ `${type2}${isSpecial ? "" : ` "${effectiveSlug}"`} not found at ${path71.relative(projectRoot, filePath)}.`,
40662
40979
  {
40663
40980
  ...opts,
40664
40981
  code: "entity_not_found",
@@ -40669,7 +40986,7 @@ async function editEntity(type2, slug, opts = {}) {
40669
40986
  }
40670
40987
  if (!process.stdin.isTTY || process.env.CI) {
40671
40988
  commandError(
40672
- `Cannot open editor in a non-interactive environment. Edit the file directly: ${path70.relative(projectRoot, filePath)}`,
40989
+ `Cannot open editor in a non-interactive environment. Edit the file directly: ${path71.relative(projectRoot, filePath)}`,
40673
40990
  {
40674
40991
  ...opts,
40675
40992
  code: "non_interactive_environment",
@@ -40745,7 +41062,7 @@ async function editEntity(type2, slug, opts = {}) {
40745
41062
  console.log();
40746
41063
  console.log(
40747
41064
  chalk42.dim(
40748
- ` Fix the file and run: iamu validate ${path70.relative(projectRoot, filePath)}`
41065
+ ` Fix the file and run: iamu validate ${path71.relative(projectRoot, filePath)}`
40749
41066
  )
40750
41067
  );
40751
41068
  console.log();
@@ -40775,7 +41092,7 @@ async function editEntity(type2, slug, opts = {}) {
40775
41092
  {
40776
41093
  ...opts,
40777
41094
  code: "validation_error",
40778
- fix: `iamu validate ${path70.relative(projectRoot, filePath)}`
41095
+ fix: `iamu validate ${path71.relative(projectRoot, filePath)}`
40779
41096
  }
40780
41097
  );
40781
41098
  }
@@ -40944,7 +41261,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40944
41261
  const byKey = /* @__PURE__ */ new Map();
40945
41262
  const byPlaceView = /* @__PURE__ */ new Map();
40946
41263
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
40947
- function addSurface(state, place, view, path77, depth) {
41264
+ function addSurface(state, place, view, path78, depth) {
40948
41265
  const key = exploreSurfaceKey(place, view);
40949
41266
  const surface = {
40950
41267
  key,
@@ -40956,7 +41273,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
40956
41273
  headings: state.headings,
40957
41274
  fields: state.fields,
40958
41275
  depth,
40959
- path: path77,
41276
+ path: path78,
40960
41277
  actions: []
40961
41278
  };
40962
41279
  surfaces.push(surface);
@@ -40974,9 +41291,9 @@ ${surface.signature}`, key);
40974
41291
  });
40975
41292
  await settle(page, settleMs);
40976
41293
  }
40977
- async function replay(path77) {
40978
- if (!("goto" in (path77[0] ?? {}))) await goto(opts.startUrl);
40979
- for (const step of path77) {
41294
+ async function replay(path78) {
41295
+ if (!("goto" in (path78[0] ?? {}))) await goto(opts.startUrl);
41296
+ for (const step of path78) {
40980
41297
  if ("goto" in step) {
40981
41298
  await goto(step.goto);
40982
41299
  } else {
@@ -41103,8 +41420,8 @@ ${sig}`, key);
41103
41420
  if (stats.surfaces >= budget.max_surfaces) {
41104
41421
  stats.truncated = true;
41105
41422
  } else {
41106
- const path77 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41107
- addSurface(after, place, view, path77, surface.depth + 1);
41423
+ const path78 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
41424
+ addSurface(after, place, view, path78, surface.depth + 1);
41108
41425
  }
41109
41426
  }
41110
41427
  }
@@ -41374,7 +41691,7 @@ __export(explore_exports, {
41374
41691
  exploreList: () => exploreList,
41375
41692
  exploreRun: () => exploreRun
41376
41693
  });
41377
- import path71 from "path";
41694
+ import path72 from "path";
41378
41695
  import chalk43 from "chalk";
41379
41696
  function fail2(message, json2) {
41380
41697
  if (json2) console.error(JSON.stringify({ error: message }));
@@ -41514,7 +41831,7 @@ async function exploreRun(url, opts) {
41514
41831
  surfaces: result.surfaces
41515
41832
  };
41516
41833
  const file = await writeExploreMap(ctx.projectRoot, map2);
41517
- const rel = path71.relative(process.cwd(), file);
41834
+ const rel = path72.relative(process.cwd(), file);
41518
41835
  if (opts.json) {
41519
41836
  console.log(JSON.stringify({ id: map2.id, path: rel, stats: map2.stats }));
41520
41837
  return;
@@ -42710,7 +43027,7 @@ __export(new_finding_exports, {
42710
43027
  });
42711
43028
  import { randomUUID as randomUUID14 } from "crypto";
42712
43029
  import { access, readFile as readFile9 } from "fs/promises";
42713
- import path72 from "path";
43030
+ import path73 from "path";
42714
43031
  import chalk46 from "chalk";
42715
43032
  async function newFinding(opts) {
42716
43033
  const ctx = await resolveProject(opts);
@@ -42798,7 +43115,7 @@ async function newFinding(opts) {
42798
43115
  }
42799
43116
  try {
42800
43117
  await access(
42801
- path72.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
43118
+ path73.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
42802
43119
  );
42803
43120
  } catch {
42804
43121
  commandError(
@@ -42817,7 +43134,7 @@ async function newFinding(opts) {
42817
43134
  if (!scenarioSlug) {
42818
43135
  try {
42819
43136
  const manifestRaw = await readFile9(
42820
- path72.join(runDirPath, "walkthrough.json"),
43137
+ path73.join(runDirPath, "walkthrough.json"),
42821
43138
  "utf8"
42822
43139
  );
42823
43140
  const manifest = JSON.parse(manifestRaw);
@@ -42964,7 +43281,7 @@ __export(summary_exports, {
42964
43281
  summaryCommand: () => summaryCommand
42965
43282
  });
42966
43283
  import { promises as fs71 } from "fs";
42967
- import path73 from "path";
43284
+ import path74 from "path";
42968
43285
  import chalk47 from "chalk";
42969
43286
  function endedFrom(value) {
42970
43287
  if (!value || typeof value !== "object") return void 0;
@@ -42995,7 +43312,7 @@ async function summaryCommand(runIdArg, opts) {
42995
43312
  let openQuestions = [];
42996
43313
  let setupNotes = [];
42997
43314
  let model = opts.model?.trim() || void 0;
42998
- const summaryPath = path73.join(runDirPath, "summary.json");
43315
+ const summaryPath = path74.join(runDirPath, "summary.json");
42999
43316
  let blocked;
43000
43317
  let ended;
43001
43318
  try {
@@ -43015,7 +43332,7 @@ async function summaryCommand(runIdArg, opts) {
43015
43332
  if (opts.file === "-") {
43016
43333
  raw = await readStdinText();
43017
43334
  } else {
43018
- raw = await fs71.readFile(path73.resolve(projectRoot, opts.file), "utf8");
43335
+ raw = await fs71.readFile(path74.resolve(projectRoot, opts.file), "utf8");
43019
43336
  }
43020
43337
  const parsed = JSON.parse(raw);
43021
43338
  if (Array.isArray(parsed.what_worked)) {
@@ -43092,7 +43409,7 @@ async function summaryCommand(runIdArg, opts) {
43092
43409
  JSON.stringify(
43093
43410
  {
43094
43411
  run_id: runId,
43095
- path: path73.relative(projectRoot, summaryPath),
43412
+ path: path74.relative(projectRoot, summaryPath),
43096
43413
  summary: data
43097
43414
  },
43098
43415
  null,
@@ -43154,7 +43471,7 @@ __export(advise_exports, {
43154
43471
  readAdvisePrompt: () => readAdvisePrompt
43155
43472
  });
43156
43473
  import { existsSync as existsSync23, promises as fs72 } from "fs";
43157
- import path74 from "path";
43474
+ import path75 from "path";
43158
43475
  import chalk48 from "chalk";
43159
43476
  function readAdvisePrompt(projectRoot) {
43160
43477
  return readInstalledPrompt(projectRoot, "advise.md");
@@ -43212,7 +43529,7 @@ async function advise(runIdArg, opts = {}) {
43212
43529
  findings2 = input.findings.length;
43213
43530
  needed = findings2 >= 2;
43214
43531
  if (!needed) reason = "fewer than two open findings in the project";
43215
- promptPath = opts.promptFile ?? path74.join(analysisDir(projectRoot), "advise_prompt.md");
43532
+ promptPath = opts.promptFile ?? path75.join(analysisDir(projectRoot), "advise_prompt.md");
43216
43533
  } else {
43217
43534
  const runId = resolveRunId(projectRoot, runIdArg ?? opts.run);
43218
43535
  if (!requireRunId(runId, opts, "iamu advise <run-id>")) return;
@@ -43223,12 +43540,12 @@ async function advise(runIdArg, opts = {}) {
43223
43540
  input = await buildAdviseInput(projectRoot, runId, manifest.scenario_slug);
43224
43541
  refsDir = runDirPath;
43225
43542
  findings2 = input.findings.length;
43226
- const advised = existsSync23(path74.join(runDirPath, ADVICE_FILE));
43543
+ const advised = existsSync23(path75.join(runDirPath, ADVICE_FILE));
43227
43544
  needed = !manifest.blocked && findings2 > 0 && !advised;
43228
43545
  if (manifest.blocked) reason = "the run was blocked";
43229
43546
  else if (advised) reason = "the run is already advised";
43230
43547
  else if (findings2 === 0) reason = "no open findings to advise";
43231
- promptPath = opts.promptFile ?? path74.join(runDirPath, "advise_prompt.md");
43548
+ promptPath = opts.promptFile ?? path75.join(runDirPath, "advise_prompt.md");
43232
43549
  }
43233
43550
  if (needed) input = await withCodeIndex(projectRoot, input);
43234
43551
  const { bundle, refs: refs2 } = promptBundle(input);
@@ -43238,11 +43555,11 @@ async function advise(runIdArg, opts = {}) {
43238
43555
  promptSizeWarning(prompt)
43239
43556
  ].filter((w) => w !== null) : [];
43240
43557
  if (prompt) {
43241
- await fs72.mkdir(path74.dirname(promptPath), { recursive: true });
43558
+ await fs72.mkdir(path75.dirname(promptPath), { recursive: true });
43242
43559
  await fs72.writeFile(promptPath, prompt, "utf8");
43243
43560
  await fs72.mkdir(refsDir, { recursive: true });
43244
43561
  await fs72.writeFile(
43245
- path74.join(refsDir, ADVISE_REFS_FILE),
43562
+ path75.join(refsDir, ADVISE_REFS_FILE),
43246
43563
  `${JSON.stringify(refs2, null, 2)}
43247
43564
  `,
43248
43565
  "utf8"
@@ -43257,8 +43574,8 @@ async function advise(runIdArg, opts = {}) {
43257
43574
  ...reason ? { reason } : {},
43258
43575
  mode: opts.project ? "project" : "run",
43259
43576
  findings: findings2,
43260
- prompt_file: prompt ? path74.relative(projectRoot, promptPath).split(path74.sep).join("/") : null,
43261
- prompt_file_abs: prompt ? path74.resolve(promptPath) : null,
43577
+ prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43578
+ prompt_file_abs: prompt ? path75.resolve(promptPath) : null,
43262
43579
  prompt_chars: prompt?.length ?? 0,
43263
43580
  warnings,
43264
43581
  advise_prompt: prompt
@@ -43334,7 +43651,7 @@ async function adviseRecord(runIdArg, opts) {
43334
43651
  const { projectRoot } = ctx;
43335
43652
  if (opts.project) {
43336
43653
  const input2 = await readAnswerInput(opts, "advice", {
43337
- defaultPath: path74.join(analysisDir(projectRoot), "answer.json"),
43654
+ defaultPath: path75.join(analysisDir(projectRoot), "answer.json"),
43338
43655
  code: "missing_advice_input",
43339
43656
  fix: "iamu advise-record --project --data '<json>'"
43340
43657
  });
@@ -43366,7 +43683,7 @@ async function adviseRecord(runIdArg, opts) {
43366
43683
  if (!manifest) return;
43367
43684
  await logTiming(runDirPath, "advise-record", "start");
43368
43685
  const input = await readAnswerInput(opts, "advice", {
43369
- defaultPath: path74.join(runDir(projectRoot, runId), "advise_answer.json"),
43686
+ defaultPath: path75.join(runDir(projectRoot, runId), "advise_answer.json"),
43370
43687
  code: "missing_advice_input",
43371
43688
  fix: `iamu advise-record ${runId} --data '<json>'`
43372
43689
  });
@@ -43416,7 +43733,7 @@ async function directionsList(opts) {
43416
43733
  problem: d.texts.problem,
43417
43734
  change: d.texts.change,
43418
43735
  leading_practice: d.texts.leading_practice,
43419
- path: path74.relative(ctx.projectRoot, d.path)
43736
+ path: path75.relative(ctx.projectRoot, d.path)
43420
43737
  })),
43421
43738
  null,
43422
43739
  2
@@ -43565,7 +43882,7 @@ __export(designer_exports, {
43565
43882
  designerRecord: () => designerRecord
43566
43883
  });
43567
43884
  import { promises as fs73 } from "fs";
43568
- import path75 from "path";
43885
+ import path76 from "path";
43569
43886
  import chalk49 from "chalk";
43570
43887
  import { ZodError as ZodError3 } from "zod";
43571
43888
  function describeError(err) {
@@ -43585,7 +43902,7 @@ async function designer(runId, opts = {}) {
43585
43902
  const { projectRoot, runDirPath } = ctx;
43586
43903
  if (!await completedManifest2(runDirPath, runId, opts)) return;
43587
43904
  const input = await buildRunDesignerInput(projectRoot, runId);
43588
- const promptPath = path75.join(runDirPath, DESIGNER_PROMPT_FILE);
43905
+ const promptPath = path76.join(runDirPath, DESIGNER_PROMPT_FILE);
43589
43906
  const prompt = input.needed ? composeDesignPrompt(
43590
43907
  projectRoot,
43591
43908
  "designer.md",
@@ -43594,7 +43911,7 @@ async function designer(runId, opts = {}) {
43594
43911
  if (prompt) {
43595
43912
  await fs73.writeFile(promptPath, prompt, "utf8");
43596
43913
  await fs73.writeFile(
43597
- path75.join(runDirPath, DESIGNER_REFS_FILE),
43914
+ path76.join(runDirPath, DESIGNER_REFS_FILE),
43598
43915
  `${JSON.stringify(input.refs, null, 2)}
43599
43916
  `,
43600
43917
  "utf8"
@@ -43605,7 +43922,7 @@ async function designer(runId, opts = {}) {
43605
43922
  installedPromptWarning(projectRoot, "designer.md"),
43606
43923
  ...input.warnings
43607
43924
  ].filter((w) => w !== null) : [];
43608
- const answer = path75.join(runDirPath, DESIGNER_ANSWER_FILE);
43925
+ const answer = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
43609
43926
  const result = {
43610
43927
  needed: input.needed,
43611
43928
  reason: input.reason,
@@ -43621,11 +43938,11 @@ async function designer(runId, opts = {}) {
43621
43938
  title: f.title
43622
43939
  })),
43623
43940
  design_files: (input.bundle.design?.files ?? []).map((f) => f.entry),
43624
- prompt_file: prompt ? path75.relative(projectRoot, promptPath).split(path75.sep).join("/") : null,
43941
+ prompt_file: prompt ? path76.relative(projectRoot, promptPath).split(path76.sep).join("/") : null,
43625
43942
  prompt_file_abs: prompt ? promptPath : null,
43626
43943
  prompt_chars: prompt?.length ?? 0,
43627
43944
  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,
43945
+ 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
43946
  hint: input.judged_against === "general-craft" ? await designDocsHint(projectRoot) : null,
43630
43947
  // No design doc recorded or found, and the template not declined:
43631
43948
  // the run summary offers to start one (iamu-design).
@@ -43671,6 +43988,9 @@ function printDesignerRecord(res) {
43671
43988
  ` ${chalk49.cyan("\u25C6")} Proposed doc edit ${p.outcome}: ${p.slug} (iamu design proposals)`
43672
43989
  );
43673
43990
  }
43991
+ for (const slug of res.verified ?? []) {
43992
+ console.log(` ${chalk49.green("\u2713")} Fix verified: ${slug}`);
43993
+ }
43674
43994
  for (const r of res.rejected) {
43675
43995
  console.log(` ${chalk49.yellow("!")} Dropped ${r.id}: ${r.reason}`);
43676
43996
  }
@@ -43756,7 +44076,7 @@ async function designerRecordLocked(projectRoot, runDirPath, runId, manifest, op
43756
44076
  return;
43757
44077
  }
43758
44078
  const input = await readAnswerInput(opts, "designer's review", {
43759
- defaultPath: path75.join(runDirPath, DESIGNER_ANSWER_FILE),
44079
+ defaultPath: path76.join(runDirPath, DESIGNER_ANSWER_FILE),
43760
44080
  code: "missing_designer_answer",
43761
44081
  fix: `iamu designer-record ${runId} --data '<json>'`
43762
44082
  });
@@ -43888,7 +44208,7 @@ __export(post_walk_exports, {
43888
44208
  postWalk: () => postWalk
43889
44209
  });
43890
44210
  import { existsSync as existsSync24, promises as fs74 } from "fs";
43891
- import path76 from "path";
44211
+ import path77 from "path";
43892
44212
  import chalk50 from "chalk";
43893
44213
  async function captureStageJson(fn) {
43894
44214
  const captured = [];
@@ -43931,7 +44251,7 @@ async function postWalk(runIdArg, opts = {}) {
43931
44251
  }
43932
44252
  const runDirPath = requireRun(projectRoot, runId, opts);
43933
44253
  if (!runDirPath) return;
43934
- const manifestPath = path76.join(runDirPath, "walkthrough.json");
44254
+ const manifestPath = path77.join(runDirPath, "walkthrough.json");
43935
44255
  let manifest;
43936
44256
  try {
43937
44257
  manifest = JSON.parse(
@@ -43972,7 +44292,7 @@ async function postWalk(runIdArg, opts = {}) {
43972
44292
  return;
43973
44293
  }
43974
44294
  const recordedNow = [];
43975
- const matchesAnswerPath = path76.join(runDirPath, MATCH_ANSWER_FILE);
44295
+ const matchesAnswerPath = path77.join(runDirPath, MATCH_ANSWER_FILE);
43976
44296
  if (existsSync24(matchesAnswerPath)) {
43977
44297
  const res = await captureStageJson(
43978
44298
  () => matchRecord(runId, { file: matchesAnswerPath, json: true })
@@ -43983,7 +44303,7 @@ async function postWalk(runIdArg, opts = {}) {
43983
44303
  });
43984
44304
  }
43985
44305
  }
43986
- const judgeAnswerPath = path76.join(runDirPath, "judge_answer.json");
44306
+ const judgeAnswerPath = path77.join(runDirPath, "judge_answer.json");
43987
44307
  if (existsSync24(judgeAnswerPath)) {
43988
44308
  const res = await captureStageJson(
43989
44309
  () => evaluateRecord(runId, { file: judgeAnswerPath, json: true })
@@ -43994,8 +44314,8 @@ async function postWalk(runIdArg, opts = {}) {
43994
44314
  });
43995
44315
  }
43996
44316
  }
43997
- const adviseAnswerPath = path76.join(runDirPath, "advise_answer.json");
43998
- if (!opts.critic && !existsSync24(path76.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
44317
+ const adviseAnswerPath = path77.join(runDirPath, "advise_answer.json");
44318
+ if (!opts.critic && !existsSync24(path77.join(runDirPath, ADVICE_FILE)) && existsSync24(adviseAnswerPath)) {
43999
44319
  const res = await captureStageJson(
44000
44320
  () => adviseRecord(runId, { file: adviseAnswerPath, json: true })
44001
44321
  );
@@ -44005,7 +44325,7 @@ async function postWalk(runIdArg, opts = {}) {
44005
44325
  });
44006
44326
  }
44007
44327
  }
44008
- const designerAnswerPath = path76.join(runDirPath, DESIGNER_ANSWER_FILE);
44328
+ const designerAnswerPath = path77.join(runDirPath, DESIGNER_ANSWER_FILE);
44009
44329
  let designerWarnings = [];
44010
44330
  let designerInvalidChecked = [];
44011
44331
  if (!existingDesignerFile(runDirPath)) {
@@ -44037,9 +44357,9 @@ async function postWalk(runIdArg, opts = {}) {
44037
44357
  }
44038
44358
  }
44039
44359
  const matchInfo = await captureStageJson(() => match(runId, { json: true }));
44040
- const evalRecorded = existsSync24(path76.join(runDirPath, "verdicts.json"));
44360
+ const evalRecorded = existsSync24(path77.join(runDirPath, "verdicts.json"));
44041
44361
  const evalInfo = evalRecorded ? null : await captureStageJson(() => evaluate(runId, { json: true }));
44042
- const adviseRecorded = existsSync24(path76.join(runDirPath, ADVICE_FILE));
44362
+ const adviseRecorded = existsSync24(path77.join(runDirPath, ADVICE_FILE));
44043
44363
  const awaitingJudge = !opts.critic && !adviseRecorded && !evalRecorded;
44044
44364
  const adviseInfo = opts.critic || adviseRecorded || awaitingJudge ? null : await captureStageJson(() => advise(runId, { json: true }));
44045
44365
  const designerRecorded = Boolean(existingDesignerFile(runDirPath));
@@ -44538,7 +44858,10 @@ function registerFindingsCommands(program) {
44538
44858
  program.command("triage [findings...]").helpGroup("Findings:").description("Set findings' triage status").requiredOption(
44539
44859
  "--status <status>",
44540
44860
  "open, addressed, dismissed, deferred or wont_fix"
44541
- ).option("--note <text>", "Optional triage note or reason").option(
44861
+ ).option(
44862
+ "--note <text>",
44863
+ "Triage note, any status. For addressed: one line on what changed on screen, so the next run's matcher can confirm the fix"
44864
+ ).option("--note-file <path>", "Read the note from a file (- reads stdin)").option(
44542
44865
  "--reason <reason>",
44543
44866
  "Dismiss reason: not_relevant (default), walker_error, out_of_scope or duplicate"
44544
44867
  ).option(
@@ -44555,7 +44878,7 @@ function registerFindingsCommands(program) {
44555
44878
  "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
44879
  ).option("--no-guidance", "Dismiss without writing a tester guidance rule").option("--json", "Output as JSON").addHelpText(
44557
44880
  "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)."
44881
+ '\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
44882
  ).action(
44560
44883
  async (refs2, opts) => {
44561
44884
  const { triage: triage2 } = await Promise.resolve().then(() => (init_triage(), triage_exports));
@@ -44654,9 +44977,9 @@ function registerProjectCommands(program) {
44654
44977
  "after",
44655
44978
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
44656
44979
  ).action(
44657
- async (path77, opts) => {
44980
+ async (path78, opts) => {
44658
44981
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
44659
- await validate2(path77, opts);
44982
+ await validate2(path78, opts);
44660
44983
  }
44661
44984
  );
44662
44985
  program.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(