Skill
Add or change a tRPC procedure in silver-castle apps/api — shared Zod schema, router/repo split, permissions, Hebrew errors, audit+notify, and gates.
Playbook (mirrored from disk)
tRPC Endpoint
Order of work:
- Shared schema first —
packages/shared/src/schemas/<domain>.ts, house idiom:
export const CreateThingInput = z.object({...}); export type CreateThingInput = z.infer<typeof CreateThingInput>
(.uuid()for ids,.max()everywhere). Barrel-export it. - Repo —
apps/api/src/repos/<domain>.repo.ts: plain async fns,db: SupabaseClientfirst
arg,const COLS = \…`select constant, throw plainnew Error(error.message)`;
enrichment joins in TS (Promise.all + Map), not SQL. - Router —
apps/api/src/routers/<domain>.ts:
requireAction('<domain>.<verb>').input(Schema).mutation(async ({ ctx, input }): Promise<Ret> => {...})
— NO .output(); return-type annotation; mutations return{ ok: true as const }or the row.
Errors:throw new TRPCError({ code: 'FORBIDDEN', message: '<Hebrew>' })— sanctioned codes;
PRECONDITION_FAILED for invalid state transitions. - Privileged write? REQUIRED:
audit(ctx.db, { actor_id, action, target_type, target_id, project_id?, meta?, ip: ctx.ip })+notify({ db, recipient_ids, kind, title, body, link })
(Hebrew title/body; link = app route). Best-effort — never let them break the main action. - Compose in
_root.tsif a new router. ESM: ALL relative imports end in.js. - New permission action → seed row in the SAME migration + update the frontend mirror
apps/web/src/lib/auth/permissions.ts(lockstep or drift).
Gates:
cd ~/silver-castle/apps/api && npx tsc -p tsconfig.json && npm test
# shared changed? also: cd ../web && npx tsc -p tsconfig.app.json --noEmit