SuperAPI AI Directory

coding-standards

Coding & development

View repository

Updated 10-16 05:49

About this skill

Coding Standards

Produce code a colleague can follow: a directory structure that shows the domain, small files that each hold one named responsibility, domain concepts as explicit types, direct control flow, ordinary names, and an explanation wherever code cannot show the reason. Review or diagnosis returns findings without implementing them. A change request includes its relevant local checks and nothing more: no repository-wide migration, cleanup, restructuring, or publication, and a pre-existing problem met on the way is a follow-up in the summary, not a change.

Start from the repository

Read the project instructions and written rules, the language and runtime support range, dependency constraints, nearby implementation and tests, and configured checks before choosing syntax, layout, or tools; then read the language reference for each language the change touches. Existing public imports, accepted inputs, serialized values, and persisted data stay compatible unless the request includes a migration. The current request takes precedence, then the project's written rules and explicit repository constraints, then this skill's defaults, which apply where the project is silent, to new and changed code; mixed existing style is not an exemption. In a repository the user does not own, a convention the touched module follows consistently counts as a repository constraint for style-only rules, such as annotating every local; mixed style there is still not an exemption. Preserve configured formatters, linters, type checkers, and test runners unless their change is in scope, and report a conflict with an older runtime or incompatible tooling instead of breaking support.

Name the responsibilities

Give each function a caller-visible responsibility and make data flow and side effects clear. When a new module or multi-phase task has unclear boundaries, a skeleton of typed signatures and short contract comments can expose them before implementation. Use it when it helps; an existing cohesive function or a small change does not need a separate skeleton pass. Revise boundaries as the implementation supplies evidence. Names that only describe position (step_two, process_data, helper) prompt a review of the responsibility. Resolve a consequential contract ambiguity from the request or repository; do not turn an unverified assumption into a promised documented contract.

A block is its own function when a reader would otherwise have to work out what it does and it hands back one named value, decision, or effect through a small signature. Call count decides nothing: a responsibility called once still earns a function, and a "one-time operation" that needs no helper is a statement or two, not a named step of the algorithm. Keep a block inline when extracting it would need more than about four inputs, return several unrelated locals, or leave either function unreadable without the other. Flatten before splitting, with early returns and a lookup table or pattern match in place of a chain of conditionals over constants. Keep parsing, computation, and output as phases joined by a named record type; I/O and mutation of shared state live in the orchestrating function, the computation beneath stays pure, and a function that mutates says so in its name or documentation. A comment that says what a block does is a prompt to extract or rename; a comment that says why stays.

A finished function has a name and contract that express one coherent responsibility, a caller whose flow and side effects are clear from its callees, and a boundary that improves understanding. A conjunction such as "and" can describe related parts of one responsibility; inspect the meaning rather than banning a word. A complexity or statement-count lint finding prompts a review of decomposition under the repository's rules, never a split by line ranges into numbered helpers or a suppression without a reason. Read functions and modules for the optional skeleton, the intent tests, the size prompts and gates, and the procedure for moving code.

Structure

Split directories by domain first and by responsibility inside each domain, as deep as the boundaries warrant. A boundary is real when the code on each side has its own vocabulary, its own reason to change, or its own I/O. Keep files small: each holds one responsibility a reader can state in a sentence, split by reason to change and never by kind or line range. At about 500 handwritten lines, split at a boundary you can name; a file past 1,000 needs a reason in the change summary. A one-off script, notebook-adjacent analysis, or tool that is genuinely one responsibility stays one file until it gains a second entry point, importer, or responsibility. Depth is not scaffolding: a directory or file exists because it has content, never as a placeholder or a per-feature template.

Directories follow boundaries; abstractions follow triggers. Choose per feature: a CRUD feature stays framework-native in its own directory, with no ports, beside a feature with rules and ports. A port, service layer, unit of work, message bus, or container appears only on a named trigger: a second implementation of an integration (including a recorded or fake adapter that a real-run check or evaluation uses), a seam an accepted design document names, a second entry point that would otherwise duplicate orchestration, or a check that would otherwise mock a vendor library. No interface with one implementation, no repository over an ORM for CRUD, no service that only forwards, no dependency-injection container while the composition root fits on one screen. An existing repository keeps its shape unless the request is to change it. State the chosen shape and its reason in the change summary when creating a project or a top-level directory. Read architecture selection for the shape table, layouts, ports and the composition root, dependency-direction checks, and each pattern's own limits.

A module's public surface is explicit and its machinery private; the language reference gives the mechanics. Order a file by importance, put a private helper directly below its only caller, and let a split-out module say why in its header documentation. Errors, constants, and small shared types live in leaf modules that import nothing from the package, so the dependency graph stays acyclic; files that depend on each other in both directions are one module split badly. Repository limits win over these sizes.

