canary-test-cli 7.0.0 → 7.2.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.
Files changed (122) hide show
  1. package/agents/skills/README.md +308 -0
  2. package/agents/skills/canary:generate.md +49 -0
  3. package/agents/skills/canary:init.md +37 -0
  4. package/agents/skills/canary:migrate.md +66 -0
  5. package/agents/skills/claude-code/canary-add-framework/SKILL.md +248 -0
  6. package/agents/skills/claude-code/canary-blackhawk/SKILL.md +170 -0
  7. package/agents/skills/claude-code/canary-blackhawk/scripts/cli.mjs +188 -0
  8. package/agents/skills/claude-code/canary-blackhawk/scripts/rules.mjs +120 -0
  9. package/agents/skills/claude-code/canary-blackhawk/scripts/scanner.mjs +244 -0
  10. package/agents/skills/claude-code/canary-blackhawk/scripts/string-literals.mjs +116 -0
  11. package/agents/skills/claude-code/canary-cassandra/SKILL.md +180 -0
  12. package/agents/skills/claude-code/canary-cassandra/scripts/cli.mjs +268 -0
  13. package/agents/skills/claude-code/canary-cassandra/scripts/engine.mjs +95 -0
  14. package/agents/skills/claude-code/canary-ci-ready/SKILL.md +161 -0
  15. package/agents/skills/claude-code/canary-ci-ready/skill.yaml +14 -0
  16. package/agents/skills/claude-code/canary-company-knowledge/SKILL.md +196 -0
  17. package/agents/skills/claude-code/canary-critical-areas/SKILL.md +142 -0
  18. package/agents/skills/claude-code/canary-critical-areas/skill.yaml +16 -0
  19. package/agents/skills/claude-code/canary-edge-case-discovery/SKILL.md +160 -0
  20. package/agents/skills/claude-code/canary-edge-case-discovery/skill.yaml +16 -0
  21. package/agents/skills/claude-code/canary-fail-fast/SKILL.md +75 -0
  22. package/agents/skills/claude-code/canary-fail-fast/scripts/cli.mjs +118 -0
  23. package/agents/skills/claude-code/canary-fail-fast/scripts/digest.mjs +69 -0
  24. package/agents/skills/claude-code/canary-fail-fast/scripts/failures.mjs +60 -0
  25. package/agents/skills/claude-code/canary-fail-fast/scripts/fastfail_check.mjs +43 -0
  26. package/agents/skills/claude-code/canary-fail-fast/scripts/parse.mjs +149 -0
  27. package/agents/skills/claude-code/canary-failure-impact/SKILL.md +153 -0
  28. package/agents/skills/claude-code/canary-failure-impact/skill.yaml +15 -0
  29. package/agents/skills/claude-code/canary-fleet-health/SKILL.md +196 -0
  30. package/agents/skills/claude-code/canary-generate-test/SKILL.md +185 -0
  31. package/agents/skills/claude-code/canary-instrument/SKILL.md +157 -0
  32. package/agents/skills/claude-code/canary-instrument/scripts/cli.mjs +178 -0
  33. package/agents/skills/claude-code/canary-instrument/scripts/otel_bootstrap/instrument.mjs +96 -0
  34. package/agents/skills/claude-code/canary-instrument/scripts/otel_bootstrap/playwright-fixture.ts +44 -0
  35. package/agents/skills/claude-code/canary-instrument/scripts/run_types.mjs +81 -0
  36. package/agents/skills/claude-code/canary-instrument/scripts/span_reader.mjs +187 -0
  37. package/agents/skills/claude-code/canary-katana/SKILL.md +243 -0
  38. package/agents/skills/claude-code/canary-katana/scripts/alarm.mjs +296 -0
  39. package/agents/skills/claude-code/canary-katana/scripts/cli.mjs +247 -0
  40. package/agents/skills/claude-code/canary-katana/scripts/diffscan.mjs +0 -0
  41. package/agents/skills/claude-code/canary-katana/scripts/ledger.mjs +183 -0
  42. package/agents/skills/claude-code/canary-pr-guardian/SKILL.md +129 -0
  43. package/agents/skills/claude-code/canary-pr-guardian/skill.yaml +17 -0
  44. package/agents/skills/claude-code/canary-promote-test/SKILL.md +228 -0
  45. package/agents/skills/claude-code/canary-savant/SKILL.md +233 -0
  46. package/agents/skills/claude-code/canary-savant/scripts/cli.mjs +274 -0
  47. package/agents/skills/claude-code/canary-savant/scripts/restoration.mjs +274 -0
  48. package/agents/skills/claude-code/canary-savant/scripts/rules.mjs +168 -0
  49. package/agents/skills/claude-code/canary-savant/scripts/runner.mjs +572 -0
  50. package/agents/skills/claude-code/canary-savant/scripts/scanner.mjs +374 -0
  51. package/agents/skills/claude-code/canary-savant/scripts/string-literals.mjs +116 -0
  52. package/agents/skills/claude-code/canary-setup-harness/SKILL.md +263 -0
  53. package/agents/skills/claude-code/canary-shadow/SKILL.md +131 -0
  54. package/agents/skills/claude-code/canary-shadow/scripts/cases.example.json +32 -0
  55. package/agents/skills/claude-code/canary-shadow/scripts/cli.mjs +195 -0
  56. package/agents/skills/claude-code/canary-ship/SKILL.md +177 -0
  57. package/agents/skills/claude-code/canary-ship/skill.yaml +16 -0
  58. package/agents/skills/claude-code/canary-strix/SKILL.md +130 -0
  59. package/agents/skills/claude-code/canary-strix/scripts/cli.mjs +255 -0
  60. package/agents/skills/claude-code/canary-strix/scripts/scanner.mjs +252 -0
  61. package/agents/skills/claude-code/canary-strix/scripts/terms.mjs +132 -0
  62. package/agents/skills/claude-code/canary-test-pipeline/SKILL.md +159 -0
  63. package/agents/skills/claude-code/canary-test-pipeline/skill.yaml +19 -0
  64. package/agents/skills/claude-code/canary-test-reporter/SKILL.md +138 -0
  65. package/agents/skills/claude-code/canary-test-reporter/scripts/cli.mjs +98 -0
  66. package/agents/skills/claude-code/canary-test-reporter/scripts/json_report.mjs +58 -0
  67. package/agents/skills/claude-code/canary-test-reporter/scripts/parse.mjs +216 -0
  68. package/agents/skills/claude-code/canary-test-reporter/scripts/render.mjs +114 -0
  69. package/agents/skills/lib/parse-args.mjs +214 -0
  70. package/dist/engine/analysis/cli.js +116 -54
  71. package/dist/engine/analysis/engine.js +34 -16
  72. package/dist/engine/analysis/reports.js +5 -4
  73. package/dist/engine/cli-commands.js +252 -106
  74. package/dist/engine/cli-common.js +15 -24
  75. package/dist/engine/cli.core.js +37 -11
  76. package/dist/engine/cli.js +2 -2
  77. package/dist/engine/company-knowledge-cli.js +2 -2
  78. package/dist/engine/core/adoption.js +408 -0
  79. package/dist/engine/core/framework-probes.js +7 -7
  80. package/dist/engine/core/fs-glob.js +2 -2
  81. package/dist/engine/core/gate-result.js +17 -0
  82. package/dist/engine/core/migrator.js +9 -17
  83. package/dist/engine/core/pattern-matcher.js +23 -5
  84. package/dist/engine/core/persona.js +421 -0
  85. package/dist/engine/core/promotion-verdict.js +261 -0
  86. package/dist/engine/core/quality-scorer.js +15 -2
  87. package/dist/engine/core/reporter.js +1 -9
  88. package/dist/engine/core/skill-dispatch.js +115 -0
  89. package/dist/engine/core/skill-examples.js +392 -0
  90. package/dist/engine/core/skill-registry.js +59 -4
  91. package/dist/engine/core/skill-surfaces.js +307 -0
  92. package/dist/engine/core/static-linter.js +310 -38
  93. package/dist/engine/core/test-files.js +77 -0
  94. package/dist/engine/core/ticket-updater.js +1 -7
  95. package/dist/engine/core/vacuity-scanner.js +726 -0
  96. package/dist/engine/core/workflow-discovery.js +2 -8
  97. package/dist/engine/core/workspace-detect.js +7 -6
  98. package/dist/engine/data/personas/registry.json +36 -0
  99. package/dist/engine/guardian/adjudication.js +5 -5
  100. package/dist/engine/guardian/analysis-emit.js +19 -28
  101. package/dist/engine/guardian/cli.js +150 -51
  102. package/dist/engine/guardian/coverage.js +1 -1
  103. package/dist/engine/guardian/diff-coverage/heuristic-tier.js +1 -1
  104. package/dist/engine/guardian/diff-coverage/orchestrator.js +2 -2
  105. package/dist/engine/guardian/pr-check.js +322 -33
  106. package/dist/engine/guardian/pr-comment.js +4 -3
  107. package/dist/engine/history/cli.js +210 -6
  108. package/dist/engine/history/ndjson-store.js +9 -5
  109. package/dist/engine/history/record.js +34 -5
  110. package/dist/engine/history/run-recorder.js +165 -0
  111. package/dist/engine/history/schema.js +25 -7
  112. package/dist/engine/history/store.js +9 -0
  113. package/dist/engine/mcp-server.js +35 -13
  114. package/dist/engine/skills-cli.js +249 -23
  115. package/dist/engine/util/ensure-ascii.js +37 -0
  116. package/dist/engine/workflow-cli.js +6 -6
  117. package/dist/gate-result.d.ts +11 -0
  118. package/dist/gate-result.js +18 -0
  119. package/dist/reporters/testtracker.d.ts +1 -1
  120. package/dist/reporters/testtracker.js +1 -1
  121. package/dist/uninstall.js +12 -5
  122. package/package.json +3 -2
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: canary-company-knowledge
3
+ description: >
4
+ Scaffold .canary/company.json — the org-specific pointer file (Confluence
5
+ spaces, Jira projects, internal docs/domains, MCP servers, dashboard, and the
6
+ user-catalog skill) that canary-ci-ready and canary-failure-impact silently
7
+ assume already exists. Use when the user says "set up company knowledge",
8
+ "bootstrap company.json", "no company.json", "canary company-knowledge init",
9
+ or when canary-ci-ready/canary-failure-impact report no user-catalog config
10
+ for auth/config failures. Scaffolds the file and prompts for the fields that
11
+ genuinely cannot be inferred — it does not claim full automation.
12
+ ---
13
+
14
+ # Canary: Company Knowledge Init
15
+
16
+ Wraps `canary company-knowledge init` to scaffold `.canary/company.json` — the
17
+ _pointers-only_ file that lets Canary skills reach into your org's internal
18
+ tooling (Confluence, Jira, internal docs, MCP servers, a user catalog) without
19
+ ever committing proprietary content to this open-core repo.
20
+
21
+ This file is silently assumed to exist by
22
+ [`canary-ci-ready`](../canary-ci-ready/SKILL.md) (user-catalog investigation for
23
+ auth/config failures) and
24
+ [`canary-failure-impact`](../canary-failure-impact/SKILL.md) (same). If neither
25
+ has ever been run with a populated `company.json`, both degrade to a generic
26
+ "check your user catalog if you have one" message. This skill closes that gap.
27
+
28
+ ## When to Use
29
+
30
+ - First-time setup for a new project or team — "we need to configure company
31
+ knowledge for canary"
32
+ - `canary-ci-ready` or `canary-failure-impact` fell back to the generic
33
+ user-catalog message and the user wants real investigation instead
34
+ - `canary company-knowledge show` reports "No company knowledge configured"
35
+ - Re-running setup to add a field that was skipped the first time (safe — `init`
36
+ merges onto existing values by default)
37
+ - NOT for storing secrets, API keys, or tokens — this file holds pointers only;
38
+ the CLI's secret heuristic (`_looks_like_secret`) rejects
39
+ `sk-`/`token`/`bearer`-shaped values and anything over 128 chars outside
40
+ `notes`
41
+ - NOT for company-specific proprietary content (client names, internal runbooks,
42
+ populated data) — per this repo's open-core boundary (`AGENTS.md`), that lives
43
+ only in a private overlay under `.canary/skills/` or the org's own tooling,
44
+ reached _via_ the pointers this file stores
45
+
46
+ ## What This Skill Cannot Automate
47
+
48
+ Every field in `.canary/company.json` is org-specific and cannot be reliably
49
+ inferred from the codebase alone — this repo is public/open-core by design, so
50
+ nothing in it names a real company, Jira project, or internal host. Don't claim
51
+ otherwise. What this skill _can_ do:
52
+
53
+ - Scaffold the file and `.gitignore` entry so it exists and is never
54
+ accidentally committed
55
+ - Prompt for each field with the exact validation format the CLI expects, so the
56
+ user isn't guessing at schema
57
+ - Detect when a value looks like a secret and refuse it before it reaches disk
58
+ - Merge onto existing values, so re-running is always safe
59
+
60
+ What it cannot do: know your Confluence space key, your Jira project prefix, or
61
+ which MCP server your org runs. Those come from the user.
62
+
63
+ ## Process
64
+
65
+ ### Phase 1: CHECK — Does It Already Exist?
66
+
67
+ ```bash
68
+ canary company-knowledge show
69
+ ```
70
+
71
+ - **"No company knowledge configured"** → proceed to Phase 2 (fresh scaffold).
72
+ - **Populated output** → note which fields are already set (shown with
73
+ `sources:` — `~/.canary/company.json`, `.canary/company.json`, or an env
74
+ layer). Proceed to Phase 2 in merge mode (no `--force`) so existing values
75
+ survive as defaults.
76
+ - **`⚠` error line** (secret-like value or malformed JSON in an existing layer)
77
+ → surface the exact warning to the user before continuing; that layer is being
78
+ skipped entirely until fixed.
79
+
80
+ ### Phase 2: SCAFFOLD — Run the Interactive Init
81
+
82
+ ```bash
83
+ canary company-knowledge init
84
+ ```
85
+
86
+ Walk the user through each prompt. For each field, explain what it's for and
87
+ give the expected shape before they answer — the CLI accepts blank input to skip
88
+ or keep the current value:
89
+
90
+ | Field | Format | Purpose |
91
+ | -------------------- | ---------------------------------------------------------- | ---------------------------------------------- |
92
+ | `confluence_spaces` | comma-separated, uppercase (`QA, ENG`) | spaces the LLM should consult for org docs |
93
+ | `jira_projects` | comma-separated, uppercase (`PROJ, OPS`) | projects for ticket cross-referencing |
94
+ | `internal_doc_urls` | one URL per line, `http(s)://` only | specific reference docs to fetch via MCP |
95
+ | `internal_domains` | comma-separated hostnames (`corp.example.com`) | flags internal-only URLs in generated content |
96
+ | `mcp_servers` | comma-separated identifiers (`plugin_atlassian_atlassian`) | which configured MCP server(s) back the above |
97
+ | `claude_code_skills` | comma-separated slugs (`team:skill-name`) | project-overlay skills to surface as available |
98
+ | `notes` | free text, ≤2048 chars, no secrets | anything else the LLM should know |
99
+
100
+ Fields not prompted by `init` (`dashboard_url`, `dashboard_token_env`,
101
+ `otel_exporter_endpoint`) can be added by hand-editing the JSON afterward —
102
+ mention this if the user needs dashboard/OTel wiring; don't skip it silently.
103
+
104
+ If the user offers a value that looks like a credential (starts with `sk-`,
105
+ `token`, `bearer`, or is unusually long), stop and remind them: secrets go in
106
+ environment variables, never in `company.json`. The CLI will reject the whole
107
+ layer if one slips through — better to catch it before submitting.
108
+
109
+ ### Phase 3: WIRE THE USER-CATALOG SKILL (Known Schema Gap)
110
+
111
+ `canary-ci-ready` and `canary-failure-impact` both read a `user_catalog_skill`
112
+ key directly from `.canary/company.json` to investigate auth/config failures.
113
+ **This key is not part of the CLI's validated schema** — it isn't in
114
+ `CompanyKnowledge`'s known fields, `company-knowledge init` never prompts for
115
+ it, and `company-knowledge show` will list it under "ignored unknown field" if
116
+ you inspect warnings. It still works for the two consuming skills because they
117
+ read the raw JSON file directly rather than going through the Python loader —
118
+ but be transparent with the user that this is an informal extension, not a
119
+ first-class field, until the schema catches up.
120
+
121
+ To wire it:
122
+
123
+ 1. Ask the user which project-overlay skill (if any) looks up test users — e.g.
124
+ `team:user-lookup`. If they don't have one, skip this phase; the consuming
125
+ skills degrade gracefully to a generic prompt.
126
+ 2. If they gave a normal skill slug, prefer adding it to `claude_code_skills` (a
127
+ real, validated field) _and_ separately hand-edit `.canary/company.json` to
128
+ add the literal key:
129
+
130
+ ```json
131
+ {
132
+ "claude_code_skills": ["team:user-lookup"],
133
+ "user_catalog_skill": "team:user-lookup"
134
+ }
135
+ ```
136
+
137
+ 3. Confirm the value matches what `canary skills run <user_catalog_skill>`
138
+ expects as an identifier (same slug format as `claude_code_skills`:
139
+ `^[a-z0-9][a-z0-9_-]*(:[a-z0-9][a-z0-9_-]*)?$`).
140
+
141
+ ### Phase 4: VERIFY
142
+
143
+ ```bash
144
+ canary company-knowledge show
145
+ ```
146
+
147
+ - Confirm every field the user just set appears in the printed output.
148
+ - Confirm no `⚠` warnings (unknown fields other than the intentional
149
+ `user_catalog_skill` extension, dropped invalid entries, secret detections).
150
+ - Confirm `.gitignore` now contains a `.canary/` line — `init` adds it
151
+ automatically, but verify if the project already had a `.gitignore` with
152
+ unusual formatting.
153
+ - If JSON output is useful for the user's own tooling: `--json`.
154
+
155
+ ## Error Handling
156
+
157
+ | Situation | What Happens | What To Do |
158
+ | --------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
159
+ | `.canary/company.json` already exists, no `--force` | `init` shows existing values as defaults and merges | Normal — just re-run `init` |
160
+ | Secret-like value entered | That whole layer is dropped; `show` prints a red `✗` | Remove the value, use an env var, re-run |
161
+ | Malformed JSON in an existing file | Layer skipped with a parse error | Fix the JSON by hand, then re-run `show` to confirm |
162
+ | Invalid entry format (e.g. lowercase Jira key) | Entry silently dropped, warning logged | Re-enter in the correct case/format |
163
+ | Unknown field in the file | Warned, not fatal, field ignored by the loader | Expected for `user_catalog_skill` today (Phase 3) — otherwise likely a typo |
164
+ | User has no Confluence/Jira/MCP setup at all | Every field is legitimately empty | Skip `init` entirely; `canary-ci-ready`/`canary-failure-impact` degrade to their generic prompts, which is correct behavior, not a bug |
165
+
166
+ ## Examples
167
+
168
+ ### Example: First-time team lead setup
169
+
170
+ **Prompt:** "Set up company knowledge for canary on this repo."
171
+
172
+ **Action:** Run `company-knowledge show` → "No company knowledge configured."
173
+ Run `company-knowledge init`. Walk through each field; the team lead has a
174
+ Confluence space (`QA`), a Jira project (`OPS`), and an Atlassian MCP server
175
+ (`plugin_atlassian_atlassian`) but no internal dashboard yet. Leave
176
+ `dashboard_url` unset. Ask about a user-catalog skill — they don't have one, so
177
+ Phase 3 is skipped. Verify with `show`.
178
+
179
+ ### Example: Retrofitting `user_catalog_skill` onto an existing file
180
+
181
+ **Prompt:** "`canary-ci-ready` keeps telling me to check my user catalog
182
+ manually, but we have a skill for that."
183
+
184
+ **Action:** Run `show` — confirm `.canary/company.json` already has
185
+ `confluence_spaces`/`jira_projects` set from a prior run. Ask for the skill slug
186
+ (`team:test-user-lookup`). Add it to both `claude_code_skills` and the raw
187
+ `user_catalog_skill` key per Phase 3. Re-run `show` to confirm no new warnings,
188
+ then re-run `canary-ci-ready` on a known failing auth test to confirm the lookup
189
+ now fires.
190
+
191
+ ## Related Skills
192
+
193
+ - [`canary-ci-ready`](../canary-ci-ready/SKILL.md) — consumes
194
+ `user_catalog_skill` for auth/config failure investigation
195
+ - [`canary-failure-impact`](../canary-failure-impact/SKILL.md) — same
196
+ investigation pattern, different trigger context
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: canary-critical-areas
3
+ description: >
4
+ Risk-based test prioritisation. Given a codebase or diff, identifies which
5
+ areas carry the most risk using git churn, downstream dependents,
6
+ business-critical signals, and existing coverage depth. Produces a ranked list
7
+ with recommended test types per area.
8
+ ---
9
+
10
+ # Canary: Critical Areas
11
+
12
+ Identifies the highest-risk areas of a codebase so test effort goes where it
13
+ matters most. Uses multiple signals, degrades gracefully when advanced tooling
14
+ is unavailable.
15
+
16
+ ## When to Use
17
+
18
+ - Before writing new tests: "where should I focus?"
19
+
20
+ - After a large diff lands: "what did this change put at risk?"
21
+
22
+ - As Phase 1 of `/canary-test-pipeline`
23
+
24
+ - When asked to prioritise test coverage
25
+
26
+ ## Signals
27
+
28
+ Collect all available signals, score each area, and rank by composite risk
29
+ score.
30
+
31
+ ### 1. Churn / hotspot (always available)
32
+
33
+ **With harness MCP available:** call `detect_anomalies` (metric `hotspotScore`,
34
+ plus its co-change / single-point-of-failure signals). Harness's hotspot score
35
+ already blends churn with structural risk, so use it directly as this signal and
36
+ skip the raw `git log` pass. Normalise the returned scores to 0–1.
37
+
38
+ **Fallback (no MCP):**
39
+
40
+ ```bash
41
+ git log --stat --since="90 days ago" -- <path> | grep -c "^"
42
+ ```
43
+
44
+ Files changed most frequently in the last 90 days score higher. Normalise to 0–1
45
+ across all files in scope.
46
+
47
+ ### 2. Downstream dependents
48
+
49
+ **With harness MCP available:** call `get_impact` for each candidate file
50
+ (`filePath`, `mode: "summary"`) and read the affected-node counts it returns
51
+ (tests / docs / code grouped by type). This is the purpose-built impact
52
+ primitive — do **not** hand-walk `get_relationships` edge-by-edge; `get_impact`
53
+ already computes the transitive downstream set. More affected nodes ⇒ higher
54
+ score.
55
+
56
+ **Fallback (no MCP):** scan for `import` statements referencing each file using
57
+ `grep -r`. Count unique files that import each candidate.
58
+
59
+ Files with more inbound dependents score higher — a change here breaks more.
60
+
61
+ ### 3. Business-critical / critical-path flags
62
+
63
+ **With harness MCP available:** call `get_critical_paths` and add a fixed boost
64
+ (+0.3) to any area whose functions appear in the returned perf-critical set.
65
+ Also query `ask_graph` for `business_fact` nodes associated with each area; any
66
+ business-critical annotation adds the same +0.3 boost (apply the boost once,
67
+ whichever signal fires).
68
+
69
+ **Fallback:** skip this signal silently (do not penalise the score).
70
+
71
+ ### 4. Coverage depth boost
72
+
73
+ If `.canary/test-inventory.json` is present: files whose endpoints are at depth
74
+ 0 or 1 receive a boost (+0.15) — low depth in a high-churn file is especially
75
+ risky.
76
+
77
+ ## Risk Score
78
+
79
+ ```text
80
+ risk_score = (churn * 0.35) + (dependents * 0.35) + (business_critical * 0.30) + depth_boost
81
+ ```
82
+
83
+ Capped at 1.0. Round to 2 decimal places.
84
+
85
+ ## Output
86
+
87
+ ```text
88
+ Critical areas — <repo> (<N> files analysed)
89
+
90
+ 1. src/loyalty/points.service.ts risk 0.92 ████████████
91
+ signals: high churn · 12 dependents · business_critical
92
+ recommended: api + integration tests
93
+
94
+ 2. src/billing/charge.service.ts risk 0.78 ██████████
95
+ signals: high churn · billing domain
96
+ recommended: api tests · /canary-failure-impact suggested
97
+
98
+ ...
99
+ ```
100
+
101
+ Show at most 10 areas. If more than 10 qualify, note the total and offer to show
102
+ all.
103
+
104
+ ## Optional Artifact
105
+
106
+ When `--save` flag is passed (or when invoked by `/canary-test-pipeline`), write
107
+ `.canary/critical-areas.json`:
108
+
109
+ ```json
110
+ {
111
+ "generated": "<ISO timestamp>",
112
+ "areas": [
113
+ {
114
+ "path": "src/loyalty/points.service.ts",
115
+ "risk_score": 0.92,
116
+ "signals": ["high_churn", "many_dependents", "business_critical"],
117
+ "recommended_test_types": ["api", "integration"],
118
+ "summary": "High-churn service with 12 downstream dependents"
119
+ }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ This file is consumed as opt-in context by `/canary-edge-cases` and
125
+ `/canary-failure-impact`.
126
+
127
+ ## Flags
128
+
129
+ - `--diff <git ref>` — scope analysis to files changed in a diff
130
+ (`git diff <ref>...HEAD`)
131
+
132
+ - `--save` — write `critical-areas.json`
133
+
134
+ ## Related skills
135
+
136
+ - `/canary-ci-ready` — check 4 consumes `critical-areas.json`
137
+
138
+ - `/canary-edge-cases` — focuses edge cases on critical areas when JSON present
139
+
140
+ - `/canary-failure-impact` — focuses tracing on critical paths when JSON present
141
+
142
+ - `/canary-test-pipeline` — Phase 1
@@ -0,0 +1,16 @@
1
+ name: canary-critical-areas
2
+ version: '1.0.0'
3
+ description:
4
+ Risk-rank a codebase or diff by git churn, downstream dependents,
5
+ business-critical signals, and coverage depth; outputs a ranked list with
6
+ recommended test types per area.
7
+ stability: static
8
+ triggers:
9
+ - manual
10
+ platforms:
11
+ - claude-code
12
+ type: rigid
13
+ tools: []
14
+ tier: 1
15
+ depends_on:
16
+ - canary-ci-ready
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: canary-edge-case-discovery
3
+ description: >
4
+ Given a feature description, function signature, or existing test suite,
5
+ surfaces edge cases worth testing across six categories. Explanation depth
6
+ scales to user skill level. Optionally focuses on critical areas when
7
+ critical-areas.json is present.
8
+ ---
9
+
10
+ # Canary: Edge Case Discovery
11
+
12
+ Surfaces the edge cases that tests typically miss: the inputs and conditions
13
+ that work in demos but break in production.
14
+
15
+ ## When to Use
16
+
17
+ - After writing happy-path tests: "what else should I test?"
18
+
19
+ - When reviewing a feature for robustness
20
+
21
+ - As Phase 2 of `/canary-test-pipeline`
22
+
23
+ - When asked "what edge cases should I cover?"
24
+
25
+ ## Input
26
+
27
+ Provide one of:
28
+
29
+ - A feature description: `"points accrual on tier upgrade"`
30
+
31
+ - A function signature:
32
+ `accruePoints(memberId: string, amount: number): Promise<Result>`
33
+
34
+ - A test file path: `tests/loyalty/points.spec.ts`
35
+
36
+ - Or nothing — Canary will infer from open files and recent context
37
+
38
+ If `.canary/critical-areas.json` is present, focus edge case discovery on the
39
+ highest-risk areas first (rank_score ≥ 0.6).
40
+
41
+ ## The Six Categories
42
+
43
+ For each category, generate specific, actionable cases — not generic advice.
44
+
45
+ ### 1. Boundary values
46
+
47
+ Zero, negative, max integer, empty string, null, undefined, one-off-by-one. For
48
+ amounts: 0, 1, MAX_SAFE_INTEGER, -1, 0.001 (floating point). For strings: empty
49
+ string, whitespace-only, max-length + 1 character.
50
+
51
+ ### 2. Race conditions
52
+
53
+ Concurrent writes to the same resource. Double-submit (user clicks twice). Stale
54
+ reads after an update. Lock contention. Out-of-order async responses.
55
+
56
+ ### 3. Locale and timezone
57
+
58
+ DST transition times. Dates at midnight UTC vs local time. Non-ASCII characters
59
+ in names and addresses. RTL text in string fields. Locale-specific number
60
+ formats (1.000,00 vs 1,000.00). Emoji in text fields.
61
+
62
+ ### 4. Partial network
63
+
64
+ Request timeout mid-flight. Dropped connection after partial response. Retry
65
+ storms (client retries while server is still processing). Response truncation.
66
+
67
+ ### 5. Unexpected input shapes
68
+
69
+ Extra fields the schema doesn't expect. Missing required fields. Wrong types
70
+ (string where number expected). SQL or script injection strings. Deeply nested
71
+ objects. Arrays where scalars expected.
72
+
73
+ ### 6. Accessibility
74
+
75
+ Keyboard-only navigation paths. Missing ARIA labels. Focus trap conditions.
76
+ Screen reader text for dynamic content. Colour contrast for status indicators.
77
+ _(Only include if the input is a UI feature or test.)_
78
+
79
+ ## Output Depth
80
+
81
+ **Consult the resolved persona; do not infer one here.** The engine owns that
82
+ decision (issue #462) so every skill adapts the same way and a downstream
83
+ overlay has one place to override.
84
+
85
+ Resolve it in this order:
86
+
87
+ 1. `--level <id>` if the caller passed one. It is the explicit override and
88
+ always wins.
89
+ 2. The `persona` block on a `canary__analyze_file` response. It carries `id`,
90
+ `depth`, `formats`, `reasoning`, plus `source`, `reason`, and `signals` — the
91
+ evidence behind the choice.
92
+ 3. Neither available → use `junior`, the engine's fallback. Erring explanatory
93
+ is deliberate: over-explaining is a mild annoyance, under-explaining silently
94
+ fails a manual tester.
95
+
96
+ Render from the persona's `depth`, not from its `id` — an overlay may add
97
+ personas this table never listed:
98
+
99
+ | `depth` | Output style |
100
+ | -------- | ----------------------------------- |
101
+ | `terse` | Bullet list of cases only |
102
+ | `brief` | Cases + one-line _why this matters_ |
103
+ | `guided` | Cases + numbered reproduction steps |
104
+
105
+ When `reasoning` is true, say which persona shaped the output and why —
106
+ `persona.reason` is written to be quoted.
107
+
108
+ Read `source` before trusting the persona, and treat the three values
109
+ differently:
110
+
111
+ - `explicit` — the reader chose this. Do not second-guess it.
112
+ - `detected` — inferred from at least two independent signals about the project.
113
+ Reliable enough to act on, still a guess; mention it is overridable with
114
+ `CANARY_PERSONA` or `--level` if the depth seems wrong for them.
115
+ - `fallback` — the engine **declined** to guess, which is the common case. That
116
+ is not a failure and not a reason to go hunting for signals of your own: it
117
+ means the evidence did not clear the floor, and re-deriving an audience here
118
+ is exactly the hand-rolling this section replaced. Use the persona as given.
119
+
120
+ **Never infer `terse` on your own.** Stripping explanation from a reader who
121
+ turns out to be a manual tester is the expensive direction of this mistake;
122
+ `fallback` is deliberately explanatory for that reason. Note that `manual` is
123
+ not inferable from a file analysis at all — it is reached by an explicit choice
124
+ — so a `fallback` persona may well be a manual tester.
125
+
126
+ The shipped personas are `sdet` (terse), `junior` (brief), and `manual`
127
+ (guided); the definitions live in `ts/src/data/personas/registry.json`.
128
+
129
+ ## Output Format (`sdet` example)
130
+
131
+ ```text
132
+ Edge cases — points accrual on tier upgrade
133
+
134
+ Boundary values
135
+ · amount = 0
136
+ · amount = MAX_SAFE_INTEGER
137
+ · memberId empty string
138
+ · memberId with special characters
139
+
140
+ Race conditions
141
+ · concurrent accrual calls for the same memberId
142
+ · double-submit within 100ms
143
+
144
+ ...
145
+
146
+ Suggested next: /canary-write-test "add edge case tests for accruePoints boundary values"
147
+ ```
148
+
149
+ ## Flags
150
+
151
+ - `--level sdet|junior|manual` — output depth. Overrides the resolved persona;
152
+ the default is the persona the engine resolved (see **Output Depth**).
153
+
154
+ ## Related skills
155
+
156
+ - `/canary-critical-areas` — produces `critical-areas.json` used for focus
157
+
158
+ - `/canary-write-test` — generates tests for the surfaced cases
159
+
160
+ - `/canary-test-pipeline` — Phase 2
@@ -0,0 +1,16 @@
1
+ name: canary-edge-case-discovery
2
+ version: '1.0.0'
3
+ description:
4
+ Surface edge cases worth testing across six categories from a feature
5
+ description, function signature, or existing suite; explanation depth scales
6
+ to user skill level.
7
+ stability: static
8
+ triggers:
9
+ - manual
10
+ platforms:
11
+ - claude-code
12
+ type: rigid
13
+ tools: []
14
+ tier: 1
15
+ depends_on:
16
+ - canary-ci-ready
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: canary-fail-fast
3
+ description:
4
+ Surface test failures fast and loud — audit a Playwright config for fail-fast
5
+ knobs (maxFailures/forbidOnly/retries) and print a loud, categorized failure
6
+ digest to the CI log + ::error annotations at run end, failing the step so the
7
+ signal can't be missed. Self-contained (bundles its own Playwright JSON parser
8
+ and failure categorizer).
9
+ cli: scripts/cli.mjs
10
+ requires: [node>=20]
11
+ ---
12
+
13
+ # Canary Fail-Fast
14
+
15
+ Make test failures **fast** (abort a broken run early) and **loud** (surface in
16
+ the CI log + Checks, not a file). Two halves:
17
+
18
+ 1. **Fail-fast config audit** — flags the absence of `maxFailures`,
19
+ `forbidOnly`, and `retries` in a `playwright.config.*`.
20
+ 2. **Loud run-end digest** — a terse, categorized failure summary to stdout +
21
+ `::error` annotations, with a non-zero exit.
22
+
23
+ Self-contained: it bundles a minimal Playwright JSON parser and the failure
24
+ categorizer, so it has no dependency on any other skill.
25
+
26
+ ## Fail-fast config (paste into `playwright.config.ts`)
27
+
28
+ ```ts
29
+ export default defineConfig({
30
+ // Fail fast in CI: abort once enough has clearly broken, never on local runs.
31
+ forbidOnly: !!process.env.CI, // a stray test.only fails the build
32
+ maxFailures: process.env.CI ? 10 : 0, // stop the run after 10 failures in CI
33
+ retries: process.env.CI ? 2 : 0, // absorb flakes in CI; surface them locally
34
+ // ...your existing config
35
+ });
36
+ ```
37
+
38
+ ## Invocation
39
+
40
+ ```bash
41
+ # Loud failure digest from a Playwright JSON run (exits non-zero on failures):
42
+ canary skills run canary-fail-fast -- --results test-results/results.json
43
+
44
+ # Audit the fail-fast config:
45
+ canary skills run canary-fail-fast -- --config playwright.config.ts
46
+
47
+ # Both at once:
48
+ canary skills run canary-fail-fast -- \
49
+ --results test-results/results.json \
50
+ --config playwright.config.ts
51
+
52
+ # Usage and the full flag list (exits 0):
53
+ canary skills run canary-fail-fast -- --help
54
+ ```
55
+
56
+ At least one of `--results` / `--config` is required. The digest exits `1` when
57
+ any test failed (so the CI step fails); the config audit alone never fails the
58
+ build.
59
+
60
+ ## CI wiring (GitHub Actions)
61
+
62
+ Run after the Playwright step with `if: always()` so the digest surfaces even
63
+ when the test step already failed:
64
+
65
+ ```yaml
66
+ - name: Run Playwright
67
+ run:
68
+ npx playwright test --reporter=json --output-file=test-results/results.json
69
+
70
+ - name: Fail-fast digest
71
+ if: always()
72
+ run: |
73
+ canary skills run canary-fail-fast -- \
74
+ --results test-results/results.json
75
+ ```