Claude Skill

testability-canary

Testability and design decoupling canary — checks for tight coupling, lack of Dependency Injection (DI), hardcoded constructors, Single Responsibility Principle (SRP) violations, and mockability gaps. Triggers on keywords: "/testability-canary", "testability-canary", "testability

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download hetcreep-coalmine-plugin_skills_testability-canary-85306d7.zip · 6 KB
Part of hetcreep/coalmine — 18 skills

Install

skills CLI npx skills add https://github.com/TheColliery/CoalMine/tree/main/plugin/skills/testability-canary
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install hetcreep-coalmine@llmmart
Git git clone https://github.com/TheColliery/CoalMine.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole hetcreep/coalmine collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Testability Canary (Decoupling & Mockability Audit)

Language: Generate EVERYTHING at runtime in the user's language — questions, answer options, menu labels, recommendations, report narrative. Detect from their messages; never default to English just because this file is English. English is allowed only for technical terms: commands, paths, code identifiers, severity labels (CRITICAL/HIGH/MEDIUM/LOW), and tier names (Light/Standard/Heavy).

Config reads — every config key, always the CASCADE, never the bare project file: ~/.claude/.coalmine.json first, then the project config (own agent dir → other known agent dirs → legacy <gitroot>/.coalmine.json), project wins per key. A bare project read is ABSENT on a machine configured only globally, so it silently yields defaults.

Audit code to ensure it is decoupled, modular, and easy to cover with automated tests.

Auditing Categories

  1. Hardcoded Constructors — instantiating deps inside classes (new DatabaseClient()) instead of injecting via constructor/factory (prevents mocking).
  2. SRP Violations — a class or method doing too many distinct duties (e.g. a service that also parses JSON and formats UI).
  3. Static Dependencies — reliance on global static methods or Singletons that make test isolation impossible.
  4. Time & Environment Coupling — direct DateTime.Now, fs, or process.env calls without an abstraction layer (fragile time/path-sensitive tests).
  5. Private Logic Gaps — complex business logic hidden in private methods that can't be tested directly (extract to testable helpers).

Per-stack patterns and the mock-strategy vocabulary: read references/checks.md before scanning.

Fix mode (choice-gated)

In Agent Context, after the report, present via ask_question:

  • Apply safe refactoring: extract hardcoded initializations into constructor params (DI) + add interface definitions. Each fix: checkpoint (git stash/commit in a git repo; else copy the file aside — never assume git) → apply → build + tests → auto-revert if newly red.
  • Let me pick: user selects specific refactoring moves.
  • Report only: exit unchanged.

Grants & denials (CLASSIFY-BLOCK)

class step it powers grant on denial
read scan coupling/DI surfaces for the categories above Read·Grep·Glob refuse that file, name it — never a clean bill
write Fix mode's safe-refactor apply, incl. checkpoint → build+tests → auto-revert if newly red Edit·Bash (checkpoint/build/revert need exec) report the fix as NOT applied AND the checkpoint/revert as NOT available, never claim done

A denial reaches the WORKER as a visible message and propagates no further — never to a caller, never as a catchable condition. Every row above states a grant or an explicit death; a step that dies says so in the output, never as a false "done"/"skipped"/"clean".

  • read denied → refuse before scanning; never a false clean bill.
  • write denied → report the change as NOT applied — never claim done.
  • network denied/unfetchable → ⚠️ unverified: check [source].
  • spawn denied → degrade per Escalation's own capability-lever fallback (never fake parallelism) and say the fan-out did not happen — already discharged there; a row above is only for a spawn this skill does OUTSIDE tier escalation.

Output

| file:line | coupling point | severity | finding | mock strategy |

Severity: CRITICAL (un-mockable external write/network call) · HIGH (SRP violation blocking unit testing) · MEDIUM (time/env coupling) · LOW (minor static dependency)

