@runcofactor/cli 0.10.0 → 0.15.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/dist/account-22LMP5PX.js +255 -0
- package/dist/account-22LMP5PX.js.map +1 -0
- package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
- package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
- package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
- package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
- package/dist/augmentor-5GMNAX4M.js +198 -0
- package/dist/augmentor-5GMNAX4M.js.map +1 -0
- package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
- package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
- package/dist/chunk-3X3NW7QA.js.map +1 -0
- package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
- package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
- package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
- package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
- package/dist/chunk-7VMO2WOZ.js.map +1 -0
- package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
- package/dist/chunk-CMGI7UZD.js.map +1 -0
- package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
- package/dist/chunk-G4UYGRZR.js +2 -0
- package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
- package/dist/chunk-JMJVGMXI.js +92 -0
- package/dist/chunk-JMJVGMXI.js.map +1 -0
- package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
- package/dist/chunk-JYGNMKZ5.js.map +1 -0
- package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
- package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
- package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
- package/dist/chunk-KRWRUU6R.js.map +1 -0
- package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
- package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
- package/dist/chunk-PRBRSIZP.js.map +1 -0
- package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
- package/dist/chunk-UETBLPKM.js.map +1 -0
- package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
- package/dist/chunk-VGYN2KU2.js.map +1 -0
- package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
- package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
- package/dist/credential-E6KUHRGU.js.map +1 -0
- package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
- package/dist/credential-template-4GPOPBGD.js.map +1 -0
- package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
- package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
- package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
- package/dist/docs-COCZYMW6.js.map +1 -0
- package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
- package/dist/doctor-ERIVTCQS.js.map +1 -0
- package/dist/environment-7Y4BV6XY.js +221 -0
- package/dist/environment-7Y4BV6XY.js.map +1 -0
- package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
- package/dist/feature-flag-MHONEX6T.js.map +1 -0
- package/dist/index.js +123 -58
- package/dist/index.js.map +1 -1
- package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
- package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
- package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
- package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
- package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
- package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
- package/dist/knobs-UQGGY3CP.js.map +1 -0
- package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
- package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
- package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
- package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
- package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
- package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
- package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
- package/dist/mcp-4LS7OJUT.js.map +1 -0
- package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
- package/dist/product-3JKSJCBC.js +346 -0
- package/dist/product-3JKSJCBC.js.map +1 -0
- package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
- package/dist/property-K5DBL4UE.js.map +1 -0
- package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
- package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
- package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
- package/dist/record-EIIYLFJ4.js.map +1 -0
- package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
- package/dist/redis-BU36EZZI.js.map +1 -0
- package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
- package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
- package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
- package/dist/sandbox-CKHXXZHU.js.map +1 -0
- package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
- package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
- package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
- package/dist/session-UTOD5ZYH.js.map +1 -0
- package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
- package/dist/skill-GMAERMQL.js.map +1 -0
- package/dist/skills/debug-run/SKILL.md +40 -23
- package/dist/skills/debug-run/history/1.2.0.md +262 -0
- package/dist/skills/debug-run/history/1.3.0.md +273 -0
- package/dist/skills/debug-run/history/1.3.1.md +279 -0
- package/dist/skills/debug-run/version.json +1 -1
- package/dist/skills/optimize-cache/SKILL.md +233 -0
- package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
- package/dist/spec-2NP36U3Y.js.map +1 -0
- package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
- package/dist/src-4T6GHXVO.js.map +1 -0
- package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
- package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
- package/dist/src-UEOOC425.js +5941 -0
- package/dist/src-UEOOC425.js.map +1 -0
- package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
- package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
- package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
- package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
- package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
- package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
- package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
- package/dist/trace-UMLOSIIR.js +16 -0
- package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
- package/dist/triage-ZWJG4NBJ.js.map +1 -0
- package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
- package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
- package/dist/workflow-T4HSAOGW.js.map +1 -0
- package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
- package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
- package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
- package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
- package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
- package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
- package/package.json +2 -1
- package/dist/chunk-4PNOV7OV.js.map +0 -1
- package/dist/chunk-A4OFTUVV.js.map +0 -1
- package/dist/chunk-B2VISN2V.js.map +0 -1
- package/dist/chunk-GKY2KXO5.js.map +0 -1
- package/dist/chunk-JICSYFRJ.js +0 -46
- package/dist/chunk-JICSYFRJ.js.map +0 -1
- package/dist/chunk-QFDOWABX.js.map +0 -1
- package/dist/chunk-SJJW77WN.js.map +0 -1
- package/dist/chunk-WOHL4FTB.js.map +0 -1
- package/dist/chunk-ZD3KHESI.js.map +0 -1
- package/dist/credential-COLTBV6D.js.map +0 -1
- package/dist/credential-template-EEPYFG7R.js.map +0 -1
- package/dist/docs-3SSORHB5.js.map +0 -1
- package/dist/doctor-OTSIWV6G.js.map +0 -1
- package/dist/feature-flag-T2LRZME3.js.map +0 -1
- package/dist/knobs-NBINZIUB.js.map +0 -1
- package/dist/mcp-54IJEOUA.js.map +0 -1
- package/dist/property-UXLOLPTD.js.map +0 -1
- package/dist/record-4TR2UIIS.js.map +0 -1
- package/dist/redis-35VBUYW7.js.map +0 -1
- package/dist/sandbox-XGSUU6G7.js.map +0 -1
- package/dist/session-MYFNZPRP.js.map +0 -1
- package/dist/skill-KM34LDMO.js.map +0 -1
- package/dist/spec-RA3CFEKG.js.map +0 -1
- package/dist/src-LB4HBTJQ.js.map +0 -1
- package/dist/trace-HXIW7U2D.js +0 -14
- package/dist/workflow-XYFH5LTP.js.map +0 -1
- package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
- package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
- /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
- /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
- /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
- /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
- /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
- /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
- /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
- /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
- /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
- /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
- /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
- /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
- /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
- /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-S7CGEK6H.js.map} +0 -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, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { brandSkillContentFor, command } from \"./brand.js\";\nimport { SKILL_TEMPLATES } from \"./skill-templates/index.js\";\n\n/**\n * Brand-neutral frontmatter key that records which template a SKILL.md was\n * rendered from. Stamped only when a skill is installed under a custom name\n * (`--name`), so `sync`/`diff` can map a renamed install back to its template\n * for version-compare and update. Deliberately not `canary-`/`CANARY_`-shaped\n * so neither brand's skill-content rewrite touches it.\n */\nexport const SKILL_SOURCE_KEY = \"skill-source\";\n\n/**\n * Render a skill template for the active brand. Install/output go through here\n * so a `cofactor` binary writes cofactor-branded skill files while `canary`\n * stays byte-for-byte identical. Authored-content tooling (`diff`, `versions`)\n * stays raw so it compares the source, not the per-brand presentation.\n */\nfunction renderForActiveBrand(template: { render: () => string }): string {\n return brandSkillContentFor(template.render(), command);\n}\n\n/**\n * Split a SKILL.md into its leading YAML frontmatter block and the body. The\n * frontmatter is the block between the first `---` line and the next `---`\n * line. Returns `null` when the content has no recognizable frontmatter, so\n * callers can skip identity rewriting rather than corrupt the file.\n */\nfunction splitFrontmatter(\n content: string\n): { frontmatter: string; body: string } | null {\n const lines = content.split(\"\\n\");\n if (lines[0] !== \"---\") return null;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === \"---\") {\n return {\n frontmatter: lines.slice(1, i).join(\"\\n\"),\n // Keep the closing `---` and everything after it in the body so a\n // round-trip with `applyInstallIdentity` reproduces the file exactly.\n body: lines.slice(i).join(\"\\n\"),\n };\n }\n }\n return null;\n}\n\n/**\n * Read a single `key: value` field out of a frontmatter block. Returns `null`\n * when the key is absent. Used to recover an installed skill's `name:` /\n * `skill-source:` for the collision guard.\n */\nfunction readFrontmatterField(frontmatter: string, key: string): string | null {\n for (const line of frontmatter.split(\"\\n\")) {\n const match = line.match(/^([A-Za-z0-9_-]+):\\s*(.*)$/);\n if (match && match[1] === key) return match[2].trim();\n }\n return null;\n}\n\n/**\n * Derive the template key an already-installed SKILL.md was rendered from:\n * the `skill-source:` stamp when present (custom-named install), otherwise the\n * `name:` field (a default install, where name === template key). `null` when\n * neither is present.\n */\nexport function installedSourceKey(content: string): string | null {\n const parts = splitFrontmatter(content);\n if (!parts) return null;\n return (\n readFrontmatterField(parts.frontmatter, SKILL_SOURCE_KEY) ??\n readFrontmatterField(parts.frontmatter, \"name\")\n );\n}\n\n/**\n * Rewrite a rendered SKILL.md so it installs cleanly under `installName`:\n * the frontmatter `name:` is set to `installName` (agents require `name:` to\n * match the install directory), and — only when the name differs from the\n * template key — a `skill-source: <templateKey>` line is stamped so\n * `sync`/`diff` can still track the install. When `installName === templateKey`\n * the content is returned unchanged, so default installs stay byte-for-byte\n * identical to today's output. Operates only on the leading frontmatter block,\n * never the body. Pure.\n */\nexport function applyInstallIdentity(\n content: string,\n installName: string,\n templateKey: string\n): string {\n if (installName === templateKey) return content;\n\n const parts = splitFrontmatter(content);\n if (!parts) return content;\n\n const out: string[] = [];\n let wroteName = false;\n let wroteSource = false;\n for (const line of parts.frontmatter.split(\"\\n\")) {\n const match = line.match(/^([A-Za-z0-9_-]+):\\s*(.*)$/);\n const key = match?.[1];\n if (key === \"name\") {\n out.push(`name: ${installName}`);\n out.push(`${SKILL_SOURCE_KEY}: ${templateKey}`);\n wroteName = true;\n wroteSource = true;\n } else if (key === SKILL_SOURCE_KEY) {\n // Re-stamp from the template key rather than trusting a stale value.\n if (!wroteSource) {\n out.push(`${SKILL_SOURCE_KEY}: ${templateKey}`);\n wroteSource = true;\n }\n } else {\n out.push(line);\n }\n }\n // Defensive: frontmatter missing `name:` / `skill-source:` still gets them.\n if (!wroteName) out.unshift(`name: ${installName}`);\n if (!wroteSource) {\n const nameIdx = out.findIndex((l) => l.startsWith(\"name:\"));\n out.splice(nameIdx >= 0 ? nameIdx + 1 : 0, 0, `${SKILL_SOURCE_KEY}: ${templateKey}`);\n }\n\n // Reassemble: leading `---`, rewritten frontmatter, then the original body\n // (which already starts with its own closing `---` fence).\n return `---\\n${out.join(\"\\n\")}\\n${parts.body}`;\n}\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 install <name> --name <as> Install under a custom skill name\",\n \" canary skill install <name> --force Overwrite a name held by another skill\",\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 \"Custom skill names (--name):\",\n \" By default a skill installs under its template name. Pass --name to install\",\n \" it under a name of your choosing — useful when you want every Canary-provided\",\n \" skill namespaced (e.g. canary-browser-testing) to avoid colliding with your\",\n \" own skills. --name sets both the install directory and the SKILL.md\",\n \" frontmatter name: (agents require those to match), and records the source\",\n \" template in a skill-source: field so `sync` and `diff` keep tracking updates\",\n \" even under the renamed install.\",\n \"\",\n \" canary skill install browser-testing --name canary-browser-testing\",\n \"\",\n \" Installing a different skill onto an existing custom name is refused unless\",\n \" you pass --force. When an agent installs skills on your behalf, it should\",\n \" follow your naming convention.\",\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 — Discover installed skills (including renamed ones)\n\nSkills can be installed under a **custom name** (\\`canary skill install <name> --name <as>\\`),\nso do not rely only on the canonical \\`installedPaths\\` below. List every \\`SKILL.md\\`\nunder your agent's skills directory — both project and personal:\n\n .claude/skills/*/SKILL.md and ~/.claude/skills/*/SKILL.md (claude)\n .codex/skills/*/SKILL.md and ~/.codex/skills/*/SKILL.md (codex)\n .github/skills/*/SKILL.md and ~/.copilot/skills/*/SKILL.md (copilot)\n\nFor each file, read its frontmatter and resolve which manifest skill it came from:\n- If a \\`skill-source: <key>\\` line is present, that \\`<key>\\` is the template name\n (the skill was installed under a custom \\`--name\\`). The install directory is the\n custom name; remember it for Step 5.\n- Otherwise the \\`name:\\` value is the template name (a default install, where the\n install name equals the template key).\n\nMatch that resolved key against the manifest \\`name\\` field. Ignore any \\`SKILL.md\\`\nwhose resolved key is not in the manifest (it isn't a Canary skill).\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. Diff by the **template\nkey** you resolved in Step 1 (not the custom install name) — the diff compares\nsource renders and is independent of how the skill was named on disk:\n\n canary skill diff <templateKey> --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 with the template key, preserving both the location AND the custom name\nit was installed under (the install directory name from Step 1):\n\n canary skill install <templateKey> # default-named project install\n canary skill install <templateKey> --personal # default-named personal install\n canary skill install <templateKey> --name <installDir> # custom-named project install\n canary skill install <templateKey> --name <installDir> --personal\n\nPreserve whichever location and name it was already installed in — if it lived in\n\\`canary-browser-testing/\\`, re-install with \\`--name canary-browser-testing\\` so the\nupdate lands in place rather than creating a second default-named copy. Do NOT install\nskills that weren't already installed — sync only updates existing installs. When the\nuser has a naming convention, keep installing under their chosen names on their behalf.\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\n/**\n * A custom install name becomes a directory segment and the frontmatter\n * `name:`, so it must be a single safe path component: word characters and\n * hyphens only, no separators, no `..`, no leading dot.\n */\nexport function isValidInstallName(value: string): boolean {\n return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value);\n}\n\nfunction parseInstallArgs(argv: string[]): {\n /** The registry/template key (positional argument). */\n templateName: string | undefined;\n /** Directory + frontmatter name to install under (defaults to templateName). */\n installName: string | undefined;\n agent: AgentType | undefined;\n personal: boolean;\n force: boolean;\n} {\n let templateName: string | undefined;\n let installName: string | undefined;\n let agent: AgentType | undefined;\n let personal = false;\n let force = 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 === \"--name\" && i + 1 < argv.length) {\n const val = argv[++i];\n if (!isValidInstallName(val)) {\n console.error(\n `Invalid skill name \"${val}\". Use letters, digits, hyphens, or underscores (e.g. canary-browser-testing).`\n );\n process.exit(1);\n }\n installName = val;\n } else if (arg === \"--personal\") {\n personal = true;\n } else if (arg === \"--force\" || arg === \"-f\") {\n force = true;\n } else if (!arg.startsWith(\"-\") && !templateName) {\n templateName = arg;\n }\n }\n\n return { templateName, installName: installName ?? templateName, agent, personal, force };\n}\n\nasync function runInstall(argv: string[]) {\n const {\n templateName,\n installName,\n agent: explicitAgent,\n personal,\n force,\n } = parseInstallArgs(argv);\n\n if (!templateName || !installName) {\n console.error(\"Missing skill name.\\n\");\n console.error(\n `Usage: canary skill install <name> [--name <install-name>] [--agent ${SUPPORTED_AGENTS.join(\"|\")}] [--personal] [--force]`\n );\n process.exit(1);\n }\n\n const template = SKILL_TEMPLATES[templateName];\n if (!template) {\n console.error(`Unknown skill template \"${templateName}\".\\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(installName, agent, personal);\n const filePath = join(dir, \"SKILL.md\");\n\n // Collision guard: refuse to clobber a SKILL.md installed under this name from\n // a DIFFERENT template (e.g. collapsing multiple skills onto one name) unless\n // --force is passed. Updating the same skill in place is always allowed.\n if (existsSync(filePath) && !force) {\n const existingSource = installedSourceKey(readFileSync(filePath, \"utf-8\"));\n if (existingSource && existingSource !== templateName) {\n console.error(\n `✗ ${installName} already holds '${existingSource}'. Pass --force to overwrite.`\n );\n process.exit(1);\n }\n }\n\n // Write the file (brand-aware: cofactor binary writes cofactor-branded text),\n // rewriting the frontmatter identity when installing under a custom name.\n const content = applyInstallIdentity(\n renderForActiveBrand(template),\n installName,\n templateName\n );\n mkdirSync(dir, { recursive: true });\n writeFileSync(filePath, content, \"utf-8\");\n\n const relPath = personal ? filePath : filePath.replace(process.cwd() + \"/\", \"\");\n const named = installName === templateName ? \"\" : ` as ${installName}`;\n console.log(`Installed ${templateName} skill for ${agent}${named} → ${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(renderForActiveBrand(template));\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\": \"fatal\" | \"fail\" | \"warn\" }\\` (fatal = halt the test now, fail = mark run failed but continue, warn = record only). **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 `.cofactor.md` specs that compile straight into Cofactor 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.3.0',\n render: () => `---\nname: test-cases\nversion: 2.3.0\ndescription: Author high-quality test cases as \\`.cofactor.md\\` specs that compile straight into Cofactor 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 (Cofactor)\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 **\\`.cofactor.md\\` spec files** — each file is one workflow, one journey, one test case. \\`cofactor spec compile <file>\\` turns them into runnable Cofactor workflows; \\`cofactor spec validate <file>\\` enforces the structural rules below. (Legacy \\`.canary.md\\` files still validate and compile; new specs default to \\`.cofactor.md\\`.)\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 \\`.cofactor.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 \\`.cofactor.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 run-failing (\\`fail\\`/\\`fatal\\`) 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 \\`.cofactor.md\\`\n\n\\`.cofactor.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\ncofactor spec --help # validate / compile / diff commands\ncofactor spec validate <file> # parse + lint offline; enforce the rules in §1\ncofactor spec compile <file> # create or update the workflow (prints the live URL)\ncofactor workflow guide # node-shape and macro reference\n\\`\\`\\`\n\nAfter \\`cofactor 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 \\`.cofactor.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 **persona** name by default (e.g. \\`actor: Admin\\` or the explicit \\`actor: persona Admin\\`), or a specific raw credential via \\`actor: credential <name>\\` when the case genuinely needs one named account. Personas resolve at run/suite level; raw credentials bind a single fixed account. Legacy \\`actor: template Admin\\` remains accepted. List personas with \\`cofactor persona list --application \"<application>\"\\`; list credentials with \\`cofactor 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 (fatal \\\\| fail \\\\| warn): <condition>\\` under the action that produced it. \\`fatal\\` ends the test now (gating preconditions), \\`fail\\` marks the run failed but keeps going (feature claims), \\`warn\\` records without failing (cosmetics) |\n| Standalone assertion | \\`**Assert** <condition>\\` node (prefer inline when possible) |\n| Role change mid-journey | \\`**Switch role to** <X>\\` — mirrors \\`actor:\\`. \\`<X>\\` is a persona name by default (\\`**Switch role to** Admin\\`), \\`persona <name>\\` is the explicit persona form (\\`**Switch role to** persona Admin\\`), \\`credential <name>\\` binds a specific raw credential (\\`**Switch role to** credential admin-bob\\`). Legacy \\`template <name>\\` remains accepted. 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** \\`.cofactor.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 - \\`cofactor property list\\` — properties (target apps)\n - \\`cofactor persona list --application \"<application>\"\\` — personas (these are what \\`actor:\\` and \\`- credential …\\` reference)\n - \\`cofactor workflow list --search <term>\\` — existing workflows to seed/link to\n - \\`cofactor 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 personas** 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 \\`.cofactor.md\\` file alongside a one-row summary line. Default location: \\`cofactor/specs/<slug>.cofactor.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| cofactor/specs/submit-ticket.cofactor.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: <persona 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 (fatal): <gating precondition — test must stop if wrong>\n- assert (fail): <observable condition — feature is broken if wrong>\n- assert (warn): <softer signal — cosmetic, run still passes>\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| cofactor/specs/submit-ticket.cofactor.md | Submit a ticket and confirm it appears in the queue | p0 | <fill in> |\n| cofactor/specs/ticket-required-fields.cofactor.md | Required-field validation blocks submit | p1 | <fill in> |\n| cofactor/specs/ticket-non-admin-reassign.cofactor.md | Non-admin cannot reassign a ticket | p1 | <fill in> |\n\n\\`cofactor/specs/submit-ticket.cofactor.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 \\`cofactor spec validate cofactor/specs/submit-ticket.cofactor.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 \\`cofactor property list\\` and \\`cofactor persona list --application \"<application>\"\\` to learn applications + the personas that map to \\`actor:\\` values. -->\n<!-- - (\\`cofactor credential list\\` shows live credentials — useful, but the \\`actor:\\` field references personas, not credentials.) -->\n<!-- - Run \\`cofactor workflow list --search <feature>\\` to avoid duplicating existing workflows. -->\n<!-- - Run \\`cofactor 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 \\`cofactor property list\\`>\n- **Default environment**: <unfilled — \\`prod\\` | \\`staging\\` | \\`dev\\`>\n- **Credentials / role matrix** (\\`actor:\\` is a persona name by default — list via \\`cofactor persona list --application \"<application>\"\\`. Prefix with \\`credential \\` to bind a specific raw credential — list via \\`cofactor credential list\\`. Use the persona form unless the case genuinely needs a fixed named account.): <unfilled — list the persona names available on the default application, 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 \\`.cofactor.md\\` files live in this repo, e.g. \\`cofactor/specs/\\`>\n- **Existing workflows to extend or avoid duplicating**: <unfilled — search via \\`cofactor 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,gBAAAA,eAAc,qBAAqB;AACnE,SAAS,eAAe;AACxB,SAAS,QAAAC,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;AA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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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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","names":["readFileSync","join","join","i","j","readFileSync"]}
|
|
@@ -38,14 +38,14 @@ The QA tester reads your `title` first, then `recommendation`. Make those two la
|
|
|
38
38
|
- `agent_confusion` — the AI test agent reasoned incorrectly, picked the wrong element, or got confused
|
|
39
39
|
- `platform_error` — defect in our test runner, harness, sandbox, or recording infrastructure
|
|
40
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.
|
|
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. **An `app_changed` verdict must be earned by the green-run comparison** (see compare-to-history): confirm the delta in both runs' evidence, never infer it from the pass→fail boundary alone. If the green baseline passed via cache replay while the failing run improvised a different live verification, that is verification drift (`agent_confusion`), not an app change.
|
|
42
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
43
|
|
|
44
44
|
Never mark `agent_confusion` or `platform_error` from a failed assertion alone. Look at multiple evidence sources first.
|
|
45
45
|
|
|
46
46
|
## Output discipline (hard rules)
|
|
47
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".
|
|
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`, `history/runs/`, `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
49
|
- **No section headings** inside `failurePoints` or `recommendation`. They are flat bullet lists.
|
|
50
50
|
- **No narrative paragraphs.** If you find yourself writing "Then…", "After that…", "Subsequently…" — collapse to a bullet.
|
|
51
51
|
|
|
@@ -81,7 +81,8 @@ Your diagnostic workspace is at `{{workspacePath}}`. Everything you need is here
|
|
|
81
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
82
|
- `variables/inputs.json`, `variables/lineage.jsonl` — run inputs + per-step variable captures.
|
|
83
83
|
- `history/recent-runs.jsonl` — last ~20 runs with status. Useful for picking a green run to compare.
|
|
84
|
-
- `history/
|
|
84
|
+
- `history/runs/<runId>/` — comparison workspaces created by the `materialize_run` tool (in-process sandbox).
|
|
85
|
+
- `history/last-green/` — populated only if you materialize a prior passing run for comparison via the CLI command.
|
|
85
86
|
- `notes/` — your working memory. Write here with `write_file` (only the workspace, no path escapes).
|
|
86
87
|
|
|
87
88
|
|
|
@@ -125,6 +126,9 @@ The visible failure is rarely the actual divergence. Common patterns:
|
|
|
125
126
|
- A cross-host navigation between two earlier steps invalidated the auth session, and the 401 doesn't appear until the next API call.
|
|
126
127
|
- A variable was captured from the wrong DOM element on step N, and step N+5 now has a meaningless `$var.X` value.
|
|
127
128
|
- The agent went down a wrong branch on a conditional, and the test ran a different path than intended.
|
|
129
|
+
- A prior step showed a validation banner or issue toast, but the workflow continued; the later step fails because the page never becomes ready.
|
|
130
|
+
- A non-blocking assertion or warning already proved a prerequisite was missing; a later action reports the same missing state as a blocking failure.
|
|
131
|
+
- A warning says an expected run, save, import, sync, or publish did not complete; the downstream failure is just the consequence of that missing prerequisite.
|
|
128
132
|
|
|
129
133
|
Walk-backwards algorithm:
|
|
130
134
|
|
|
@@ -133,10 +137,13 @@ Walk-backwards algorithm:
|
|
|
133
137
|
3. Compare to the instruction by `read_file` on `workflow/nodes/<nodeId>.json` — does the screenshot match the intent of `actionDescription` and `inlineAssertions`?
|
|
134
138
|
4. Skim the rollup's "Agent thoughts" section. Was the agent confused? Did it pick the wrong element?
|
|
135
139
|
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.
|
|
140
|
+
6. Check whether the step has warnings, non-blocking failures, cache fallback, validation banners, issue toasts, or visible error badges. Treat these as root-cause candidates even if the step status is `success`.
|
|
141
|
+
7. If everything looks normal here, repeat with `steps/<NNN-2>-...md`, `<NNN-3>`, and so on.
|
|
137
142
|
|
|
138
143
|
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
144
|
|
|
145
|
+
If the reported failure says a later state was not established, your final answer must name the earlier step that should have established it and the concrete evidence showing why it did not.
|
|
146
|
+
|
|
140
147
|
|
|
141
148
|
## Visual evidence — when to read frames
|
|
142
149
|
|
|
@@ -156,35 +163,45 @@ How to use vision:
|
|
|
156
163
|
Do not use `bash` to view images — `read_file` is the only tool that returns vision content.
|
|
157
164
|
|
|
158
165
|
|
|
159
|
-
## Comparing to a passing run
|
|
166
|
+
## Comparing to a passing run — the green→red methodology
|
|
160
167
|
|
|
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.
|
|
168
|
+
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. A green→red boundary with an unchanged test eliminates only test-authoring causes. FOUR hypotheses stay live until the comparison discriminates between them: a legitimate app change (`app_changed`), an app regression (`software_bug`), an agent error (`agent_confusion`), and a platform error (`platform_error`). Never default to one of them — earn the verdict with evidence.
|
|
162
169
|
|
|
163
170
|
Procedure:
|
|
164
171
|
|
|
165
|
-
1.
|
|
166
|
-
`
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
1. **Materialize the green baseline.** `summary.md`'s "History" section names the green run and the exact mechanism for this workspace:
|
|
173
|
+
- In the in-process sandbox: call the `materialize_run` tool with the green run's id. The result names the comparison workspace path (`history/runs/<greenRunId>/`).
|
|
174
|
+
- In a CLI-materialized workspace: run the printed `canary workflow debug materialize <greenRunId> --out history/last-green/` command via `bash`.
|
|
175
|
+
Follow whichever instruction `summary.md` prints. `<green>` below means the comparison workspace path.
|
|
176
|
+
2. **Establish execution mode on BOTH sides of the boundary — before any visual comparison.** Read each workspace's `timeline/cache-decisions.jsonl` and `thoughts/all.jsonl`. A cache-replayed run has few or no agent thoughts (it replayed recorded actions); a live-agent run has many. The mode pairing changes which hypotheses are plausible:
|
|
177
|
+
- **Green replayed, red fell to live agent** → the red run executed a *different procedure* than every green before it. Compare the recorded recipe's verification with what the live agent improvised — a verification-method mismatch points to `agent_confusion` (drift), and the cache miss/uncertainty itself may point to `platform_error`. Do NOT classify `app_changed` from a replay-vs-live pair without an independent, concrete app-behavior delta visible in both runs' frames.
|
|
178
|
+
- **Both live-agent** → thoughts diff cleanly; interpretation drift vs. a genuine page delta is directly observable.
|
|
179
|
+
- **Green replayed, red replay-failed** → suspect `platform_error` (replay machinery) or a genuine app change that broke the recorded selectors; the frames decide.
|
|
180
|
+
3. **Visual comparison.** `read_file` the suspect step's screenshot in both runs:
|
|
169
181
|
- `browser/frames/<NNNN>-...png` — failing
|
|
170
|
-
-
|
|
171
|
-
The visual delta is often the smoking gun.
|
|
172
|
-
4. Diff thoughts and node configs
|
|
173
|
-
- `bash diff thoughts/by-step/<stepId>.md
|
|
174
|
-
- `bash diff workflow/snapshot.json
|
|
182
|
+
- `<green>/browser/frames/<NNNN>-...png` — passing
|
|
183
|
+
The visual delta is often the smoking gun. Same screens → the app did not change; weigh the agent/platform hypotheses. (If the green workspace has no frames — no trace — fall back to thoughts and node-config diffs.)
|
|
184
|
+
4. **Diff thoughts and node configs:**
|
|
185
|
+
- `bash diff thoughts/by-step/<stepId>.md <green>/thoughts/by-step/<stepId>.md`
|
|
186
|
+
- `bash diff workflow/snapshot.json <green>/workflow/snapshot.json | head -100`
|
|
175
187
|
|
|
176
188
|
Common signals from comparison:
|
|
177
189
|
|
|
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`.
|
|
190
|
+
- Identical authored workflow + visual/label delta **confirmed in both runs' frames** = **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
191
|
- Different workflow snapshot = **test changed** (possibly `test_underspecified` if the new instructions are ambiguous).
|
|
180
|
-
- Same screenshot but different thoughts = **agent interpretation drift** (
|
|
192
|
+
- Same screenshot but different thoughts = **agent interpretation drift** (`agent_confusion`, or `test_underspecified` if the test allowed the prior interpretation).
|
|
181
193
|
- Same everything but new network 5xx = **dependency/backend issue** (likely `software_bug` or `dependency_issue`).
|
|
194
|
+
- Green passed by cache replay while red improvised live = **verification drift**, not an app change — see step 2.
|
|
195
|
+
|
|
196
|
+
## When comparison is unavailable
|
|
197
|
+
|
|
198
|
+
If the comparison workspace cannot be produced (the materialize tool/command fails), you have NOT performed the required comparison. Do not pick `app_changed` at high confidence on the regime steer alone — keep the live hypotheses in your writeup, record an explicit openQuestion that the green baseline could not be materialized for comparison, and cap your confidence accordingly.
|
|
182
199
|
|
|
183
200
|
## The regime gates your verdict
|
|
184
201
|
|
|
185
202
|
`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
203
|
|
|
187
|
-
- **`STABLE_REGRESSION` + AUTHORED TEST UNCHANGED** → the test did not change across the pass→fail boundary, so `test_underspecified` is **off the table
|
|
204
|
+
- **`STABLE_REGRESSION` + AUTHORED TEST UNCHANGED** → the test did not change across the pass→fail boundary, so `test_underspecified` is **off the table** — but that alone does NOT pick a verdict. Run the methodology above to discriminate between `app_changed`, `software_bug`, `agent_confusion`, and `platform_error`. Only classify `app_changed` after confirming a concrete behavior delta against the green workspace; name the delta and record in `openQuestions` that you could not verify whether the change was intentional.
|
|
188
205
|
- **`STABLE_REGRESSION` + AUTHORED TEST CHANGED** → the test was edited; judge whether the new instructions are ambiguous (`test_underspecified`) before blaming the app.
|
|
189
206
|
- **`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
207
|
- **`NEW` / `PERSISTENT_FAIL`** → no stable green baseline; rely on the within-run procedure (verify + walk backwards).
|
|
@@ -224,19 +241,19 @@ When suggesting test instruction changes:
|
|
|
224
241
|
- 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
242
|
|
|
226
243
|
|
|
227
|
-
##
|
|
244
|
+
## Standing rule: assertion severity and step/run status
|
|
228
245
|
|
|
229
|
-
|
|
246
|
+
Assertion severity drives what a failure means — `fatal` halts the run at the failing step, `fail` marks the step (and ultimately the run) failed but execution continues, `warn` records a warning and the run still passes. Because `fail` and `warn` are non-blocking, later steps run normally after an assertion failure. Assertion records carry a `nonBlocking` flag reflecting how each was treated at run time.
|
|
230
247
|
|
|
231
248
|
When investigating:
|
|
232
249
|
|
|
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.**
|
|
250
|
+
- 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. Entries marked not-checked (`checked: false`) were configured on the node but never evaluated — the step broke before assertions ran; do not read them as failures.
|
|
251
|
+
- **Do not treat "failed assertion + completed step" on its own as evidence of `platform_error` or contradictory state.** For `warn`/`fail` severities (`nonBlocking` true), that combination is expected and carries no signal about the actual cause.
|
|
235
252
|
- 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
253
|
|
|
237
254
|
When `platform_error` is still a live possibility:
|
|
238
255
|
|
|
239
|
-
- A failed assertion
|
|
256
|
+
- A failed `fatal`-severity assertion (`nonBlocking: false`) alongside a step recorded as completed successfully. That is a genuine disagreement worth flagging.
|
|
240
257
|
- Infrastructure-level failures (browser session died, worker crash, our own stack traces) independent of any assertion outcome.
|
|
241
258
|
|
|
242
259
|
Always consult the assertions section of the rollup before writing a bookkeeping-bug narrative.
|