@iamu-dev/iamu 0.2.0-beta.5 → 0.2.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 +251 -28
- package/dist/eval/gemini-eval-cli.js +976 -0
- package/dist/index.js +251 -28
- package/dist/templates/prompts/walk.md +16 -4
- package/dist/templates/skills/iamu-new.md +8 -4
- package/dist/templates/skills/iamu-propose.md +1 -2
- package/dist/templates/skills/iamu-run.md +26 -11
- package/dist/templates/skills/iamu-setup.md +79 -62
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -199,17 +199,24 @@ function defaultConfig(params) {
|
|
|
199
199
|
cloud_api_url: null
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
var baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
202
|
+
var modelTierSchema, projectModelsConfigSchema, baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
203
203
|
var init_config = __esm({
|
|
204
204
|
"../../packages/protocol/dist/schemas/frontmatter/config.js"() {
|
|
205
205
|
"use strict";
|
|
206
206
|
init_common();
|
|
207
207
|
init_version();
|
|
208
|
+
modelTierSchema = z4.string().min(1).max(128).describe("Model role tier ('fast', 'thorough', 'inherit') or a specific model identifier.");
|
|
209
|
+
projectModelsConfigSchema = z4.object({
|
|
210
|
+
walker: modelTierSchema.optional(),
|
|
211
|
+
matcher: modelTierSchema.optional(),
|
|
212
|
+
judge: modelTierSchema.optional()
|
|
213
|
+
}).strict();
|
|
208
214
|
baseProjectFields = {
|
|
209
215
|
schema_version: z4.literal(SCHEMA_VERSION),
|
|
210
216
|
id: uuidSchema,
|
|
211
217
|
project_slug: slugSchema,
|
|
212
|
-
project_name: z4.string().min(1)
|
|
218
|
+
project_name: z4.string().min(1),
|
|
219
|
+
models: projectModelsConfigSchema.optional()
|
|
213
220
|
};
|
|
214
221
|
localOnlyConfig = z4.object({
|
|
215
222
|
...baseProjectFields,
|
|
@@ -816,7 +823,14 @@ var init_scenario = __esm({
|
|
|
816
823
|
reviews: z15.array(z15.string().min(1).max(64)).max(16).default([]),
|
|
817
824
|
baseline_score: z15.number().min(0).max(100).nullish(),
|
|
818
825
|
current_score: z15.number().min(0).max(100).nullish(),
|
|
819
|
-
target_score: z15.number().min(0).max(100).default(95)
|
|
826
|
+
target_score: z15.number().min(0).max(100).default(95),
|
|
827
|
+
/** Environment state preconditions and approximation guidance. */
|
|
828
|
+
state: z15.object({
|
|
829
|
+
/** Description of data/records expected to exist in the environment. */
|
|
830
|
+
description: z15.string().max(2048).nullish(),
|
|
831
|
+
/** Guidance on how to squint or approximate if exact records are absent. */
|
|
832
|
+
approximation: z15.string().max(2048).nullish()
|
|
833
|
+
}).nullish()
|
|
820
834
|
});
|
|
821
835
|
}
|
|
822
836
|
});
|
|
@@ -1153,7 +1167,21 @@ var init_walkthrough_bundle = __esm({
|
|
|
1153
1167
|
judge_prompt: z19.string().optional(),
|
|
1154
1168
|
judge_model: z19.string().optional()
|
|
1155
1169
|
}).optional()
|
|
1156
|
-
}).optional()
|
|
1170
|
+
}).optional(),
|
|
1171
|
+
models: z19.object({
|
|
1172
|
+
walker: z19.string().optional(),
|
|
1173
|
+
matcher: z19.string().optional(),
|
|
1174
|
+
judge: z19.string().optional(),
|
|
1175
|
+
walker_reported: z19.string().optional(),
|
|
1176
|
+
matcher_reported: z19.string().optional(),
|
|
1177
|
+
judge_reported: z19.string().optional(),
|
|
1178
|
+
walker_tier: z19.string().optional(),
|
|
1179
|
+
matcher_tier: z19.string().optional(),
|
|
1180
|
+
judge_tier: z19.string().optional(),
|
|
1181
|
+
execution_mode: z19.enum(["multi_agent", "single_agent"]).optional(),
|
|
1182
|
+
velocity_sec_per_step: z19.number().optional(),
|
|
1183
|
+
total_duration_sec: z19.number().optional()
|
|
1184
|
+
}).optional().describe("AI model configuration and reported runtime provenance for this walkthrough.")
|
|
1157
1185
|
});
|
|
1158
1186
|
walkthroughBundleSchema = z19.object({
|
|
1159
1187
|
manifest: bundleManifestSchema,
|
|
@@ -1376,6 +1404,7 @@ var init_walkthrough_canonical = __esm({
|
|
|
1376
1404
|
build: buildMetadataSchema.optional(),
|
|
1377
1405
|
viewport: runViewportSchema.optional(),
|
|
1378
1406
|
environment: z20.string().optional(),
|
|
1407
|
+
models: z20.record(z20.unknown()).optional(),
|
|
1379
1408
|
findings: z20.array(canonicalFindingSchema),
|
|
1380
1409
|
steps: z20.array(canonicalStepSchema),
|
|
1381
1410
|
human_tests: z20.array(canonicalHumanTestSchema),
|
|
@@ -7247,6 +7276,7 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
|
|
|
7247
7276
|
...manifest.build ? { build: manifest.build } : {},
|
|
7248
7277
|
...manifest.viewport ? { viewport: manifest.viewport } : {},
|
|
7249
7278
|
...manifest.environment ? { environment: manifest.environment } : {},
|
|
7279
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7250
7280
|
findings: canonicalFindings,
|
|
7251
7281
|
steps: canonicalSteps,
|
|
7252
7282
|
human_tests: humanTests,
|
|
@@ -7407,6 +7437,20 @@ function renderCanonicalMarkdown(canonical) {
|
|
|
7407
7437
|
lines.push(`- Viewport: ${canonical.viewport.device} (${canonical.viewport.width}\xD7${canonical.viewport.height})`);
|
|
7408
7438
|
}
|
|
7409
7439
|
lines.push(`- Started: ${canonical.provenance.started_at}`, `- Completed: ${canonical.provenance.completed_at}`, `- Walker prompt: ${canonical.provenance.walker_prompt.id}@${canonical.provenance.walker_prompt.version}`, `- Findings: ${canonical.findings.length}`, `- Steps: ${canonical.steps.length}`);
|
|
7440
|
+
const models = canonical.models;
|
|
7441
|
+
if (models) {
|
|
7442
|
+
const parts = [
|
|
7443
|
+
models.walker ? `Walker: ${models.walker}${models.walker_reported ? ` (${models.walker_reported})` : ""}` : "",
|
|
7444
|
+
models.judge ? `Judge: ${models.judge}${models.judge_reported ? ` (${models.judge_reported})` : ""}` : "",
|
|
7445
|
+
models.matcher ? `Matcher: ${models.matcher}` : ""
|
|
7446
|
+
].filter(Boolean);
|
|
7447
|
+
if (parts.length > 0) {
|
|
7448
|
+
lines.push(`- Models: ${parts.join(" \xB7 ")}`);
|
|
7449
|
+
}
|
|
7450
|
+
if (typeof models.velocity_sec_per_step === "number") {
|
|
7451
|
+
lines.push(`- Velocity: ~${models.velocity_sec_per_step}s/step (${models.total_duration_sec}s total)`);
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7410
7454
|
const what = canonical.what_worked;
|
|
7411
7455
|
if (what && what.length > 0) {
|
|
7412
7456
|
lines.push("", "## What worked");
|
|
@@ -7776,6 +7820,7 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
|
|
|
7776
7820
|
report_markdown: report3,
|
|
7777
7821
|
verdict: verdictArray,
|
|
7778
7822
|
judge_meta: judgeMeta,
|
|
7823
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7779
7824
|
...buildFields(manifest),
|
|
7780
7825
|
...await readCanonicalExtras(projectRoot, runId)
|
|
7781
7826
|
},
|
|
@@ -12266,15 +12311,6 @@ function mcpJson(cmd) {
|
|
|
12266
12311
|
2
|
|
12267
12312
|
);
|
|
12268
12313
|
}
|
|
12269
|
-
function vscodeMcpJson(cmd) {
|
|
12270
|
-
return JSON.stringify(
|
|
12271
|
-
{
|
|
12272
|
-
servers: { iamu: { type: "stdio", command: cmd[0], args: cmd.slice(1) } }
|
|
12273
|
-
},
|
|
12274
|
-
null,
|
|
12275
|
-
2
|
|
12276
|
-
);
|
|
12277
|
-
}
|
|
12278
12314
|
function detectHosts(projectRoot) {
|
|
12279
12315
|
return HOST_ADAPTERS.filter(
|
|
12280
12316
|
(host) => host.detectionPaths.some((p) => existsSync3(join3(projectRoot, p)))
|
|
@@ -12323,13 +12359,17 @@ var init_registry = __esm({
|
|
|
12323
12359
|
{
|
|
12324
12360
|
name: "GitHub Copilot",
|
|
12325
12361
|
value: "github-copilot",
|
|
12326
|
-
detectionPaths: [
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12362
|
+
detectionPaths: [
|
|
12363
|
+
".github/copilot-instructions.md",
|
|
12364
|
+
".github/skills",
|
|
12365
|
+
".copilot"
|
|
12366
|
+
],
|
|
12367
|
+
supportsSkills: true,
|
|
12368
|
+
getSkillsDir: (root) => join3(root, ".github", "skills"),
|
|
12369
|
+
getCommandPath: (root, id) => join3(root, ".github", "skills", `iamu-${id}`, "SKILL.md"),
|
|
12370
|
+
formatCommand: richFormat,
|
|
12371
|
+
getMcpConfigPath: (root) => join3(root, ".mcp.json"),
|
|
12372
|
+
formatMcpConfig: mcpJson
|
|
12333
12373
|
},
|
|
12334
12374
|
{
|
|
12335
12375
|
name: "Cline",
|
|
@@ -12511,6 +12551,14 @@ function isCliWrittenSkill(path28) {
|
|
|
12511
12551
|
return false;
|
|
12512
12552
|
}
|
|
12513
12553
|
}
|
|
12554
|
+
function isCliWrittenPrompt(path28) {
|
|
12555
|
+
try {
|
|
12556
|
+
const content = readFileSync3(path28, "utf-8");
|
|
12557
|
+
return content.includes("iamu") || /^generatedBy:/m.test(content);
|
|
12558
|
+
} catch {
|
|
12559
|
+
return false;
|
|
12560
|
+
}
|
|
12561
|
+
}
|
|
12514
12562
|
function planSkillFiles(hosts, projectRoot, version) {
|
|
12515
12563
|
const skillTemplates = loadSkillTemplates(version);
|
|
12516
12564
|
const files = [];
|
|
@@ -12539,6 +12587,19 @@ function planSkillFiles(hosts, projectRoot, version) {
|
|
|
12539
12587
|
}
|
|
12540
12588
|
}
|
|
12541
12589
|
}
|
|
12590
|
+
if (host.value === "github-copilot") {
|
|
12591
|
+
for (const cmd of commandsFromSkills(skillTemplates)) {
|
|
12592
|
+
const legacyPrompt = join4(
|
|
12593
|
+
projectRoot,
|
|
12594
|
+
".github",
|
|
12595
|
+
"prompts",
|
|
12596
|
+
`iamu-${cmd.id}.prompt.md`
|
|
12597
|
+
);
|
|
12598
|
+
if (isCliWrittenPrompt(legacyPrompt)) {
|
|
12599
|
+
files.push({ host, path: legacyPrompt, content: null });
|
|
12600
|
+
}
|
|
12601
|
+
}
|
|
12602
|
+
}
|
|
12542
12603
|
} else {
|
|
12543
12604
|
for (const cmd of commandsFromSkills(skillTemplates)) {
|
|
12544
12605
|
files.push({
|
|
@@ -12563,6 +12624,15 @@ function detectInstalledHosts(projectRoot) {
|
|
|
12563
12624
|
const skillDir = join4(host.getSkillsDir(projectRoot), "iamu-setup");
|
|
12564
12625
|
if (existsSync5(skillDir)) return true;
|
|
12565
12626
|
}
|
|
12627
|
+
if (host.value === "github-copilot") {
|
|
12628
|
+
const legacyPrompt = join4(
|
|
12629
|
+
projectRoot,
|
|
12630
|
+
".github",
|
|
12631
|
+
"prompts",
|
|
12632
|
+
"iamu-setup.prompt.md"
|
|
12633
|
+
);
|
|
12634
|
+
if (existsSync5(legacyPrompt)) return true;
|
|
12635
|
+
}
|
|
12566
12636
|
return false;
|
|
12567
12637
|
});
|
|
12568
12638
|
}
|
|
@@ -14413,6 +14483,18 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14413
14483
|
}
|
|
14414
14484
|
const build = detectBuild({ cwd: projectRoot, label: opts.build });
|
|
14415
14485
|
const environment = resolveEnvironment(opts.env, start2.url);
|
|
14486
|
+
const cfg = await readConfig(projectRoot).catch(() => null);
|
|
14487
|
+
const walker = opts.walker ?? process.env.IAMU_WALKER_MODEL ?? cfg?.models?.walker ?? "fast";
|
|
14488
|
+
const judge = opts.judge ?? process.env.IAMU_JUDGE_MODEL ?? cfg?.models?.judge ?? "thorough";
|
|
14489
|
+
const matcher = opts.matcher ?? process.env.IAMU_MATCHER_MODEL ?? cfg?.models?.matcher ?? "thorough";
|
|
14490
|
+
const models = {
|
|
14491
|
+
walker,
|
|
14492
|
+
judge,
|
|
14493
|
+
matcher,
|
|
14494
|
+
walker_tier: ["fast", "thorough", "inherit"].includes(walker) ? walker : "custom",
|
|
14495
|
+
judge_tier: ["fast", "thorough", "inherit"].includes(judge) ? judge : "custom",
|
|
14496
|
+
matcher_tier: ["fast", "thorough", "inherit"].includes(matcher) ? matcher : "custom"
|
|
14497
|
+
};
|
|
14416
14498
|
let revisit = [];
|
|
14417
14499
|
try {
|
|
14418
14500
|
revisit = await computeRevisitPlaces(projectRoot, scenarioSlug, start2.url);
|
|
@@ -14430,6 +14512,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14430
14512
|
status: "running",
|
|
14431
14513
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14432
14514
|
completed_at: null,
|
|
14515
|
+
models,
|
|
14433
14516
|
...build ? { build } : {},
|
|
14434
14517
|
...environment ? { environment } : {},
|
|
14435
14518
|
...revisit.length > 0 ? { revisit } : {},
|
|
@@ -14451,6 +14534,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14451
14534
|
...targetWarning ? { target_warning: targetWarning } : {},
|
|
14452
14535
|
persona_slug: fm.persona,
|
|
14453
14536
|
outline_slug: fm.outline,
|
|
14537
|
+
models,
|
|
14454
14538
|
build: build ?? null,
|
|
14455
14539
|
environment: environment ?? null,
|
|
14456
14540
|
revisit,
|
|
@@ -14469,6 +14553,9 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14469
14553
|
console.log(` ${chalk11.dim("persona:")} ${fm.persona}`);
|
|
14470
14554
|
console.log(` ${chalk11.dim("outline:")} ${fm.outline}`);
|
|
14471
14555
|
console.log(` ${chalk11.dim("target:")} ${start2.url}`);
|
|
14556
|
+
console.log(
|
|
14557
|
+
` ${chalk11.dim("models:")} walker: ${chalk11.cyan(walker)} \xB7 judge: ${chalk11.cyan(judge)} \xB7 matcher: ${chalk11.cyan(matcher)}`
|
|
14558
|
+
);
|
|
14472
14559
|
if (targetWarning) {
|
|
14473
14560
|
console.log(` ${symbols.warning} ${chalk11.yellow(targetWarning)}`);
|
|
14474
14561
|
}
|
|
@@ -16265,7 +16352,10 @@ var init_browse_actions = __esm({
|
|
|
16265
16352
|
usage: "goto <url>",
|
|
16266
16353
|
minArgs: 1,
|
|
16267
16354
|
run: async (p, args) => {
|
|
16268
|
-
await p.goto(args[0], {
|
|
16355
|
+
await p.goto(args[0], {
|
|
16356
|
+
waitUntil: "domcontentloaded",
|
|
16357
|
+
timeout: 1e4
|
|
16358
|
+
});
|
|
16269
16359
|
}
|
|
16270
16360
|
},
|
|
16271
16361
|
click: {
|
|
@@ -16426,6 +16516,12 @@ var init_browse_driver = __esm({
|
|
|
16426
16516
|
commandLine: (pid) => readProcessCommandLine(pid),
|
|
16427
16517
|
kill(pid) {
|
|
16428
16518
|
try {
|
|
16519
|
+
if (process.platform !== "win32") {
|
|
16520
|
+
try {
|
|
16521
|
+
process.kill(-pid, "SIGTERM");
|
|
16522
|
+
} catch {
|
|
16523
|
+
}
|
|
16524
|
+
}
|
|
16429
16525
|
process.kill(pid, "SIGTERM");
|
|
16430
16526
|
} catch {
|
|
16431
16527
|
}
|
|
@@ -16652,6 +16748,7 @@ __export(browse_exports, {
|
|
|
16652
16748
|
readBrowseState: () => readBrowseState,
|
|
16653
16749
|
shouldDisableSandbox: () => shouldDisableSandbox,
|
|
16654
16750
|
start: () => start,
|
|
16751
|
+
waitForVisualStability: () => waitForVisualStability,
|
|
16655
16752
|
withPage: () => withPage
|
|
16656
16753
|
});
|
|
16657
16754
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -16801,6 +16898,8 @@ function chromeArgs(opts, port, headless) {
|
|
|
16801
16898
|
`--window-size=${opts.width ?? "1280"},${opts.height ?? "900"}`,
|
|
16802
16899
|
"--no-first-run",
|
|
16803
16900
|
"--no-default-browser-check",
|
|
16901
|
+
"--password-store=basic",
|
|
16902
|
+
"--use-mock-keychain",
|
|
16804
16903
|
...headless ? ["--headless=new", "--disable-dev-shm-usage", "--disable-gpu"] : [],
|
|
16805
16904
|
...shouldDisableSandbox() ? ["--no-sandbox"] : [],
|
|
16806
16905
|
"about:blank"
|
|
@@ -16858,6 +16957,45 @@ async function stop(opts, driver) {
|
|
|
16858
16957
|
}
|
|
16859
16958
|
report2(opts, { stopped: true }, [` ${chalk20.green("\u2713")} Browser stopped`]);
|
|
16860
16959
|
}
|
|
16960
|
+
async function waitForVisualStability(page, maxWaitMs = 300) {
|
|
16961
|
+
let timer;
|
|
16962
|
+
try {
|
|
16963
|
+
const nodeTimeout = new Promise((resolve3) => {
|
|
16964
|
+
timer = setTimeout(resolve3, maxWaitMs + 100);
|
|
16965
|
+
});
|
|
16966
|
+
const browserWait = page.evaluate((maxWait) => {
|
|
16967
|
+
return new Promise((resolve3) => {
|
|
16968
|
+
const timeout = setTimeout(resolve3, maxWait);
|
|
16969
|
+
const done = () => {
|
|
16970
|
+
clearTimeout(timeout);
|
|
16971
|
+
resolve3();
|
|
16972
|
+
};
|
|
16973
|
+
const g = globalThis;
|
|
16974
|
+
if (typeof g.requestAnimationFrame !== "function") {
|
|
16975
|
+
done();
|
|
16976
|
+
return;
|
|
16977
|
+
}
|
|
16978
|
+
g.requestAnimationFrame(() => {
|
|
16979
|
+
g.requestAnimationFrame?.(() => {
|
|
16980
|
+
const animations = g.document?.getAnimations ? g.document.getAnimations().filter((a) => a.playState === "running" && a.finished) : [];
|
|
16981
|
+
if (animations.length === 0) {
|
|
16982
|
+
done();
|
|
16983
|
+
return;
|
|
16984
|
+
}
|
|
16985
|
+
Promise.allSettled(
|
|
16986
|
+
animations.map((a) => a.finished)
|
|
16987
|
+
).finally(done);
|
|
16988
|
+
});
|
|
16989
|
+
});
|
|
16990
|
+
});
|
|
16991
|
+
}, maxWaitMs).catch(() => {
|
|
16992
|
+
});
|
|
16993
|
+
await Promise.race([browserWait, nodeTimeout]);
|
|
16994
|
+
} catch {
|
|
16995
|
+
} finally {
|
|
16996
|
+
if (timer) clearTimeout(timer);
|
|
16997
|
+
}
|
|
16998
|
+
}
|
|
16861
16999
|
async function withPage(opts, driver, fn, target) {
|
|
16862
17000
|
const state = readBrowseState();
|
|
16863
17001
|
if (!state) {
|
|
@@ -16877,9 +17015,9 @@ async function withPage(opts, driver, fn, target) {
|
|
|
16877
17015
|
await fn(page);
|
|
16878
17016
|
await page.waitForLoadState("domcontentloaded").catch(() => {
|
|
16879
17017
|
});
|
|
16880
|
-
await page.waitForTimeout(400);
|
|
16881
17018
|
let screenshot = null;
|
|
16882
17019
|
if (opts.shot) {
|
|
17020
|
+
await waitForVisualStability(page);
|
|
16883
17021
|
screenshot = path22.resolve(opts.shot);
|
|
16884
17022
|
mkdirSync(path22.dirname(screenshot), { recursive: true });
|
|
16885
17023
|
await page.screenshot({ path: screenshot, fullPage: Boolean(opts.full) });
|
|
@@ -18438,13 +18576,21 @@ function defaultFrontmatter(type2, id, slug, name, opts) {
|
|
|
18438
18576
|
url: opts.url,
|
|
18439
18577
|
authority_level: opts.authority ?? "informational"
|
|
18440
18578
|
};
|
|
18441
|
-
case "scenario":
|
|
18579
|
+
case "scenario": {
|
|
18580
|
+
const stateObj = opts.stateDescription || opts.stateApproximation ? {
|
|
18581
|
+
state: {
|
|
18582
|
+
...opts.stateDescription ? { description: opts.stateDescription } : {},
|
|
18583
|
+
...opts.stateApproximation ? { approximation: opts.stateApproximation } : {}
|
|
18584
|
+
}
|
|
18585
|
+
} : {};
|
|
18442
18586
|
return {
|
|
18443
18587
|
...base,
|
|
18444
18588
|
outline: opts.outline ?? null,
|
|
18445
18589
|
persona: opts.persona ?? null,
|
|
18446
|
-
...opts.entry ? { entry_path: opts.entry } : {}
|
|
18590
|
+
...opts.entry ? { entry_path: opts.entry } : {},
|
|
18591
|
+
...stateObj
|
|
18447
18592
|
};
|
|
18593
|
+
}
|
|
18448
18594
|
default:
|
|
18449
18595
|
return base;
|
|
18450
18596
|
}
|
|
@@ -18838,6 +18984,17 @@ function composeWalkerPrompt(p) {
|
|
|
18838
18984
|
"",
|
|
18839
18985
|
"## First-person brief",
|
|
18840
18986
|
p.brief ?? "",
|
|
18987
|
+
...p.context ? ["", "## Product domain & regulatory context", p.context.trim()] : [],
|
|
18988
|
+
...p.state?.description || p.state?.approximation ? [
|
|
18989
|
+
"",
|
|
18990
|
+
"## Expected state & semantic squinting",
|
|
18991
|
+
p.state.description ? `Expected state: ${p.state.description}` : "",
|
|
18992
|
+
p.state.approximation ? `Approximation strategy: ${p.state.approximation}` : "",
|
|
18993
|
+
"Squinting Guardrails:",
|
|
18994
|
+
"\u2022 If the exact target record/option is not available, apply the approximation strategy above to pick the closest plausible entity within the domain flow.",
|
|
18995
|
+
"\u2022 Squinting does NOT permit drifting to unrelated settings, account profiles, or off-target pages. Stay strictly within the scenario journey.",
|
|
18996
|
+
"\u2022 If the system is completely empty or unseeded, and your persona role cannot create data, do NOT report false product bugs about the empty state. Note that preconditions are unseeded/blocked."
|
|
18997
|
+
].filter(Boolean) : [],
|
|
18841
18998
|
...revisitSection(p.revisit ?? []),
|
|
18842
18999
|
"",
|
|
18843
19000
|
"## Access",
|
|
@@ -18866,7 +19023,7 @@ function composeWalkerPrompt(p) {
|
|
|
18866
19023
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
18867
19024
|
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --json \u2014 then add the body (description + "## Recommendation") below the frontmatter of the file it returns.`,
|
|
18868
19025
|
"`--step <N>` is the step whose screenshot shows the problem (or strength): it is the evidence people see next to the finding, so record that step and its screenshot first, and pick the screenshot where the thing is actually visible.",
|
|
18869
|
-
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...]} \u2014 one sentence each, in the persona's language.`,
|
|
19026
|
+
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...], "model": "<your model identifier if known, e.g. claude-3-5-haiku, gemini-3.8-flash, or unspecified>"} \u2014 one sentence each, in the persona's language.`,
|
|
18870
19027
|
"",
|
|
18871
19028
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
18872
19029
|
"",
|
|
@@ -18986,7 +19143,8 @@ async function instructionsWalk(opts) {
|
|
|
18986
19143
|
const result = {
|
|
18987
19144
|
scenario: {
|
|
18988
19145
|
slug: sfm.slug,
|
|
18989
|
-
brief: scenario.body.trim() || null
|
|
19146
|
+
brief: scenario.body.trim() || null,
|
|
19147
|
+
state: sfm.state ?? null
|
|
18990
19148
|
},
|
|
18991
19149
|
persona: {
|
|
18992
19150
|
slug: sfm.persona,
|
|
@@ -19040,7 +19198,9 @@ async function instructionsWalk(opts) {
|
|
|
19040
19198
|
runDirPath,
|
|
19041
19199
|
projectRoot,
|
|
19042
19200
|
surface: opts.surface ?? "browse",
|
|
19043
|
-
viewport
|
|
19201
|
+
viewport,
|
|
19202
|
+
state: sfm.state ?? null,
|
|
19203
|
+
context: contextYml
|
|
19044
19204
|
}) : null
|
|
19045
19205
|
};
|
|
19046
19206
|
if (opts.json) {
|
|
@@ -19052,6 +19212,11 @@ async function instructionsWalk(opts) {
|
|
|
19052
19212
|
console.log(` ${chalk26.dim("scenario:")} ${sfm.slug}`);
|
|
19053
19213
|
console.log(` ${chalk26.dim("persona:")} ${pfm.name} (${pfm.role})`);
|
|
19054
19214
|
console.log(` ${chalk26.dim("outline:")} ${ofm.title}`);
|
|
19215
|
+
if (sfm.state?.description || sfm.state?.approximation) {
|
|
19216
|
+
console.log(
|
|
19217
|
+
` ${chalk26.dim("state:")} ${sfm.state.description ? sfm.state.description : sfm.state.approximation ?? ""}`
|
|
19218
|
+
);
|
|
19219
|
+
}
|
|
19055
19220
|
if (targetUrl) console.log(` ${chalk26.dim("target:")} ${targetUrl}`);
|
|
19056
19221
|
console.log(
|
|
19057
19222
|
` ${chalk26.dim("access:")} ${projectAccess || personaAccess ? `${projectAccess ? "project" : ""}${projectAccess && personaAccess ? " + " : ""}${personaAccess ? "persona" : ""}` : "not recorded (walker would run as whoever is signed in)"}`
|
|
@@ -19688,6 +19853,36 @@ async function complete(runId, opts) {
|
|
|
19688
19853
|
return;
|
|
19689
19854
|
}
|
|
19690
19855
|
const completedAt = pickCompletedAt(run.manifest);
|
|
19856
|
+
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
19857
|
+
let judgeReported;
|
|
19858
|
+
try {
|
|
19859
|
+
const verdictsRaw = await fs26.readFile(
|
|
19860
|
+
path26.join(run.runDirPath, "verdicts.json"),
|
|
19861
|
+
"utf8"
|
|
19862
|
+
);
|
|
19863
|
+
const parsed = JSON.parse(verdictsRaw);
|
|
19864
|
+
if (parsed.provenance?.model) {
|
|
19865
|
+
judgeReported = parsed.provenance.model;
|
|
19866
|
+
}
|
|
19867
|
+
} catch {
|
|
19868
|
+
}
|
|
19869
|
+
const stepCount = provenanceAudit.step_count;
|
|
19870
|
+
const durationSec = provenanceAudit.duration_seconds ?? (run.manifest.created_at && Date.parse(completedAt) > Date.parse(run.manifest.created_at) ? Math.round(
|
|
19871
|
+
(Date.parse(completedAt) - Date.parse(run.manifest.created_at)) / 1e3 * 10
|
|
19872
|
+
) / 10 : null);
|
|
19873
|
+
const velocitySec = provenanceAudit.avg_step_interval_seconds ?? (durationSec != null && stepCount > 1 ? Math.round(durationSec / (stepCount - 1) * 10) / 10 : null);
|
|
19874
|
+
const existingModels = run.manifest.models ?? {};
|
|
19875
|
+
const updatedModels = {
|
|
19876
|
+
...existingModels,
|
|
19877
|
+
...walkerSummary.model ? { walker_reported: walkerSummary.model } : {},
|
|
19878
|
+
...judgeReported ? { judge_reported: judgeReported } : {},
|
|
19879
|
+
...durationSec != null ? { total_duration_sec: durationSec } : {},
|
|
19880
|
+
...velocitySec != null ? { velocity_sec_per_step: velocitySec } : {}
|
|
19881
|
+
};
|
|
19882
|
+
run.manifest = {
|
|
19883
|
+
...run.manifest,
|
|
19884
|
+
models: updatedModels
|
|
19885
|
+
};
|
|
19691
19886
|
const assembled = await assembleRun(
|
|
19692
19887
|
run.projectRoot,
|
|
19693
19888
|
runId,
|
|
@@ -19848,6 +20043,17 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
19848
20043
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
19849
20044
|
console.log(` started ${chalk30.dim(manifest.created_at)}`);
|
|
19850
20045
|
console.log(` completed ${chalk30.dim(completedAt)}`);
|
|
20046
|
+
const models = manifest.models;
|
|
20047
|
+
if (models?.velocity_sec_per_step != null) {
|
|
20048
|
+
console.log(
|
|
20049
|
+
` velocity ${chalk30.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk30.dim(`(${models.total_duration_sec}s total)`)}`
|
|
20050
|
+
);
|
|
20051
|
+
}
|
|
20052
|
+
if (models && (models.walker || models.judge || models.matcher)) {
|
|
20053
|
+
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
20054
|
+
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
20055
|
+
console.log(` models ${chalk30.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
20056
|
+
}
|
|
19851
20057
|
console.log();
|
|
19852
20058
|
console.log(
|
|
19853
20059
|
` ${chalk30.dim("canonical:")} ${chalk30.dim(path26.relative(projectRoot, assembled.canonicalPath))}`
|
|
@@ -19941,9 +20147,11 @@ async function readWalkerSummary(runDirPath) {
|
|
|
19941
20147
|
await fs26.readFile(path26.join(runDirPath, "summary.json"), "utf8")
|
|
19942
20148
|
);
|
|
19943
20149
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
20150
|
+
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
19944
20151
|
return {
|
|
19945
20152
|
what_worked: strings(data.what_worked),
|
|
19946
|
-
open_questions: strings(data.open_questions)
|
|
20153
|
+
open_questions: strings(data.open_questions),
|
|
20154
|
+
model
|
|
19947
20155
|
};
|
|
19948
20156
|
} catch {
|
|
19949
20157
|
return {};
|
|
@@ -20684,6 +20892,12 @@ function registerProjectCommands(program) {
|
|
|
20684
20892
|
).option("--outline <slug>", "Outline slug (scenario only)").option("--persona <slug>", "Persona slug (scenario only)").option(
|
|
20685
20893
|
"--entry <path>",
|
|
20686
20894
|
"Where the situation starts, as a path on the product, e.g. /en/episodes/1 (scenario only)"
|
|
20895
|
+
).option(
|
|
20896
|
+
"--state-description <text>",
|
|
20897
|
+
"Assumed environment data/records state (scenario only)"
|
|
20898
|
+
).option(
|
|
20899
|
+
"--state-approximation <text>",
|
|
20900
|
+
"Guidance on how persona should approximate/squint if records are missing (scenario only)"
|
|
20687
20901
|
).option("--url <url>", "Document the source cites (source only, required)").option(
|
|
20688
20902
|
"--authority <level>",
|
|
20689
20903
|
"legislation | regulation | standard | company_policy | guideline | informational (source only, default informational)"
|
|
@@ -20725,6 +20939,15 @@ function registerWalkthroughCommands(program) {
|
|
|
20725
20939
|
).option(
|
|
20726
20940
|
"--env <name>",
|
|
20727
20941
|
"Environment the product runs in (default: the target URL's origin)"
|
|
20942
|
+
).option(
|
|
20943
|
+
"--walker <tierOrModel>",
|
|
20944
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for driving browser navigation (default: fast)"
|
|
20945
|
+
).option(
|
|
20946
|
+
"--judge <tierOrModel>",
|
|
20947
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for human test evaluation (default: thorough)"
|
|
20948
|
+
).option(
|
|
20949
|
+
"--matcher <tierOrModel>",
|
|
20950
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for finding matching (default: thorough)"
|
|
20728
20951
|
).option(
|
|
20729
20952
|
"--check",
|
|
20730
20953
|
"Enforce target URL reachability (fail fast if server is offline)"
|
|
@@ -70,11 +70,9 @@ Continuity: steps form ONE ongoing monologue. From step 2 onward, don't restate
|
|
|
70
70
|
|
|
71
71
|
## Pacing
|
|
72
72
|
|
|
73
|
-
**Aim for 10-20 steps.**
|
|
73
|
+
**Aim for 10-20 steps.** When evaluating a form or journey, inspect all relevant sections across the workflow (e.g., travel details, per diem, deductions, expenses). Do not abandon the walkthrough after logging a single defect — continue through the remaining sections to uncover all distinct compliance and usability gaps before completing. A complete walkthrough with sharp findings across all sections gives the team high-value signal.
|
|
74
74
|
|
|
75
|
-
Do
|
|
76
|
-
|
|
77
|
-
Do try key interactions that reveal something: click the main CTA, open a dropdown, check what filters exist. These are cheap observations with high signal.
|
|
75
|
+
Do try key interactions that reveal something: click the main CTA, open a dropdown, check what options or deductions exist. These are cheap observations with high signal.
|
|
78
76
|
|
|
79
77
|
Follow the persona's motivation. Spend time on what matters to them. When you have enough evidence to answer "would this person come back?" — you're done.
|
|
80
78
|
|
|
@@ -100,6 +98,20 @@ Your browser tooling has limitations that can look like product bugs. Before rec
|
|
|
100
98
|
|
|
101
99
|
Do NOT silently suppress findings you suspect are tooling issues. Record them with the caveat — the human triaging can verify.
|
|
102
100
|
|
|
101
|
+
## Expected state vs product bugs (Empty state & unseeded data)
|
|
102
|
+
|
|
103
|
+
When an environment is unseeded or test records are missing:
|
|
104
|
+
- An empty table or "No records found" screen is NOT automatically a product bug. If your scenario assumes existing data (e.g. reviewing past entries or auditing submitted claims), check whether the environment simply lacks seed data.
|
|
105
|
+
- If your persona role is a creator (e.g. employee or consultant creating an item), click the creation CTA (e.g. "+ Ny reseräkning", "+ New") and proceed with your task.
|
|
106
|
+
- If your persona role is read-only (e.g. an auditor or manager) and cannot create data, do NOT record false "missing data" bugs against the product. Conclude the walk noting that the environment lacks the required preconditions.
|
|
107
|
+
|
|
108
|
+
## Semantic squinting (Adapting to environmental mismatch)
|
|
109
|
+
|
|
110
|
+
Your prompt may include an approximation strategy (e.g. "If Malmö is missing, choose Göteborg").
|
|
111
|
+
- **Squint at the closest match:** When the exact target name, category, or record described in your brief isn't available in this environment, pick the closest available alternative that allows testing the workflow.
|
|
112
|
+
- **Stay in the journey:** Approximation is bounded. Never drift to unrelated modules (e.g. settings, profile, admin panels) just because a specific test record was absent.
|
|
113
|
+
- **Log the approximation:** In your step note (`note`), state clearly that you chose the closest alternative according to your instructions.
|
|
114
|
+
|
|
103
115
|
## Portal-rendered UI
|
|
104
116
|
|
|
105
117
|
Radix / Headless UI / Floating UI render dropdowns and listboxes into portals that do not appear in accessibility snapshots. If an expandable combobox looks empty after click, try: press ArrowDown then Enter, or evaluate a script in the page to probe the DOM. Take a fresh screenshot.
|
|
@@ -95,9 +95,13 @@ Human Checkpoint: <Concrete binary yes/partial/no question scored at evaluation>
|
|
|
95
95
|
Roles with Stakes:
|
|
96
96
|
• <Role 1>: <Stake>
|
|
97
97
|
• <Role 2>: <Stake>
|
|
98
|
+
|
|
99
|
+
Execution Preconditions (resolved at iamu-run):
|
|
100
|
+
• Assumed State: <What records/data should exist, e.g. "Department with existing risk register containing open hazards">
|
|
101
|
+
• Squint Guidance: <How the walker should approximate if exact records are missing, e.g. "If no packaging hazard exists, select any physical safety hazard and proceed with delegation">
|
|
98
102
|
```
|
|
99
103
|
|
|
100
|
-
Ask the user: "Ready to test this draft, or would you like to refine the focus question or circumstances? (Enter to accept, or type adjustments)"
|
|
104
|
+
Ask the user: "Ready to test this draft, or would you like to refine the focus question, preconditions, or circumstances? (Enter to accept, or type adjustments)"
|
|
101
105
|
|
|
102
106
|
### 4. Pick or propose personas
|
|
103
107
|
|
|
@@ -164,12 +168,12 @@ The `--role` and `--disposition` flags populate frontmatter directly. If `iamu/c
|
|
|
164
168
|
|
|
165
169
|
**4. Scenario** (the binding):
|
|
166
170
|
```bash
|
|
167
|
-
iamu new scenario --name "maria-gomez--busy-shift-handoff" --outline busy-shift-handoff --persona maria-gomez --json
|
|
171
|
+
iamu new scenario --name "maria-gomez--busy-shift-handoff" --outline busy-shift-handoff --persona maria-gomez [--entry /path/on/the/product] [--state-description "<Assumed state description>"] [--state-approximation "<Squint guidance for walker>"] --body "<First-person brief in persona voice (3–5 sentences)>" --json
|
|
168
172
|
```
|
|
169
173
|
When the situation begins on a specific page (the persona follows a link from a video, an email, a search result), add `--entry /path/on/the/product` — the run starts there instead of the home page.
|
|
170
|
-
`--outline` is required. `--persona` is optional (omit for drafts).
|
|
174
|
+
`--outline` is required. `--persona` is optional (omit for drafts). Pass `--state-description` and `--state-approximation` if preconditions were defined in step 3 to populate the scenario's `state:` frontmatter automatically.
|
|
171
175
|
|
|
172
|
-
For drafts (no persona): omit `--persona`, omit
|
|
176
|
+
For drafts (no persona): omit `--persona`, omit `--body`.
|
|
173
177
|
|
|
174
178
|
### 7. Summarize
|
|
175
179
|
|
|
@@ -39,7 +39,6 @@ Read `iamu/context.yml`. Extract and hold:
|
|
|
39
39
|
- **Domain + industry** — what world does this product operate in?
|
|
40
40
|
- **Actors** — real roles with real responsibilities. These are scenario and persona seed material.
|
|
41
41
|
- **Governed_by** — regulations and standards (if applicable). These create structural tensions in regulated domains.
|
|
42
|
-
- **Glossary** — domain vocabulary. Scenarios and personas must use these terms, not generic UI words.
|
|
43
42
|
- **Notes body** — extended context, often containing workflow descriptions, pain points, organizational dynamics.
|
|
44
43
|
|
|
45
44
|
### 2. Load existing coverage
|
|
@@ -116,7 +115,7 @@ Next action:
|
|
|
116
115
|
Ground rules for generation:
|
|
117
116
|
|
|
118
117
|
- **Start from the actor's week, not the feature list.** What does this person actually do on a Tuesday? What goes wrong? What requires judgment?
|
|
119
|
-
- **Use domain vocabulary
|
|
118
|
+
- **Use authentic domain vocabulary.** Speak in the language of the domain and the product UI (e.g., "skyddsombud" rather than generic "safety representative"), avoiding abstract test jargon.
|
|
120
119
|
- **Prefer mundane frequency over dramatic edge cases.** The scenario that happens every week reveals more than the one that happens once a year.
|
|
121
120
|
- **Structural tensions come from the domain, not from product limitations.** A real tension: "the regulation requires immediate reporting but the coordinator handles 3 sites." A fake tension: "the CSV upload has a 50-row limit."
|
|
122
121
|
- **Never mention the product, its UI, its features, or its limitations.** The scenario exists in the domain. The product encounter happens during the walkthrough.
|