Reporting: call ReportFindings when callable — file/line MUST be the defect site, never the enclosing function; an unresolvable line reports your best guess, named imprecise in the wrap-up — never dropped, never faked. Severity prefixed in summary (e.g. [HIGH] …), ranked most-severe first, SUSPECTED as verdict: PLAUSIBLE; chat then carries only the wrap-up line (counts · coverage gaps · overflow past 32 · any imprecise-line findings) + the fix menu, never a restatement of findings. Not callable → the table above, unchanged. An Apply-fixes click = consent to the safe-fix class only — gated the same as this skill's own fix-mode (Hook Context needs an interactive session, per the Hook Context rule below) — composing with (never bypassing) the fix-mode discipline. After any fix round, re-report the same findings with outcome: fixed/skipped/no_change_needed — skipping this leaves the round UNFINISHED.

Escalation — Scope & Model Quality

Tiers are capability targets, not platform commands — resolve each to your host's nearest lever. No lever for one? Degrade gracefully — never fake parallelism you can't do; escalate via model tier + reasoning depth instead.

Level Intent Capability target Cost
Light Spot coupling check, key classes only Cheapest model · single agent, no sub-agents. Low
Standard Balanced decoupling audit, multi-category Balanced model · raised reasoning · sub-agents per category only if your platform runs concurrent workers (else single-agent). Balanced
Heavy Full 5-category audit + adversarial verify Most capable model + largest context · deepest reasoning · max sub-agent fan-out if supported · adversarial cross-check where available. High

Per-platform Heavy levers + Heavy-run durability: read references/escalation.md before a Heavy run. No concurrent fan-out on your host → escalate by model + reasoning only.

Agent Context (interactive): score the tier rubric, then call ask_question once with the 3 tiers — the pick marked ✓, score shown, labels localized — and wait for the choice before starting. ask_question = your platform's question tool: Claude Code AskUserQuestion · Cline ask_question · Copilot askQuestions · Gemini CLI ask_user (business-tier product; individual tiers ended 2026-06-18 → Antigravity CLI) · Codex request_user_input · Cursor/Devin Desktop (ex-Windsurf)/Antigravity built-in prompts; none → numbered text menu.

Tier rubric (deterministic): +1 each — ① >20 files or whole-repo/cross-module reach ② >2 of this skill's categories relevant ③ release/security/pre-ship context ④ findings will drive code changes. 0–1 Light · 2–3 Standard · 4 Heavy. Freshness cap: scope already audited ≥Standard this session → cap at Light (re-auditing fresh ground wastes tokens; scope to what changed). Default tier: honor .coalmine.json defaultTier unless the user requests a tier for that run — an explicit request overrides everything.

Hook Context (auto-triggered): auto-Light, no tier question, no sub-agents — report first. Interactive session (a user is present) → follow this skill's own Fix mode section, if it defines one, for what to offer after the report; non-interactive → report-only. Where a Fix mode section exists, never fix without a chosen option.

Entanglement: after the report, if confirmed findings fall in another canary's domain, offer it once via ask_question (one line, max one offer): perf/N+1 → scale-canary · contract/serialization/config → drift-canary · failure-path/retry → resilience-audit · logging/metrics → telemetry-canary · coupling/DI → testability-canary · dependency/CVE → supply-chain-audit · unverified version-sensitive claim → source-grounding · missing/stale rule → gold-standard.

Self error-report: if this skill misbehaves (contradictory instruction, broken procedure, wrong finding class), OFFER to file it at https://github.com/HetCreep/CoalMine/issues/new/choose with a user-reviewed summary — never auto-submit, never include unapproved code or paths.

