kramme-cc-workflow/skills/kramme:code:api-design/SKILL.md
(experimental) Design stable APIs and module boundaries. Covers contract-first approach, Hyrum's Law, validation placement (at boundaries, not between internal functions), consistent error shapes with HTTP status mapping, naming conventions, and TypeScript patterns for interface stability. Use when adding HTTP endpoints, public modules, SDK surfaces, or any interface with external or cross-team callers. Includes a Design It Twice mode (opt-in via --design-twice or the phrase 'design it twice') that drafts radically different shapes — in parallel via sub-agents on Claude Code, sequentially elsewhere — before committing to one.
npx skillsauth add abildtoft/kramme-cc-workflow kramme:code:api-designInstall 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.
Design stable APIs and module boundaries with a contract-first workflow. Decide the shape, validation points, error cases, and naming before writing the handler, so the interface does not have to be rediscovered or reshaped once callers exist — the contract is surface area you can never take back.
"With a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what you promise in the contract."
Implication: every observable behavior is part of the contract — including bugs, timing quirks, ordering of fields, and undocumented side effects. You cannot "fix" an observable behavior later without breaking at least one caller. Design the contract now as if every incidental detail is permanent, because it is.
Before sketching any type or route, emit a SIMPLICITY CHECK marker stating the smallest coherent interface that satisfies the requirement. Only expand beyond that if a concrete requirement forces it.
SIMPLICITY CHECK: <the smallest interface that meets the requirement>
If the interface you end up designing is not the smallest version, write a second line explaining what forced the expansion. If there is no forcing requirement, ship the smaller surface.
Design the contract before the handler. The contract is: input type, output type, error cases, HTTP status mapping, and naming. Write it down — as a TypeScript type, an OpenAPI stub, a comment block, whatever the project reads — before writing implementation code.
Signs the contract is not first:
If any of these are happening, stop. Write the contract, then continue.
Validation belongs at the points where untrusted data enters your trusted code, and nowhere else.
Validating between internal functions duplicates work, hides the real boundary, and gives false confidence that unvalidated paths are safe. If you feel the urge to validate inside the boundary, the boundary is probably in the wrong place.
Every error returned from the API uses the same shape: { code, message, details? } (the APIError type). HTTP status comes from a fixed mapping: 400 invalid data, 401 not authenticated, 403 not authorized, 404 not found, 409 conflict, 422 validation failed, 500 server error (never expose internals).
Never mix error shapes across endpoints — callers build parsing logic against the first shape they see, and Hyrum's Law nails that shape in place. The canonical APIError type, full status mapping, worked examples per status code, and the 500-disclosure rule live in references/error-shapes.md.
Quick reference for REST/JSON surfaces:
/tasks, not /createTask).camelCase.is, has, or can (isArchived, hasPermission).UPPER_SNAKE (STATUS_ACTIVE, not active or Active).Full table plus worked good/bad examples and per-ecosystem deviations (Python snake_case, Go PascalCase for exported fields) live in references/naming-conventions.md.
List endpoints paginate. Always. Even the ones that "only return a few rows today." Fixed shape:
type PaginatedResponse<T> = {
data: T[];
pagination: {
page: number;
pageSize: number;
totalItems: number;
totalPages: number;
};
};
Callers build pagination UI and prefetching logic against this shape. Adding pagination later is a breaking change; adding it now is free.
Three patterns keep TypeScript interfaces stable under Hyrum's Law:
CreateTaskInput is not Task. Do not reuse the read type as the write payload.Code examples, factory patterns, and the per-pattern rationale live in references/typescript-patterns.md. Non-TS projects can skip this rule, but the underlying intent (don't conflate read and write shapes; don't let raw strings flow where a typed ID is expected) applies everywhere.
When you notice an adjacent endpoint with an inconsistent shape, a sibling that uses a different error format, or a neighboring module that missed pagination, emit a NOTICED BUT NOT TOUCHING marker and move on. Do not silently reshape the adjacent surface during this design.
NOTICED BUT NOT TOUCHING: <what you saw>
Why skipping: <out-of-scope / unrelated / deferred>
The reason: reshaping adjacent contracts is itself a breaking change, and it deserves its own design pass — not a drive-by edit during work on a different surface.
/teams/:teamId/tasks, not /getTasksForTeam)./tasks?status=OPEN&assignee=abc).PATCH with only the changed fields.PUT with the complete resource.The first interface that comes to mind is rarely the best. Design It Twice drafts radically different shapes for the same problem in parallel, then compares them in the open before any single design is locked in.
Platform requirement — the parallel form requires a multi-agent capability (Claude Code's Agent tool). On platforms without it, fall back to the sequential variant: draft each design in turn under the same constraint slate, taking care not to read prior designs while drafting the next, then compare them with the same rubric. references/design-it-twice.md covers both forms.
When this mode applies — opt-in only. Trigger on --design-twice, on the user phrase "design it twice" or "show me alternatives". For high-leverage surfaces (SDK, cross-team contract) where the obvious shape feels suspiciously obvious, ask before entering this mode. Do not enter it by default; the cost of multiplication is wasted on low-stakes interfaces.
How it works — frame the problem (not the interface), produce 3+ designs each pinned to a different constraint (minimize methods / maximize flexibility / optimize common case / ports & adapters), present each in full sequentially, then compare in prose by depth, locality, and seam placement. End with a single recommendation: pick one, hybridize and explain the borrow, or redesign because the framing was wrong. See references/design-it-twice.md for the full process, prompt template, and comparison rubric.
Relationship to the rules — Design It Twice is upstream of Rule 0–7. Once a design is picked, every rule above applies to that design. The mode does not loosen any rule; it raises the chance the picked design is worth applying them to.
kramme:siw:generate-phases — when a planned phase introduces a new interface, run this skill first to lock the contract before slicing begins.kramme:code:incremental — once the contract is locked, the implementation is sliced through the incremental loop. Each slice conforms to the contract rather than rediscovering it.kramme:injection-reviewer and kramme:auth-reviewer agents verify the validation and authorization boundaries set here. A contract that declares its validation boundary makes these reviews mechanical.These are the lies you will tell yourself to justify shipping an unstable contract:
If you notice any of these in your own design, stop and redesign:
/api/createTask, /deleteUser).user_internal_id, debug_payload).error field, 400 with no error at all.Before declaring the contract done, self-check every item:
APIError shape with a correct HTTP status from the Rule 3 mapping (full table in references/error-shapes.md)?NOTICED BUT NOT TOUCHING entry for every inconsistency in adjacent code that this design did not fix?If any answer is no, close the gap before handing off to implementation.
development
One-way migration of a local SIW project into Linear. Creates one Linear project, migrates the main spec and supporting specs as Linear Documents, creates milestones from SIW phases and issues from SIW issues, then prompts to retire the local siw/ files via /kramme:siw:remove. Linear becomes the source of truth; this is not a two-way sync and keeps no rerun mapping, so re-running can duplicate issues. Use when moving a planned SIW initiative into Linear for good. Not for implementing issues, defining new SIW issues, or generating an issue breakdown.
development
Compare an existing PR's title and body against the actual branch diff and report drift — false claims, missing major changes, stale scope, missing risk callouts. Use after pushing changes to a branch with an open PR, or before requesting review. Read-only by default; add --fix to delegate to kramme:pr:generate-description for an updated description. Complements kramme:pr:code-review (which checks description accuracy as one signal among many code-quality checks) by being a fast, focused, single-purpose check that runs in seconds.
tools
Reviews plugin skills for focused scope, progressive disclosure, portability, safety, retry behavior, and documentation quality. Use when auditing a SKILL.md, skill directory, or proposed skill text against skill-authoring standards. Not for creating new skills, editing skills, or reviewing ordinary application code.
tools
Reviews recent agent session transcripts to find repeated manual workflows or repeated user asks, then proposes and optionally scaffolds only useful new skills or custom subagents. Use when the user asks to inspect recent sessions, find automation opportunities, or create reusable workflows from repeated work. Not for summarizing one session, general retrospectives, or codebase refactoring.