plugins/dev/skills/validate-type-safety/SKILL.md
Run the full 5-step TypeScript validation gate: type check, reward hacking scan, test inclusion, tests, and lint. This skill provides a structured multi-step pipeline that you cannot replicate on your own — it detects the project's package manager and linter automatically, checks tsconfig strictness, and invokes /scan-reward-hacking internally. **ALWAYS consult this skill when** the user mentions 'validate types', 'check type safety', 'type validation', 'type safety gate', wants to verify TypeScript changes before a PR, after completing a plan phase, or says anything about running type checks + tests + lint together. Even if you think you can run tsc yourself, use this skill — it catches issues you'd miss (tsconfig strictness, test exclusions, reward hacking patterns).
npx skillsauth add coalesce-labs/catalyst validate-type-safetyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Final verification gate before marking any TypeScript work complete. Combines type checking with reward hacking detection, test verification, and linting. ALL 5 steps must pass.
Before running any commands, detect the project's package manager and linting tool. Do NOT
hardcode bun, npm, or trunk — detect from what's available.
Check for lockfiles in the project root:
| Lockfile | Package Manager | Run Command |
|----------|----------------|-------------|
| bun.lockb or bun.lock | bun | bun run |
| pnpm-lock.yaml | pnpm | pnpm run |
| yarn.lock | yarn | yarn run |
| package-lock.json | npm | npm run |
If multiple exist, prefer in the order listed above. Store as $PM_RUN (e.g., bun run).
Check for linting tools in this order:
| Check | Linter | Command |
|-------|--------|---------|
| .trunk/ directory exists | Trunk | trunk check --ci --upstream origin/main |
| biome.json or biome.jsonc exists | Biome | ${PM_RUN} biome check . |
| eslint.config.* or .eslintrc.* exists | ESLint | ${PM_RUN} eslint . |
If none found, skip Step 5 and note it in the output.
Look for a type-check or typecheck script in package.json:
${PM_RUN} type-check (or typecheck)npx tsc --noEmit (fallback)For monorepos, check if the root package.json has a workspace-level type-check script first.
Read the project's tsconfig.json (or the tsconfig covering changed files in a monorepo). Verify:
"strict": true is set — WARN if missing"noUncheckedIndexedAccess": true — INFO if missing (recommended)"noImplicitReturns": true — INFO if missing (recommended)"exactOptionalPropertyTypes": true — INFO if missing (recommended)For monorepos: use the Glob tool to find tsconfig.json files, then identify which one covers the
changed files (check include/exclude paths and references).
This step is informational only — it does not cause a FAIL verdict, but findings are reported.
${PM_RUN} type-check # or detected equivalent
Expected: Zero errors, exit code 0.
If errors exist, they MUST be fixed. Do not proceed to Step 2 until type-check passes.
Execute the /scan-reward-hacking skill on the changed files.
Expected: PASS verdict with no unfixed CRITICAL or HIGH severity patterns.
Check that test files are NOT excluded from type checking. Use Grep to search tsconfig files (in changed packages for monorepos):
\.test\.ts or \.spec\.ts in exclude arrays of tsconfig filesexclude arraysExpected: No tsconfig files exclude test files from type checking.
${PM_RUN} test
For monorepos with many packages, if the project has a --filter or --scope option, run tests
only for affected packages when possible.
Expected: All tests pass. Type fixes should not break functionality.
# Use detected linter command from Tooling Detection
trunk check --ci --upstream origin/main # or biome/eslint equivalent
Expected: Zero lint errors.
If no linter was detected, skip this step and note it in the output.
If type-coverage is available (check with which type-coverage or npx type-coverage --help),
report the numeric coverage score:
npx type-coverage --at-least 0
This is informational only — does not affect the verdict.
Present results in this format:
## Type Safety Validation Results
### Tooling Detected
- Package manager: {bun|pnpm|yarn|npm}
- Linter: {trunk|biome|eslint|none}
- Type check command: {detected command}
### Step 0: tsconfig Strictness (Informational)
- strict: true — {YES / MISSING}
- noUncheckedIndexedAccess — {YES / missing (recommended)}
- noImplicitReturns — {YES / missing (recommended)}
- exactOptionalPropertyTypes — {YES / missing (recommended)}
### Step 1: TypeScript Type Check
- Status: PASS / FAIL
- Errors: {count} (list if any)
### Step 2: Reward Hacking Scan
- Status: PASS / FAIL
- Critical: {count}
- High: {count}
- (Details if any issues found)
### Step 3: Test File Inclusion
- Status: PASS / FAIL
- tsconfig files excluding tests: {count} (list if any)
### Step 4: Tests
- Status: PASS / FAIL
- Failed tests: {count} (list if any)
### Step 5: Lint Check
- Status: PASS / FAIL / SKIPPED (no linter detected)
- Errors: {count} (list if any)
### Type Coverage (if available)
- Coverage: {X}% ({Y}/{Z} types)
---
## Overall Verdict: PASS / FAIL
{If FAIL, list all items that must be fixed}
PASS requires ALL of the following:
FAIL if ANY check fails.
Do NOT mark the work as complete. Instead:
For efficiency, stop on the first failing step. Report which step failed and what needs fixing. Re-run the full validation after fixes are applied.
/implement-plan as part of quality gates/oneshot Phase 4 (Validate + Quality Gates)/scan-reward-hacking internally for Step 2development
Migrate a single-harness repo to the dual-harness layout so both Claude Code and Codex load the same instructions and skills — AGENTS.md as the portable canonical doc, a thin CLAUDE.md `@AGENTS.md` bridge, and a `.agents/skills` dir with a `.claude/skills` symlink onto it. Use when asked to migrate to dual-harness, make this repo work in both Claude and Codex, or for agent metadata cleanup.
tools
Goal-driven senior-engineer pipeline-unstick sweep (CTL-1176 rung 3). Given the stuck/failed/needs-human set (or ONE ticket handed by the recovery router), its GOAL is to get the pipeline MOVING again — not to fix one ticket's review findings (that is phase-remediate). It runs AFTER the eyes (diagnostician evidence) and the hands (deterministic unstuck-sweep seams) have already tried, and it CONSUMES their output from a recovery-pass.json brief rather than re-diagnosing or redoing their narrow work. It acts like a senior engineer with full tool access — it resolves merge conflicts, rebases, force-pushes, merges green PRs, and re-dispatches stalled phases AUTONOMOUSLY — and escalates to the operator ONLY for a genuine value judgment / something that degrades other functionality / a real cost-benefit trade-off / a serious architecture change / an ADR conflict. On escalation it AUTHORS the operator inbox row + the push notification (executive-voiced). Dispatched as a `claude --bg` job by phase-agent-dispatch via slash command, AND invocable bare by the operator as a sweep — hence `user-invocable: true`. Ships behind CATALYST_RECOVERY_PASS (off by default — no live behavior change until shadow/enforce).
tools
Diagnose and fix Catalyst setup issues. Validates tools, database, config, OTel, direnv, and thoughts. Automatically fixes what it can — creates directories, initializes the database, sets WAL mode, runs migrations. Use for new installs, upgrades, or when something isn't working.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies genuine blockers (a semantic second-pass over the backlog — NOT a prose scrape; CTL-838), estimates scope, writes triage.json, and posts a triage analysis comment to Linear. Triage completion is signaled by that comment plus the local triage.json — there is no `triaged` label. Emits phase.triage.complete.<TICKET> on success and phase.triage.fai