Domain models and types

Model domain concepts as explicit types. A closed set is an enum with explicit, stable external values; values a provider may extend stay open strings with validated syntax. A record that crosses a data boundary (input, configuration, persisted or transmitted data, values passed between phases) is a validated record type, parsed once at the boundary so typed values flow inside. An identifier or unit that must not be confused with another of the same shape gets a distinct type where the language supports one. Keep one type per meaning, with no parallel record representations of the same concept; services and resource owners stay ordinary classes or modules, not data records.

Use the language's current standard facilities rather than hand-rolled equivalents, and annotate types wherever the language allows: parameters, returns, fields, module values, and locals where the language permits it. The annotation is for the reader of a diff or review who has no language server, and the need grows with every external library involved. Import an SDK's real client, request, response, and stream types instead of an untyped escape hatch, and add no cast or suppression merely to make an annotation present. Past a handful of parameters, pass the rest by name. The language reference names the concrete tools; for Python they are StrEnum, Pydantic where the project uses it, annotations on every local, and uv.

Implementing from a design document

When an accepted design document (from design-doc, when installed) names interfaces, seams, adapters, and a verification split, implementation follows it. Its seams table decides where ports go: each named seam gets a port in the consuming feature and an adapter per listed implementation, and a component the document writes as plain code gets none. Its verification split decides what each component gets: a real-run check, a contract test (only for the essential contracts the document lists under the split with their reasons), or evaluation for learned or judged behavior. Its Operating boundary section, present for an attack or red-team agent, names the enforcement code that must exist: authorized targets and scope, budgets, containment, the never-do list, and evidence handling, each enforced in code rather than prompt text and given a real-run check. The composition root wires the production adapter the document names. Evaluation calls the same interface with production configuration, never a side path that scores something callers do not receive. Prompts, decoding settings, model revisions, and thresholds are adapter configuration, recorded with the adapter revision. When the code cannot follow the document, say so and record the deviation; do not redesign silently. Do this only when such a document exists; without one, do not invent speculative ports.

Agent loops

For an LLM episode loop, the task contract is the interface: goal, inputs, allowed actions, budget in turns, tokens, and wall time, termination conditions, and the outcome type the loop returns. The loop (model, prompt, scaffold) is the adapter behind that contract, so a scripted policy and a model-driven loop can be swapped and compared. Each tool sits behind its own port with a production adapter and a recorded or fake adapter, so real-run checks and evaluation can replay episodes. Budget, termination, and tool permissions are plain decision code, a function of the loop state that returns continue or stop with a reason, never prompt text. The loop's output is the trajectory record (each model call and tool call with arguments and results, the decisions taken, the termination reason, and the adapter revisions) together with the end state, which evaluation scores instead of the agent's claim of success. A judge that grades outcomes inside the system sits behind its own port. A queue worker keeps the long-running worker shape: loop shell, handlers, pure decisions, and ports for the queue, store, and model client. Read architecture selection for the layout and side-effect handling.

Explain what the code cannot show

Add the explanation where a maintainer meets the decision: a file or module header for what the file owns, a type's documentation for its role and invariants, a function's documentation for the contract a caller depends on (units, ordering, ownership, sentinels, failures, side effects, lifecycle, cancellation), and a block comment for a reason, ordering constraint, workaround, or rejected alternative. A public name without documentation is a defect; a private helper whose name and signature say everything needs none. Do not repeat types the annotations carry, narrate statements, or invent rationale for a sleep, retry, or fallback; "thread-safe", "idempotent", and "guaranteed cleanup" need evidence. Follow the repository's documentation convention and preserve tool directives (lint suppressions, type-checker ignores, coverage pragmas) unless that tool's behavior is in scope.

A documentation-only or comment-only request authorizes no signature, annotation, control-flow, or dependency change, and "review", "audit", or "check" returns findings without edits. For Python, run the doc-only guard after the edit: from inside the edited Git repository, resolving the directory that holds this SKILL.md, run python3 <skill-dir>/scripts/verify_doc_only_diff.py --base HEAD -- path/to/file.py; it fails on any executable AST change or changed tool directive. For another language, inspect the diff for executable changes by hand and say so. Read docstrings and comments for the contract map and review checklist, which apply in any language, and for Python's contracts, patterns, and the guard's limits.

Errors and resources

A package has one error root, with cases named by what the caller would do differently, carrying the facts a caller acts on as fields, with transport and library errors translated at one seam. Resource ownership and cancellation are explicit. Read correctness and verification when changing error handling, I/O, concurrency, or project documents.

