Use these portable rules directly for your codebase.
Portable agent rules — tests, code shape, design slop
Project-agnostic. Copy the three sections below into any repo's AGENTS.md (or CLAUDE.md, .cursorrules, CONTRIBUTING.md) and delete the framing. Nothing here names a product, a file or a dependency.
Numbers marked (tune) are defaults chosen to be uncomfortable enough to matter; adjust per project but keep them explicit.
1. Tests must prove something
A test is a claim about behaviour that could break, and whose breaking would be silent or expensive. Anything else is maintenance cost with no return.
Write the test when it pins:
A rule that came from measured or documented platform behaviour — the API that returns 401 for a missing permission, the record that arrives as 400 instead of 404, the date field that is epoch millis on one endpoint and ISO on another. These are exactly the facts a future refactor will "clean up" wrongly.
A boundary where the wrong answer is invisible: an empty result set, a cursor the API silently ignored, an empty response body, a timeout that returned partial data.
An invariant a future change could break without noticing: a write is never retried; two duplicated config blocks stay identical; a pure module never imports the platform.
Arithmetic whose error is subtle: contrast ratios, attempt counts, backoff steps, page arithmetic, currency rounding.
Do not write the test when it only:
Restates the body of the function under test.
Covers a trivial getter, setter or formatter with no branching.
Asserts a constant equals its own literal — unless other code depends on that literal, in which case it is a contract, not a constant.
Exercises framework, browser or vendor behaviour you do not control.
Repeats a path a behavioural test two lines up already covers, with no distinct failure mode.
The check: finish the sentence "this fails if …" with something a user would notice or a reviewer would miss. If you cannot, delete the test.
Two corollaries:
Prove a new gate fails before trusting it. Plant the violation, watch it fail with a readable message, remove it. A check never seen red is indistinguishable from one that cannot go red.
Test count is an outcome, never a target. One sharp test beats eleven shallow ones; the shallow ones make the real ones slower to run and harder to find.
2. Design pass before every commit
Walk the diff against these nine. A tripped item is fixed now, or deliberately accepted and recorded in the decisions log with the reason.
FactorWhat it looks likeThe fix
Implicit couplingA module keyed by a string another module must reproduce exactly (endpoint labels, storage keys, event names, message shapes). The two drift apart and nothing fails.Give the shared thing one exported identity and have callers reference it, never retype it. Validate at the boundary so a mismatch is loud.
Layer-first folderslib/ accumulating one flat pile of unrelated files because "that is where helpers go".Fine while small. Past ~5–7 files, group feature-first (lib/export/, lib/render/) instead of growing a utils.js.
DuplicationThe same rule stated twice — two limit ranges, two copies of a config block, one fact stored in two fields.One constant, one function, one owner. Where duplication is physically forced (JSON config, CSS media query), add a test asserting the copies are equal.
Representable illegal statesA result object where status: 200 with kind: 'error' is constructible; flags that can disagree with the data they describe.Derive one field from another instead of storing both. Make the shape per-variant: each error kind carries only the fields it needs.
God filesAny file approaching ~400 lines (tune), or a file with more than one reason to change.Split along the seam that already exists, before it becomes urgent. A test can hold the line so it fails instead of drifting.
Shotgun surgeryOne conceptual change touching many files — "add an endpoint" edits five places.Introduce the missing registry or seam so the change has one home.
Layering violationsA pure layer importing the platform (chrome.*, window, fs), a renderer calling the network, UI reading credentials. Mocked platforms hide this, so behavioural tests still pass.One layer owns the platform and the network; everything below takes injected dependencies. Add a source-level check, because mocks make this invisible at runtime.
Pass-through wrappersA function that forwards arguments to another and adds no policy.Delete it, or give it a reason to exist: timeout, retry, logging, normalising, validation.
Errors / validation not at the boundaryNull checks scattered through renderers; the same payload shape guessed in three places; raw vendor objects flowing inward.Validate and normalise once, at ingest: external response → internal shape at the API boundary, user input → typed value at the UI boundary. A renderer checking for undefined is evidence the boundary did not do its job.
3. Design slop indicators
The marks of generated filler. Any one makes a purpose-built tool look like a template; several together make it look dishonest about how much thought went into it. Check the diff against this list in the same pass as section 2.
IndicatorWhy it reads as generatedWhat to do instead
TagsA pill or badge on every field — category, count, host, source, type — so the row becomes a row of chips. Decoration standing in for hierarchy: if everything is tagged, nothing is prioritised.Plain text for labels and values. A pill only for state that changes and that the user acts on (running, complete, failed). Never two pills for one fact.
Italicised wordsItalics as the emphasis of last resort, sprinkled through labels and helper text to make flat copy feel designed.Weight and colour carry emphasis. Italic is reserved for quoted user content — that is data, not styling.
Display serif + negative kerningThe strongest single tell: an editorial display serif (Instrument Serif, Playfair, Fraunces) imported into a data utility, with tracking crushed to −0.03em or tighter to imitate print polish. It trades legibility for a mood the product has not earned.One bundled, legible typeface. Heading tracking no tighter than −0.01em, body at 0.
Over the top detailed UIShowing every computable number because it can: nine statistics, four progress readouts, a log tail. A utility is judged on the one task it exists for.The primary surface shows state, one count, the destination, one action. Detail belongs in the report, not the surface used while working.
DividersA hairline between every row and section until the surface reads as a ruled spreadsheet.Spacing separates. A border appears only where a region genuinely ends or a card needs an edge.
Multiple colour shades and opacitiesFive blues, three greys per surface, stray rgba() in components, tinted backgrounds that encode nothing. A palette that cannot be named is not doing work.Status colour means exactly one thing. Everything else is a token. Opacity is for disabled state only. A genuinely new shade becomes a token before it becomes a value.
Containers with bordersEvery group wrapped in a bordered box, then those boxes nested inside another. Boxes used as layout, hiding a spacing problem behind a rectangle.One card where a group is genuinely a unit. Nesting is usually a layout problem wearing a box.
DiagramsA diagram in the product UI: boxes and arrows describing the pipeline, the more detailed the worse. They date instantly, contradict the code within a release, and cannot be read at a glance.If the flow cannot be told in one sentence plus a progress bar, the flow is the problem. Diagrams belong in docs — hand-drawn if anywhere — not in the product.
Section numbering"01 — Setup", "02 — Connect": numbers nobody will cite, implying a procedure where there is only a page.Numbered steps only when the order is mandatory, and then they are steps, not sections.
A dense interface can look busy — that is data, and it is welcome. Busy from styling is not the same thing.
Wiring it up
Put sections 1–3 above your project-specific rules, or below them if the project has binding design authority. If a project has a design doc that governs tokens and values, state explicitly that the design doc wins on values and any conflict is raised, not decided.
Keep a docs/decisions.md (numbered, dated, append-only, superseded rather than edited) and a docs/learning.md (rule-shaped lessons: what bit you and the rule it produced). A rule that lives only in a chat transcript is lost.
Update the docs in the same commit as the code they describe. A stale doc is a defect.
Record deviations instead of breaking the rules silently — a rule without an escape hatch gets ignored wholesale.
One-paragraph version
For small repos where a single page is enough:
Write a test only when it pins measured platform behaviour, an invisible boundary, an invariant a future change could break silently, or arithmetic whose error is subtle — and be able to finish "this fails if …" with something a user would notice. Never test that a function returns its own body. Before each commit, check the diff for implicit coupling (a string two modules must retype), god files, duplication, representable illegal states, layering violations, and validation that happens below the boundary. Check the UI for the marks of generated filler: a pill on every field, decorative italics, a display serif with crushed tracking, every number shown because it can be, hairlines between every row, a palette of unnamed shades and opacities, nested bordered boxes, diagrams in the product, and 01/02 section numbering. A dense interface may be busy from data; busy from styling is not the same thing.
Will this hurt Beehiv's bottom line, mostly not as enterprises need solutions that work and they can get fast support regardless of pricing. They will never switch over to a vibe coded alternative, but for normies like us, oh boi we are in for a treat.















