@runcofactor/cli 0.10.0
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/README.md +125 -0
- package/dist/api-catalog-GFGCLVS4.js +401 -0
- package/dist/api-catalog-GFGCLVS4.js.map +1 -0
- package/dist/attachment-SJC7D7FY.js +276 -0
- package/dist/attachment-SJC7D7FY.js.map +1 -0
- package/dist/auth-EN6NQCJY.js +31 -0
- package/dist/auth-EN6NQCJY.js.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-3CZU7XCS.js +272 -0
- package/dist/chunk-3CZU7XCS.js.map +1 -0
- package/dist/chunk-4NRDONSC.js +1048 -0
- package/dist/chunk-4NRDONSC.js.map +1 -0
- package/dist/chunk-4PNOV7OV.js +51980 -0
- package/dist/chunk-4PNOV7OV.js.map +1 -0
- package/dist/chunk-52IO7ACJ.js +2941 -0
- package/dist/chunk-52IO7ACJ.js.map +1 -0
- package/dist/chunk-6LEZCKKV.js +663 -0
- package/dist/chunk-6LEZCKKV.js.map +1 -0
- package/dist/chunk-A4OFTUVV.js +170 -0
- package/dist/chunk-A4OFTUVV.js.map +1 -0
- package/dist/chunk-B2VISN2V.js +2459 -0
- package/dist/chunk-B2VISN2V.js.map +1 -0
- package/dist/chunk-CLAWT7BJ.js +271 -0
- package/dist/chunk-CLAWT7BJ.js.map +1 -0
- package/dist/chunk-GKY2KXO5.js +281 -0
- package/dist/chunk-GKY2KXO5.js.map +1 -0
- package/dist/chunk-I4QLI3JO.js +413 -0
- package/dist/chunk-I4QLI3JO.js.map +1 -0
- package/dist/chunk-IMPZAX6L.js +180 -0
- package/dist/chunk-IMPZAX6L.js.map +1 -0
- package/dist/chunk-J6SH7GQT.js +241 -0
- package/dist/chunk-J6SH7GQT.js.map +1 -0
- package/dist/chunk-JDBCZ3Z2.js +298 -0
- package/dist/chunk-JDBCZ3Z2.js.map +1 -0
- package/dist/chunk-JICSYFRJ.js +46 -0
- package/dist/chunk-JICSYFRJ.js.map +1 -0
- package/dist/chunk-JWHYXEA2.js +32 -0
- package/dist/chunk-JWHYXEA2.js.map +1 -0
- package/dist/chunk-N7HFALX3.js +88 -0
- package/dist/chunk-N7HFALX3.js.map +1 -0
- package/dist/chunk-Q4BO4Z5V.js +178 -0
- package/dist/chunk-Q4BO4Z5V.js.map +1 -0
- package/dist/chunk-QFDOWABX.js +195 -0
- package/dist/chunk-QFDOWABX.js.map +1 -0
- package/dist/chunk-SJJW77WN.js +605 -0
- package/dist/chunk-SJJW77WN.js.map +1 -0
- package/dist/chunk-VKVL7WBN.js +50 -0
- package/dist/chunk-VKVL7WBN.js.map +1 -0
- package/dist/chunk-WOHL4FTB.js +353 -0
- package/dist/chunk-WOHL4FTB.js.map +1 -0
- package/dist/chunk-XQXVG7RA.js +426 -0
- package/dist/chunk-XQXVG7RA.js.map +1 -0
- package/dist/chunk-ZD3KHESI.js +94 -0
- package/dist/chunk-ZD3KHESI.js.map +1 -0
- package/dist/credential-COLTBV6D.js +450 -0
- package/dist/credential-COLTBV6D.js.map +1 -0
- package/dist/credential-template-EEPYFG7R.js +291 -0
- package/dist/credential-template-EEPYFG7R.js.map +1 -0
- package/dist/debug-workflow-KOVH5GOP.js +259 -0
- package/dist/debug-workflow-KOVH5GOP.js.map +1 -0
- package/dist/dist-44PZR7BQ.js +423 -0
- package/dist/dist-44PZR7BQ.js.map +1 -0
- package/dist/docs-3SSORHB5.js +366 -0
- package/dist/docs-3SSORHB5.js.map +1 -0
- package/dist/doctor-OTSIWV6G.js +132 -0
- package/dist/doctor-OTSIWV6G.js.map +1 -0
- package/dist/feature-flag-T2LRZME3.js +283 -0
- package/dist/feature-flag-T2LRZME3.js.map +1 -0
- package/dist/file-type-S3UUBVCT.js +4205 -0
- package/dist/file-type-S3UUBVCT.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +982 -0
- package/dist/index.js.map +1 -0
- package/dist/init-TGWRECT2.js +208 -0
- package/dist/init-TGWRECT2.js.map +1 -0
- package/dist/install-DXSNWZKO.js +295 -0
- package/dist/install-DXSNWZKO.js.map +1 -0
- package/dist/issues-XIZOPX6T.js +564 -0
- package/dist/issues-XIZOPX6T.js.map +1 -0
- package/dist/knobs-NBINZIUB.js +295 -0
- package/dist/knobs-NBINZIUB.js.map +1 -0
- package/dist/list-3TDHK637.js +59 -0
- package/dist/list-3TDHK637.js.map +1 -0
- package/dist/local-HICVD6PU.js +100 -0
- package/dist/local-HICVD6PU.js.map +1 -0
- package/dist/login-UOIUXVUK.js +315 -0
- package/dist/login-UOIUXVUK.js.map +1 -0
- package/dist/mcp-54IJEOUA.js +1956 -0
- package/dist/mcp-54IJEOUA.js.map +1 -0
- package/dist/pdf-extract-ABWV3L5X.js +13 -0
- package/dist/pdf-extract-ABWV3L5X.js.map +1 -0
- package/dist/pdfjs-DQ2YA5S3.js +41561 -0
- package/dist/pdfjs-DQ2YA5S3.js.map +1 -0
- package/dist/property-UXLOLPTD.js +701 -0
- package/dist/property-UXLOLPTD.js.map +1 -0
- package/dist/property-skill-guide.md +189 -0
- package/dist/psql-BYUUNLGG.js +115 -0
- package/dist/psql-BYUUNLGG.js.map +1 -0
- package/dist/record-4TR2UIIS.js +233 -0
- package/dist/record-4TR2UIIS.js.map +1 -0
- package/dist/redis-35VBUYW7.js +116 -0
- package/dist/redis-35VBUYW7.js.map +1 -0
- package/dist/release-OOLTUBUT.js +354 -0
- package/dist/release-OOLTUBUT.js.map +1 -0
- package/dist/sandbox-XGSUU6G7.js +2046 -0
- package/dist/sandbox-XGSUU6G7.js.map +1 -0
- package/dist/scenarios-GWLJEGHA.js +280 -0
- package/dist/scenarios-GWLJEGHA.js.map +1 -0
- package/dist/session-MYFNZPRP.js +2414 -0
- package/dist/session-MYFNZPRP.js.map +1 -0
- package/dist/skill-KM34LDMO.js +1839 -0
- package/dist/skill-KM34LDMO.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +262 -0
- package/dist/skills/debug-run/history/1.0.0.md +244 -0
- package/dist/skills/debug-run/history/1.0.1.md +245 -0
- package/dist/skills/debug-run/history/1.0.2.md +254 -0
- package/dist/skills/debug-run/history/1.1.0.md +262 -0
- package/dist/skills/debug-run/version.json +3 -0
- package/dist/spec-RA3CFEKG.js +951 -0
- package/dist/spec-RA3CFEKG.js.map +1 -0
- package/dist/src-LB4HBTJQ.js +1516 -0
- package/dist/src-LB4HBTJQ.js.map +1 -0
- package/dist/src-N3TPKGTY.js +244 -0
- package/dist/src-N3TPKGTY.js.map +1 -0
- package/dist/src-UVDX5UP6.js +17 -0
- package/dist/src-UVDX5UP6.js.map +1 -0
- package/dist/start-PSEQSQA3.js +151 -0
- package/dist/start-PSEQSQA3.js.map +1 -0
- package/dist/tag-YY6YAABQ.js +22 -0
- package/dist/tag-YY6YAABQ.js.map +1 -0
- package/dist/task-IQC3YR5K.js +922 -0
- package/dist/task-IQC3YR5K.js.map +1 -0
- package/dist/test-suite-DZNJFXDW.js +394 -0
- package/dist/test-suite-DZNJFXDW.js.map +1 -0
- package/dist/trace-HXIW7U2D.js +14 -0
- package/dist/trace-HXIW7U2D.js.map +1 -0
- package/dist/triage-ORG45R5W.js +16 -0
- package/dist/triage-ORG45R5W.js.map +1 -0
- package/dist/upgrade-L2VYMP2V.js +61 -0
- package/dist/upgrade-L2VYMP2V.js.map +1 -0
- package/dist/workflow-XYFH5LTP.js +3708 -0
- package/dist/workflow-XYFH5LTP.js.map +1 -0
- package/dist/workflow-cache-NQJQFWMV.js +253 -0
- package/dist/workflow-cache-NQJQFWMV.js.map +1 -0
- package/dist/workflow-debug-BGD4UIXU.js +927 -0
- package/dist/workflow-debug-BGD4UIXU.js.map +1 -0
- package/dist/workflow-proposals-MTUDX46O.js +382 -0
- package/dist/workflow-proposals-MTUDX46O.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/skill.ts","../src/skill-templates/issue-log-xref.ts","../src/skill-templates/browser-testing.ts","../src/skill-templates/agent-tasks.ts","../src/skill-templates/canary-basic.ts","../src/skill-templates/test-cases.ts","../src/skill-templates/debug-run.ts","../src/skill-templates/index.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { SKILL_TEMPLATES } from \"./skill-templates/index.js\";\n\nexport type AgentType = \"claude\" | \"codex\" | \"copilot\";\n\nconst SUPPORTED_AGENTS: AgentType[] = [\"claude\", \"codex\", \"copilot\"];\n\ntype DetectAgentOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n exists?: (path: string) => boolean;\n};\n\ntype InstallPathOptions = {\n cwd?: string;\n home?: string;\n};\n\nfunction isAgentType(value: string): value is AgentType {\n return value === \"claude\" || value === \"codex\" || value === \"copilot\";\n}\n\n/**\n * Detect which AI agent is currently running based on environment variables\n * and project directory structure.\n */\nexport function detectAgent(options: DetectAgentOptions = {}): AgentType | null {\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n const pathExists = options.exists ?? existsSync;\n\n // Claude Code sets this\n if (env.CLAUDE) return \"claude\";\n\n // OpenAI Codex sets CODEX_* variables in managed sessions\n if (\n env.CODEX ||\n env.CODEX_CI ||\n env.CODEX_THREAD_ID ||\n env.CODEX_MANAGED_PACKAGE_ROOT ||\n env.AGENT_MODE === \"codex\"\n )\n return \"codex\";\n\n // GitHub Copilot agent mode\n if (\n env.GITHUB_COPILOT ||\n env.COPILOT_AGENT ||\n env.AGENT_MODE === \"copilot\"\n )\n return \"copilot\";\n\n // Fall back to directory heuristics in the current working directory\n const foundAgents = SUPPORTED_AGENTS.filter((agent) =>\n pathExists(join(cwd, agent === \"copilot\" ? \".github\" : `.${agent}`))\n );\n\n // If only one exists, use that\n if (foundAgents.length === 1) return foundAgents[0] ?? null;\n\n // Multiple or none — can't determine\n return null;\n}\n\n/**\n * Resolve the install directory for a skill based on agent type.\n *\n * Project-level paths:\n * claude → .claude/skills/<name>/\n * codex → .codex/skills/<name>/\n * copilot → .github/skills/<name>/\n *\n * Personal paths (--personal):\n * claude → ~/.claude/skills/<name>/\n * codex → ~/.codex/skills/<name>/\n * copilot → ~/.copilot/skills/<name>/\n */\nexport function resolveInstallDir(\n skillName: string,\n agent: AgentType,\n personal: boolean,\n options: InstallPathOptions = {}\n): string {\n const base = personal\n ? (options.home ?? homedir())\n : (options.cwd ?? process.cwd());\n\n switch (agent) {\n case \"claude\":\n return join(base, \".claude\", \"skills\", skillName);\n case \"codex\":\n return join(base, \".codex\", \"skills\", skillName);\n case \"copilot\":\n return personal\n ? join(base, \".copilot\", \"skills\", skillName)\n : join(base, \".github\", \"skills\", skillName);\n }\n}\n\nfunction printSkillHelp() {\n const lines = [\n \"Usage:\",\n \" canary skill <name> Output skill template to stdout\",\n \" canary skill install <name> Install skill (auto-detects agent)\",\n \" canary skill install <name> --agent codex Install for a specific agent\",\n \" canary skill install <name> --personal Install to ~/.<agent>/skills/\",\n \" canary skill list List available skill templates\",\n \" canary skill sync Print manifest + instructions for the\",\n \" agent to version-compare and update\",\n \" installed skills\",\n \" canary skill diff <name> [--from V] [--to V] Unified diff between two versions of\",\n \" a skill (defaults: previous → current)\",\n \" canary skill versions <name> List all known versions of a skill\",\n \"\",\n \"Agent auto-detection:\",\n \" The install command detects which AI agent is running and writes the\",\n \" SKILL.md to the correct location:\",\n \"\",\n \" Agent Project path Personal path\",\n \" ───── ──────────── ─────────────\",\n \" claude .claude/skills/<name>/ ~/.claude/skills/<name>/\",\n \" codex .codex/skills/<name>/ ~/.codex/skills/<name>/\",\n \" copilot .github/skills/<name>/ ~/.copilot/skills/<name>/\",\n \"\",\n \" Detection checks (in order):\",\n \" 1. CLAUDE env var → claude\",\n \" 2. CODEX_* env vars → codex\",\n \" 3. GITHUB_COPILOT env var → copilot\",\n \" 4. .claude/.codex/.github dirs → whichever single one exists\",\n \" 5. If ambiguous, use --agent to specify explicitly\",\n \"\",\n \"Available templates:\",\n ];\n\n for (const [key, template] of Object.entries(SKILL_TEMPLATES)) {\n lines.push(\n ` ${key.padEnd(24)} v${template.version.padEnd(7)} ${template.description}`\n );\n }\n\n console.log(lines.join(\"\\n\"));\n}\n\nfunction compareSemver(a: string, b: string): number {\n const pa = a.split(\".\").map((n) => parseInt(n, 10) || 0);\n const pb = b.split(\".\").map((n) => parseInt(n, 10) || 0);\n for (let i = 0; i < 3; i++) {\n const d = (pa[i] ?? 0) - (pb[i] ?? 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n return 0;\n}\n\nfunction knownVersions(name: string): string[] {\n const t = SKILL_TEMPLATES[name];\n if (!t) return [];\n const all = new Set<string>([t.version, ...Object.keys(t.previousVersions ?? {})]);\n return [...all].sort(compareSemver);\n}\n\nfunction renderAtVersion(name: string, version: string): string | null {\n const t = SKILL_TEMPLATES[name];\n if (!t) return null;\n if (version === t.version) return t.render();\n const prev = t.previousVersions?.[version];\n return prev ? prev() : null;\n}\n\nfunction parseFlagArgs(argv: string[]): {\n positional: string[];\n flags: Record<string, string | true>;\n} {\n const positional: string[] = [];\n const flags: Record<string, string | true> = {};\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i];\n if (a.startsWith(\"--\")) {\n const key = a.slice(2);\n const next = argv[i + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n } else {\n positional.push(a);\n }\n }\n return { positional, flags };\n}\n\nfunction runVersions(argv: string[]) {\n const [name] = argv;\n if (!name || !SKILL_TEMPLATES[name]) {\n console.error(`Usage: canary skill versions <name>`);\n process.exit(1);\n }\n const versions = knownVersions(name);\n const current = SKILL_TEMPLATES[name].version;\n for (const v of versions) {\n console.log(v === current ? `${v} (current)` : v);\n }\n}\n\nfunction jsUnifiedDiff(\n a: string,\n b: string,\n labelA: string,\n labelB: string\n): string {\n const aLines = a.split(\"\\n\");\n const bLines = b.split(\"\\n\");\n const n = aLines.length;\n const m = bLines.length;\n const dp: number[][] = Array.from({ length: n + 1 }, () =>\n Array.from<number>({ length: m + 1 }).fill(0)\n );\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i][j] =\n aLines[i] === bLines[j]\n ? dp[i + 1][j + 1] + 1\n : Math.max(dp[i + 1][j], dp[i][j + 1]);\n }\n }\n const out: string[] = [`--- ${labelA}`, `+++ ${labelB}`];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (aLines[i] === bLines[j]) {\n out.push(` ${aLines[i]}`);\n i++;\n j++;\n } else if (dp[i + 1][j] >= dp[i][j + 1]) {\n out.push(`-${aLines[i++]}`);\n } else {\n out.push(`+${bLines[j++]}`);\n }\n }\n while (i < n) out.push(`-${aLines[i++]}`);\n while (j < m) out.push(`+${bLines[j++]}`);\n return out.join(\"\\n\") + \"\\n\";\n}\n\nfunction runDiff(argv: string[]) {\n const { positional, flags } = parseFlagArgs(argv);\n const [name] = positional;\n\n if (!name) {\n console.error(\n \"Usage: canary skill diff <name> [--from <version>] [--to <version>]\"\n );\n process.exit(1);\n }\n\n const template = SKILL_TEMPLATES[name];\n if (!template) {\n console.error(`Unknown skill \"${name}\".`);\n process.exit(1);\n }\n\n const versions = knownVersions(name);\n const to =\n typeof flags.to === \"string\" ? flags.to : template.version;\n const from =\n typeof flags.from === \"string\"\n ? flags.from\n : versions.filter((v) => v !== to).slice(-1)[0];\n\n if (!from) {\n console.error(\n `No prior version of \"${name}\" to diff against. Only known version: ${template.version}.`\n );\n process.exit(1);\n }\n\n const fromContent = renderAtVersion(name, from);\n const toContent = renderAtVersion(name, to);\n\n if (fromContent === null) {\n console.error(\n `Version \"${from}\" of skill \"${name}\" is not in the CLI. Known: ${versions.join(\", \")}.`\n );\n process.exit(1);\n }\n if (toContent === null) {\n console.error(\n `Version \"${to}\" of skill \"${name}\" is not in the CLI. Known: ${versions.join(\", \")}.`\n );\n process.exit(1);\n }\n\n if (from === to) {\n console.log(`(no diff — both sides are v${from})`);\n return;\n }\n\n process.stdout.write(\n jsUnifiedDiff(fromContent, toContent, `${name}@${from}`, `${name}@${to}`)\n );\n}\n\nfunction runSync() {\n const manifest = Object.entries(SKILL_TEMPLATES).map(([key, t]) => ({\n name: key,\n version: t.version,\n description: t.description,\n knownVersions: knownVersions(key),\n installedPaths: {\n claudeProject: `.claude/skills/${key}/SKILL.md`,\n claudePersonal: join(homedir(), \".claude\", \"skills\", key, \"SKILL.md\"),\n codexProject: `.codex/skills/${key}/SKILL.md`,\n codexPersonal: join(homedir(), \".codex\", \"skills\", key, \"SKILL.md\"),\n copilotProject: `.github/skills/${key}/SKILL.md`,\n copilotPersonal: join(homedir(), \".copilot\", \"skills\", key, \"SKILL.md\"),\n },\n }));\n\n const instructions = `# Canary Skill Sync — Instructions for the Agent\n\nYou are an AI coding agent (Claude, Codex, Copilot, etc.). The user ran \\`canary skill sync\\`\nto bring their installed Canary skill templates up to date. Follow these steps:\n\n## Step 1 — For each skill in the manifest below\n\nCheck whether it is installed in any of the \\`installedPaths\\` (project or personal,\nfor your agent type). A skill is considered installed if the \\`SKILL.md\\` file exists.\n\n## Step 2 — Read the version of each installed skill\n\nThe \\`SKILL.md\\` frontmatter contains a \\`version: X.Y.Z\\` line. Parse that value.\nIf the frontmatter has no \\`version:\\` field, treat the installed version as \\`0.0.0\\`.\n\n## Step 3 — Compare semver\n\nFor each installed skill, compare \\`installedVersion\\` to the \\`version\\` in the manifest:\n- If \\`installedVersion < manifestVersion\\` → the skill is **outdated**, update it.\n- If equal or greater → leave it alone.\n\n## Step 4 — For each outdated skill, inspect what changed\n\nBefore overwriting, read the diff so you know what's new:\n\n canary skill diff <name> --from <installedVersion>\n\nThe CLI retains past renders of each skill in \\`previousVersions\\`, so this prints a\nunified diff from the user's installed version → the current version. Only versions\nlisted in \\`knownVersions\\` in the manifest can be diffed; if \\`installedVersion\\` is\nolder than the oldest known version, diff against the oldest known version instead\nand warn the user that the older content is no longer reproducible.\n\nUse the diff to:\n- Summarize what changed for the user (new sections, removed guidance, renamed commands).\n- Decide whether any user-local edits to the installed SKILL.md would be clobbered —\n if the user appears to have edited their local copy, surface that and ask before\n overwriting.\n\n## Step 5 — Update outdated skills\n\nRe-install by running:\n\n canary skill install <name> # for project installs\n canary skill install <name> --personal # for personal installs\n\nPreserve whichever location it was already installed in. Do NOT install skills that\nweren't already installed — sync only updates existing installs.\n\n## Step 6 — Report\n\nPrint a concise summary: which skills were updated (old → new version), which were\nalready current, and which were not installed (skipped).\n\n## Manifest\n\n\\`\\`\\`json\n${JSON.stringify({ skills: manifest }, null, 2)}\n\\`\\`\\`\n`;\n\n process.stdout.write(instructions);\n}\n\nfunction parseInstallArgs(argv: string[]): {\n name: string | undefined;\n agent: AgentType | undefined;\n personal: boolean;\n} {\n let name: string | undefined;\n let agent: AgentType | undefined;\n let personal = false;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--agent\" && i + 1 < argv.length) {\n const val = argv[++i];\n if (!isAgentType(val)) {\n console.error(\n `Unknown agent \"${val}\". Supported agents: ${SUPPORTED_AGENTS.join(\", \")}`\n );\n process.exit(1);\n }\n agent = val;\n } else if (arg === \"--personal\") {\n personal = true;\n } else if (!arg.startsWith(\"-\") && !name) {\n name = arg;\n }\n }\n\n return { name, agent, personal };\n}\n\nasync function runInstall(argv: string[]) {\n const { name, agent: explicitAgent, personal } = parseInstallArgs(argv);\n\n if (!name) {\n console.error(\"Missing skill name.\\n\");\n console.error(\n `Usage: canary skill install <name> [--agent ${SUPPORTED_AGENTS.join(\"|\")}] [--personal]`\n );\n process.exit(1);\n }\n\n const template = SKILL_TEMPLATES[name];\n if (!template) {\n console.error(`Unknown skill template \"${name}\".\\n`);\n console.error(\"Available templates:\");\n for (const [key, t] of Object.entries(SKILL_TEMPLATES)) {\n console.error(` ${key.padEnd(24)} ${t.description}`);\n }\n process.exit(1);\n }\n\n // Resolve agent type\n const agent = explicitAgent ?? detectAgent();\n if (!agent) {\n console.error(\n \"Could not auto-detect the AI agent. Use --agent to specify:\\n\"\n );\n console.error(\n \" canary skill install <name> --agent claude # Claude Code\"\n );\n console.error(\n \" canary skill install <name> --agent codex # OpenAI Codex\"\n );\n console.error(\n \" canary skill install <name> --agent copilot # GitHub Copilot\"\n );\n process.exit(1);\n }\n\n const dir = resolveInstallDir(name, agent, personal);\n const filePath = join(dir, \"SKILL.md\");\n\n // Write the file\n mkdirSync(dir, { recursive: true });\n writeFileSync(filePath, template.render(), \"utf-8\");\n\n const relPath = personal ? filePath : filePath.replace(process.cwd() + \"/\", \"\");\n console.log(`Installed ${name} skill for ${agent} → ${relPath}`);\n}\n\nexport async function runSkill(argv: string[]) {\n const [sub] = argv;\n\n if (!sub || sub === \"help\" || sub === \"--help\" || sub === \"-h\" || sub === \"list\") {\n printSkillHelp();\n return;\n }\n\n // canary skill install <name> [--agent ...]\n if (sub === \"install\") {\n await runInstall(argv.slice(1));\n return;\n }\n\n // canary skill sync — print instructions for the agent\n if (sub === \"sync\") {\n runSync();\n return;\n }\n\n if (sub === \"diff\") {\n runDiff(argv.slice(1));\n return;\n }\n\n if (sub === \"versions\") {\n runVersions(argv.slice(1));\n return;\n }\n\n // canary skill <name> — output to stdout (backward compatible)\n const template = SKILL_TEMPLATES[sub];\n if (!template) {\n console.error(`Unknown skill template \"${sub}\".\\n`);\n printSkillHelp();\n process.exit(1);\n }\n\n process.stdout.write(template.render());\n}\n","export type SkillTemplate = {\n name: string;\n description: string;\n version: string;\n render: () => string;\n /**\n * Renderers for past versions of this skill, keyed by semver.\n * When bumping `version` + `render`, move the previous render fn in here\n * under its old version string so `canary skill diff` can reproduce it.\n */\n previousVersions?: Record<string, () => string>;\n};\n\nexport const issueLogXrefTemplate: SkillTemplate = {\n name: \"issue-log-xref\",\n description:\n \"Cross-reference Canary issues with server logs to find root causes\",\n version: \"1.0.0\",\n render: () => `---\nname: issue-log-xref\nversion: 1.0.0\ndescription: Cross-reference Canary QA issues with server logs to identify root causes. Use when investigating why a test failed, correlating browser errors with backend logs, or debugging issues found by Canary.\nallowed-tools: Bash(canary issues:*), Bash(canary login:*), Grep, Read\n---\n\n# Issue Log Cross-Reference\n\nCross-reference Canary-detected issues with server logs to identify root causes, correlate errors, and provide actionable debugging context.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Access to server logs (see Log Source Configuration below)\n\n## Workflow\n\n### 1. Fetch issues from Canary\n\nList recent issues to find ones worth investigating:\n\n\\`\\`\\`bash\ncanary issues list --format markdown\n\\`\\`\\`\n\nUse filters to narrow down:\n\n\\`\\`\\`bash\ncanary issues list --severity high --status open --format markdown\n\\`\\`\\`\n\n### 2. Get issue details\n\nOnce you identify an issue, fetch its full diagnostics:\n\n\\`\\`\\`bash\ncanary issues get <issue-id> --format markdown\n\\`\\`\\`\n\nNote the following from the issue details:\n- **Timestamps** — when the issue was detected\n- **URLs / endpoints** — which pages or API routes were involved\n- **Error messages** — any visible error text captured in screenshots or DOM\n- **HTTP status codes** — failed network requests\n\n### 3. Access server logs\n\n<!-- ============================================================\n LOG SOURCE CONFIGURATION\n\n Replace {{LOG_SOURCE_INSTRUCTIONS}} below with instructions\n specific to your infrastructure. The AI agent reading this\n skill will use these instructions to fetch logs.\n\n Examples:\n\n **AWS CloudWatch:**\n Query CloudWatch Logs Insights for the relevant log group:\n \\`\\`\\`bash\n aws logs filter-log-events \\\\\n --log-group-name /ecs/my-api \\\\\n --start-time <epoch-ms> \\\\\n --end-time <epoch-ms> \\\\\n --filter-pattern \"<error-text>\"\n \\`\\`\\`\n\n **Local log files:**\n Search local log files for matching entries:\n \\`\\`\\`bash\n grep -i \"<error-text>\" /var/log/my-app/*.log\n \\`\\`\\`\n\n **Datadog:**\n Use the Datadog CLI or API to query logs:\n \\`\\`\\`bash\n dog logs list --query \"service:my-api status:error\" \\\\\n --from <ISO-timestamp> --to <ISO-timestamp>\n \\`\\`\\`\n\n **kubectl (Kubernetes):**\n Fetch pod logs for the relevant service:\n \\`\\`\\`bash\n kubectl logs -l app=my-api --since=1h | grep \"<error-text>\"\n \\`\\`\\`\n============================================================ -->\n\n{{LOG_SOURCE_INSTRUCTIONS}}\n\n### 4. Cross-reference and correlate\n\nMatch the Canary issue with log entries using these correlation points:\n\n- **Timestamps** — find log entries within a few seconds of the issue detection time\n- **Error messages** — search logs for the same error text shown in the issue\n- **HTTP status codes** — look for 4xx/5xx responses matching the failed requests\n- **URLs / routes** — filter logs by the endpoint or page path from the issue\n- **Stack traces** — identify the code path that produced the error\n- **Request IDs** — if your app uses correlation/request IDs, trace the full request lifecycle\n\n## Analysis Guidelines\n\nWhen presenting findings:\n\n1. **State the match confidence** — clear match, likely match, or circumstantial\n2. **Show the timeline** — lay out events in chronological order across both sources\n3. **Identify the root cause** — distinguish between the symptom (what Canary caught) and the cause (what the logs reveal)\n4. **Suggest next steps** — point to the specific code, service, or configuration that needs attention\n5. **Note gaps** — if logs are missing or incomplete, say so explicitly rather than speculating\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const browserTestingTemplate: SkillTemplate = {\n name: 'browser-testing',\n description: 'Use the Canary CLI to control a browser for testing web applications',\n version: '1.0.0',\n render: () => `---\nname: browser-testing\nversion: 1.0.0\ndescription: Control a local browser via the Canary CLI to test web applications interactively. Use when the user asks to test, browse, interact with, or verify a web application using canary browser sessions.\nallowed-tools: Bash(canary session:*), Bash(canary login:*), Bash(canary workflow:*), Bash(canary credential:*), Read\n---\n\n# Browser Testing with Canary\n\nUse the \\`canary session\\` CLI to launch and control a local browser for testing web applications. You interact with pages through **snapshots** (accessibility tree + screenshot), then issue commands to click, type, navigate, and assert.\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- Run \\`canary session --help\\` for the full command reference\n\n## Environments & Authentication\n\nCanary credentials are stored remotely and belong to a **property** (an app with a base URL). When the user asks you to test, determine the environment:\n\n### Remote / Deployed environments (default)\n\nCredentials are pre-configured with storage state. Just use \\`--credential\\` to load one:\n\n\\`\\`\\`bash\n# Interactive picker — shows credential name, property name, and base URL\ncanary session start --credential\n\n# By name\ncanary session start --credential \"Admin User\" --url https://staging.example.com\n\\`\\`\\`\n\n### Local development\n\nCredentials stored in Canary won't work against localhost because session cookies are bound to the remote domain. When testing a local dev server:\n\n1. **Ask the user to log in manually.** Start a session without credentials and tell them to authenticate in the browser:\n\n\\`\\`\\`bash\ncanary session start --url http://localhost:5173\n\\`\\`\\`\n\nThen tell the user: \"I've opened a browser to your local app. Please log in, and let me know when you're ready for me to continue.\"\n\n2. **Wait for confirmation** before proceeding with testing.\n\n3. Alternatively, if the user has a **local storage state file**, use it:\n\n\\`\\`\\`bash\ncanary session start --storage-state ./local-auth.json --url http://localhost:5173\n\\`\\`\\`\n\n**Rule:** If the URL is localhost or 127.0.0.1, do NOT use \\`--credential\\` — it will download remote session state that won't work locally. Instead, prompt the user to log in.\n\n## Core Workflow\n\nThe fundamental loop is: **snapshot → read → act → snapshot → verify**.\n\n### 1. Start a session\n\n\\`\\`\\`bash\n# Start a headed browser (you can watch it)\ncanary session start --url https://your-app.com\n\n# Start with a saved credential (interactive picker shows property + base URL)\ncanary session start --credential\n\n# Start with a specific credential by name\ncanary session start --credential \"Admin User\" --url https://your-app.com\n\n# Load a credential into an already-running session\ncanary session load-credential\n\\`\\`\\`\n\n### 2. Take a snapshot to see the page\n\nSnapshots are your primary way to understand what's on the page. They return an **accessibility tree** with element references (refs) and a **screenshot**.\n\n\\`\\`\\`bash\n# Full snapshot: accessibility tree + screenshot\ncanary session snapshot\n\n# Search for specific elements (fastest way to find what you need)\ncanary session snapshot --search \"Submit\"\ncanary session snapshot --search \"Email|Password\"\n\n# Expand a collapsed section (use the ref shown in the tree, e.g. [e50])\ncanary session snapshot --expand e50\n\n# Screenshot only (useful for visual verification)\ncanary session snapshot --mode screenshot\n\n# Tree only (useful when you need ref details, no image)\ncanary session snapshot --mode tree\n\\`\\`\\`\n\n**Reading snapshot output:**\n\nThe accessibility tree shows elements with refs like \\`[ref=e15]\\`. Use these refs in subsequent commands. The tree is structured to show the page hierarchy — forms, buttons, links, headings, etc.\n\nWhen you use \\`--search\\`, matching elements are highlighted in the output and on the screenshot. Search is OR-matched and case-insensitive. Use search to **locate** elements, not to validate visual states — search overlays can obscure subtle UI changes.\n\nLarge sections of the tree may be collapsed (shown as e.g. \\`(127 elements - use expand=\"e50\" to see content)\\`). Use \\`--expand <ref>\\` to reveal the full contents of a collapsed section.\n\n### 3. Interact with the page\n\nAlways reference elements by their **ref** from the snapshot.\n\n\\`\\`\\`bash\n# Click an element\ncanary session click --ref e15 --element \"Submit button\"\n\n# Type into an input field\ncanary session type --ref e7 --text \"user@example.com\" --element \"Email input\"\n\n# Type and submit (presses Enter after typing)\ncanary session type --ref e7 --text \"search query\" --element \"Search box\" --submit\n\n# Fill multiple form fields at once\ncanary session fill --fields '[{\"ref\":\"e3\",\"value\":\"John\"},{\"ref\":\"e4\",\"value\":\"john@example.com\"}]'\n\n# Select a dropdown option\ncanary session select --ref e12 --value \"admin\" --element \"Role dropdown\"\n\n# Press a keyboard key\ncanary session press --key Enter\ncanary session press --key Escape\ncanary session press --key ArrowDown\n\n# Hover to reveal hidden content (tooltips, dropdowns)\ncanary session hover --ref e8 --element \"User menu\"\n\\`\\`\\`\n\n### 4. Navigate\n\n\\`\\`\\`bash\ncanary session navigate --url https://your-app.com/settings\ncanary session back\n\\`\\`\\`\n\n### 5. Scroll\n\n\\`\\`\\`bash\n# Scroll down the page\ncanary session scroll --direction down\n\n# Scroll with more intensity (1-10, default 3)\ncanary session scroll --direction down --amount 8\n\n# Scroll up\ncanary session scroll --direction up\n\\`\\`\\`\n\n### 6. Verify results\n\nAfter every action, take a snapshot to verify the result:\n\n\\`\\`\\`bash\n# Check what happened after clicking submit\ncanary session snapshot\n\n# Search for a success message\ncanary session snapshot --search \"Success|saved\"\n\n# Check for error messages\ncanary session snapshot --search \"Error|failed|invalid\"\n\n# Check console for JavaScript errors\ncanary session console --only-errors\n\n# Check network requests for failed API calls\ncanary session network\n\\`\\`\\`\n\n## Session Management\n\n\\`\\`\\`bash\n# List all active sessions\ncanary session list\n\n# Check session details\ncanary session status\n\n# Stop a session\ncanary session stop\n\n# Stop all sessions\ncanary session stop --all\n\n# When multiple sessions are active, specify which one\ncanary session snapshot --session s1\ncanary session click --ref e5 --element \"Button\" --session s2\n\\`\\`\\`\n\n## Advanced Techniques\n\n### Waiting for page changes\n\n\\`\\`\\`bash\n# Wait for text to appear (after navigation or async action)\ncanary session wait --text \"Dashboard loaded\"\n\n# Wait for text to disappear (loading spinner)\ncanary session wait --text \"Loading...\"\n\n# Wait a fixed time (use sparingly)\ncanary session wait --time 2\n\\`\\`\\`\n\n### Coordinate-based clicking\n\nWhen ref-based clicking doesn't work (overlays, canvas elements), use coordinates. First take a snapshot, look at the screenshot, estimate coordinates, then click:\n\n\\`\\`\\`bash\n# Click at specific coordinates\ncanary session click --x 450 --y 300 --element \"Canvas element\"\n\\`\\`\\`\n\n### Tab management\n\n\\`\\`\\`bash\ncanary session tabs --action list\ncanary session tabs --action new\ncanary session tabs --action select --index 0\ncanary session tabs --action close --index 1\n\\`\\`\\`\n\n### Evaluating JavaScript\n\n\\`\\`\\`bash\ncanary session evaluate --js \"() => document.title\"\ncanary session evaluate --js \"() => window.location.href\"\ncanary session evaluate --js \"() => document.querySelectorAll('.error').length\"\n\\`\\`\\`\n\n### Taking labeled screenshots\n\n\\`\\`\\`bash\n# Take a labeled screenshot for later reference\ncanary session screenshot --label \"before-submit\"\n\n# Full page screenshot\ncanary session screenshot --full-page --label \"full-page-state\"\n\\`\\`\\`\n\n### File downloads\n\n\\`\\`\\`bash\n# List downloaded files\ncanary session evaluate --js \"() => 'trigger download'\"\n# ... after download completes:\ncanary session list-downloads\ncanary session read-download\n\\`\\`\\`\n\n## Testing Guidelines\n\n1. **Always snapshot after actions** — never assume an action succeeded. Take a snapshot to verify.\n2. **Use search to locate elements** — \\`--search \"Submit\"\\` is faster than scanning the full tree.\n3. **Use refs, not coordinates** — ref-based interaction is more reliable. Only fall back to coordinates when refs fail.\n4. **Check for errors** — after form submissions or navigation, check both the page content and console/network for errors.\n5. **Wait for async operations** — if an action triggers an API call, use \\`wait --text\\` for the expected result rather than fixed delays.\n6. **Load credentials for authenticated testing** — use \\`canary session start --credential\\` or \\`canary session load-credential\\` to test as specific users.\n7. **Report what you find** — describe what you see in snapshots, what worked, what failed, and include relevant refs or error messages.\n\n## Improving Testability (Local Development)\n\nWhen testing locally, you have access to the source code — use this advantage. If the accessibility tree is confusing or elements are hard to target, **diagnose why** and suggest concrete improvements to the user.\n\n### When to investigate\n\nIf you encounter any of these signals, dig deeper before moving on:\n- Multiple elements with identical or vague labels (e.g., several \"Button\" or \"div\" entries)\n- Interactive elements missing from the accessibility tree entirely\n- Custom components that render as opaque containers with no semantic children\n- Difficulty distinguishing between similar repeated elements (e.g., rows in a table)\n- Elements you can see in the screenshot but can't find in the tree\n\n### How to investigate\n\n1. **Use JavaScript evaluation** to inspect the actual DOM structure:\n \\`\\`\\`bash\n canary session evaluate --js \"() => document.querySelector('[data-testid]')?.outerHTML\"\n canary session evaluate --js \"() => document.querySelectorAll('button').length\"\n canary session evaluate --js \"() => [...document.querySelectorAll('button')].map(b => ({ text: b.textContent?.trim(), role: b.getAttribute('role'), ariaLabel: b.getAttribute('aria-label') }))\"\n \\`\\`\\`\n\n2. **Read the component source code** — find and read the relevant component or template file to understand why the accessibility tree looks the way it does.\n\n3. **Compare screenshot vs tree** — take a screenshot-only snapshot and a tree-only snapshot. If you can see something visually but it's missing or ambiguous in the tree, that's a testability gap.\n\n### What to suggest\n\nWhen you find testability gaps, suggest specific fixes to the user. Frame suggestions as improvements that help both automated testing and human accessibility:\n\n- **Missing labels**: \"The 'Save' buttons in each card are indistinguishable in the accessibility tree. Adding \\`aria-label=\"Save {itemName}\"\\` or \\`data-testid=\"save-{itemId}\"\\` would make them individually targetable.\"\n- **Non-semantic elements**: \"This clickable \\`<div>\\` doesn't appear as a button in the tree. Changing it to \\`<button>\\` or adding \\`role=\"button\"\\` would fix this.\"\n- **Missing headings/landmarks**: \"This page section has no heading — screen readers and test agents can't tell what it contains. Adding an \\`<h2>\\` or \\`aria-label\\` to the \\`<section>\\` would help.\"\n- **Unlabeled form fields**: \"These inputs have placeholder text but no associated \\`<label>\\`. Adding \\`<label htmlFor=\"...\">\\` or \\`aria-label\\` would make them reliably targetable.\"\n- **Repeated generic elements**: \"The table rows are hard to distinguish. Adding \\`data-testid=\"row-{id}\"\\` or meaningful \\`aria-label\\` values to each row would make them individually addressable.\"\n\n**Tone**: These are suggestions, not demands. Present them as quick wins that improve both test reliability and accessibility. Group them at the end of your testing report rather than interrupting the testing flow.\n\n## Creating Tests from Browser Exploration\n\nAfter exploring an app via \\`canary session\\`, you can persist what you discovered as a reusable test workflow.\n\n### Explore → Author → Create flow\n\n1. **Explore**: Use \\`canary session\\` to navigate, snapshot, and interact with the app\n2. **Author**: Construct workflow JSON with nodes and playbook steps based on what was observed\n3. **Create**: Pipe JSON to \\`canary workflow create\\`\n4. **Verify**: Check with \\`canary workflow get <id>\\`\n\n### Node authoring guidance\n\n- Group related actions into single action nodes (one node per logical step, not per click)\n- Node types: \\`login\\` (authentication), \\`navigate\\` (go to URL), \\`action\\` (interact with elements), \\`assertion\\` (verify standalone page state), \\`end\\` (terminal node)\n- Every action node **must** have a \\`customActionTitle\\` — a short (3-8 word) human-readable title describing the step\n- **Prefer inline assertions** on action nodes over separate assertion nodes. Add \\`inlineAssertions\\` to verify outcomes right after the action completes. Use separate \\`assertion\\` nodes only for standalone page-level verifications (e.g., initial page state before any action)\n- Inline assertion format: \\`{ \"condition\": \"...\", \"severity\": \"fail\" }\\`. **Do not author an \\`id\\` field** — the server stamps a UUID on persist. Hand-rolled UUIDs (especially placeholders like \\`a1b2c3d4-...\\`) are rejected and replaced.\n\n### Playbook step authoring guidance\n\n- Use **semantic targets** from the accessibility tree (button text, field labels, placeholder text) — NOT refs or CSS selectors\n- Target format: describe as a human would (\"Submit button\", \"Email input field\", \"Status dropdown\")\n- For \\`fill\\` actions: include \\`params: { \"value\": \"...\" }\\`\n- **Always prefer macros over hardcoded test data.** Macros generate fresh values each run, preventing flaky tests from stale or colliding data. Never hardcode emails, names, phone numbers, or dates when a macro can generate them.\n- Prefix test data with \"QA Test\" for identifiability (e.g., \\`\"QA Test $FIRST_NAME()\"\\`)\n- Valid actions: \\`click\\`, \\`fill\\`, \\`fill_form\\`, \\`select\\`, \\`navigate\\`, \\`wait\\`, \\`scroll\\`, \\`hover\\`, \\`upload\\`, \\`verify\\`, \\`press_key\\`, \\`drag\\`, \\`resize\\`, \\`capture\\`, \\`extract_table\\`, \\`python_execute\\`, \\`verify_download\\`, \\`verify_download_content\\`, \\`screenshot\\`, \\`agent_loop\\`\n\n### Using macros and variables\n\nMacros are resolved at run time — each execution gets fresh values, making tests repeatable and collision-free.\n\n**Available macros:**\n\n| Macro | Args | Output | Example |\n|-------|------|--------|---------|\n| \\`$EMAIL()\\` | — | Unique test email | \\`test-xk9mq2@example-test.com\\` |\n| \\`$FIRST_NAME()\\` | — | Random first name | \\`Jordan\\` |\n| \\`$LAST_NAME()\\` | — | Random last name | \\`Chen\\` |\n| \\`$PHONE()\\` | — | US phone (555 prefix) | \\`+1-555-382-7491\\` |\n| \\`$UUID()\\` | — | Random UUID v4 | \\`a1b2c3d4-...\\` |\n| \\`$RANDOM_INT(min, max)\\` | 2 ints | Random integer (inclusive) | \\`$RANDOM_INT(100,999)\\` → \\`427\\` |\n| \\`$RANDOM_STRING(length)\\` | 1 int (1–1000) | Alphanumeric string | \\`$RANDOM_STRING(8)\\` → \\`xK9mQ3bR\\` |\n| \\`$TODAY()\\` | — | Current date (YYYY-MM-DD) | \\`2026-04-06\\` |\n| \\`$DATE(days)\\` | 1 int | Date offset from today | \\`$DATE(-7)\\` → last week |\n| \\`$NOW()\\` | — | ISO 8601 timestamp | \\`2026-04-06T14:30:00.000Z\\` |\n| \\`$TIMESTAMP()\\` | — | Unix milliseconds | \\`1771520400000\\` |\n| \\`$SEQUENCE(\"name\")\\` | counter name | Auto-incrementing per run | \\`1\\`, \\`2\\`, \\`3\\`... |\n\n**When to use macros:**\n- Email fields → \\`$EMAIL()\\`\n- Name fields → \\`$FIRST_NAME()\\` / \\`$LAST_NAME()\\`\n- Amounts, quantities, identifiers → \\`$RANDOM_INT()\\` or \\`$RANDOM_STRING()\\`\n- Date fields → \\`$TODAY()\\` / \\`$DATE()\\`\n- Sequential numbering → \\`$SEQUENCE(\"name\")\\`\n- Macros combine inline: \\`\"QA Test $FIRST_NAME() $LAST_NAME()\"\\`, \\`\"order-$RANDOM_STRING(6)\"\\`\n\n**Variables (\\`$var.name\\`):**\n- Steps can reference data captured from earlier steps using \\`$var.name\\`\n- Variable values are extracted during execution and available to subsequent steps\n- Use step-qualified refs when multiple steps set the same variable: \\`$var.Step2_Create_Client.name\\`\n\n### Credential selection\n\n**Always use \\`credentialId\\` (UUID) in the login node — never just \\`credentialName\\`.**\n\n1. **List available credentials** before authoring:\n \\`\\`\\`bash\n canary credential list --json\n \\`\\`\\`\n This returns each credential's \\`id\\`, \\`name\\`, \\`isDefault\\`, \\`propertyName\\`, and environment info.\n\n2. **Pick the right credential**:\n - Prefer the **default credential** (\\`isDefault: true\\`)\n - **Never** use a credential with \"local\" in the name — it won't work in deployed environments\n - If multiple non-local credentials exist and you're unsure which to use, **ask the user**\n\n3. **Set both \\`credentialId\\` and \\`credentialName\\`** in the login node's \\`configJson\\`:\n \\`\\`\\`json\n { \"credentialId\": \"4f6db838-...\", \"credentialName\": \"QA User\" }\n \\`\\`\\`\n\n4. **If no matching credential exists**, warn the user that one needs to be created first in the web app.\n\nThe CLI will auto-resolve credentials if \\`credentialId\\` is missing, but it may fail if the choice is ambiguous. Always specify the ID explicitly for reliable uploads.\n\n### Environment-aware publishing\n\n- **CLI-created workflows default to \\`published\\`** — no flag needed. Reliability is tracked automatically by the workflow's \\`health\\` (starts \\`unverified\\`); a flow that keeps failing becomes \\`degraded\\` and is skipped in suites until edited or re-enabled.\n- **Deployed/known environment**: Use \\`--run-and-publish\\` — test runs immediately, publishes if it passes.\n\n### Usage\n\n\\`\\`\\`bash\n# Create (published by default)\necho '<workflow JSON>' | canary workflow create --from-stdin\n\n# Run-then-publish for deployed environments\necho '<workflow JSON>' | canary workflow create --from-stdin --run-and-publish --property \"My App\"\n\n# From file\ncanary workflow create --from-file workflow.json\n\\`\\`\\`\n\n### Example workflow JSON\n\n\\`\\`\\`json\n{\n \"name\": \"Verify user can create an invoice\",\n \"description\": \"Tests the invoice creation flow end-to-end\",\n \"nodes\": [\n {\n \"nodeType\": \"login\",\n \"configJson\": {\n \"credentialId\": \"4f6db838-16be-49e8-8887-8d0be1c83a87\",\n \"credentialName\": \"QA User\"\n }\n },\n {\n \"nodeType\": \"navigate\",\n \"configJson\": { \"pageUrl\": \"/invoices\", \"pageTitle\": \"Invoices\" }\n },\n {\n \"nodeType\": \"action\",\n \"configJson\": {\n \"customActionTitle\": \"Create a new invoice\",\n \"actionDescription\": \"Fill out the new invoice form and save\",\n \"playbookSteps\": [\n { \"order\": 1, \"action\": \"click\", \"target\": \"New Invoice button\" },\n { \"order\": 2, \"action\": \"fill\", \"target\": \"Client name field\", \"params\": { \"value\": \"QA Test $FIRST_NAME()\" } },\n { \"order\": 3, \"action\": \"fill\", \"target\": \"Amount field\", \"params\": { \"value\": \"$RANDOM_INT(100,999)\" } },\n { \"order\": 4, \"action\": \"click\", \"target\": \"Save button\" }\n ],\n \"inlineAssertions\": [\n {\n \"condition\": \"Success message 'Invoice created' is visible and the new invoice appears in the list\",\n \"severity\": \"fail\"\n }\n ]\n }\n }\n ]\n}\n\\`\\`\\`\n\n### Updating existing workflows\n\nUse \\`canary workflow sync\\` to replace a workflow's nodes. It accepts the same\nsimple format as create — no edges, positions, or IDs needed:\n\n\\`\\`\\`bash\n# From stdin\necho '{\"nodes\": [...]}' | canary workflow sync <workflowId> --from-stdin\n\n# From file\ncanary workflow sync <workflowId> --from-file updated.json\n\n# Create a proposal for review instead of applying directly\ncanary workflow sync <workflowId> --from-file updated.json --propose\n\\`\\`\\`\n\nWhen using \\`--propose\\` on an existing workflow, fetch the current state first\nand reuse each node's \\`id\\` for steps you keep — only omit \\`id\\` for genuinely\nnew steps. This is the best way to ensure the diff stays accurate so reviewers\nsee only the fields that actually changed:\n\n\\`\\`\\`bash\n# Round-trip pattern for proposals\ncanary workflow get <workflowId> --format json > current.json\n# edit current.json: change configJsons, keep ids you're not replacing\ncanary workflow sync <workflowId> --from-file current.json --propose\n\\`\\`\\`\n\n### Generating API sequences\n\nConvert a browser-based workflow to a fast API sequence using its latest successful run:\n\n\\`\\`\\`bash\n# Generate from the workflow's latest run\ncanary workflow generate-api-sequence <workflowId>\n\n# With additional guidance\ncanary workflow generate-api-sequence <workflowId> --guidance \"Focus on the create endpoint\"\n\n# JSON output\ncanary workflow generate-api-sequence <workflowId> --json\n\\`\\`\\`\n\n### Reference\n\nRun \\`canary workflow guide\\` for the full authoring reference:\n\n\\`\\`\\`bash\ncanary workflow guide # All sections\ncanary workflow guide node-types # Available node types\ncanary workflow guide macros # Dynamic test data ($EMAIL, $RANDOM_STRING, etc.)\ncanary workflow guide playbook # Playbook steps and caching\ncanary workflow guide seed-workflows # Seed workflow patterns\ncanary workflow guide assertions # Inline and standalone assertions\n\\`\\`\\`\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const agentTasksTemplate: SkillTemplate = {\n name: 'agent-tasks',\n description: 'Manage trigger-based agent tasks for deferred work items',\n version: '1.0.0',\n render: () => `---\nname: agent-tasks\nversion: 1.0.0\ndescription: Manage trigger-based agent tasks via the Canary CLI. Use when the user asks to create a task, schedule post-deploy work, set up reminders for flag cleanup, track knob deprecation, or manage deferred work items triggered by system events.\nallowed-tools: Bash(canary task:*), Read, Grep, Glob\n---\n\n# Agent Tasks with Canary\n\nUse \\`canary task\\` to create and manage **trigger-based work items** — deferred tasks that become actionable when a system event fires (deploy, flag change, schedule, etc.).\n\n## Prerequisites\n\n- \\`canary\\` CLI installed and authenticated (\\`canary login\\`)\n- The **AGENT_TASKS** feature flag must be enabled for the org\n- Run \\`canary task --help\\` for the full command reference\n\n## Quick Reference\n\n\\`\\`\\`bash\n# Create a task\ncanary task create --title \"Remove feature flag\" \\\\\n --description \"Delete COOL_FEATURE flag and all gating code\" \\\\\n --trigger flag-deleted --flag COOL_FEATURE\n\n# List tasks\ncanary task list # all tasks\ncanary task list --status waiting # waiting for trigger\ncanary task list --status actionable # ready to work on\ncanary task list --trigger production-deploy # by trigger type\n\n# Manage tasks\ncanary task get <id> # view details + event history\ncanary task update <id> --title \"New title\" # update task fields\ncanary task claim <id> # mark in progress\ncanary task complete <id> # mark done\ncanary task complete <id> --commit abc123 # done, link to resolution commit\ncanary task cancel <id> # cancel\ncanary task trigger <id> # manually fire trigger\n\\`\\`\\`\n\n## Trigger Types\n\n| Type | Fires when... | Auto-fired? | Required flag |\n|------|---------------|-------------|---------------|\n| \\`production-deploy\\` | Code deployed to production | **Yes** | — |\n| \\`flag-deprecated\\` | Feature flag set to deprecated | Yes | \\`--flag <name>\\` |\n| \\`flag-cleanup\\` | Feature flag set to ready_for_cleanup | Yes | \\`--flag <name>\\` |\n| \\`flag-deleted\\` | Feature flag deleted | Yes | \\`--flag <name>\\` |\n| \\`knob-deprecated\\` | Knob set to deprecated | Yes | \\`--knob <key>\\` |\n| \\`knob-cleanup\\` | Knob set to ready_for_cleanup | Yes | \\`--knob <key>\\` |\n| \\`knob-deleted\\` | Knob deleted | Yes | \\`--knob <key>\\` |\n| \\`scheduled\\` | A date/time is reached | Yes | \\`--scheduled-date <ISO>\\` |\n| \\`pr-merged\\` | A specific PR is merged | Yes | \\`--pr <number>\\` |\n| \\`immediate\\` | Immediately (no waiting) | N/A | — |\n| \\`manual\\` | Manually triggered via CLI | N/A | — |\n\n**Note:** \\`pr-merged\\` tasks are scoped to the linked repository selected by \\`--repo\\`, \\`--role\\`, or \\`--repo-id\\`.\n\n## Task Lifecycle\n\n\\`\\`\\`\n ┌─── immediate ───► actionable\n │ │\n create ──────────┤ claim\n │ │\n └─── other types ──► waiting ──trigger──► actionable ──► in_progress ──► completed\n ├──► canceled\n └──► expired\n\\`\\`\\`\n\n- **waiting**: Created, waiting for its trigger event\n- **actionable**: Trigger fired (or immediate type), ready to be worked on\n- **in_progress**: Claimed by someone, actively being worked\n- **completed**: Done (optionally linked to a resolution commit)\n- **canceled** / **expired**: Terminated without completion\n\n## Common Workflows\n\n### Post-deploy cleanup (remove temporary code after next production deploy)\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Remove backwards-compat shim for widget API\" \\\\\n --description \"Old endpoint can be removed now that new endpoint is deployed.\" \\\\\n --trigger production-deploy \\\\\n --priority 1 \\\\\n --tag cleanup --tag api\n\\`\\`\\`\n\nThis task automatically becomes actionable after the next successful production deployment.\n\n### Flag removal after GA\n\nWhen a feature graduates from behind a flag:\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Clean up COOL_FEATURE flag gating code\" \\\\\n --description \"Feature is GA. Remove all gating checks and delete the constant.\" \\\\\n --trigger flag-deprecated --flag COOL_FEATURE \\\\\n --tag cleanup --tag feature-flags\n\\`\\`\\`\n\nAuto-fires when someone runs \\`canary feature-flag lifecycle COOL_FEATURE --stage deprecated\\`.\n\n### Knob deprecation follow-up\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Remove agent.truncation.enabled knob reads\" \\\\\n --description \"Replace all knob reads with the hardcoded final value.\" \\\\\n --trigger knob-deprecated --knob agent.truncation.enabled \\\\\n --tag cleanup\n\\`\\`\\`\n\n### Scheduled future work\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Rotate API signing keys\" \\\\\n --description \"Quarterly key rotation per security policy.\" \\\\\n --trigger scheduled --scheduled-date 2026-06-30T00:00:00Z \\\\\n --priority 2 --tag security\n\\`\\`\\`\n\n### Immediate task (starts actionable, no trigger wait)\n\n\\`\\`\\`bash\ncanary task create \\\\\n --title \"Fix type error in auth middleware\" \\\\\n --description \"Strict mode caught a missing null check. Needs immediate fix.\" \\\\\n --trigger immediate \\\\\n --priority 2 --assign dev@example.com\n\\`\\`\\`\n\n## Integration Points\n\n### Feature Flags → Tasks\n\nFlag lifecycle changes automatically fire matching task triggers:\n- \\`canary feature-flag lifecycle <name> --stage deprecated\\` → fires \\`flag-deprecated\\` tasks\n- \\`canary feature-flag lifecycle <name> --stage ready_for_cleanup\\` → fires \\`flag-cleanup\\` tasks\n- \\`canary feature-flag delete <name>\\` → fires \\`flag-deleted\\` tasks\n\n### Knobs → Tasks\n\nSame pattern as flags:\n- \\`canary knobs lifecycle <key> --stage deprecated\\` → fires \\`knob-deprecated\\` tasks\n- \\`canary knobs lifecycle <key> --stage ready_for_cleanup\\` → fires \\`knob-cleanup\\` tasks\n- \\`canary knobs delete <key>\\` → fires \\`knob-deleted\\` tasks\n\n### Production Deploys → Tasks\n\nThe deployment notifier Lambda fires \\`production-deploy\\` task triggers automatically on successful production deployments. Any waiting tasks with this trigger type transition to \\`actionable\\`.\n\n## Tips\n\n- \\`--commit\\` defaults to \\`git HEAD\\` — auto-detects the current commit\n- \\`--repo\\` is auto-selected if the org has a single linked repository\n- \\`--tag\\` is repeatable: \\`--tag cleanup --tag api --tag urgent\\`\n- \\`--description-file <path>\\` reads description from a file (useful for long markdown descriptions)\n- \\`--json\\` on any command returns structured JSON output for scripting\n- \\`--priority\\`: 0 = normal (default), 1 = high, 2 = critical\n- \\`--expires <ISO date>\\` sets an expiry after which the task auto-expires if not completed\n- Use \\`canary task trigger <id>\\` to manually fire any waiting task's trigger\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const canaryBasicTemplate: SkillTemplate = {\n name: 'canary-basic',\n description: 'Learn the Canary CLI — commands, help, and common patterns',\n version: '1.0.0',\n render: () => `---\nname: canary-basic\nversion: 1.0.0\ndescription: Reference guide for the Canary CLI. Invoke when anyone mentions canary, canary CLI, or asks about CLI commands for testing, browser sessions, workflows, issues, or releases.\nallowed-tools: Read, Grep, Glob\n---\n\n# Canary CLI Guide\n\nThe \\`canary\\` CLI is a command-line tool for running tests, controlling browser sessions, managing workflows, and inspecting issues.\n\n## Install & Login\n\n\\`\\`\\`bash\n# Install globally\nnpm install -g @canaryai/cli\n# or\nbun add -g @canaryai/cli\n\n# Authenticate (opens browser)\ncanary login\n\n# Login to a specific org (for multi-org users)\ncanary login --org \"My Org\"\n\\`\\`\\`\n\n### Environment switching\n\n\\`\\`\\`bash\n# Production (default)\ncanary login\n\n# Dev\ncanary login --app-url https://app.dev.trycanary.ai --api-url https://api.dev.trycanary.ai\n\n# Local\ncanary login --app-url http://localhost:5173 --api-url http://localhost:3000\n\n# Or set the env var\nexport CANARY_API_URL=http://localhost:3000\n\\`\\`\\`\n\n---\n\n## Command Reference\n\n### Test Suites\n\n\\`\\`\\`bash\n# Trigger a test suite and stream results\ncanary test-suite run\ncanary test-suite run --tag smoke\ncanary test-suite run --property \"My App\" --environment staging --verbose\n\n# Query test suite results\ncanary test-suite list\ncanary test-suite get <id> --failures-only\ncanary test-suite diagnostics <workflowId> <runId>\ncanary test-suite steps <workflowId> <runId>\n\\`\\`\\`\n\nRun options: \\`--token\\`, \\`--property\\`, \\`--environment\\`, \\`--tag\\`, \\`--name-pattern\\`, \\`--verbose\\`\n\n---\n\n### Browser Sessions\n\nStart and control browser sessions for interactive testing.\n\n#### Session lifecycle\n\n\\`\\`\\`bash\ncanary session start # Start a new session\ncanary session start --credential \"admin\" # Start with a credential\ncanary session start --url https://example.com --headless\ncanary session list # List active sessions\ncanary session status # Show session details\ncanary session stop # Stop the current session\ncanary session stop --all # Stop all sessions\ncanary session load-credential # Load a credential into a running session\ncanary session allow-screenshots # Grant Claude auto-read for screenshots\n\\`\\`\\`\n\n#### Browser actions\n\n\\`\\`\\`bash\n# Navigation\ncanary session navigate --url https://example.com\ncanary session back\n\n# Observe the page\ncanary session snapshot # Get accessibility tree\ncanary session snapshot --mode screenshot # Get a screenshot\ncanary session snapshot --mode both # Tree + screenshot\ncanary session snapshot --search \"Login\" # Search for text\ncanary session snapshot --expand e5 # Expand a collapsed ref\ncanary session screenshot --full-page # Full-page screenshot\n\n# Interact with elements (use ref from snapshot)\ncanary session click --ref e12\ncanary session type --ref e8 --text \"hello\" --submit\ncanary session fill --fields '[{\"ref\":\"e1\",\"value\":\"user@example.com\"},{\"ref\":\"e2\",\"value\":\"pass123\"}]'\ncanary session select --ref e15 --value \"option-1\"\ncanary session hover --ref e3\ncanary session press --key Enter\ncanary session scroll --direction down --amount 3\n\n# Tabs\ncanary session tabs # List tabs\ncanary session tabs --action new # Open new tab\ncanary session tabs --action select --index 0 # Switch to tab\ncanary session tabs --action close # Close current tab\n\n# Debugging\ncanary session wait --text \"Success\" --timeout 5000\ncanary session console --only-errors # Show console messages\ncanary session network # Show network requests\ncanary session evaluate --js \"document.title\" # Run JavaScript\n\\`\\`\\`\n\nCommon options: \\`--session <id|name>\\` (target a specific session), \\`--json\\`, \\`--env\\`\n\n#### Control (for cloud-connected sessions)\n\n\\`\\`\\`bash\ncanary session take-control # Force CLI control (pauses cloud)\ncanary session resume-cloud # Resume cloud execution\n\\`\\`\\`\n\n---\n\n### Recording\n\nRecord browser interactions for creating test workflows.\n\n\\`\\`\\`bash\ncanary record\ncanary record --credential admin --url https://example.com\ncanary record --output ./recordings --no-upload # Save locally only\n\\`\\`\\`\n\nOptions: \\`--credential\\`, \\`--url\\`, \\`--output\\`, \\`--env\\`, \\`--no-upload\\`\n\n---\n\n### Workflows\n\nInspect and manage workflow definitions.\n\n\\`\\`\\`bash\n# List and search\ncanary workflow list\ncanary workflow list --search \"checkout\" --status published --json\n\n# Get workflow definition\ncanary workflow get <workflowId> --format markdown\ncanary workflow get <workflowId> --json\n\n# Create from file or stdin\ncanary workflow create --from-file workflow.json\ncanary workflow create --run-and-publish --property \"My App\"\n\n# Update workflow\ncanary workflow patch <workflowId> --name \"New Name\" --status published\n\n# Run a workflow\ncanary workflow run <workflowId> --stream\n\n# Replace all nodes/edges\ncanary workflow sync <workflowId> --from-file updated.json\n\n# Authoring guide\ncanary workflow guide\ncanary workflow guide selectors\n\\`\\`\\`\n\nList filters: \\`--search\\`, \\`--flow-type\\` (standard|setup|seed|teardown), \\`--status\\` (draft|published), \\`--page\\`, \\`--page-size\\`\n\nWorkflow statuses: \\`draft\\` = editable, not run by suites. \\`published\\` = active, included in regression runs. Reliability is tracked automatically by the \\`health\\` dimension — a flow that keeps failing in suites becomes \\`degraded\\` and is skipped (shown \"degraded — not run\") until it's edited or manually re-enabled.\n\n---\n\n### Issues\n\nSearch and inspect QA issues detected by Canary.\n\n\\`\\`\\`bash\n# List issues\ncanary issues list\ncanary issues list --severity high --status open --format markdown\ncanary issues list --search \"timeout\" --property-id <uuid>\n\n# Get issue details\ncanary issues get <issue-id>\ncanary issues get <issue-id> --format markdown\n\\`\\`\\`\n\nList options: \\`--search\\`, \\`--severity\\` (low|medium|high|unknown), \\`--status\\` (open|closed|not_a_bug), \\`--property-id\\`, \\`--page\\`, \\`--page-size\\`, \\`--json\\`, \\`--format markdown\\`\n\n---\n\n### Release QA (CI/CD)\n\nTrigger and monitor Release QA runs from CI pipelines.\n\n\\`\\`\\`bash\ncanary release trigger --property-id <uuid>\ncanary release status <run-id> --json\ncanary release run --property-id <uuid> --timeout 600 # Trigger and poll\n\\`\\`\\`\n\n---\n\n### Properties & Credentials\n\n\\`\\`\\`bash\ncanary property list # List properties and linked repos\ncanary credential list # List credentials with environment info\n\\`\\`\\`\n\n---\n\n### Superadmin Commands\n\nThese commands require superadmin access.\n\n\\`\\`\\`bash\n# Knobs (global runtime config)\ncanary knobs list\ncanary knobs get <key>\ncanary knobs set <key> <value> --type boolean|number|string\ncanary knobs toggle <key>\ncanary knobs delete <key>\ncanary knobs lifecycle <key> --stage <stage>\n\n# Feature flags (per-org gating)\ncanary feature-flag list\ncanary feature-flag create <name> --description \"...\"\ncanary feature-flag enable <name> --org <orgId>\ncanary feature-flag disable <name> --org <orgId>\ncanary feature-flag delete <name>\ncanary feature-flag lifecycle <name> --stage <stage>\n\n# Database (read-only)\ncanary psql \"SELECT * FROM knobs LIMIT 5\" --json\n\n# Redis (read-only)\ncanary redis \"GET some:key\" --json\n\n# Documentation pages\ncanary docs <sub-command>\n\n# Debug sessions\ncanary debug-session --env dev --json\n\\`\\`\\`\n\n---\n\n## Common Patterns\n\n| Pattern | Description |\n|---------|-------------|\n| \\`--json\\` | Raw JSON output (most commands) |\n| \\`--format markdown\\` | Markdown-formatted output (issues, workflows) |\n| \\`--env <env>\\` | Target environment: \\`local\\`, \\`dev\\`, \\`prod\\` |\n| \\`--verbose, -v\\` | Show all events / detailed output |\n| \\`-h, --help\\` | Show help for any command |\n\n### Environment variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| \\`CANARY_API_URL\\` | \\`https://api.trycanary.ai\\` | API endpoint |\n| \\`CANARY_APP_URL\\` | \\`https://app.trycanary.ai\\` | App URL for login |\n| \\`CANARY_API_TOKEN\\` | — | API key (alternative to \\`canary login\\`) |\n\n## Getting Help\n\n\\`\\`\\`bash\ncanary help # Show all commands\ncanary <command> --help # Show help for a specific command\ncanary version # Show CLI version\ncanary upgrade # Upgrade to latest version\n\\`\\`\\`\n`,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\n\nexport const testCasesTemplate: SkillTemplate = {\n name: 'test-cases',\n description:\n 'Author high-quality test cases as `.canary.md` specs that compile straight into Canary workflows. Invoke when the user asks to write test cases, draft a test plan, plan QA scenarios, decide what to test, or convert a PRD/ticket/design into tests.',\n version: '2.2.0',\n render: () => `---\nname: test-cases\nversion: 2.2.0\ndescription: Author high-quality test cases as \\`.canary.md\\` specs that compile straight into Canary workflows. Invoke when the user asks to write test cases, draft a test plan, plan QA scenarios, decide what to test, or convert a PRD/ticket/design into tests.\nallowed-tools: Read, Grep, Glob, Bash, Write\n---\n\n# Test Case Authoring (Canary)\n\nUse this skill when the user asks for test cases, a test plan, QA scenarios, \"what should we test for X\", or wants to turn a PRD / ticket / design into tests. The output is one or more **\\`.canary.md\\` spec files** — each file is one workflow, one journey, one test case. \\`canary spec compile <file>\\` turns them into runnable Canary workflows; \\`canary spec validate <file>\\` enforces the structural rules below.\n\nThe skill has three layers:\n1. **Industry rubric** — what makes a test case good (ISTQB-aligned).\n2. **Spec mapping** — how each rubric piece becomes a section of \\`.canary.md\\`.\n3. **Domain Context** — customer-specific sources of truth, filled in once.\n\nRead the Domain Context block at the bottom **first**. If it is unfilled, follow the discovery instructions there before authoring any cases.\n\n---\n\n## 1. The 7-rule rubric\n\nEach case must satisfy all seven. If a rule cannot be satisfied, say so explicitly and explain why — don't silently skip.\n\n### 1. One coherent journey per case (atomic, but not micro)\nA case covers a single coherent journey — one user objective, possibly with several verifications along the way. \"Atomic\" means *coherent*, not *minimal*: if multiple checks share the same setup and naturally flow from one another, they belong in the same case.\n\n- Good (coherent journey, multiple checks): \"Onboard an org: create org → invite a user → claim invite → land on dashboard.\" Each step verifies its own outcome inline.\n- Good (single short journey): \"Submit a ticket and confirm it appears in the queue.\"\n- Bad (unrelated jobs): \"Submit a ticket, edit a user, generate a report.\" — three journeys, three separate \\`.canary.md\\` files.\n\n**Rule of thumb:** if isolating a check (e.g. \"tagging works\") would force you to duplicate most of another case's setup, fold it in as an inline assertion on the larger journey instead. Splitting only pays off when the setup is genuinely different or the failure modes are independent.\n\n### 2. Specific, observable expected results\nEvery assertion must be something a stranger could verify without judgement.\n- Good: \"URL ends with \\`/dashboard\\`.\" / \"A row with name \\`X\\` appears in the table.\"\n- Bad: \"The page looks correct.\" / \"It works.\"\n\nLint enforces this as **R1**: a spec with zero \\`fail\\`-severity assertions warns.\n\n### 3. Independent and self-sufficient\nA case must not depend on another case running first or on long-lived shared state. Provision data inline (\\`## Inputs\\` + \\`## Scenarios\\`, or a \\`**Seed**\\` node) and clean up in \\`## Cleanup\\`. Use macros (\\\\$EMAIL, \\\\$UUID) so reruns don't collide.\n\n### 4. Traceable\nEvery case carries \\`requirementRef:\\` in frontmatter pointing at the ticket / PRD / design. If you can't find one, flag the gap to the user — untraceable specs rot. Lint **R2** warns when missing.\n\n### 5. Risk-prioritized\nTag each case with a priority via the \\`tags:\\` array. Use one of: \\`p0\\`, \\`p1\\`, \\`p2\\`.\n- **p0** — happy path of the core promise; if this breaks, the feature is broken.\n- **p1** — primary edge cases, validation, role variants, common error states.\n- **p2** — rare conditions, visual/agentic checks, low-frequency edges.\n\n### 6. Includes preconditions, data, and cleanup\n- **Auth/role preconditions** → \\`actor:\\` in frontmatter.\n- **Other preconditions** (feature flags, seeded data, sister flows) → bulleted prose in \\`## Preflight\\`.\n- **Test data** → \\`## Inputs\\` (with macros) and per-variant \\`## Scenarios\\`.\n- **Teardown** → bulleted prose in \\`## Cleanup\\`.\n\n### 7. Written as human intent, not UI mechanics\nAction titles describe *what the user is trying to do*; the optional \\`> blockquote\\` underneath gives just enough context for the recorder/agent. Don't write click-by-click sequences.\n- Good: \\`**Action — Assign the ticket to Support**\\`\n- Bad: \\`**Action — Click 'Assign' dropdown, scroll, click 'Support', click Save**\\`\n\nLint **R9** warns when an action reads as click-by-click.\n\n---\n\n## 2. Coverage checklist\n\nBefore handing back a set of specs, sanity-check coverage. For each, either include a case or note why it doesn't apply:\n- Happy path (p0)\n- Primary edges / boundary inputs\n- Negative paths / validation errors\n- Permissions and role variants (admin / member / unauthenticated)\n- Empty, loading, and error states\n- Cross-entity side effects (does this change something visible elsewhere?)\n- Cleanup / undo where applicable\n\nAim for the smallest set that proves the feature works. Five sharp cases beat twenty fuzzy ones.\n\n---\n\n## 3. Authoring \\`.canary.md\\`\n\n\\`.canary.md\\` is one workflow per file. Frontmatter sets identity; H2 sections carry prose and structured config; \\`## Journey\\` is the linear test itself. The compiler validates structure; lint enforces the rubric semantics above.\n\nRun these for the definitive grammar — do **not** restate them here:\n\n\\`\\`\\`bash\ncanary spec --help # validate / compile / diff commands\ncanary spec validate <file> # parse + lint offline; enforce the rules in §1\ncanary spec compile <file> # create or update the workflow (prints the live URL)\ncanary workflow guide # node-shape and macro reference\n\\`\\`\\`\n\nAfter \\`canary spec compile\\`, echo the printed \\`URL:\\` line back to the user verbatim — it points at the workflow you just created (\\`https://app.trycanary.ai/flows/<id>\\` for prod, \\`https://app.dev.trycanary.ai/flows/<id>\\` for dev). Don't construct it yourself.\n\nMapping at a glance:\n\n| Rubric piece | Where it lives in \\`.canary.md\\` |\n|-----------------------------------------|---------------------------------------------------------------------------|\n| Case title | \\`frontmatter.name\\` |\n| Requirement / ticket ref | \\`frontmatter.requirementRef\\` |\n| Priority (rule 5) | \\`tags: [p0]\\` (or p1/p2) in frontmatter |\n| Auth role precondition | \\`actor: <X>\\` + \\`authenticated: true\\` in frontmatter. \\`<X>\\` is a credential **template** name by default (e.g. \\`actor: Admin\\` or the explicit \\`actor: template Admin\\`), or a specific raw credential via \\`actor: credential <name>\\` when the case genuinely needs one named account. Templates resolve at run/suite level; raw credentials bind a single fixed account. List templates with \\`canary credential-template list --property \"<property>\"\\`; list credentials with \\`canary credential list\\`. |\n| Feature flag / seeded data preconditions| Bulleted prose in \\`## Preflight\\` (\"Feature flag X enabled.\", \"Seed workflow Y published.\") |\n| One-paragraph user-visible promise | \\`## Spirit\\` (blockquote) |\n| Workflow-level declared inputs | \\`## Inputs\\` (e.g. \\`- foo: \\\\$EMAIL\\`) — parses to \\`workflows.inputs_json\\`, surfaces in Workflow Settings → Inputs |\n| Per-step bound values | \\`- field X: Y\\` under an Action — populates *that* step's Inputs panel with \\`Y\\` as the default (compiler mirrors fields → \\`dataRequirements\\` + \\`inputValues\\` on the node). Prefer \\`## Inputs\\` for values shared across actions. |\n| Variants (free / enterprise, etc.) | \\`## Scenarios\\` with per-scenario \\`values:\\` / \\`credentials:\\` overrides |\n| Steps (human intent) | \\`## Journey\\` — \\`**Navigate to** …\\`, \\`**Action — …**\\`, \\`**Seed** …\\` |\n| Inline expected results | \\`- assert (fail \\\\| warn): <condition>\\` under the action that produced it |\n| Standalone assertion | \\`**Assert** <condition>\\` node (prefer inline when possible) |\n| Role change mid-journey | \\`**Switch role to** <X>\\` — mirrors \\`actor:\\`. \\`<X>\\` is a credential template name by default (\\`**Switch role to** Admin\\`), or \\`credential <name>\\` to bind a specific raw credential (\\`**Switch role to** credential admin-bob\\`). Compiles to a login node with \\`performLogin: true\\`. Repeat as many times as needed in one journey. |\n| Cross-action data flow | \\`- capture: <name>\\` on the upstream action, \\`\\\\$var.<name>\\` downstream |\n| Cleanup | Bulleted prose in \\`## Cleanup\\` |\n\nWhen a journey shares setup and naturally flows end-to-end, emit **one** \\`.canary.md\\` with multiple action nodes and inline assertions. When journeys are unrelated, emit **one file per journey**; if one journey depends on data created by another, link them with \\`**Seed** <spec-name>\\` or \\`**Setup** <spec-name>\\` at the top of the dependent file.\n\n### Inputs defaults — the decision rule\n\nWhen you need a value (workflow-level \\`## Inputs\\` or step-level \\`- field\\`), pick where it comes from in this order:\n\n1. **Names existing data in the target environment** — a property name, a credential, a seeded record, an existing org/user/team. **Stop and ask the user for the value before writing the spec.** Don't hand-pick. The right sources of truth:\n - \\`canary property list\\` — properties (target apps)\n - \\`canary credential-template list --property \"<property>\"\\` — credential templates (these are what \\`actor:\\` and \\`- credential …\\` reference)\n - \\`canary workflow list --search <term>\\` — existing workflows to seed/link to\n - \\`canary feature-flag list --env <env>\\` — flag names for Preflight prose\n2. **Synthesizable** — a new email, a UUID, a throwaway string, an ISO date. Pick a macro (\\`\\\\$EMAIL\\`, \\`\\\\$UUID\\`, \\`\\\\$ISO_DATE\\`) and move on without asking. These render as pills in the Inputs panel and resolve at run time.\n3. **Prefer credential templates** for any auth/role-scoped input. \\`actor: Admin\\` (frontmatter) and \\`- credential admin: Admin\\` (inputs) are cleaner than passing \\`username:\\` + \\`password:\\` and work across environments. Reach for the raw-credential form (\\`actor: credential <name>\\`, \\`**Switch role to** credential <name>\\`, \\`- credential admin: <name>\\`) only when the case genuinely requires a specific fixed account.\n\n---\n\n## 4. Output format\n\nFor each case, write a \\`.canary.md\\` file alongside a one-row summary line. Default location: \\`canary/specs/<slug>.canary.md\\` (confirm with the user if their repo uses a different layout — check Domain Context below).\n\n### Summary table\n\n| file | name | priority | requirementRef |\n|-----------------------------------|--------------------------------------------|----------|------------------|\n| canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <ticket/PRD ref> |\n\n### Per-case file\n\n\\`\\`\\`markdown\n---\nname: <one user goal, action-oriented>\nactor: <credential template name from Domain Context — verbatim, e.g. \"Admin\"; or \\`credential <name>\\` for a specific raw credential>\nauthenticated: true\ntags: [p0] # or p1 / p2; add other tags from Domain Context's tagging conventions\nrequirementRef: <ticket id, PRD path, design link>\n---\n\n# <same as name>\n\n## Spirit\n> <one short paragraph: the user-visible promise this case proves>\n\n## Preflight\n- <feature flag / seed / sister flow / role precondition, one per bullet>\n\n## Cleanup\n- <records to delete, flags to reset>\n\n## Inputs\n- <name>: <value or macro>\n\n## Journey\n\n**Navigate to** \\\\\\`/some/path\\\\\\`.\n\n**Action — <human intent>**\n> <one-sentence context for the recorder/agent>\n- field <name> [(type)]: <value or \\\\$input>\n- capture: <name> # optional, only if a later step needs it via \\\\$var.<name>\n- assert (fail): <observable condition>\n- assert (warn): <softer signal>\n\n**End**.\n\\`\\`\\`\n\nKeep journeys short (3–8 nodes). If a case grows past that, it's probably two cases.\n\n---\n\n## 5. Worked example (generic)\n\nRequest: \"Test the support ticket flow.\"\n\n| file | name | priority | requirementRef |\n|-------------------------------------------|-----------------------------------------------------|----------|----------------|\n| canary/specs/submit-ticket.canary.md | Submit a ticket and confirm it appears in the queue | p0 | <fill in> |\n| canary/specs/ticket-required-fields.canary.md | Required-field validation blocks submit | p1 | <fill in> |\n| canary/specs/ticket-non-admin-reassign.canary.md | Non-admin cannot reassign a ticket | p1 | <fill in> |\n\n\\`canary/specs/submit-ticket.canary.md\\`:\n\n\\`\\`\\`markdown\n---\nname: Submit a ticket and confirm it appears in the queue\nactor: Admin\nauthenticated: true\ntags: [p0, smoke, support]\nrequirementRef: <fill in>\n---\n\n# Submit a ticket and confirm it appears in the queue\n\n## Spirit\n> A member can file a support ticket from the in-app form and immediately\n> see it land in their team's queue without a refresh.\n\n## Preflight\n- At least one Support team exists in the test org (seed workflow \\\\\\`support-team-seed\\\\\\`).\n- Feature flag \\\\\\`support_inbox_v3\\\\\\` is enabled.\n\n## Cleanup\n- Delete the created ticket.\n\n## Inputs\n- subject: \"QA \\\\$UUID\"\n- priority: high\n\n## Journey\n\n**Navigate to** \\\\\\`/support/new\\\\\\`.\n\n**Action — Submit the ticket**\n> Fill subject and priority, then submit.\n- field subject: \\\\$subject\n- field priority (select): \\\\$priority\n- assert (warn): Toast contains \"Ticket created\"\n- assert (fail): URL ends with \\\\\\`/support\\\\\\`\n\n**Navigate to** \\\\\\`/support/queue\\\\\\`.\n\n**Action — Confirm it appears in the queue**\n> Locate the new ticket in the team queue.\n- assert (fail): A row matching \\\\\\`\\\\$subject\\\\\\` is visible\n\n**End**.\n\\`\\`\\`\n\nAfter writing, run \\`canary spec validate canary/specs/submit-ticket.canary.md\\` and resolve any errors / warnings before handing back.\n\n---\n\n## 6. Anti-patterns\n\n- Mega-files that try to cover five unrelated journeys.\n- The opposite: splitting one coherent journey into N tiny files that each re-create the same setup. Bundle them into one \\`## Journey\\` instead.\n- Click-by-click action titles (\"Click X, scroll, click Y\").\n- \"Looks correct\" / \"everything works\" assertions.\n- Hardcoded emails, IDs, or names that collide on rerun. Use macros (\\\\$EMAIL, \\\\$UUID).\n- Specs that depend on another spec having run first without an explicit \\`**Seed**\\` / \\`**Setup**\\` node.\n- Skipping \\`requirementRef\\` because \"it's obvious\".\n- Asserting state via toasts only — confirm the underlying record changed.\n- Dropping the priority tag — risk-rank every case (\\`p0\\` / \\`p1\\` / \\`p2\\`).\n\n---\n\n## 7. Domain Context\n\n<!-- AGENT/CUSTOMER: fill in this block on first use of the skill, then commit it. -->\n<!-- If any field below is \"<unfilled>\", do a discovery pass before authoring cases: -->\n<!-- - Read README.md, CLAUDE.md, AGENTS.md, docs/, specs/ for requirement and design pointers. -->\n<!-- - Scan recent git log for ticket-tracker references (JIRA-123, LIN-456, #issue). -->\n<!-- - Run \\`canary property list\\` and \\`canary credential-template list --property \"<property>\"\\` to learn properties + the credential templates that map to \\`actor:\\` values. -->\n<!-- - (\\`canary credential list\\` shows live credentials — useful, but the \\`actor:\\` field references credential *templates*, not credentials.) -->\n<!-- - Run \\`canary workflow list --search <feature>\\` to avoid duplicating existing workflows. -->\n<!-- - Run \\`canary feature-flag list\\` to confirm flag names referenced in Preflight prose. -->\n<!-- - Propose values to the user, get confirmation, then edit this block in place. -->\n\n- **Requirements / PRDs**: <unfilled — paths or URLs, e.g. \\`docs/specs/\\`, Notion space, Linear project>\n- **Designs**: <unfilled — Figma links, design system doc>\n- **Ticket tracker**: <unfilled — Jira/Linear project key + how to format \\`requirementRef\\` (e.g. \\`PROJ-123\\` or a URL)>\n- **Default property**: <unfilled — the property name to target; find via \\`canary property list\\`>\n- **Default environment**: <unfilled — \\`prod\\` | \\`staging\\` | \\`dev\\`>\n- **Credentials / role matrix** (\\`actor:\\` is a credential template name by default — list via \\`canary credential-template list --property \"<property>\"\\`. Prefix with \\`credential \\` to bind a specific raw credential — list via \\`canary credential list\\`. Use the template form unless the case genuinely needs a fixed named account.): <unfilled — list the credential template names available on the default property, with a one-line note on which to use when. Common shape: \\`actor: Admin\\` (standard user), \\`actor: Superadmin\\` (full access).>\n- **Tagging conventions**: <unfilled — e.g. \\`smoke\\`, \\`regression\\`, plus the priority tags \\`p0\\` / \\`p1\\` / \\`p2\\`>\n- **Spec directory**: <unfilled — where \\`.canary.md\\` files live in this repo, e.g. \\`canary/specs/\\`>\n- **Existing workflows to extend or avoid duplicating**: <unfilled — search via \\`canary workflow list --search <term>\\`>\n- **Glossary / domain terms**: <unfilled — terms that show up in tickets and should appear verbatim in spec names>\n- **Other sources of truth**: <unfilled — runbooks, support tickets, analytics dashboards>\n\nWhen this block is fully populated, every case you author should reference at least the **Requirements**, **Default property**, **Spec directory**, and **Credentials** entries.\n`,\n};\n","/**\n * `debug-run` skill template.\n *\n * Unlike the other entries in SKILL_TEMPLATES (which inline their content as\n * JS strings), the debug-run skill is rendered from the Handlebars partials in\n * `@chatsdet/diagnostic-playbook`. Source of truth = the playbook partials.\n *\n * Versioning: each released version is a frozen snapshot file under\n * `packages/canary-cli/skills/debug-run/history/<version>.md`. The current\n * version is stamped in `version.json` next to the history dir. To mint a new\n * version after editing partials, run:\n *\n * bun run --filter=@canaryai/cli bump-skill debug-run [patch|minor|major]\n *\n * The drift test in `apps/api/tests/unit/diagnostics/skill-rendered.test.ts`\n * fails if the live `renderSkill()` output diverges from\n * `history/<currentVersion>.md`, forcing a bump rather than silent edits to a\n * previously-released version. `previousVersions` is auto-populated from the\n * history dir so `canary skill diff debug-run` can replay any released version.\n */\n\nimport { readFileSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { SkillTemplate } from './issue-log-xref.js';\n\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the on-disk location of the debug-run skill assets.\n *\n * At runtime the assets live in one of two places depending on whether we\n * are running from the source tree (tests, dev), the bundled dist (npm\n * install), or from a published @canaryai/cli inside node_modules:\n *\n * - source: packages/canary-cli/src/skill-templates/debug-run.ts\n * → ../../skills/debug-run/\n * - dist: packages/canary-cli/dist/<chunk>.js\n * → ./skills/debug-run/ (copied by `cp -r skills dist/`)\n *\n * The first candidate that has a readable `version.json` wins.\n */\nfunction resolveSkillDir(): string {\n const candidates = [\n join(HERE, '..', '..', 'skills', 'debug-run'), // source tree\n join(HERE, 'skills', 'debug-run'), // dist\n join(HERE, '..', 'skills', 'debug-run'), // dist split-chunk variant\n ];\n for (const c of candidates) {\n try {\n readFileSync(join(c, 'version.json'), 'utf-8');\n return c;\n } catch {\n // try next\n }\n }\n throw new Error(\n `Could not locate debug-run skill assets relative to ${HERE}. Tried: ${candidates.join(', ')}`,\n );\n}\n\nconst SKILL_DIR = resolveSkillDir();\nconst HISTORY_DIR = join(SKILL_DIR, 'history');\n\nconst versionMeta = JSON.parse(\n readFileSync(join(SKILL_DIR, 'version.json'), 'utf-8'),\n) as { current: string };\n\nfunction readVersion(version: string): string {\n return readFileSync(join(HISTORY_DIR, `${version}.md`), 'utf-8');\n}\n\nfunction listVersionsOnDisk(): string[] {\n return readdirSync(HISTORY_DIR)\n .filter((f) => f.endsWith('.md'))\n .map((f) => f.slice(0, -3));\n}\n\nconst previousVersions: Record<string, () => string> = {};\nfor (const v of listVersionsOnDisk()) {\n if (v !== versionMeta.current) {\n previousVersions[v] = () => readVersion(v);\n }\n}\n\nexport const debugRunTemplate: SkillTemplate = {\n name: 'debug-run',\n description:\n 'Diagnose why a workflow test failed — root-cause an end-to-end run from its materialised artifacts',\n version: versionMeta.current,\n render: () => readVersion(versionMeta.current),\n previousVersions,\n};\n","import type { SkillTemplate } from './issue-log-xref.js';\nimport { issueLogXrefTemplate } from './issue-log-xref.js';\nimport { browserTestingTemplate } from './browser-testing.js';\nimport { agentTasksTemplate } from './agent-tasks.js';\nimport { canaryBasicTemplate } from './canary-basic.js';\nimport { testCasesTemplate } from './test-cases.js';\nimport { debugRunTemplate } from './debug-run.js';\n\n// SkillTemplate kept file-local (defined in issue-log-xref.ts)\n\nexport const SKILL_TEMPLATES: Record<string, SkillTemplate> = {\n 'canary-basic': canaryBasicTemplate,\n 'agent-tasks': agentTasksTemplate,\n 'issue-log-xref': issueLogXrefTemplate,\n 'browser-testing': browserTestingTemplate,\n 'test-cases': testCasesTemplate,\n 'debug-run': debugRunTemplate,\n};\n"],"mappings":";;;;AAAA,SAAS,YAAY,WAAW,qBAAqB;AACrD,SAAS,eAAe;AACxB,SAAS,QAAAA,aAAY;;;ACWd,IAAM,uBAAsC;AAAA,EACjD,MAAM;AAAA,EACN,aACE;AAAA,EACF,SAAS;AAAA,EACT,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8GhB;;;AC9HO,IAAM,yBAAwC;AAAA,EACnD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["join","join","i","j"]}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debug-run
|
|
3
|
+
description: Root-cause a Canary workflow run end-to-end. Materializes the run's diagnostic workspace and explores it with Read (multimodal), Bash, Grep, and Glob. Use when the user asks "why did this run fail/hang/take so long", references a runId, pastes a flows/?runId=... URL, or asks "what changed since it last passed".
|
|
4
|
+
allowed-tools: Read, Grep, Glob, Bash(canary workflow debug:*), Bash(canary workflow runs:*), Bash(jq:*), Bash(diff:*), Bash(sed -n:*), Bash(head:*), Bash(tail:*), Bash(wc:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(cat:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- AUTOGENERATED from packages/diagnostic-playbook/. Do not edit by hand. -->
|
|
8
|
+
<!-- After editing the partials, mint a new version: -->
|
|
9
|
+
<!-- bun run --filter=@canaryai/cli bump-skill debug-run [patch|minor|major] -->
|
|
10
|
+
|
|
11
|
+
# Workflow Run Debug Guide
|
|
12
|
+
|
|
13
|
+
You are debugging a Canary workflow run end-to-end. The same diagnostic data the v2 failure-diagnostic agent uses is available to you here — materialize it once, then explore.
|
|
14
|
+
|
|
15
|
+
## Materialize the workspace
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
canary workflow debug materialize <runId> --out ./diag-<runId>/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This produces a structured workspace on disk. After it completes, all subsequent navigation uses Read/Bash/Grep against that directory. The workspace is the **single source of truth** — the v2 agent and you read the exact same files.
|
|
22
|
+
|
|
23
|
+
If you don't have a workflowId, list runs first:
|
|
24
|
+
```bash
|
|
25
|
+
canary workflow runs list <workflowId>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Your job
|
|
29
|
+
|
|
30
|
+
You are a senior QA engineer diagnosing why an end-to-end workflow test failed for a QA tester who has to **fix the test**. The reported failure is a **hypothesis**. Your job is to verify it with evidence and produce a tight, scannable report — see the output rubric.
|
|
31
|
+
|
|
32
|
+
The QA tester reads your `title` first, then `recommendation`. Make those two land. Everything else is supporting detail.
|
|
33
|
+
|
|
34
|
+
## Failure categories — pick exactly one
|
|
35
|
+
|
|
36
|
+
- `software_bug` — defect in the application under test (4xx/5xx from app APIs, JS exceptions, broken UI, a behavior delta that is clearly broken)
|
|
37
|
+
- `dependency_issue` — required data, fixture, or external service was missing for the test
|
|
38
|
+
- `agent_confusion` — the AI test agent reasoned incorrectly, picked the wrong element, or got confused
|
|
39
|
+
- `platform_error` — defect in our test runner, harness, sandbox, or recording infrastructure
|
|
40
|
+
- `test_underspecified` — the test instructions themselves are wrong or ambiguous. **Only valid with positive evidence that the authored test changed or is genuinely ambiguous** (e.g. the History section reports AUTHORED TEST CHANGED, or two runs with the same instructions reasonably diverged). **Never** classify `test_underspecified` when the authored test is unchanged across a pass→fail boundary — an unchanged test that used to pass is not a test problem.
|
|
41
|
+
- `app_changed` — the application's behavior, labels, copy, or flow changed versus the last passing run **while the authored test stayed the same**, and you cannot verify from inside the sandbox whether the change was intentional. This is the correct verdict for an unchanged test that breaks because the app moved underneath it (e.g. a button relabeled "Login" → "Acting As"). Name the concrete delta and recommend "X appears to have changed to Y; update the test to match if intentional." Reserve `software_bug` for deltas that are clearly defects (error page, 5xx, broken layout); use `app_changed` when the new behavior looks deliberate or you simply cannot tell.
|
|
42
|
+
- `data_collision` — the workflow interacted with the **wrong data**: a leftover org from a prior session, a duplicated row, a record from another run, the wrong tenant, etc. The point of diagnosis is *why the workflow drifted onto wrong data* — stale cached recipe pointing at the wrong DOM node, locator matching multiple elements, variable bound to an earlier session/org, agent fallback choosing the first match. **Do not** investigate the provenance of the colliding data.
|
|
43
|
+
|
|
44
|
+
Never mark `agent_confusion` or `platform_error` from a failed assertion alone. Look at multiple evidence sources first.
|
|
45
|
+
|
|
46
|
+
## Output discipline (hard rules)
|
|
47
|
+
|
|
48
|
+
- **Never** mention internal artifact paths in any user-visible field. Banned substrings in `title`, `failurePoints`, `recommendation`, `evidence.description`, `evidence.detail`, `openQuestions`, `comparisonWithHistory`: `steps/`, `workflow/nodes/`, `logs/`, `thoughts/`, `history/last-green`, `browser/frames/`, `the ledger`, `the workspace`, `<stepId>`, `<nodeId>`. Speak in domain terms: "the authored step description", "the network log", "the agent thought at step 4", "the last-passing run on YYYY-MM-DD".
|
|
49
|
+
- **No section headings** inside `failurePoints` or `recommendation`. They are flat bullet lists.
|
|
50
|
+
- **No narrative paragraphs.** If you find yourself writing "Then…", "After that…", "Subsequently…" — collapse to a bullet.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Workspace layout
|
|
54
|
+
|
|
55
|
+
Your diagnostic workspace is at `{{workspacePath}}`. Everything you need is here. Read in three tiers:
|
|
56
|
+
|
|
57
|
+
**Tier 1 — always read first:**
|
|
58
|
+
- `summary.md` — run-level overview, step table, error counts, notable events, history hint, and pointers to the right rollup files
|
|
59
|
+
- `reported-failure.md` — the test runner's hypothesis (verify, do not trust)
|
|
60
|
+
|
|
61
|
+
**Tier 2 — drill-down per step:**
|
|
62
|
+
- `steps/<NNN>-<stepId>.md` — per-step rollup (timing, status, error, thoughts, in-window network/console events, navigation, assertions, artifact paths). Read the failed step's rollup first, then walk backwards.
|
|
63
|
+
|
|
64
|
+
**Tier 3 — windowed reads only:**
|
|
65
|
+
- `ledger.md` — full chronological narrative. **Do not read whole.** Open `ledger-index.md` first for the step→line-range mapping, then `bash("sed -n '<a>,<b>p' ledger.md")`.
|
|
66
|
+
|
|
67
|
+
## Other artifacts
|
|
68
|
+
|
|
69
|
+
- `browser/frames/<NNNN>-<absoluteTsMs>.png` — screenshots per browser action. Read with `read_file`; the model sees them as vision input.
|
|
70
|
+
- `browser/doms/<NNNN>-<absoluteTsMs>.html` — composed self-contained HTML per action.
|
|
71
|
+
- `browser/a11y/<NNNN>-<absoluteTsMs>.yaml` — what the agent's accessibility view looked like.
|
|
72
|
+
- `browser/actions.jsonl` — parsed Playwright actions with timestamps. Use `jq` to filter.
|
|
73
|
+
- `browser/trace.zip` — raw Playwright trace (escape hatch).
|
|
74
|
+
- `workflow/nodes/<nodeId>.json` — full configJson per workflow node. Quote `actionDescription`, `inlineAssertions` from here.
|
|
75
|
+
- `workflow/snapshot.json` — full workflow snapshot the run executed.
|
|
76
|
+
- `thoughts/all.jsonl` — every agent thought with stepId, timestamp, content. Use `jq` to filter.
|
|
77
|
+
- `thoughts/by-step/<stepId>.md` — readable per-step thoughts.
|
|
78
|
+
- `timeline/{steps,actions,navigation,assertions,cache-decisions}.jsonl` — structured event streams.
|
|
79
|
+
- `logs/network.jsonl`, `logs/network-errors.jsonl` — network requests; errors are pre-filtered.
|
|
80
|
+
- `logs/console.jsonl`, `logs/console-errors.jsonl` — console logs; errors+warnings are pre-filtered.
|
|
81
|
+
- `logs/sentry-logs.jsonl`, `logs/sentry-spans.jsonl` — Sentry telemetry. May contain a `partial: true` first line if the Sentry token wasn't available.
|
|
82
|
+
- `variables/inputs.json`, `variables/lineage.jsonl` — run inputs + per-step variable captures.
|
|
83
|
+
- `history/recent-runs.jsonl` — last ~20 runs with status. Useful for picking a green run to compare.
|
|
84
|
+
- `history/last-green/` — populated only if you materialize a prior passing run for comparison.
|
|
85
|
+
- `notes/` — your working memory. Write here with `write_file` (only the workspace, no path escapes).
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Investigation procedure (Claude Code edition)
|
|
89
|
+
|
|
90
|
+
Follow this in order. Adapt the v2 agent's procedure to use Claude Code's built-in tools.
|
|
91
|
+
|
|
92
|
+
1. **Anchor at the reported failure.** `Read summary.md` first — it has the run-level overview, the failed step's ordinal, the file path of the failed step's rollup, and the history hint. Treat the reported failure as a hypothesis, not a fact.
|
|
93
|
+
2. **Verify with evidence — including visual evidence.** `Read steps/<NNN>-<failedStepId>.md` for the failure context. Read the screenshot file referenced there (`browser/frames/<NNNN>-...png`) — `Read` is multimodal, vision describes what's on screen. Look for unexpected modals, banners, blank areas, wrong brand, layout shifts. Cross-reference network errors and console errors in the rollup.
|
|
94
|
+
3. **Walk backwards.** The reported failure is rarely the divergence point. Read the prior step's rollup (`steps/<NNN-1>-...md`), look at its screenshot, and ask: did this step actually accomplish its goal? Repeat going backwards until you find a step where the actual outcome diverged from the intended outcome.
|
|
95
|
+
4. **Compare to history.** If `summary.md`'s history section shows recent passes, the most recent green run's testRunId and materialize command appear there. Run that command, then visually compare the suspect step's screenshot in both runs.
|
|
96
|
+
5. **Present clear evidence.** Your write-up cites specific artifacts (file paths, timestamps, screenshot indices, network entries). When visual comparison is decisive, name both screenshot paths.
|
|
97
|
+
|
|
98
|
+
## Important
|
|
99
|
+
|
|
100
|
+
- Never `Read ledger.md` without a specific window — open `ledger-index.md` first to find the line range, then `Bash sed -n '<a>,<b>p' ledger.md`.
|
|
101
|
+
- The workspace is the canonical data layer. If you can't answer a question from the workspace, that's a signal the materializer is missing a domain — file a follow-up rather than reaching for psql/sentry CLI directly.
|
|
102
|
+
|
|
103
|
+
## Verifying the reported failure
|
|
104
|
+
|
|
105
|
+
The failure report (in `reported-failure.md` and the top of `summary.md`) is the **runner's hypothesis** about what failed. It is wrong often enough that you must always verify before forming a diagnosis.
|
|
106
|
+
|
|
107
|
+
Things that look like a step-3 failure but are really upstream:
|
|
108
|
+
- The agent on step 1 picked the wrong "Sign in" link, ended up on the consumer site instead of the staging site, and the failure surfaces as `AuthenticationError` two steps later.
|
|
109
|
+
- A cache-replay on step 2 used a stale selector that matched a hidden element; the step "succeeded" but didn't actually click anything; step 3 fails because the modal never opened.
|
|
110
|
+
- The runner timed out a step but the page actually loaded fine — the timeout is a platform issue, not the app's fault.
|
|
111
|
+
|
|
112
|
+
Always cross-reference at least three sources before forming a hypothesis:
|
|
113
|
+
1. The screenshot at the failure timestamp (`browser/frames/...`)
|
|
114
|
+
2. The agent's thoughts during the failed step (`thoughts/by-step/<stepId>.md`)
|
|
115
|
+
3. Either the network errors (`logs/network-errors.jsonl`) OR the console errors (`logs/console-errors.jsonl`) within ±2s of the failure timestamp
|
|
116
|
+
|
|
117
|
+
If the screenshot, thoughts, and logs all paint the same picture, the reported failure is probably accurate. If any one disagrees, walk backwards (see procedure step 3).
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## Walking backwards
|
|
121
|
+
|
|
122
|
+
The visible failure is rarely the actual divergence. Common patterns:
|
|
123
|
+
|
|
124
|
+
- A `click` succeeded 3 steps ago but landed on the *wrong* element — the cache-replay layer hid the mismatch — and the failure surfaces only when the next required field can't be found.
|
|
125
|
+
- A cross-host navigation between two earlier steps invalidated the auth session, and the 401 doesn't appear until the next API call.
|
|
126
|
+
- A variable was captured from the wrong DOM element on step N, and step N+5 now has a meaningless `$var.X` value.
|
|
127
|
+
- The agent went down a wrong branch on a conditional, and the test ran a different path than intended.
|
|
128
|
+
|
|
129
|
+
Walk-backwards algorithm:
|
|
130
|
+
|
|
131
|
+
1. `read_file` `steps/<NNN-1>-<prevStepId>.md` for the step immediately before the failure.
|
|
132
|
+
2. `read_file` its screenshot (`browser/frames/<NNNN>-...png`). **Look at it.** Did the step actually accomplish its goal?
|
|
133
|
+
3. Compare to the instruction by `read_file` on `workflow/nodes/<nodeId>.json` — does the screenshot match the intent of `actionDescription` and `inlineAssertions`?
|
|
134
|
+
4. Skim the rollup's "Agent thoughts" section. Was the agent confused? Did it pick the wrong element?
|
|
135
|
+
5. Skim the rollup's "Navigation" section. A cross-host transition (⚠ marker) is a strong signal that auth state was lost between this step and the next.
|
|
136
|
+
6. If everything looks normal here, repeat with `steps/<NNN-2>-...md`, `<NNN-3>`, and so on.
|
|
137
|
+
|
|
138
|
+
Stop when you find a step where the actual outcome diverged from the intended outcome. That is your root-cause candidate. Cite that step's screenshot and the diverging detail in your `actuallyHappened` narration.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## Visual evidence — when to read frames
|
|
142
|
+
|
|
143
|
+
A large fraction of diagnoses turn on what the page actually *looked like* at a given moment. Text alone cannot tell you:
|
|
144
|
+
- Whether an unexpected modal/banner is blocking the click target
|
|
145
|
+
- Whether the layout shifted such that the agent's selector resolved to a different element
|
|
146
|
+
- Whether the brand on screen matches what the test expected (cross-host redirects often land on a sibling brand)
|
|
147
|
+
- Whether a stale cached fragment is being shown
|
|
148
|
+
- Whether a button is disabled, missing, or moved
|
|
149
|
+
|
|
150
|
+
How to use vision:
|
|
151
|
+
|
|
152
|
+
- Use `read_file` on `browser/frames/<NNNN>-<absoluteTsMs>.png`. The file extension triggers multimodal output — the model receives the image as vision input on the same turn.
|
|
153
|
+
- For run-vs-run comparison, read the frame at the same step in `history/last-green/browser/frames/<NNNN>-...png` and the failing run's frame in adjacent `read_file` calls. Reason about the visual delta directly.
|
|
154
|
+
- When visual evidence is decisive, include a `before_after` entry in your `evidence[]` with the two image paths.
|
|
155
|
+
|
|
156
|
+
Do not use `bash` to view images — `read_file` is the only tool that returns vision content.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
## Comparing to a passing run
|
|
160
|
+
|
|
161
|
+
If `summary.md`'s history section names a recent green run, comparison is **required** — it is the highest-yield step for a pass→fail regression. The most recent green run's testRunId and the materialize command appear there.
|
|
162
|
+
|
|
163
|
+
Procedure:
|
|
164
|
+
|
|
165
|
+
1. From `summary.md`'s "History" section, copy the materialize command. It looks like:
|
|
166
|
+
`canary workflow debug materialize <greenRunId> --out history/last-green/`
|
|
167
|
+
2. Run that exact command via `bash`. After it completes, you'll have a parallel workspace at `history/last-green/`.
|
|
168
|
+
3. `read_file` the suspect step's screenshot in both runs:
|
|
169
|
+
- `browser/frames/<NNNN>-...png` — failing
|
|
170
|
+
- `history/last-green/browser/frames/<NNNN>-...png` — passing
|
|
171
|
+
The visual delta is often the smoking gun.
|
|
172
|
+
4. Diff thoughts and node configs:
|
|
173
|
+
- `bash diff thoughts/by-step/<stepId>.md history/last-green/thoughts/by-step/<stepId>.md`
|
|
174
|
+
- `bash diff workflow/snapshot.json history/last-green/workflow/snapshot.json | head -100`
|
|
175
|
+
|
|
176
|
+
Common signals from comparison:
|
|
177
|
+
|
|
178
|
+
- Identical authored workflow + visual/label delta = **app changed**. Classify `app_changed` (intent unverifiable) unless the delta is clearly a defect (error page, 5xx, broken layout), in which case `software_bug`.
|
|
179
|
+
- Different workflow snapshot = **test changed** (possibly `test_underspecified` if the new instructions are ambiguous).
|
|
180
|
+
- Same screenshot but different thoughts = **agent interpretation drift** (likely `test_underspecified` if the test allowed the prior interpretation).
|
|
181
|
+
- Same everything but new network 5xx = **dependency/backend issue** (likely `software_bug` or `dependency_issue`).
|
|
182
|
+
|
|
183
|
+
## The regime gates your verdict
|
|
184
|
+
|
|
185
|
+
`summary.md`'s History section reports a **Regime** and, when available, an **AUTHORED TEST UNCHANGED / CHANGED** line computed from a content fingerprint of the authored definition. Do not overrotate on a bare "last run green, this run red" — gate the conclusion on the regime:
|
|
186
|
+
|
|
187
|
+
- **`STABLE_REGRESSION` + AUTHORED TEST UNCHANGED** → the test did not change across the pass→fail boundary, so `test_underspecified` is **off the table**. Classify `app_changed` (or `software_bug` if the delta is clearly a defect). Name the concrete behavior delta and record in `openQuestions` that you could not verify whether the change was intentional.
|
|
188
|
+
- **`STABLE_REGRESSION` + AUTHORED TEST CHANGED** → the test was edited; judge whether the new instructions are ambiguous (`test_underspecified`) before blaming the app.
|
|
189
|
+
- **`FLAKY`** → a single green→red does **NOT** justify `app_changed`. The test oscillates; weigh `agent_confusion`, intermittent `dependency_issue`, and `data_collision`. Comparison still helps, but only conclude `app_changed` with a **clear, persistent** behavior delta — never a one-off.
|
|
190
|
+
- **`NEW` / `PERSISTENT_FAIL`** → no stable green baseline; rely on the within-run procedure (verify + walk backwards).
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## Standing rules
|
|
194
|
+
|
|
195
|
+
## Standing rule: auth errors after a cross-host transition
|
|
196
|
+
|
|
197
|
+
If the failure looks like a credential or session issue (401/403, "session expired", login redirect, "please sign in"), **first reconstruct the URL timeline around the failure**.
|
|
198
|
+
|
|
199
|
+
1. Read the failed step's rollup (`steps/<NNN>-<stepId>.md`) — the "Navigation (in step window)" section lists URL transitions and marks cross-host transitions with `⚠ cross-host`.
|
|
200
|
+
2. Skim the prior step's navigation as well — the cross-host hop often happens between steps.
|
|
201
|
+
3. Compare host names case-insensitively, stripping a leading `www.`. So `staging.example.com` → `www.example.com` is a cross-host change; `www.example.com` ↔ `example.com` is not.
|
|
202
|
+
|
|
203
|
+
**If the hostname changed between two entries shortly before the auth failure, the test navigated off its authenticated origin. That is the root cause, not stale credentials.** Do not classify as `dependency_issue` (credentials problem) without ruling out cross-host.
|
|
204
|
+
|
|
205
|
+
Auth errors after a cross-host transition are almost never credential issues.
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
## Standing rule: workflow variables and macros
|
|
209
|
+
|
|
210
|
+
Workflow steps can use **macros** and **variable references** in their configuration:
|
|
211
|
+
|
|
212
|
+
- **Macros** (generate values at runtime): `$MACRO_NAME(args)` — e.g. `$EMAIL()`, `$UUID()`, `$RANDOM_STRING(10)`. Used in `inputValues` on action nodes; resolved fresh each run.
|
|
213
|
+
- **Variable references** (reference values from earlier steps): `$var.variable_name`. Variables are created when a step's `inputValues` are resolved. **A variable defined in step N is NOT available within step N itself** — only in step N+1 and later. This is because variables are registered after the step's inputs are resolved.
|
|
214
|
+
|
|
215
|
+
Common variable-related failures:
|
|
216
|
+
- A step references `$var.X` where X is defined in that same step (will be unresolved → blank value used).
|
|
217
|
+
- A step references `$var.X` where X was defined in a step that was skipped or failed earlier.
|
|
218
|
+
- Misspelled variable names (e.g. `$var.client_name` vs `$var.clientName`).
|
|
219
|
+
- An `inputValue` was set without a matching `dataRequirements` entry — only inputs with a dataRequirement become variables.
|
|
220
|
+
|
|
221
|
+
When suggesting test instruction changes:
|
|
222
|
+
- Use `read_file` on `workflow/nodes/<nodeId>.json` to see the full configJson before proposing a `configPatch`.
|
|
223
|
+
- Preserve any existing `$var.` references and `$MACRO()` calls — don't break them.
|
|
224
|
+
- For array fields like `inlineAssertions` or `playbookSteps`, the `configPatch` must be a **full replacement array**, not a partial — read the existing array first and include the merged result.
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## Conditional rule: non-blocking inline assertions
|
|
228
|
+
|
|
229
|
+
For this org, an inline assertion on an action step is allowed to fail without failing the step — the agent judges the broader outcome and may still proceed. Assertion records carry a `nonBlocking` flag reflecting how each was treated at run time.
|
|
230
|
+
|
|
231
|
+
When investigating:
|
|
232
|
+
|
|
233
|
+
- Open the step's rollup (`steps/<NNN>-<stepId>.md`) and look at the "Assertions" section. Each line shows `passed`/`FAILED`, severity, and a `nonBlocking` marker.
|
|
234
|
+
- **Do not treat "failed assertion + completed step" on its own as evidence of `platform_error` or contradictory state.** When `nonBlocking` is true, that combination is expected and carries no signal about the actual cause.
|
|
235
|
+
- A non-blocking failed assertion does not tell you which category applies. It only rules out one specific false-positive narrative (`platform_error` / bookkeeping). Continue investigating from network, console, thoughts, visual, and history. The actual cause may be any of `software_bug`, `dependency_issue`, `agent_confusion`, `test_underspecified`, or `platform_error`.
|
|
236
|
+
|
|
237
|
+
When `platform_error` is still a live possibility:
|
|
238
|
+
|
|
239
|
+
- A failed assertion with `nonBlocking: false` (or the field absent) alongside a step recorded as completed successfully. That is a genuine disagreement worth flagging.
|
|
240
|
+
- Infrastructure-level failures (browser session died, worker crash, our own stack traces) independent of any assertion outcome.
|
|
241
|
+
|
|
242
|
+
Always consult the assertions section of the rollup before writing a bookkeeping-bug narrative.
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
## Reporting
|
|
246
|
+
|
|
247
|
+
The QA tester reading your report sees, by default, only the `title`, `Key Evidence`, and `Recommendation`. Everything else is twirl-down. Optimise for that.
|
|
248
|
+
|
|
249
|
+
- `title` — one sentence, concrete and specific. The thesis.
|
|
250
|
+
- `failurePoints` — 2–4 bullets describing the failure; quote the authored expectation inline only when it sharpens the point.
|
|
251
|
+
- `recommendation` — 1–3 imperative bullets matched to the failure category:
|
|
252
|
+
- `software_bug` → name the app defect; the underlying error is quoted in an evidence item
|
|
253
|
+
- `dependency_issue` → name the missing data/fixture/credential and where to provision
|
|
254
|
+
- `agent_confusion` → quote the confused thought, point at visual evidence the agent missed, suggest test-instruction tweaks
|
|
255
|
+
- `platform_error` → describe the harness failure, suggest filing a platform issue
|
|
256
|
+
- `test_underspecified` → quote the ambiguous instruction; provide a clearer rewording
|
|
257
|
+
- `data_collision` → explain *why* the workflow drifted onto wrong data (stale cached recipe, ambiguous locator, stale variable binding); propose a recipe/locator/variable fix only when confidence is ≥80
|
|
258
|
+
- `openQuestions` (optional) — what you couldn't determine. Skip if you have full conviction.
|
|
259
|
+
|
|
260
|
+
Never reference internal artifact paths (`steps/…`, `workflow/nodes/…`, `logs/…`, `thoughts/…`, `history/…`, `browser/frames/…`, "the ledger", "the workspace") in any field the QA tester sees. Speak in domain terms: "the authored step description", "the network log", "the agent thought at step 4".
|
|
261
|
+
|
|
262
|
+
Don't hand back "the run failed" or a Sentry error message alone. The whole point is to triangulate to a concrete cause.
|