Verification and completion

Verify through the real entry point (the command, route, job, or public function a caller uses) on fixed input, and leave an artifact another person can rerun: the command, the input, and the output or log path. Do this whenever public imports, accepted and rejected inputs, serialized fields and enum values, error behavior, or cleanup paths change, and before claiming that any change works. Run the repository's configured checks relevant to the changed behavior through the language's project runner. Report three things separately: whether the code runs (real-run and configured checks, with results), whether quality is established (for learned or judged behavior, an evaluation through the production interface, never an impression from a few runs), and whether it is deployed (where and at which revision, or not deployed). Evidence before claims: state what a check you ran shows, and name each check not run and why. A formatter cannot establish runtime correctness, a type checker cannot establish external-data validation, a passing complexity linter cannot establish that a function is readable, and timings from a functional or evaluation run are not performance evidence; capacity and latency under load belong to load-testing.

Finish when the requested result is implemented or reviewed and the relevant checks have run or their exact limitation is reported. State the behavior changed, the structure and architecture decisions with their reasons, the checks and results with the rerunnable artifact, and anything not verified. No plan file or report template for ordinary work. Continue an already-requested publication through draft-pr, or an already-requested commit and push through commit-and-push.

Tests

The default is no new tests. Verification comes from real entry-point runs on fixed input and, for learned or judged behavior, from evaluation. Add a test only for an essential module contract: one that callers or stored data depend on, whose silent break would be costly (security, money, data loss, reported numbers), and that a real run does not exercise cheaply, such as a parser's accept and reject boundary, a serialized format, an error mapping callers branch on, or an agent loop's budget and permission checks. An accepted design document lists these under its verification split.

For an essential contract, implement first. Then a subagent that has not seen the implementation writes the tests from the interface contract alone, in Detroit (classicist) style: real collaborators, outcomes checked through the public interface, fakes only at external I/O and model seams, and no mocks that mirror interactions. Read test author packet for what the packet includes (the interface contract, input and output examples, error behavior, and the verification split) and excludes (implementation paths and code). Where the harness cannot start a subagent, write the packet, then write the tests from it without reopening the implementation, and report that the author was not blind.

When a new test fails, decide which of three it is before changing anything: an implementation bug, fixed in the code; a contract gap, where the contract was silent or ambiguous, settled from the request or design document or by asking the user when consequential, then written into the contract; or a bad test, which asserted something the contract does not say, fixed or dropped. Do not keep a test that merely restates the implementation, such as expected values copied from the code or assertions on calls between internal helpers.

Ask the user when a team's documented policy or a reviewer asks for tests, and when existing tests would be removed or changed; a test that fails during a refactor is diagnosed first, and the diagnosis goes with the question. When CI enforces tests, write only what passing requires, against the contract, and say so in the summary. Read correctness and verification for the full policy.

Language references

Language Read when Entry
Python Writing, structuring, typing, documenting, or reviewing Python; setting up a Python project or its dependencies Python, which links the deeper Python references by condition

A language without a reference here uses the rules above with its current standard tooling and the repository's conventions; name the concrete tools chosen in the change summary. A new language reference goes under references/<language>/ with the same entry structure.

Reference files and tooling

File Read or run when Content
architecture selection Starting a project, adding a top-level directory, introducing an interface, wiring integrations, implementing from a design document, building an agent loop Structure rule, shape table, layouts, triggers, ports and composition root, design-document mapping, agent-loop shape, stop rules, pattern catalog, evidence
functions and modules Writing a multi-phase function or a module, splitting or moving code, choosing size gates Optional skeleton, intent tests, size prompts and gates, move procedure
correctness and verification Changing errors, resources, concurrency, or project documents; verifying and reporting a change; deciding on tests Error hierarchy, cleanup, cancellation, real-run checks, status report, test policy, configured checks
test author packet Handing an essential contract's tests to an author who has not seen the implementation Packet contents and exclusions, author instructions, example, reconciliation
scripts/verify_doc_only_diff.py After a doc-only Python edit with a Git base AST and tool-directive comparison; scripts/smoke_test.sh exercises it

Gotchas

  • A prompt that discourages helpers "for one-time operations", read literally as "inline every single-caller step", produces the giant function it warns against. A one-time operation is a statement or two; a named responsibility called once is still a function.
  • Deep directories invite scaffolding. A domain directory with a model, rules, and an adapter is structure; an interface, factory, and implementation class for one behavior is scaffolding.
  • Documentation can be runtime data (a CLI help text, a schema description), and a comment can change what a tool does even though the compiler or interpreter ignores it. A doc-only guard that compares executable code cannot see those consumers.
  • Language-specific gotchas are in the language reference.