Installs into .claude/skills of the current project.
Are you the author of Code Law?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/alleneubank-code-law)
---
name: code-law
description: Use when writing or changing code in any language. Not for prose, docs-only, or config-only changes.
---
# Code Law
The craft law for code. `AGENTS.md` keeps the law whose violation is
unrecoverable — boundary, secrets, publish, done-claims.
This skill carries the law whose violation the harness and behavior-first
verification catch.
**A property you want held is made unwritable first, detected second.**
Before reaching for any check, shape the code so the defect cannot be written:
a type the wrong value cannot inhabit, visibility that hides the wrong call, a
module boundary, the invariant kept with the state it protects, a capability
that only the authorized scope can obtain. Correct types are the proof. They
need no checker, fixture, or mutation run to show that they hold. Choose this
even when a check would be quicker to add today.
Where construction cannot hold a property, a sentence saying "be
deterministic" still changes nothing; a floor that reddens on a violation is
what holds. A floor is a runner setting where one exists (network off,
parallel random order), one-off evidence where it needs judgment, and a
permanent test only when it meets the bar in `testing-best-practices`.
What construction cannot hold goes to the cheapest existing check that can
express it: the compiler, then rules an existing linter already ships, turned
on and configured, then a behavior test. Craft rules a mainstream linter or
compiler already checks (function length, complexity, warnings as errors,
unchecked results, unused values) are enforced through that tool's
configuration. A project never writes its own checks over its repository,
whether they read source or artifacts such as schemas, manifests, plans, and
docs: no analyzer scripts, linter plugins, custom rule files, or checkers
that re-judge an artifact. Data that crosses a boundary has one typed
definition that each reader parses at its boundary; its test feeds vectors
written from the spec through the product's own parser. A second hand-written
interpreter of the same data is a duplicate, not independence. A finding that
something is not typed is answered with a type. A check that only confirms a
file or string changed prevents no defect and is not added. When no rung can
hold an invariant, because the defect is rare, hard to enforce, or impossible
to catch, a waiver comment at the site says it is unchecked; it does not
become a review deliberation or a contrived example. The waiver is the last
rung, never a shortcut past a redesign. A recurring finding is answered by a
redesign that makes its class unwritable. Retire a check when its defect can
no longer be written or another check covers it.
## Craft
- Write for a human maintainer who has neither this conversation nor the
author. Reading healthy code should be almost as easy as reading prose in
the reader's native language; a reviewer who struggles for more than a few
seconds has found a defect in the code, not in themselves. Nothing from the
task leaks in: no names, comments, or structure that refer to the request,
the conversation, the change's history (`new`, `v2`, `fixed`, `updated`,
`refactored`), or the author's reasoning. When a plain form and a clever
form are both correct, the plain form ships.
- Types first: define types and data models before logic; make illegal
states unrepresentable; schema changes drive implementation.
- Assert the invariants code relies on. Programmer errors (violated
invariants) are asserted and crash; operating errors (bad input,
timeouts) are handled and reported — never confuse the two. Assertions
stay on in production: a failed assertion converts a correctness failure
into an availability failure, which is the cheaper of the two. The unit
that crashes is the smallest restartable unit that shares no mutable state
with its neighbours (a request, a feature, an actor); where no such unit
can be named, it is the process — catching a panic inside shared mutable
state is not recovery, it is corruption. Assert positive and negative
space; pair assertions across independent points; prefer the cheaper rung
(compile-time > runtime > test).
- Put a limit on everything. Every input has a named bound decided at design
time — message size, batch size, connection count, queue depth — and every
loop, queue, buffer, cache, retry, and recursion carries an explicit bound
derived from it; intentionally infinite loops assert it. When the honest
bound is "the disk" or "the user", pick a number anyway: exceeding a bound
is an operating error the caller handles, never a growth event. Where the
inputs are bounded, capacities follow from them, so long-running services
and data planes size their structures from the bounds at startup and do not
allocate on the hot path; a structure that is full says so instead of
growing.
- Optimize for the reader's cognitive load. Keep each function at one level of
abstraction; order statements with their data flow; keep relevant details
close and hide only details whose abstraction reduces what the reader must
hold. Abstraction and indirection are costs too, so add them only when the
resulting contract is clearer.
- Prefer immutability and pure functions; isolate side effects at system
boundaries; push `if`s up and `for`s down — parents own control flow and
state, leaves stay pure. Every branch accounts for its complement: a guard
clause is right for an exceptional path or short case when the guard itself
handles or asserts the negative space; use `else` when complementary
branches are both core logic; an `if` whose other case nothing handles,
asserts, or rules out by type is the defect. Break up nested control flow
and mixed boolean expressions into named decisions.
- Errors are handled or propagated, never swallowed. Keep the failure region
narrow: wrap only the operation whose failure is handled, catch only the error
meant to be handled, and preserve the original cause when adding context.
Prefer a specific check to a catch-all: catching every error and continuing
is rarely safe, and a broad handler behind your own specific check is dead
code that hides the next failure.
Validate at system boundaries and only there — a check protecting a state an
upstream boundary already guarantees is deleted, not kept for safety. Publish
externally visible state only after every fallible operation succeeds, so a
failure leaves the prior state intact. External calls carry explicit timeouts
and bounded retries with backoff; handle edge cases explicitly.
- Refactor with clean breaks: update all callers, complete the migration,
delete superseded code — supersession is the default; confirm
replace-vs-add in one line only when genuinely ambiguous. When existing
structure fights the requested change, make a behavior-preserving prefactor
unit first when it can stay independently green and focused. Local unpublished
work may be regrouped this way; changing declared scope, rollout, or a
published contract still requires confirmation. An unshipped feature has no
compatibility surface: its experimental behavior is not a contract, so it
earns no shim, no dual path, and no deprecation window. Tests that redden
under a supposedly behavior-preserving refactor are first suspected of having
caught a regression it introduced; only change detectors, as
`testing-best-practices` defines them, are part of the migration.
- Name clearly and precisely at the code's abstraction level. Omit type words,
surrounding context, and filler the reader already has; prefer a longer name
to an ambiguous one. Established abbreviations such as `RPC` are useful when a
future reader in the domain will understand them; expand obscure or local
abbreviations. Name booleans positively, use long-form flags, and put units and
qualifiers last by descending significance (`latency_ms_max`).
- Comments earn their place by explaining intent, rationale, non-obvious
constraints, or an apparent rule violation. Preserve comments that protect an
ordering or safety constraint. Replace narration of what code does with clearer
names or structure, and replace enforceable assumptions with assertions.
Public API comments state the contract, not implementation details.
- Declare variables at the smallest scope, computed closest to use. A value the
environment, operator, or caller must choose belongs in configuration; a fixed
domain invariant belongs in a clearly named constant or type.
## Interfaces and abstraction
- Prefer platform-supported design over a custom wrapper, and derivation from a
source of truth over cached local state and the cleanup machinery it needs.
- Make the correct call easy and misuse hard. Prefer compiler-enforced contracts,
then runtime checks, then documentation. Return fully initialized values, use
domain types for constrained values and units, and group parameters that form
one concept into a value object. Put lifetime collaborators in construction and
pass per-call work to the operation.
- Choose defaults by the cost of a mistake. Destructive or irreversible behavior
is explicit opt-in; environment-specific targets that cannot be chosen safely
are required rather than guessed. A convenient default is valid only when its
accidental use is acceptably safe.
- Deduplicate when evidence shows one shared concept that should evolve together,
not merely identical syntax today. Keep coincidentally equal domain rules
separate; when evidence is insufficient, tolerate duplication until the common
abstraction becomes clear. Build for current and planned use cases, not merely
possible ones. A test seam is justified by its real consumer, but speculative
generality is not.
- Wrap an external API when doing so clearly bounds change or gives the domain a
better contract; do not wrap familiar standard types by reflex.
- When a CLI, API, control plane, or automation surface is operated by agents,
load `agent-operability`. Its inspect/plan/apply/verify contract is the public
expression of this skill's deterministic, idempotent, atomic, observable,
evented, and contextual properties—not a second implementation path.
## Threat model
Security-relevant code is written and reviewed against a stated threat model.
A bug is a vulnerability only when it lets an actor do something they could
not already do. Unless the project's SPEC says otherwise, the default model is:
- **Defended against:** network attackers (DNS, routing, Wi-Fi, on-path
interception), malicious remote peers, and untrusted input: protocol data,
files, and content the software did not author.
- **Trusted:** root, the user's own account, every other local account on the
host, physical access, the OS and its configuration, and installed
executables and dependencies. An actor with shell or disk access already
owns the machine, and no check in the software changes that.
Hosts are single-tenant. Shared hosts, group-writable package prefixes
(Linuxbrew), permissive ACLs, and other weaker setups a user chooses are that
user's risk and a documented limitation, not a design input. Do not add
ownership, mode, or parent-chain checks whose only purpose is to stop local
accounts.
A project tightens the default in its SPEC when the software crosses a local
privilege boundary: setuid programs or root daemons, multi-tenant services, or
anything acting for a less-privileged local principal. The SPEC names the added
actors, and the rest of the default stands.
A review finding that needs a trusted actor is answered by citing the model.
It does not block, does not count as a second instance of a defect class, and
needs no waiver. The driver may still take a trivial in-scope fix as ordinary
hardening. The model itself can be challenged: a reviewer who argues it is
wrong for the project takes that to the human.
## Properties
Apply each property where its failure mode exists; non-applicability needs no
waiver. Builds and deterministic test harnesses owe determinism, hermeticity,
and isolation. Retried effects, reconcilers, and at-least-once consumers owe
idempotency. Operations that can expose partial state owe atomicity. Side-
effecting and live surfaces owe observability. Async coordination should be
evented, and authority-bearing effects should be contextual. When an applicable
property cannot hold, carry a waiver.
### Deterministic — same inputs, same outputs, same interleaving
Sources of drift: wall clock, unseeded randomness, UUIDs, map and set
iteration order, `readdir` order, thread scheduling, locale, timezone, hash
seeds. The fix is always the same shape: inject the clock, the seed, the id
generator, the scheduler. Never reach for them ambiently.
**Floor:** two checks, and conflating them is the common mistake.
*Repeatability* — run twice with the **same** seed and diff; any difference
is nondeterminism. *Order-independence* — vary only what must not matter
(runner shuffle seed, thread scheduling, map iteration order) and assert the
result is unchanged. A seeded algorithm legitimately produces different
output under a different **algorithm** seed; that seed is an input, so
changing it proves nothing. For engines with a fixed input set, a seeded
simulator whose emitted trajectory is compared against a golden run.
### Hermetic — depends only on declared inputs
Failures: tests that reach the network, tools resolved off `PATH`, absolute
paths, implicit environment variables, host state. Determinism says the same
inputs give the same output; hermeticity says the inputs are all declared.
Reproducible = hermetic + deterministic.
**Floor:** run with the network off, a scrubbed environment, and a foreign
`$HOME`.
### Idempotent — applying twice equals applying once
Three shapes people conflate: the idempotent operation (`f(f(x)) == f(x)`),
the convergent reconciliation (desired state, applied to a fixpoint), and
exactly-once effects via an idempotency key. Its sibling is **atomic**:
idempotency covers the re-run, atomicity covers the interrupted run —
write-temp-then-rename, all-or-nothing apply. Neither alone is crash-safe.
**Floor:** run it twice; assert the second run is a no-op and the diff is
empty. Then kill it mid-apply and run it again.
### Isolated — no interference between concurrent instances
Own tmpdir, own port, own database, own fixtures. Distinct from hermetic:
hermetic is no external dependence, isolated is no inter-instance
interference. A flake that root-causes to shared state between tests is an
isolation defect, not a flake.
**Floor:** run the suite in parallel and in random order.
### Observable — the failure is diagnosable from artifacts alone
Emit structured fields rather than prose-only log lines, with enough context to
identify the failing operation and state.
**Floor:** cause the failure, then diagnose it using only the emitted logs,
metrics, and traces — without re-running and without adding instrumentation.
### Evented — react to state change, don't mistake time for state
Wait on a condition, not a duration. A duration-only sleep used as proof that a
condition now holds is the defect. Bounded backoff before rechecking a condition
is valid when no event source exists; the condition and deadline, never elapsed
time alone, decide success. Two pairings matter more than the property itself:
- **Level-triggered, edge-woken.** Pure edge-triggered systems drop events
and never recover. The event says *when* to reconcile; the desired-state
comparison decides *what* to do.
- **Delivery is at-least-once, so consumers must be idempotent.** Evented
without idempotent is a duplicate-effect bug waiting for its first retry.
**Floor:** reject duration-only success; assert the condition and a deadline.
Where polling is unavoidable, perturb the backoff and prove the same terminal
state.
### Contextual — the call carries what dictates its possibilities
A side-effecting function receives the context of the event that authorizes
it. One structure does four jobs: capability (what this call may do), scope
(deadline and cancellation), provenance (which event, on whose behalf), and
the fields its logs and spans carry. "Dictates the possibilities" is the
load-bearing part — the argument shape should make illegal *effects*
unrepresentable, which is types-first applied to effects and authority
rather than to data.
**Floor:** one assertion per job the context does, and the first is the one
usually missing. *Capability* — construct a context that withholds an
authority and assert the call fails rather than reaching for an ambient
fallback; without this, code that kept its ambient authority passes every
other check. *Scope* — assert cancellation propagates to the leaf.
*Provenance* — assert every record on the path carries the correlation id.
## Waivers
An instance that cannot hold a property, or an invariant no rung can hold,
carries a waiver comment naming the property or invariant, why this instance
can't hold it or why holding it costs more than the defect could, and whether
it is debt or a permanent exemption. For an
invariant, the reason names the design that would make the defect unwritable
and why it does not fit here. Debt names what would remove it.
```
// WAIVER(<property or invariant>): <why this instance cannot hold it, or why it is not worth holding>. <Debt — removed by X | Permanent — X makes it impossible.>
```
The `WAIVER(` tag is literal in every language's comment syntax, so one search
lists every waiver in a project. A waiver is written prose a maintainer can evaluate, never a suppression flag.
A waiver whose reason a redesign within scope would remove is a defect: ship
the redesign instead. The same invariant waived at a second site means its
class needs the redesign. A waiver list that grows without its debt entries
shrinking means the property was stated too strongly — weaken the property,
don't grow the list.