Files (coalmine)
  • references
    • checks.md 2.3 KB
      <!-- coalmine: verified 2026-06-12 · revalidate 90d · definition file for testability-canary -->
      # Testability canary — concrete detection procedures
      
      ## 1. Hardcoded constructors (no seam to mock)
      - Grep `new ` inside constructors/methods for I/O classes: `new HttpClient(`, `new SqlConnection(`, `new SmtpClient(`, `new S3Client(`, `new PrismaClient(` etc.
      - Right shape: dependency arrives via constructor param / factory / DI container registration. The class should depend on an interface/abstract type where one exists.
      - Flag only I/O or stateful deps — `new List<>()`/value objects are fine.
      
      ## 2. SRP violations blocking unit tests
      - One class that parses + computes + persists + formats: count distinct reasons to change. >2 = flag with the split suggestion.
      - Heuristic greps: a "Service" importing both an HTTP framework and a DB driver; methods >50 lines mixing I/O with branching logic.
      
      ## 3. Static / singleton dependencies
      | Stack | Patterns |
      |---|---|
      | C# | `static` mutable fields · `Foo.Instance` · `ServiceLocator` · static `HttpClient` used directly in logic |
      | TS/JS | module-level mutable singletons imported everywhere · `export const db = new Client()` consumed deep in logic |
      | Python | module-global clients (`requests.Session()` at import time) · singletons via module state |
      | Java/Kotlin | `getInstance()` chains · static utility classes wrapping I/O |
      - Fix shape: pass the instance in; keep module-level only for pure/stateless helpers.
      
      ## 4. Time & environment coupling
      - Direct calls inside business logic: `DateTime.Now`/`UtcNow` · `Date.now()`/`new Date()` · `time.time()`/`datetime.now()` · `process.env`/`os.environ` · `fs`/file paths.
      - Fix shape: inject a clock (`IClock`, `() => Date`), read env/config once at the boundary and pass values down.
      - Only flag where behavior depends on the value (scheduling, expiry, paths) — timestamps on log lines are fine.
      
      ## 5. Private logic gaps
      - Complex branching (cyclomatic >5) inside private methods with no public seam: recommend extracting a pure function/module with direct unit tests.
      - Do NOT recommend reflection or exposing privates — extraction only.
      
      ## Mock strategy column (output)
      For each finding name the seam: constructor injection · interface extract · clock injection · boundary param · pure-function extract.
      
    • escalation.md 1.4 KB
      <!-- coalmine: verified 2026-07-23 · revalidate 30d · shared escalation detail for all canaries -->
      # Heavy-tier escalation — per-platform levers & durability
      
      Read this only before a **Heavy** run (deep fan-out). Light/Standard never need it.
      
      ## Per-platform Heavy lever
      Use your host's, if it has concurrent fan-out:
      
      - **Claude Code** → Dynamic Workflows / `ultracode` (≤16 concurrent agents)
      - **OpenAI Codex** → `xhigh` + subagents + Cloud `--attempts`
      - **Cursor** → Max Mode + parallel Cloud Agents
      - **Amp** → Oracle + subagents
      - **GitHub Copilot** → `/fleet` (Copilot CLI) + Cloud agent
      - **Goose** → subagents
      - **JetBrains** → Junie CLI
      - **Gemini CLI (business-tier product; individual tiers ended 2026-06-18 → Antigravity CLI) / Cline (read-only) / Devin Desktop (ex-Windsurf)** → subagents
      
      No concurrent fan-out on your host → escalate by model tier + reasoning depth only; never fake parallelism you cannot do.
      
      ⚠️ Subagent support CHURNS fast — most major agents added it through 2026 — so verify your platform's current capability rather than trusting this list.
      
      ## Heavy-run durability
      Run in short phases, reading results between them. If a run dies, recover finished sub-agent results from your platform's run records and re-spawn only what is missing. On Claude Code, fan out with the bundled `coalmine-scanner` agent (read-only, one dimension per spawn, table output).
      
  • skill-meta.json 183 B
    { "lightIntent": "Spot coupling check, key classes only", "standardIntent": "Balanced decoupling audit, multi-category", "heavyIntent": "Full 5-category audit + adversarial verify" }
    
  • SKILL.md 8.1 KB
    ---
    name: testability-canary
    description: >-
      Testability and design decoupling canary — checks for tight coupling, lack of Dependency Injection (DI), hardcoded constructors, Single Responsibility Principle (SRP) violations, and mockability gaps. Triggers on keywords: "/testability-canary", "testability-canary", "testability audit", "decoupling". Use when refactoring coupling, introducing DI, or making code unit-testable.
    ---
    
    # Testability Canary (Decoupling & Mockability Audit)
    
    **Language:** Generate EVERYTHING at runtime in the user's language — questions, answer options, menu labels, recommendations, report narrative. Detect from their messages; never default to English just because this file is English. English is allowed only for technical terms: commands, paths, code identifiers, severity labels (CRITICAL/HIGH/MEDIUM/LOW), and tier names (Light/Standard/Heavy).
    
    **Config reads — every config key, always the CASCADE, never the bare project file:** `~/.claude/.coalmine.json` first, then the project config (own agent dir → other known agent dirs → legacy `<gitroot>/.coalmine.json`), project wins per key. A bare project read is ABSENT on a machine configured only globally, so it silently yields defaults.
    
    Audit code to ensure it is decoupled, modular, and easy to cover with automated tests.
    
    ## Auditing Categories
    1. **Hardcoded Constructors** — instantiating deps inside classes (`new DatabaseClient()`) instead of injecting via constructor/factory (prevents mocking).
    2. **SRP Violations** — a class or method doing too many distinct duties (e.g. a service that also parses JSON and formats UI).
    3. **Static Dependencies** — reliance on global static methods or Singletons that make test isolation impossible.
    4. **Time & Environment Coupling** — direct `DateTime.Now`, `fs`, or `process.env` calls without an abstraction layer (fragile time/path-sensitive tests).
    5. **Private Logic Gaps** — complex business logic hidden in private methods that can't be tested directly (extract to testable helpers).
    
    Per-stack patterns and the mock-strategy vocabulary: read `references/checks.md` before scanning.
    
    ## Fix mode (choice-gated)
    
    In Agent Context, after the report, present via `ask_question`:
    
    - **Apply safe refactoring:** extract hardcoded initializations into constructor params (DI) + add interface definitions. Each fix: checkpoint (git stash/commit in a git repo; else copy the file aside — never assume git) → apply → build + tests → auto-revert if newly red.
    - **Let me pick:** user selects specific refactoring moves.
    - **Report only:** exit unchanged.
    
    ## Grants & denials (CLASSIFY-BLOCK)
    | class | step it powers | grant | on denial |
    |---|---|---|---|
    | read | scan coupling/DI surfaces for the categories above | `Read`·`Grep`·`Glob` | refuse that file, name it — never a clean bill |
    | write | Fix mode's safe-refactor apply, incl. checkpoint → build+tests → auto-revert if newly red | `Edit`·`Bash` (checkpoint/build/revert need exec) | report the fix as NOT applied AND the checkpoint/revert as NOT available, never claim done |
    
    A denial reaches the WORKER as a visible message and propagates no further — never to a
    caller, never as a catchable condition. Every row above states a grant or an explicit death;
    a step that dies says so in the output, never as a false "done"/"skipped"/"clean".
    
    - **read** denied → refuse before scanning; never a false clean bill.
    - **write** denied → report the change as NOT applied — never claim done.
    - **network** denied/unfetchable → `⚠️ unverified: check [source]`.
    - **spawn** denied → degrade per Escalation's own capability-lever fallback (never fake
      parallelism) and say the fan-out did not happen — already discharged there; a row above
      is only for a spawn this skill does OUTSIDE tier escalation.
    
    ## Output
    `| file:line | coupling point | severity | finding | mock strategy |`
    
    Severity: CRITICAL (un-mockable external write/network call) · HIGH (SRP violation blocking unit testing) · MEDIUM (time/env coupling) · LOW (minor static dependency)
    
    **Reporting:** call `ReportFindings` when callable — `file`/`line` MUST be the defect site, never the enclosing function; an unresolvable line reports your best guess, named imprecise in the wrap-up — **never dropped, never faked.** Severity prefixed in `summary` (e.g. `[HIGH] …`), ranked most-severe first, SUSPECTED as `verdict: PLAUSIBLE`; chat then carries only the wrap-up line (counts · coverage gaps · overflow past 32 · any imprecise-line findings) + the fix menu, never a restatement of findings. Not callable → the table above, unchanged. An Apply-fixes click = consent to the safe-fix class only — gated the same as this skill's own fix-mode (Hook Context needs an interactive session, per the Hook Context rule below) — composing with (never bypassing) the fix-mode discipline. **After any fix round, re-report the same findings with `outcome: fixed`/`skipped`/`no_change_needed` — skipping this leaves the round UNFINISHED.**
    
    ## Escalation — Scope & Model Quality
    
    Tiers are **capability targets**, not platform commands — resolve each to your host's nearest lever. No lever for one? **Degrade gracefully — never fake parallelism you can't do**; escalate via model tier + reasoning depth instead.
    
    | Level | Intent | Capability target | Cost |
    |---|---|---|---|
    | **Light** | Spot coupling check, key classes only | Cheapest model · single agent, no sub-agents. | Low |
    | **Standard** | Balanced decoupling audit, multi-category | Balanced model · raised reasoning · sub-agents per category **only if your platform runs concurrent workers** (else single-agent). | Balanced |
    | **Heavy** | Full 5-category audit + adversarial verify | Most capable model + largest context · deepest reasoning · max sub-agent fan-out **if supported** · adversarial cross-check where available. | High |
    
    Per-platform Heavy levers + Heavy-run durability: read `references/escalation.md` before a Heavy run. No concurrent fan-out on your host → escalate by model + reasoning only.
    
    **Agent Context (interactive):** score the tier rubric, then call `ask_question` once with the 3 tiers — the pick marked `✓`, score shown, labels localized — and wait for the choice before starting. `ask_question` = your platform's question tool: Claude Code `AskUserQuestion` · Cline `ask_question` · Copilot `askQuestions` · Gemini CLI `ask_user` (business-tier product; individual tiers ended 2026-06-18 → Antigravity CLI) · Codex `request_user_input` · Cursor/Devin Desktop (ex-Windsurf)/Antigravity built-in prompts; none → numbered text menu.
    
    **Tier rubric (deterministic):** +1 each — ① >20 files or whole-repo/cross-module reach ② >2 of this skill's categories relevant ③ release/security/pre-ship context ④ findings will drive code changes. **0–1 Light · 2–3 Standard · 4 Heavy.** **Freshness cap:** scope already audited ≥Standard this session → cap at Light (re-auditing fresh ground wastes tokens; scope to what changed). **Default tier:** honor `.coalmine.json` `defaultTier` unless the user requests a tier for that run — an explicit request overrides everything.
    
    **Hook Context (auto-triggered):** auto-Light, no tier question, no sub-agents — report first. Interactive session (a user is present) → follow this skill's own Fix mode section, if it defines one, for what to offer after the report; non-interactive → report-only. Where a Fix mode section exists, never fix without a chosen option.
    
    **Entanglement:** after the report, if confirmed findings fall in another canary's domain, offer it once via `ask_question` (one line, max one offer): perf/N+1 → scale-canary · contract/serialization/config → drift-canary · failure-path/retry → resilience-audit · logging/metrics → telemetry-canary · coupling/DI → testability-canary · dependency/CVE → supply-chain-audit · unverified version-sensitive claim → source-grounding · missing/stale rule → gold-standard.
    
    **Self error-report:** if this skill misbehaves (contradictory instruction, broken procedure, wrong finding class), OFFER to file it at https://github.com/HetCreep/CoalMine/issues/new/choose with a user-reviewed summary — never auto-submit, never include unapproved code or paths.
    
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related