plugins/dev/skills/fix-typescript/SKILL.md
Fix TypeScript errors with strict anti-reward-hacking rules. **ALWAYS use when** the user says 'fix type errors', 'fix typescript', 'type-check is failing', or when TypeScript compilation errors need to be resolved. Ensures runtime type safety — fixes root causes instead of silencing errors with casts.
npx skillsauth add coalesce-labs/catalyst fix-typescriptInstall 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.
You are fixing TypeScript type errors. This command embeds strict rules to prevent "reward hacking" - patterns that make linters pass without actually fixing type safety issues.
Your goal is RUNTIME TYPE SAFETY, not just passing linters.
If your fix would make the linter happy but could still crash at runtime, it's wrong.
These patterns are EXPLICITLY FORBIDDEN. Using them will require rework:
| Pattern | Why Forbidden | What To Do Instead |
| ----------------------------------------- | ---------------------------------------- | ------------------------------------- |
| as unknown as Type (undocumented) | Erases all type info, defeats TypeScript | Fix the source to return correct type |
| as any | Disables type checking entirely | Use proper typing or Zod validation |
| void (0 as unknown as Type) | Tricks linter into thinking type is used | Delete the unused type |
| const _var = ... (local variable) | Suppresses unused warning | Delete the unused variable |
| export type Foo (when unused elsewhere) | Suppresses unused warning | Remove export or delete the type |
| // @ts-ignore or // @ts-expect-error | Hides real type problems | Fix the actual type error |
| Commenting out code | Dead code clutters codebase | Delete it (git has history) |
| Excluding files from tsconfig | Hides errors in those files | Include files, fix errors |
Fix the SOURCE to return the correct type:
// WRONG - Casting a query result
const users = (await db.from("users").select("*")) as unknown as User[];
// CORRECT - Use the query builder's generic typing
const { data: users } = await db.from("users").select("*").returns<User[]>();
// WRONG - Casting a service result
const account = (await accountService.findById(id)) as Account;
// CORRECT - Fix the service to return the right type
const account = await accountService.findById(id); // Returns Account | null
if (account === null) throw new NotFoundError();
Validate with Zod at the boundary:
// WRONG - Trust external data
const webhook = req.body as WebhookPayload;
// CORRECT - Validate at boundary
const webhook = WebhookPayloadSchema.parse(req.body);
Type assertions are ONLY acceptable for third-party library limitations with full documentation:
// ACCEPTABLE - Third-party library type gap with documentation
// LIBRARY TYPE LIMITATION: The thirdPartyWrapper() function returns a type
// that TypeScript can't verify implements the expected interface.
// Verified at runtime that the object has the required methods.
// TODO: Remove when library updates types (tracked in TICKET-XXX)
const wrapped = thirdPartyResult as unknown as ExpectedInterface;
Requirements for acceptable assertions:
/scan-reward-hacking before marking completeFirst, check package.json for a type-check script. Then detect the package manager by looking for lock files:
bun.lockb -> bun run type-checkpnpm-lock.yaml -> pnpm type-checkyarn.lock -> yarn type-checkpackage-lock.json -> npm run type-checkIf no lock file is found, fall back to npx tsc --noEmit.
You MUST run these checks:
# Type check must pass (use the detected package manager)
# e.g., npm run type-check / yarn type-check / bun run type-check / pnpm type-check
# Scan for forbidden patterns (run /scan-reward-hacking command)
grep -r "as unknown as" [files-you-changed]
grep -r "void (0" [files-you-changed]
grep -r "as any" [files-you-changed]
If ANY forbidden pattern exists in your changes without proper documentation, your work is not complete.
If you need as, ask "Why doesn't the type already match?" and fix THAT.
Type assertions are a code smell. They mean either:
Never use assertions to silence errors - fix the underlying type mismatch.
testing
Phase-agent that fixes a failing verify verdict so the pipeline self-heals instead of stalling to needs-human (CTL-653). Reads `${ORCH_DIR}/workers/<ticket>/verify.json`, fixes the `findings[]` (every severity:"high" plus the regression_risk drivers) directly via Edit/Write, commits the remediation, and emits `phase.remediate.complete.<ticket>`. The scheduler's router then re-dispatches `verify` to re-check (the verify⇄remediate cycle, cap 3). Dispatched as a `claude --bg` job by `phase-agent-dispatch`, which invokes it via slash command — hence `user-invocable: true`.
development
Phase agent for the verify step of the 9-phase orchestrator pipeline (CTL-450). NEW skill — has no canonical wrapper. Runs read-only adversarial verification against the implement-phase diff: tsc, tests, lint, security scan, reward-hacking scan, code review, test coverage, silent-failure hunt. Writes ${ORCH_DIR}/workers/<TICKET>/verify.json then emits phase.verify.complete.<ticket>. Reads phase-implement.json as its prior-phase artifact. NEVER writes application code — only test files allowed. Spawned via phase-agent-dispatch via slash command — hence `user-invocable: true`.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies dependencies, 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.failed.<TICKET> on error. Dispatched by the phase-agent orchestrator (CTL-452)
testing
Phase agent for the review step of the 9-phase orchestrator pipeline (CTL-450). Wraps the /review skill (gstack) — explicitly skips /ultrareview per user decision. Reads verify.json from the prior phase, runs /review against the diff, writes ${ORCH_DIR}/workers/<TICKET>/review.json, and creates a remediation commit for any HIGH-severity finding that has a deterministic fix. Emits phase.review.complete.<ticket>. Spawned via phase-agent-dispatch via slash command — hence `user-invocable: true`.