Version History
What changed in each version of the standards, and exactly what a project has to do about it. Written for the AI agent doing the update.
Every change to the standards gets a new version number. Each project records the full version it last applied in its AGENTS.md, under "Code standards", so the project pairs with the package and knows where to start:
**Code standards version:** 3.2.2
How versions are numbered
| Part that changes | Example | Means |
|---|---|---|
| First | 3.2.2 → 4.0.0 | A major update: it changes how most of a project's code is written, so every project reworks it |
| Middle | 3.2.2 → 3.3.0 | Files move: the update moves or renames files in a project. Plan it, and take a regression baseline first |
| Last | 3.2.2 → 3.2.3 | Changes inside files only. Nothing moves |
The first number goes up only when most projects must rework most of their code, such as a new framework or a standard replaced by a different one. A rule added, changed or clarified never bumps it. Every new number can ask for work, whichever part changed. 3.1.1 and 3.2.1 came out before this rule and moved files in a last-number update; their moves are listed under Structural changes.
Updating a project to a new version
Read only the entries newer than the project's version. A project on 3.2.1 updating to 3.3.0 reads the 3.2.2 and 3.3.0 entries on this page, and nothing else. A project that recorded only a major number, such as 3, first finds the full version it actually applied: the @activatus/code-standards version in pnpm-lock.yaml at the last commit that changed its **Code standards version:** line. Record that version in AGENTS.md and start from there. Only when it cannot tell does it read every entry of that major version from its start:
git show "$(git log -1 --format=%h -G'Code standards version' -- AGENTS.md)":pnpm-lock.yaml \
| grep -o "@activatus/code-standards@[0-9.]*" | head -1
Do not re-read every standard or check the whole codebase: each version lists every change that matters, and how to find the code it affects.
- Find the project's version in its
AGENTS.md. If it is only a major number, find the full one first, as above. If there is none, the project has never followed the standards: follow Setup instead. - Update the package:
pnpm add -D @activatus/code-standards@latest, never a typed version number. If pnpm refuses because the version is too new, add the package tominimumReleaseAgeExcludeinpnpm-workspace.yaml, as in Setup. The entry is the plain package name, with no version. If pnpm wrote one with versions in it, such as'@activatus/code-standards@3.0.4 || 3.0.5', replace it with the plain name: otherwise pnpm adds another version to it on every update. Then read this page fromnode_modules/@activatus/code-standards/docs/version-history.md. - Read each entry newer than the project's version, oldest first. For a middle-number entry, take the regression check baseline before any file moves. Skip any change to a standard the project opted out of: they are listed in
docs/development/coding-standards.md. - For each change, do the "What to do" steps, then run the "How to check" searches. A search that still finds something means the change is not finished. When a change says to copy a line from the
AGENTS.mdtemplate, and the project'sAGENTS.mdrecords the standards in its own words instead, write the updated rule there in the project's own shape. - Strike out what the standard now covers. After the changes, re-read the project's list of opted-out and changed standards in
docs/development/coding-standards.md. Take off the list any exception the new version has made part of the standard, and update any rule it records for an old shape of the standard. A differences list that still describes the old standard sends the next agent wrong. - Run
pnpm lint,pnpm format:check,pnpm testand the build after each change.format:checkchecks every file git does not ignore; never narrow it to changed files. - Ask any Working with the agent question whose answer is not recorded in
docs/development/coding-standards.md, under "Details". - Run
pnpm standards:check, and fix what it finds. See Checking a project. - Set the version in
AGENTS.mdto the installed package's full version once every change is done.
A project already on the installed full version has no version changes to make: follow Already on this version.
When a change does not fit the project, for example it renames a folder the project does not have, skip it. If you are not sure, ask; do not guess.
Adding a version
Anyone changing the standards adds a version, alongside the change:
- Pick the number from How versions are numbered. A major update is
4.0.0. An update that moves files bumps the middle number,3.2.2→3.3.0. An update that only changes the inside of files bumps the last,3.2.2→3.2.3. Set it inpackage.json, then publish it to npm (npmjs.com) withpnpm publish. pnpm is the tool; npm is where the package lives, and projects install it from there withpnpm add. - For every change, write: what changed, why, what to do in a project, and how to check it is done. Write it for an agent that has not read the rest of the standards.
- If a project has nothing to do, say so.
- Newest version at the top.
- Work in progress on a major update goes under
## Version N — not released yet, with a line saying projects should not apply it yet. Changes can be added to it over time. To release it: drop "not released yet", add the Released date and Updates from line, setpackage.jsontoN.0.0, then publish. - Changing a released major version goes under its version, newest at the top. Update the version's changes in place where they are now wrong. Write each entry so a project already on the version before it knows what to do.
- Every entry is headed
**Fixes in 3.2.3.**, with its number, followed by "If the project already applied 3.2.2, check these:". - A middle-number entry adds
**Structural changes in 3.3.0.**straight under its heading, listing each change that moves or renames files in a project, with a rough size, such as "one move per file", so a project can plan the work and take a regression check baseline first. A last-number entry never moves files. - Anything a version can change in the
AGENTS.mdtemplate lives between thecode-standardsmarkers. A project copies only that section on an update, andpnpm standards:checkcompares only that section, so a rule outside the markers never reaches a project that is already set up. pnpm devtestmust pass before publishing. Run it, thenpnpm publish. It checks the lint packages are the latest a project would install, then runs the tests, lints and formats every template the way a project does, and checks formatting, types and the site build.- Every release commit is titled
docs: version N.N.N — <what changed>, such asdocs: version 3.2.2 — full version numbers, so releases are easy to find in the git log. - Every number is published. Publish each one to npm as soon as its entry is written. A Fixes entry for a version that was never published sends a project to install something that does not exist.
- Judgement calls after release are fixes, not workarounds. When a project's update needed a call the steps did not cover, add it under the version's Fixes so every other project gets it too. A project should never keep a private workaround for an unclear step.
Version 3
Released: 2026-10-17 · Updates from: version 2 · Latest: 3.3.6
Install it with pnpm add -D @activatus/code-standards@latest. Do change 1 first: it says which parts of the project's AGENTS.md are now the project's own to write.
Fixes in 3.3.6. If the project already applied 3.3.5, check these:
-
The templates are now formatted with the template Prettier config. The import sorting added in 3.3.3 reordered the imports in the four
eslint.config.mjstemplates, so afterpnpm formata project's copy no longer matched the template, andpnpm standards:checkfailed standard 6.pageLayout.visual.ts, the docs site'sindex.tsx, and the tables in theontology.mdanddesign-system.mdtemplates changed the same way. Every template is now already formatted, and the code standards check this before each release, sopnpm formatleaves a copied template as it is.What to do: copy the project's ESLint template over
eslint.config.mjsagain, and, if the project has a regression check,tests/visual/pageLayout.visual.ts. Runpnpm format: neither should change. If the project recorded a standard 6 exception or an unfinished change only because the copied ESLint config was reformatted, remove it. -
The Stylelint templates ignore generated CSS. They only ignored
vendor/, sopnpm lint:cssalso linted.next/,out/,build/,coverage/anddocsSite/, and reported errors in CSS nobody wrote. They now ignore the same folders as the ESLint templates.What to do: copy the Stylelint template over
stylelint.config.mjsagain. If the project added a.stylelintignorefor those folders, delete it, and remove its line from "Unfinished changes" indocs/development/coding-standards.md.
How to check: pnpm format changes no copied template; pnpm standards:check passes, or lists only recorded exceptions; pnpm lint:css passes with no .stylelintignore.
Fixes in 3.3.5. If the project already applied 3.3.4, check these:
-
pnpm standards:checknow reports a missing Stylelint config. Standard 8 covers all hand-written CSS, and a Tailwindglobals.cssis hand-written CSS, but the check only looked atstylelint.config.mjswhen there was one. A project with a.cssor.scssfile outsidevendor/anddocsSite/, and nostylelint.config.mjs, now fails standard 8. See Stylelint.What to do: if the project has no
stylelint.config.mjs:- Install the packages from the Stylelint page.
- Copy
static/templates/stylelint/css/stylelint.config.mjs, or thescssone for SCSS. - Add the
lint:cssandlint:css:fixscripts from the Stylelint page, runpnpm lint:css, and fix what it finds. - If
docs/development/coding-standards.mdsays Stylelint was set up, check that it really was.
How to check: pnpm standards:check passes, or lists only recorded exceptions; pnpm lint:css passes.
Fixes in 3.3.4. If the project already applied 3.3.3, check these:
-
The config templates now pass the template ESLint config.
prettier.config.mjs,next.config.mjs, bothstylelint.config.mjstemplates and the regression check'splaywright.config.mjsstarted with a one-line/** @type … */comment, whichunicorn/single-line-block-comment-stylerejects. They now use a JSDoc comment over three lines, with no*at the start of each line, which TypeScript reads the same way. The regression check'splaywright.config.mjsandpageLayout.visual.tshad three more lint errors, now fixed. The code standards now lint every template the way a project does, with the latest lint packages, before every release, so this cannot ship again.What to do: copy each of these templates the project uses over its own again:
prettier.config.mjs,next.config.mjs,stylelint.config.mjs, and, if the project has a regression check,tests/visual/playwright.config.mjsandtests/visual/pageLayout.visual.ts. If the project turned a lint rule off for one of them while waiting for this version, remove that fromeslint.overrides.mjsand fromdocs/development/coding-standards.md. -
Tests mock
server-only.import "server-only";always throws outside a Next.js build, so any Vitest test that imports a server-only component fails before it runs. See Importing components.What to do: if any file imports
server-only, addvi.mock("server-only", () => ({}));to the Vitest setup file, and make surevitest.config.mjslists that file insetupFiles. Copy the Code standards section ofAGENTS.mdfrom the template again.
How to check: pnpm lint passes with no rule turned off for a template; pnpm test passes; pnpm standards:check passes, or lists only recorded exceptions.
Fixes in 3.3.3. If the project already applied 3.3.2, check these:
-
The Ontology and Documentation sections of
AGENTS.mdare now inside thecode-standardsmarkers. They were above them, so copying the Code standards section on an update never brought them in, andpnpm standards:checknever noticed a project without them. 3.3.2's "Record a check only after it has run" rule was one of the rules that never arrived. Anything a version can change now lives inside the markers. The Documentation section's line aboutdocsSite/now starts "If the project has adocsSite/".What to do: copy the Code standards section of
AGENTS.mdfrom the template again: everything from<!-- code-standards:start -->to<!-- code-standards:end -->. It now ends with the Ontology and Documentation sections. Then delete the project's old Ontology and Documentation sections above the markers, so neither appears twice. Anything project-specific in them moves to a section of its own, outside the markers. -
A section whose pages already share a folder needs no route group. When all a section's pages sit under one URL folder, such as
app/orders/[id]/, that folder is the section, and components three or more of its pages use go in its owncomponents/folder. Never move pages just to make a route group. See Where components live. Nothing to do. -
Prettier sorts imports. The Prettier template adds
@ianvs/prettier-plugin-sort-imports: Node's own modules, then packages,@/aliases,../, then./, with a blank line between groups. Directives such as"use client"stay at the top, type imports stay on their own line, and side-effect imports, such as CSS, never move. Nobody sorts imports by hand. The seven-step order on Import Organization is gone. See Import sorting.What to do:
pnpm add -D @ianvs/prettier-plugin-sort-imports.- Copy
static/templates/prettier.config.mjsover the project's again. A project without Tailwind removesprettier-plugin-tailwindcssandtailwindStylesheetas before. - Move each side-effect import, such as
import "./globals.css";, below the file's other imports. - Run
pnpm format, and commit the result on its own, with nothing else in it, so the reordering never hides a real change in a review.
-
Components are imported from their category.
components/ui/,components/layout/andcomponents/shared/each have anindex.ts, and code outside a category imports from it:import { Button } from "@/components/ui";. There is nocomponents/index.ts, andpnpm standards:checknow reports one. A category'sindex.tsexports only components a Client Component can import: a Client Component that imports a category pulls in everything it exports, so one server-only component in it breaks the build. A component that reaches server-only code, such as authentication, the database, secrets ornext/headers, starts withimport "server-only";, is left out of theindex.ts, and is imported from its own folder, such as@/components/layout/siteHeader. Tested with a Next.js 16 build: the build fails with a server-only component in the category'sindex.ts, and passes once it is imported from its own folder. See Importing components.What to do:
- Delete any
components/index.ts, and point its imports at the category:@/components/ui,@/components/layoutor@/components/shared. - Give each category an
index.tsif it has none. - For each component that reaches server-only code, add
import "server-only";at the top, take it out of its category'sindex.ts, and import it from its own folder. - Copy the Code standards section of
AGENTS.mdfrom the template again, if not already done for the first fix above.
- Delete any
-
"Unfinished changes": write "None", with no note after it. 3.3.2 said "exactly None", but the check also accepts "None." with a full stop. The template now says what the check does. Nothing to do.
How to check: pnpm standards:check passes, or lists only recorded exceptions; AGENTS.md has one Ontology and one Documentation section, both inside the markers; pnpm format:check passes; git ls-files components/index.ts finds nothing; no category's index.ts exports a file that grep -rln "server-only" components lists.
Fixes in 3.3.2. If the project already applied 3.3.1, check these:
-
Only well-formed lines under "Details" count as exceptions.
pnpm standards:checkused to read any line under "Details" indocs/development/coding-standards.mdthat started with a number as an exception to that standard, so a plain numbered list switched standards off without a word. Now a line counts only in the shapeN. Name — skipped: reasonorN. Name — changed: reason, with the number and name from the setup list. Any other line that starts with a number fails the check, and a misread line can never be hidden as an exception itself.What to do: run
pnpm standards:check. For each line under "Details" it reports, write it in the exception shape, or turn it into a bullet point that does not start with a number. Copy the "Details" and "Unfinished changes" notes fromstatic/templates/docs/development/coding-standards.mdif the project's still has the old ones. -
An exception can cover only some files. Name them in backticks after
for, as in the setup example. The check then hides findings for those files only.**matches any depth,*stays inside one folder, and a path ending in/covers the whole folder. An exception that names no files still covers the whole standard. Aforwith no path in backticks fails the check, because it would otherwise cover every file.What to do: nothing, unless the project skipped a whole standard only to cover some files. Then narrow that exception to the files, with the person's yes.
-
AGENTS.mdand the agent skills are Prettier-ignored. The check compares them word for word with the templates, and Prettier reformatted them, so a formatted project always failed standard 21. There is now a.prettierignoretemplate: see What Prettier skips.What to do: add these lines to the project's
.prettierignore, then copy the Code standards section ofAGENTS.mdand the skills from the templates again, so Prettier's changes to them are undone:.prettierignoreAGENTS.md.agents/skills/.claude/commands/If the project wrapped the section in
<!-- prettier-ignore-start -->and<!-- prettier-ignore-end -->to work around this, remove those two comments. -
components/holds onlyui/,layout/andshared/, as Where components live already said.pnpm standards:checknow reports any other folder, and any component file, directly incomponents/.What to do: run
pnpm standards:check, and move each folder or file it reports into the home that fits:components/ui/,components/layout/,components/shared/, or thecomponents/folder of the page or section that uses it. -
Only
r,sand shared values go inlib/constants/. Every link still goes inrand every piece of text people see or hear ins, so links change in one place and the text can be translated. Every other value, such as a number, list, status or internal string, now lives at the top of the file that uses it until more than one file needs it, then moves to the file inlib/constants/that fits. Forcing a value only one file uses into a global file sent readers across the project to find it.lib/constants/may hold any file named after what it holds, such as the newstatus.tsfor statuses the app shares; onlyroutes.ts,strings.tsandindex.tsare required, and every file in it is exempt frommax-lines. A constant kept at the top of its file is typed from that file's own.types.ts;lib/types/is for the shared constants. See Constants.What to do: nothing has to move. Copy the Code standards section of
AGENTS.mdfrom the template again, and copy the project's ESLint template overeslint.config.mjsagain: themax-linesblock now readslib/constants/*.ts. A value moved intolib/constants/only because the old rule said so may move back to the file that uses it when that file is next changed. Remove any comment that names a "Details" entry for a value that fitted no file: such a value now goes inunsortedValues.ts, below. -
When it is not clear where a value goes, it goes in
lib/constants/unsortedValues.ts. The agent carries on, and at the end of the work asks the person where each value in it goes, then moves each one and deletes the file.pnpm standards:checkreports the file for as long as it exists. See When it is not clear where a value goes.What to do: nothing. Copied in with the
AGENTS.mdsection above. -
Record a check only after it has run. A doc never says a URL, commit, date or "passed" for a check that has not happened yet. Write what was decided, and add the result once it exists. The
AGENTS.mdtemplate, Docs and Regression Checks say so.What to do: read
docs/testing/regression.mdand any other doc that records a check. Remove any result, URL or commit for a check that has not actually run. Copied intoAGENTS.mdwith the section above. Changed in 3.3.3: the rule was in the template's Documentation section, outside the markers, so copying the section did not bring it in. Do the 3.3.3 step. -
Comparing two local servers: copy
node_modules, never symlink it. Turbopack refuses anode_modulesthat points outside the project folder. On macOS,cp -Rcmakes a copy-on-write clone in seconds. See Comparing two local servers instead. Nothing to do. -
"Unfinished changes" says exactly "None" when nothing is left. A note after it, such as "None. ESLint was switched on in an earlier ticket.", reads as an unfinished change. The template and the check's report now say so.
What to do: if the project's "Unfinished changes" says "None" with a note after it, leave only "None". The history belongs in the change that did the work.
How to check: pnpm standards:check passes, or lists only recorded exceptions; .prettierignore lists AGENTS.md, .agents/skills/ and .claude/commands/; grep -rn "prettier-ignore" AGENTS.md finds nothing; pnpm format:check passes; lib/constants/unsortedValues.ts does not exist.
Fixes in 3.3.1. If the project already applied 3.3.0, check these:
-
A single number or string needs no type of its own. 3.3.0 said every constant gets a type from
lib/types/. It means every object and list constant:const COOKIE_DURATION_DAYS = 30;is already exactly typed. The Animation and Artwork examples now have their types. Nothing to do, unless the project added: numberor: stringonly to follow 3.3.0: those can stay or go. -
The
readonlysearch also findsstring[],number[]andboolean[]lists. 3.3.0's search only found named types.What to do: run the new search in 3.3.0's How to check, and make each hit that a constant uses
readonly. -
The regression baseline comes from the last known-good page. That is the live production site, unless the branch's page has already changed from production, such as a redesign not yet live: then it is the branch's last commit before the refactor, built and served on its own. Record where the baseline came from in
docs/testing/regression.md. See Regression Checks. The comment and error message in theplaywright.config.mjstemplate say the same; a project's frozen copy need not change.What to do: if the project's
docs/testing/regression.mddoes not say where its baseline came from, add it: the production URL, or the commit and the date. -
AGENTS.md: anas constthat cannot be avoided is one of the allowed lint disables, besideletand a type cast, as 3.3.0 already said. Copy the Code standards section from the template again. -
3.3.0's What to do had a step joined onto the end of a list. "Copy the TypeScript skill again" and the disable-with-reason line now sit in their own paragraph. Nothing new to do.
How to check: the 3.3.0 How to check searches find nothing; docs/testing/regression.md says where the baseline came from.
Fixes in 3.3.0. If the project already applied 3.2.2, check these:
Structural changes in 3.3.0. These move files. Plan them, and take a regression check baseline first:
-
Every type a constant needs moves into
lib/types/, with the types it depends on: one move per type. A component'stypes.tsleft empty is deleted. -
Every
as constvalue found outsidelib/constants/moves into the constants file that fits, with its type: one move per value. These stay where they are:- values in tests
- internal strings, as a named constant at the top of the file that uses them
- a class name only one file uses
- numbers in config files
-
Constants get real types, from
lib/types/. Every object and list constant exceptrandsis typed where it is declared, with a type fromlib/types/. Changed in 3.3.1: a single number or string needs no type of its own. "lib/constants/never imports fromlib/types/" is withdrawn: it only ever guarded against a loop of types, whichimport typeremoves at build, and it pushed projects intoas constand into retyping values wherever they were read.lib/types/now reads a constant's type withimport typestraight from its file, never through@/lib/constants. See Types for constants.What to do: change every import in
lib/types/from@/lib/constantsto the constant's own file, asimport type. Give every object and list constant exceptrandsa type fromlib/types/, in files named by topic, and move the types it uses there with what they depend on. Point imports at@/lib/types, delete componenttypes.tsfiles left empty, and remove imports left unused. Then remove the workarounds: values retyped where they are read, test values retyped or bent around missing properties. Make every list in a constant's typereadonly, including lists inside an item. Copy the Code standards section ofAGENTS.mdfrom the template again. -
No
as constanywhere. A written type is the contract; when the code needs exact values, they go in the type. Each constant's list type isreadonly. A list that must hold every id is checked with aRecordor a test. ESLint now reportsas constas an error:consistent-type-assertionsalways let it through, so the configs ban it withno-restricted-syntax. See Noas const.What to do: copy the project's ESLint template over
eslint.config.mjsagain. Replace eachas constwith a real type where the value is declared. Anas constvalue found outsidelib/constants/moves into the matching constants file with its type. These stay where they are, each given a type:- values in tests
- internal strings, as a named constant at the top of the file that uses them
- a class name only one file uses
- numbers in config files
Copy
static/templates/skills/typescript.mdinto.agents/skills/and.claude/commands/again. Where oneas constis genuinely unavoidable, disable the rule on that line with the reason. -
The regression test template has no
as const, and a frozen test may take a change that touches only types. A code standards update that changes only types may be applied to a frozen test with the person's yes. Nothing the test does changes, and the check must still pass against the existing baseline. See Regression Checks.What to do: if the project has a copy of
static/templates/tests/visual/pageLayout.visual.ts, copy the new one over it. In a project's own visual test, replace anyas constwith a real type. Run the regression check against the existing baseline: it must pass unchanged.
How to check: pnpm lint and pnpm typecheck pass; these searches find nothing:
# lib/types/ reading constants through the barrel
grep -rn 'from "@/lib/constants"' lib/types/
# as const anywhere in code we write
grep -rn "as const" --include='*.ts' --include='*.tsx' --exclude-dir=node_modules --exclude-dir=vendor .
This search lists types in lib/types/ with a list that is not readonly. Fix each hit that a constant uses; a type only props or parameters use can stay as it is:
grep -rnE "(^|[^y] )([A-Z][A-Za-z]*Type|string|number|boolean)\[\]" lib/types/
Fixes in 3.2.2. If the project already applied 3.2.1, check these:
-
The project records the full version.
**Code standards version:**inAGENTS.mdis now the full version last applied, such as3.1.0, not the major number alone, so the project and the package pair up and the next update knows where to start. Versions are now numbered by what they do: see How versions are numbered.pnpm standards:checkreads the full version, and flags a project that recorded only3, an older version, or a version newer than the installed package.What to do: if
AGENTS.mdrecords only a major number, find the full version the project applied from its lockfile, as in Updating a project to a new version, and start this update from there. Copy the Code standards section ofAGENTS.mdfrom the template again. When the update is done, set the version to the installed package's full version. -
The standards are the base, and agents build freely from there. The
AGENTS.mdtemplate now says so: most code is something the standards never mention, and the agent decides it and keeps it the same across the codebase. Only skipping, loosening or changing a standard needs the person's yes; the agent then records it and keeps going, never stopping to wait for a new version of the standards. Copied in with the section above. -
Where each constant goes is the project's call. The Constants table says what each file is for, not where every value in every project goes. 3.2.1's examples and check named values from one project; they are now neutral, and
pnpm standards:checkno longer guesses a value's file from its name. It lists the file, and the project decides.What to do: best practice is every value in
lib/constants/in the file the table says fits, with ids and CSS following the two rules below. Bring the project's values in line with that. A value that fits none goes in the closest file, with a comment naming its entry under "Details" incoding-standards.md. Changed in 3.3.2: onlyr,sand shared values go inlib/constants/; a value one file uses stays at the top of that file. Skip this step if the project is updating past 3.3.2. -
An id lives in the object that uses it. A value that picks which item something acts on is a key inside that object, never a loose constant. A list of keys a loop runs over stays a list. See the before and after.
What to do: move each loose id constant into the object that uses it.
-
CSS belongs in a stylesheet, never in a constant. Styles written as a string in TypeScript move to
globals.css. Styles for when JavaScript is off go there under@media (scripting: none). A media query the code reads, such as one an animation library matches, is not CSS: it stays in its object inanimation.ts.What to do: move any CSS string out of
lib/constants/and components intoglobals.css. -
sholds only text people see or hear. Internal strings, such as ids, keys, event names and log messages, are nots: each is a key in the object that uses it, or a named constant at the top of the file that uses it.What to do: nothing, unless the project moved internal strings into
s. Move those back beside the code that uses them. -
animation.tsandartwork.tsare split by one question. Where something ends up is artwork; how and when it gets there is animation. Drawing data the code reads goes inartwork.ts; a static drawing nothing reads goes in an.svgfile or an icon component.classNames.tscan hold a set of class names keyed by variant, named_CLASS_NAMES. Nothing to move unless the project placed a value by the old wording. -
The regression baseline comes first. Take it before any file moves, then do the constants moves and the folder moves as separate steps, with a full regression run after each. See Stage 0.
-
pnpm standards:checkreports headings for standards 11 and 13, which 3.2.1 printed blank. Nothing to do. -
Setup asks a fifth agent question: does the person want to be told about coding decisions the agent makes that the code standards do not cover? Someone who reads the code may want to hear each one; someone who does not may not care. See Told about decisions.
What to do: ask the person the question in setup step 4, and add the answer to the "Working with the agent" line under "Details" in
coding-standards.md. If they said yes, add the rule under Decisions in the Project workflow section ofAGENTS.md.
How to check: AGENTS.md records the full installed version; pnpm standards:check passes, or lists only recorded exceptions; these searches find nothing, or only values the project has recorded:
# CSS written in TypeScript, however it is quoted, on one line or many
grep -rnE "@media|@keyframes|!important|[a-z-]+: *[^;,]*(px|rem|none|auto|block|flex|grid);" \
lib/constants/ app/ components/ --include='*.ts' --include='*.tsx' --exclude='*.test.*'
# A loose id constant
grep -rnE "export const [A-Z_]+_ID = " lib/constants/
Fixes in 3.2.1. If the project already applied 3.2.0, check these:
Structural changes in 3.2.1. These move files. Plan them, and run a regression check first:
-
Every component
constants.ts, and every other file inlib/constants/, moves into the seven standard files: one move per file.pnpm standards:checklists them. -
Every sub-component folder that is not directly in its root component's
components/folder moves there: one move per folder. If the project did 3.1.1's move, only folders inside a grouping folder are left.pnpm standards:checklists them. -
lib/constants/has seven standard files:routes.ts,strings.ts,numbers.ts,noData.ts, and three new ones, created only once there is something to put in them:animation.ts: how and when things move.artwork.ts: what is drawn, where it ends up, and how it is grouped.classNames.ts: Tailwind class names shared between files, only when a component cannot hold them and they still repeat. A class name one file uses stays inline.
Each animation and artwork object stays whole, its values named by their keys. Never lift them out into
numbers.ts, which keeps loose single numbers only: lifting them out makes long names and splits one object across files. Each file is one file, never split by topic. See Constants.What to do: move every component
constants.ts, and every other file inlib/constants/, into the seven files. Where each value goes is the project's call: read the Constants table, decide each value's home. A value that fits none goes in the closest file, with a comment naming its entry under "Details" incoding-standards.md, and the work carries on. Ineslint.config.mjs, copy the template over it again: themax-linesblock now readslib/constants/{routes,strings,numbers,noData,animation,artwork,classNames}.ts. Copy the Constants rules intoAGENTS.mdfrom the template. Changed in 3.3.2: onlyr,sand shared values go inlib/constants/; a value one file uses stays at the top of that file. Skip this step if the project is updating past 3.3.2. -
A loop over a list looks each item up by its key. Write a key out by name when you mean one item,
r.plans.starter. Inside a loop over the list, use the item's key, typed fromr:keyof typeof r.plans. Delete separate lookup objects that map a key to its link:ralready is that lookup. See A list of items with links. -
pnpm standards:checkchecks more of the How to check lists: sub-components sit flat in their root component'scomponents/folder; the homepage is inapp/(homepage)/;docs/development/coding-standards.mdrecords the agent answers under "Details";docs/design/design-system.mdhas a filled-in Colour palette table; andlib/constants/holds only the seven standard files. Fix what it finds. -
Keep every docs folder. 3.2.0's rule is final: no docs folder is deleted, even one that does not apply yet.
How to check: pnpm standards:check passes, or lists only recorded exceptions; lib/constants/ holds only the seven standard files and index.ts; no component has its own constants.ts.
Fixes in 3.2.0. If the project already applied 3.1.1, check these:
Structural changes in 3.2.0. Only one: restore any docs folder deleted under 3.1.1, from the templates. Small.
- A standards check ships with the package. Add the script
"standards:check": "node node_modules/@activatus/code-standards/scripts/verifyStandards/verifyStandards.mjs"topackage.json, runpnpm standards:check, and fix what it finds. It follows the exceptions recorded indocs/development/coding-standards.md. See Checking a project. Copy the Code standards section ofAGENTS.mdfrom the template again: it now says to run the check. - Keep every docs folder and file, even ones that do not apply yet. 3.1.1 allowed deleting a folder that could not apply; that is withdrawn, because the folders keep every project's docs laid out the same and a small project grows into them. Restore any folder deleted under 3.1.1 from the templates, and remove its entry from
coding-standards.md. - The React version lives in
eslint.overrides.mjs. A project on a React version other than 19 sets{ settings: { react: { version: "19.2" } } }there, and copies the template overeslint.config.mjsunchanged. - The
writing-docs.mdandcoding-standards.mdtemplates gained a## Whysection. Add one to each if the project's copies have none. - New, optional: the Migrating guide has new stages, among them a regression check (Stage 0), constants (Stage 4), components (Stage 5) and docs (Stage 6), and the docs standard has Converting existing docs. Nothing to do for a project already on version 3.
- New, optional: Regression Checks for Refactors, a Playwright check that proves a refactor changed nothing visible. Nothing to do unless the project wants it.
How to check: pnpm standards:check passes, or lists only recorded exceptions; every folder from the doc templates exists in docs/.
Fixes in 3.1.1. If the project already applied 3.1.0, check these:
Structural changes in 3.1.1. These move files. Plan them, and run a regression check first:
-
The homepage moves into
app/(homepage)/:app/page.tsxand the homepage's components. Small. -
The site frame moves into
components/layout/: the navigation, header and footer. Small. -
Every component
constants.tsmoves intolib/constants/: one move per file. Follow the 3.2.1 rule, not this version's. -
Every nested sub-component folder moves flat into its parent's
components/folder: one move per folder. -
rorssplit into several files only to stay under 300 lines goes back into one file. Small. -
routes.ts,strings.ts,numbers.tsandnoData.tscan run past 300 lines. They hold data only, somax-linesno longer applies to them. Splittingrorsacross files to stay under 300 lines only made labels harder to find. Ineslint.config.mjs, add this block straight after the test-file block:{// r, s, numbers and noData are lists, not logic, so they can be long.files: ["lib/constants/{routes,strings,numbers,noData}.ts"],rules: { "max-lines": "off" },},In a JavaScript project, the file pattern ends
.{js,mjs}instead of.ts. If the project already splitrorsinto several files only to get under 300 lines, put each back into one file. Update the 300-line rule inAGENTS.mdfrom the template. -
Setup's agent questions are now their own step, step 4, and every setup and update asks them. Agents were skipping them after the standards list. If
docs/development/coding-standards.mddoes not record all four answers (the GitHub repo, the Linear project, the status circles and asking before committing), ask the person the missing ones and record them. -
The standards are the project's base, and changing one needs the person's yes. An agent never skips, loosens or changes a standard on its own. It asks first, records the change in
docs/development/coding-standards.md, under "Details" (number, name, what is different, why, and the date), then makes it. Copy the new rule intoAGENTS.mdfrom the template, under Code standards. -
ESLint changes live in
eslint.overrides.mjs.eslint.config.mjsis now an unchanged copy of the template, so an update can copy it straight over. Copystatic/templates/eslint.overrides.mjsto the project root. Move every rule the project added or changed ineslint.config.mjsinto it, each with a comment naming the standard's number, and check each has an entry incoding-standards.md. Ask the person about any that do not. Then copy the project's template fromstatic/templates/eslint/overeslint.config.mjs. See Project-specific overrides. -
Components are shared on the third use, not the second. On the second use, copy the component into the second page's
components/folder, with a comment in each copy naming the other. On the third use, merge them into the section'scomponents/folder orcomponents/shared/, with props designed from all three uses. Nothing to move in the project: components already shared stay where they are. Update the components rule inAGENTS.mdfrom the template. See Where components live. -
Sort, find, get, filter and format functions go in
utils/from the start, even with one user. Move any such function written inside a component into that component'sutils/folder. -
The codeReview skill checks for these. Copy
static/templates/skills/codeReview.mdinto.agents/skills/and.claude/commands/again. -
The homepage is a route group,
app/(homepage)/. It holds the homepage'spage.tsxand its owncomponents/andutils/, and is still served at/. Moveapp/page.tsxthere, and move the homepage's components out ofapp/components/intoapp/(homepage)/components/. See Where components live. -
The site frame lives in
components/layout/: navigation, header, footer and anything else that frames the pages. It may readrands. Move those components there fromcomponents/shared/orapp/components/. Layout primitives such as Duplex stay incomponents/ui/. -
A props-only component that is not on the Design System Components list stays in
components/ui/. Record it, with its props, indocs/development/coding-standards.md, under a new "Components for the standards" heading (copy it from the template), and tell the person. Move back intocomponents/ui/any such component that was moved out only because it was not listed. -
Four components joined the list: Brand Logo, Notification Card, Scroll Cue and Dropdown Menu. If the project has them, check their props match the list.
-
Every named number lives in
lib/constants/numbers.ts, even one only one file uses, in one file that is never split. Move numbers from the tops of app files and from any topic files (durations.ts,limits.ts) into it. Changed in 3.2.1: motion and artwork objects stay whole inanimation.tsandartwork.ts. Do the 3.2.1 step instead of moving their numbers here. Config files and scripts outside the app keep theirs at the top. Changed in 3.3.2: onlyr,sand shared values go inlib/constants/; a value one file uses stays at the top of that file. Skip this step if the project is updating past 3.3.2. -
No component-level
constants.ts. Every link, piece of text and number goes inlib/constants/, even if one file uses it, so all the text is in one place for translation. Move any component'sconstants.tsintor,sandnumbers.ts. Changed in 3.2.1: there are seven constants files to move into. Do the 3.2.1 step instead. Changed in 3.3.2: onlyr,sand shared values go inlib/constants/; a value one file uses stays at the top of that file. Skip this step if the project is updating past 3.3.2. -
Write
randskeys out by name,r.plans.starter, neverr.plans[planId]. A list of items keeps its words insand its links inrunder the same keys. See A list of items with links. -
No
"#"placeholder links. Show the text without a link and put{/* TODO: … */}above it until the page exists. Unfinished work is marked// TODO: Message. -
lib/constants/never imports fromlib/types/. Move any type it imports from there beside the constant. Withdrawn in 3.3.0: constants now take their types fromlib/types/. Do the 3.3.0 step instead. -
Create
noData.tsonly once the data's shape is decided. An emptynoData.tscan be deleted. Stand-in data asked for earlier carries the comment// Not the real schema: a basic shape to finish the task. -
A component's sub-components sit flat in its one
components/folder, two levels at most. Move any sub-component folder sitting directly in its parent, or nested inside another sub-component, into the parent'scomponents/folder. -
The palette is listed in
docs/design/design-system.md: each colour's name, value, class names and use.globals.cssholds only colours Tailwind does not have. Copy the Colour palette section from the template and fill it in. -
AGENTS.md: the Code standards section sits between
<!-- code-standards:start -->and<!-- code-standards:end -->, and an update replaces everything between them. Copy the section from the template, markers included. Move any project exception written into it intodocs/development/coding-standards.md, and anything else the project added, such as a framework's block, outside the markers. -
Docs: copy the new
docs/product/ontology.mdtemplate if the project has none, and ask the person to review it. Fix the link inoverview/writing-docs.mdifwhat-is-it.mdwas renamed. Retitlehow-it-works.md"Overview — How It Works". Delete any version 2 leftovers:_category_.json, thesidebar_position,sidebar_label,slug,statusandlastReviewedfront matter, anddocs/index.md. -
Decision Docs are confirmed with the person before an agent creates, changes or deletes one, unless
AGENTS.mdnames an agent that manages them.A folder that cannot apply may be deleted with the person's yes, recorded inWithdrawn in 3.2.0: keep every folder. Moving or reformatting a doc does not change its Updated date.coding-standards.md. -
Stylelint configs name their export:
const config = { … }; export default config;. Change the project'sstylelint.config.mjsto match. -
The Linear ticket prefix is the team's, not the project's. In
AGENTS.md, Project workflow, write the Linear workspace, team and project, such asactivatus-life, teamACT, project Launchpad.
How to check: pnpm lint passes with routes.ts, strings.ts, numbers.ts or noData.ts over 300 lines; app/page.tsx has moved to app/(homepage)/page.tsx; lib/constants/ has numbers.ts and no component has its own constants.ts; AGENTS.md has the code-standards markers; the navigation, header and footer are in components/layout/; eslint.config.mjs matches the template exactly, with eslint.overrides.mjs beside it; every rule in eslint.overrides.mjs has an entry in coding-standards.md; lib/constants/ has no strings/ or routes/ folder made only to stay under the limit; docs/development/coding-standards.md records all four agent answers.
Fixes in 3.1.0. If the project already applied 3.0.9, check these:
- Install and update with
@latest. Usepnpm add -D @activatus/code-standards@latest, never a typed version number.package.jsonkeeps whatever version pnpm writes. Nothing to change in the project. minimumReleaseAgeExcludeis the plain package name. Inpnpm-workspace.yaml, the entry is"@activatus/code-standards", with no version. If it has versions in it, such as'@activatus/code-standards@3.0.4 || 3.0.5', replace it with the plain name. Otherwise pnpm adds another version on every update.- Setup asks whether AI should ask before every commit and push. It is optional, a yes or no, recorded under Who commits in Project workflow. Ask the person the question. A yes is written under Who commits; a no writes nothing, and the agent uses its own judgement. Write their answer in
docs/development/coding-standards.md, under "Details".
How to check: minimumReleaseAgeExclude lists "@activatus/code-standards" with no version, running pnpm add -D @activatus/code-standards@latest again leaves pnpm-workspace.yaml unchanged, and if the person said yes, Who commits in AGENTS.md has the asking rule.
Fixes in 3.0.9. If the project already applied 3.0.8, check these:
-
The project links are optional, like the status circles. The GitHub repo, the Linear project and the status circles are all optional, and setup offers all three every time. If the project does not use a link, delete its line from Project workflow in
AGENTS.md. Record all three answers indocs/development/coding-standards.md, under "Details". An agent still never copies a Linear workspace or ticket prefix from another project: ifAGENTS.mddoes not name the Linear project, it asks before creating, linking or citing any ticket. -
formatandformat:checkget their files from git. Prettier reads the project's.gitignoreand.prettierignore, but not.git/info/excludeor a person's global git ignore, so local tool files kept out of git that way, such as.claude/settings.local.json, madepnpm format:checkfail on that machine only, andpnpm formatrewrote them. Replace both scripts inpackage.json:"format": "git ls-files -z --cached --others --exclude-standard | xargs -0 prettier --write --ignore-unknown --no-error-on-unmatched-pattern","format:check": "git ls-files -z --cached --others --exclude-standard | xargs -0 prettier --check --ignore-unknown --no-error-on-unmatched-pattern"Do not shorten them back to
prettier --write .orprettier --check .. See What Prettier skips. -
Take local tool files back out of
.prettierignore. If the project added files that are kept out of git only on someone's machine, such as.claude/settings.local.jsonor.impeccable/*.local.json, remove them: git's ignore rules cover them now. Keep entries for files in git that a tool rewrites, such aspnpm-workspace.yaml. -
The 3.0.8 fix for option B missed a line. If 3.0.7 also put "Ask before every commit and push" at the very top of
AGENTS.md, remove that line too, unless the person wants to keep it as the project's own "Who commits" rule. -
Nothing to do in a project: release commits are titled
docs: version N.N.N — <what changed>.
How to check: AGENTS.md has no line for a project link the person said "None" to, and no "Ask before every commit and push" line unless the person kept it. docs/development/coding-standards.md records the answer for each of the three options. Both scripts in package.json start with git ls-files, .prettierignore has no local tool files, and pnpm format:check passes.
Fixes in 3.0.8. If the project already applied 3.0.7, check these:
- Ask before every commit and push (option B) is gone. Whether agents commit is the project's own workflow, as in change 1. If the project took B in 3.0.7, the rule stays only if the person wants it: it belongs under Who commits in Project workflow, in the project's own words. Remove the "Ask before every commit and push" section copied from the template. 3.0.7 also put the rule at the very top of
AGENTS.md: remove that line too, unless the person wants to keep it as the project's own "Who commits" rule. Take B offdocs/development/coding-standards.md. - The Linear project can come from the project's own docs. If they already name it, read it from there and confirm it with the person. Never copy it from another project.
pnpm-workspace.yamlis Prettier-ignored. pnpm rewrites it with single quotes on every install, sopnpm format:checkfailed after every update. Addpnpm-workspace.yamlto.prettierignore.- The update steps cover pnpm's release-age check: if pnpm refuses a version that has only just been published, add the package to
minimumReleaseAgeExclude. See Setup. - 3.0.3 and 3.0.6 were never published to npm. Their fixes are in 3.0.4 and 3.0.7. Every patch is now published.
How to check: .prettierignore lists pnpm-workspace.yaml, pnpm format:check passes, and AGENTS.md has no "Ask before every commit and push" section unless the person chose to keep that rule.
Fixes in 3.0.7. New, optional: Working with Agents. Setup now asks for the project's GitHub repo and Linear project, and offers status circles. (3.0.7 also offered asking before every commit and push. 3.0.8 removed it.) If the project already applied 3.0.5:
- Offer the GitHub repo and the Linear project, both optional, and add each one the person confirms to the Project workflow section of
AGENTS.md, from the template. Read the repo fromgit remote get-url originand confirm it with the person. Read the Linear project from the project's own docs if they name it, and confirm it; otherwise ask. Never copy it from another project. - Print the status circles question to the person. If they say yes, copy the Status circles section from the template into Project workflow. Write their answer in
docs/development/coding-standards.md, under "Details". IfAGENTS.mdalready has status circles in the project's own words, keep that.
How to check: Project workflow in AGENTS.md names each link the person confirmed, and docs/development/coding-standards.md records the answer for each option.
Fixes in 3.0.6. Never published to npm: these fixes are in 3.0.7. Nothing to do in a project. The update steps gained two rules: when the project's AGENTS.md records the standards in its own words, an update writes the changed line in that shape; and after an update the project's list of opted-out and changed standards is re-read, taking off it anything the new version now covers. The 3.0.5 numbers change has its check spelled out: pnpm lint reporting a no-magic-numbers finding means it is not finished. A project-specific block in an ESLint config is now defined: it is recorded in the project's docs/development/coding-standards.md, or pnpm lint fails without it.
Fixes in 3.0.5. The ESLint configs now pass their own rules. Copied in as they were, they gave three problems on the config file itself: No magic number: 3 (no-magic-numbers), and in Next.js projects two warnings, import/no-anonymous-default-export and import-x/no-named-as-default-member. Copy the new ESLint config for the project's framework over eslint.config.mjs, keeping any project-specific block: one that is recorded in the project's docs/development/coding-standards.md, or without which pnpm lint fails. A block that is neither, and the lint passes without it, is a leftover: ask before removing it. Then run pnpm lint: the config file itself has no problems. If the project already fixed these three by hand, there is nothing to do.
max-depthuses a named constant,MAXIMUM_NESTING_DEPTH.- The array is assigned to
configbeforeexport default config. - The eslint-comments preset is imported by name:
import { recommended as eslintCommentsRecommended } from "@eslint-community/eslint-plugin-eslint-comments/configs";. - Numbers have a home: Numbers. A number used in one file is named at the top of that file, and one shared across the app goes in
lib/constants/, in a file named after its topic, such asdurations.ts. There is nonobject and nonumbers.ts. If the project has a file of unrelated numbers, split it by topic. Copy the updated No magic numbers line from theAGENTS.mdtemplate. Runpnpm lint: ano-magic-numbersfinding means the change is not finished. docsSite/is ignored. It is the standard docs viewer, with its own packages and build, and without the ignore ESLint lints its built files. If the project already ignores it, nothing changes.- The one-parameter selectors moved into their own
SINGLE_OBJECT_PARAMETERlist. Next.js: route handlers inapp/**/route.tsmay now take(request, context). If the project switched the rule off for them some other way, such as an override block oreslint-disablecomments, remove it.
Fixes in 3.0.4. If the project already applied 3.0.3, do these:
- New standard 12.1: links, text and stand-in data live in
lib/constants/, asr,sand noData.randsare the only single-letter names allowed. Do change 11. - The standards site has a home page, and the docs moved to
/docs/.... Old links redirect, so nothing breaks. Update any link to the standards site in the project'sAGENTS.mdor docs to the/docs/address, such ashttps://activatus-code-standards.vercel.app/docs/ontology.
Fixes in 3.0.3. Never published to npm: these fixes are in 3.0.4. Change 8 now says how to type a function inside an object that is built somewhere else: give the function its own Props type, such as LaunchSessionProps, and use it. If the project wrote those parameter types out inline, such as ({ worktreePath }: { worktreePath: string }), replace each with the function's Props type. Also new: type first, never satisfies. It is an ESLint error now, so copy the new ESLint config and replace each satisfies with a type on the declaration, except in Storybook story files.
Fixes in 3.0.2. Nothing to do in a project:
- The update steps say how to pick up a patch: the version number in
AGENTS.mdstays the same, so always read the version's Fixes list after updating the package. - A removed decimal standard keeps its number, such as
11.2. Removed in version N. - When lint must pass names the usual ways projects check it: a CI job or a git hook.
Fixes in 3.0.1. If the project already applied 3.0.0, check these:
- Setup list numbers are permanent again: 19, 20 and 22 are marked removed, and AGENTS.md and the Ontology are back to 21 and 23. Choices recorded under the version 2 numbers are still right.
- The Husky lint hook is no longer a standard. Lint must pass before work is finished; how that is enforced is the project's choice.
- Every name ending in
ReferenceusesRef, not only React refs. - The rename script takes a list of renames with
--list.
1. Project workflow is no longer part of the standards
Why: The standards are a base for code. How a project runs its work — branch names, commit messages, Pull Requests, merging, releases, and whether AI agents may commit — is each project's own decision, and every project makes it differently.
What to do:
- The Branch Naming and Commit Messages standards are gone, along with standards 19, 20 and 22 from the setup list. Keep whatever the project already does, and make sure it is written up in the project's own docs, usually
docs/development/. - Standards 19, 20 and 22 keep their numbers, marked as removed. No other number changes. If
docs/development/coding-standards.mdrecords a choice for 19, 20 or 22, move that rule into Project workflow and take it off the list. - In
AGENTS.md, replace the "Development workflow" section with a Project workflow section, copied from the template. Fill it in with the project's own rules: who commits, branches, commit messages, Pull Requests, merging and releases. Put the most important rule for agents, usually whether they may commit, at the very top: one line, or a short section if it needs more. - Remove the "Never commit code" and "Do not create, switch or delete branches" lines from "Out of scope for AI agents", unless they are the project's own rule. If they are, they belong in Project workflow.
- Commitlint and the Husky hooks (
.husky/commit-msgand.husky/pre-commit) are no longer templates. Keep them if the project wants them: they are now the project's choice. The standard is only thatpnpm lintpasses before work is finished. See When lint must pass.
How to check: AGENTS.md has a Project workflow section, and every rule in it is written up in the project's docs.
2. lib, ref and repo are dictionary words, and library/ is lib/
Why: They are the words developers use. lib/ is the name Next.js and shadcn/ui use too.
What to do:
- Rename the
library/folder tolib/withgit mv, and fix the imports:@/library/→@/lib/. - Rename every name ending in
Referenceto end inRef, whatever it refers to:inputReference→inputRef,surfaceReference→surfaceRef. Renamerepositoryback torepowhere it is short for a Git repository, such asrepositoryName→repoName. Use the editor's Rename Symbol (F2), or the rename script. - Copy the new ESLint config. The naming rule now has
replacements: { docs: false, lib: false, props: false, ref: false, repo: false, utils: false }andallowList: { params: true, searchParams: true }. - Update the "Dictionary words stay short" line in
AGENTS.mdfrom the template, and removelib/→library/andrepo→repositoryfrom its Never / Always table.
How to check: There is no library/ folder, grep -rn "@/library" --exclude-dir=node_modules . finds nothing, and pnpm lint passes.
3. utils, services and lib are defined
Why: It wasn't clear which functions go in utils/, services/ or lib/. See utils, services and lib.
What to do: Check each function in those three folders against the definitions, and move any that are in the wrong one. The common one: a function in utils/ that calls a database or an API belongs in services/ or lib/. Shared constants and types live in lib/constants/ and lib/types/; a component's own types stay in its types.ts. Move any root-level constants/ or types/ folder into lib/.
How to check: Nothing in utils/ imports from services/, lib/integrations/ or a database client, and there is no root-level constants/ or types/ folder.
4. The Ontology only holds code words
Why: Project-management words mean different things in different projects. Launchpad's "project" is a product made of several MVPs, and its Pull Requests go through feature → dev → uat → main.
What to do: The code Ontology no longer defines project, repository, branch, commit, pull_request, pull_request_review or production, and human, developer and agent no longer say who may commit. If the project uses those words, define them in its own docs/product/ontology.md, the way the project actually works.
How to check: The project's ontology defines every project-management word it uses.
5. Every config file is .mjs
Why: .mjs works in TypeScript and JavaScript projects alike, so every project looks the same.
What to do:
- Rename each config file that is
.js,.tsor.mtsto.mjs, even in a TypeScript project, wherever the tool can read.mjs:prettier.config.mjs,next.config.mjs,eslint.config.mjs,docsSite/docusaurus.config.mjs,docsSite/sidebars.mjsand so on. Usegit mv. Never convert a config to.tsor.mts. - Remove any TypeScript-only syntax as you go. A type annotation becomes a JSDoc comment:
const config: NextConfig = {becomes/** @type {import("next").NextConfig} */ const config = {. Copy thedocsSite/templates, which are already.mjs. - Update anything that names the old file, such as
sidebarPath: './sidebars.mjs'in the docs site config.
How to check: ls *.config.* docsSite/*.config.* docsSite/sidebars.* shows only .mjs files, and pnpm lint, pnpm format:check, the build and pnpm docs:build still run.
6. Prettier: 70-character lines and tabs
Why: A lot of code is read and reviewed on a phone, where short lines fit the screen. Tabs let each person see the indent at the width they like.
What to do:
- Copy the new
prettier.config.mjsand.editorconfig. Prettier now hasprintWidth: 70anduseTabs: true, and keeps spaces for Markdown and YAML. EditorConfig indents with tabs, except Markdown, YAML and Python. - Run
pnpm formatonce, on its own, with no other changes mixed in. It touches nearly every file, so keep it apart from real changes. - If the project shows code on a web page, such as its docs site, add
pre, code { tab-size: 2; }to its CSS, so tabs are not shown 8 characters wide. ThedocsSite/template already has it.
How to check: pnpm format:check passes.
7. When a name clashes, rename our own thing first
Why: A third-party name is the name everyone knows it by, and a clash usually means our own name is too vague. Renaming an import for taste makes the code harder to search and to match against the package's docs.
What to do: Search for renamed imports: grep -rn "import {[^}]* as " --include=*.ts --include=*.tsx --exclude-dir=node_modules .. For each one:
- If nothing in the file clashes with the original name, undo the rename and use the original name.
- If our own thing clashes, rename ours to a more specific full-word name, with the editor's Rename Symbol or the rename script. Then undo the import rename. For example, our
createClientbecomescreateProjectDatabaseClient, and Supabase'screateClientkeeps its name. - If both names are third-party, or ours is fixed by a framework (such as a Next.js route's
GET), keep the import renamed, as the owner plus the original name, never shortened:Link as LucideLinkIcon.
See When a name clashes.
How to check: Every renamed import that is left clashes with another third-party name or a framework-fixed name, and starts with its owner's name.
8. Name a type after what it is, not the job it does
Why: A type named after its role in one function, such as StartAgentRunDependenciesType, is a second name for what is really part of that function's props.
What to do: Find types used for only one field of one Props type. Move each one inside that Props and delete the separate type. Where the object is built somewhere else, don't just delete its type: the functions inside lose their parameter types and the type check fails. Give each function its own Props type for its single object parameter, named after the function, and use it there: launchSession: async ({ worktreePath }: LaunchSessionProps) =>. Don't write the type out inline, such as ({ worktreePath }: { worktreePath: string }).
Type first, never satisfies. A value gets its type where it is declared, and satisfies is now an ESLint error. Replace each { … } satisfies SomeType with a type on the declaration: const config: ConfigType = { … }. Leave story files alone: they follow Storybook's docs, satisfies Meta<typeof Component> with StoryObj<typeof meta>, and ESLint allows it there. Copy the new ESLint config to switch the check on.
How to check: No type is used only as the type of one field of one Props, grep -rn " satisfies " --include=*.ts --include=*.tsx --exclude=*.stories.* --exclude-dir=node_modules . finds nothing, and the type check and pnpm lint pass.
9. "go" means carry on
Why: People often send just "go" to nudge an agent along, for example when their connection drops and the agent looks stuck. An agent must never read it as permission to commit.
What to do: Copy the line that starts "go" means carry on from the top of the AGENTS.md template into the top of the project's AGENTS.md.
How to check: The project's AGENTS.md has the line near the top.
10. Unfinished changes are recorded, not hidden
Why: A big change, such as switching on the standard ESLint config, may be finished later. The version number has to stay honest about that.
What to do: Add an "Unfinished changes" section to docs/development/coding-standards.md, from the template. List any change from any version that is not done yet, with what is left and where it is being done, or write "None". See Finishing a change later.
How to check: docs/development/coding-standards.md has an "Unfinished changes" section.
11. Links, text and stand-in data live in lib/constants/
Why: One place for every link and every piece of text means nothing is hard-coded in a component, and the keys of s become the translation keys when a site gets more languages. noData lets every page render while working locally with no internet.
What to do:
- Create
lib/constants/withroutes.ts,strings.ts,noData.tsandindex.ts, as in Constants. If the project already has them somewhere else, such asapp/constants/, move the folder withgit mvand fix the imports:@/app/constants→@/lib/constants. routes.tsexportsr, with every link: the app's own pages, outside sites, andmailto:,tel:andsms:links.strings.tsexportss, with every piece of static text. Use named exports,export const r = { … }, neverexport default.- Move links and text written straight into components into
rands. - Rename each noData export to
NO_DATA_plus the thing it stands in for:noDataInsights→NO_DATA_INSIGHTS. Move dummy values, such asdummyHeading, out ofsto the top ofnoData.tsasDUMMY_HEADINGand the rest. Move anything innoData.tsthat is real copy, such as a page's metadata, intos. - Use noData only in local development:
query?.insights ?? (IS_LOCAL_DEVELOPMENT ? NO_DATA_INSIGHTS : []). - Inside
lib/constants/, files import each other directly,import { s } from "./strings";, never through@/lib/constants. - Copy the new Links, text and stand-in data line and the
randsexception from theAGENTS.mdtemplate. - Add
12.1to the project's standards: it is a new standard, so ask the person whether the project follows it. Record a no indocs/development/coding-standards.md.
How to check: grep -rn "export default" lib/constants and grep -rn "noData[A-Z]\|dummy" lib/constants/strings.ts find nothing, and the type check, pnpm lint and the build pass.
No action needed
- The Ontology defines
dependency: something the code relies on but does not define itself. Ticket relationships are not dependencies; each project names those itself. - The package now ships a rename script for agents working from the command line, using TypeScript's own rename:
node node_modules/@activatus/code-standards/scripts/renameSymbol.mjs <file> <currentName> <newName>. Pass--list renames.txtto do many renames in one run, one per line. It can't reach keys in untyped object literals, such as the expected value in a test'stoEqual({ … }): search the tests for old names and fix those by hand. See Renaming from the command line. - The Setup guide says the standards are a base for code only, and explains how to get past pnpm's
minimumReleaseAgewhen a new version is too fresh to install. - Folder Structure explains each
lib/folder in one line. - The ESLint page says the config needs ESLint 10.4 or later, and the React configs explain why they set
settings.react.version.
Version 2
Released: 2026-10-15 · Updates from: version 1
1. The standards are an npm package — do this first
Why: A project installs the standards, so an agent reads the exact version the project uses, straight from node_modules. The package version is the standards version: 2.0.0 is version 2.
What to do:
pnpm add -D @activatus/code-standards- Read the rest of this update from
node_modules/@activatus/code-standards/docs/version-history.md. The templates to copy are innode_modules/@activatus/code-standards/static/templates/. - In
AGENTS.md, under "Code standards", say the standards are installed as@activatus/code-standards, and that agents read them fromnode_modules/@activatus/code-standards/docs/. Copy the wording from theAGENTS.mdtemplate.
How to check: @activatus/code-standards is in the project's devDependencies.
2. The person agrees which standards apply — do this second
Why: Not every standard suits every project. The person now picks, from a numbered list, and the choices are written down so every agent follows the same ones.
What to do:
- Print the numbered list in Setup, step 3 to the person, exactly as written, and record their answer in
docs/development/coding-standards.md. - For the rest of this update, skip any change to a standard the person opted out of.
How to check: docs/development/coding-standards.md lists the opted-out standards by number, or says "No differences".
3. docs is a dictionary word: the docs folder is docs/ again
Why: docs is a word, not an abbreviation, and documentation is long and hard to read in names.
What to do:
- Rename the folders with
git mv:documentation/→docs/,documentationSite/→docsSite/. - If both
docsSite/anddocumentationSite/exist, merge them intodocsSite/: move each tracked file fromdocumentationSite/withgit mv, keeping whichever copy is newer, then deletedocumentationSite/. Ask the person before deleting a file that exists in only one of them. - Rename
docs/overview/writing-documentation.md→docs/overview/writing-docs.md, and fix every link to it. - Rename the environment variable
DOCUMENTATION_URL→DOCS_URLinnext.config.mjs,.env.local,.env.exampleand the hosting provider's settings. - Rename the route
/documentation→/docs. Copy the new rewrite from thenext.config.mjstemplate. - Re-copy
docsSite/docusaurus.config.tsfrom thedocsSite/template, keeping the project's name and theme. Version 2 changed it:baseUrl: '/docs/',path: '../docs', full-word variable names (rest→remainingArguments,doc→page), andstatic/img→static/images. Rename the project'sdocsSite/static/img/tostatic/images/to match. - Rename the scripts in
package.json:documentation:install→docs:install,documentation:dev→docs:dev,documentation:build→docs:build. - If the docs site's generated folders were ever committed, stop tracking them:
git rm -r --cached docsSite/.docusaurus docsSite/build. They are in thedocsSite/template's.gitignore. - Update every mention in
AGENTS.md,README.mdand the docs themselves. - Update every ignore list that names the old folders: ESLint, Prettier,
tsconfig.json, Vitest or Jest, and.gitignore. A leftover docs site with its ownnode_modulesmakes test runners and linters run its files too.
How to check: grep -rn "documentation/\|'../documentation'\|documentationSite\|DOCUMENTATION_URL\|documentation:" --exclude-dir=node_modules . finds nothing. Normal sentences that say "documentation" are fine.
4. props is a dictionary word: prop types end in Props
Why: props is the word every React developer uses. properties is harder to read.
What to do:
- Rename every type ending in
Propertiesto end inProps:ButtonProperties→ButtonProps. Never use find-and-replace. Use the editor's Rename Symbol (F2) or, for an agent working from the command line, the rename script in version 3 and later of the package:node node_modules/@activatus/code-standards/scripts/renameSymbol.mjs <file> <currentName> <newName>. Both use TypeScript's own rename, so they change every use and nothing else. - Rename variables and parameters called
propertiestoprops, including spreads:...buttonProperties→...buttonProps. - If the project already uses the standard ESLint config, copy the new ESLint config. It needs ESLint 10.4 or later. Two things changed in it:
- The type-name rule is now
(Props|Type)$. unicorn/name-replacementsnow hasreplacements: { docs: false, props: false, utils: false }.
- The type-name rule is now
- If the project never had the standard ESLint config, do only the renames in this change. Switching the config on can find hundreds of problems, so do it separately with Migrating an Existing Project, one stage per Pull Request, and record it as unfinished: see Finishing a change later.
How to check: grep -rn "Properties\b" --include=*.ts --include=*.tsx --exclude-dir=node_modules . finds nothing of ours, and pnpm lint passes.
5. utils is a dictionary word: reusable functions live in utils/
Why: utils is the word developers use. utilities and helpers are not.
What to do:
- Rename every
utilities/folder toutils/withgit mv, at every level: global, section and component. - Rename any
helpers/folder toutils/. Split ahelpers.tsgrab-bag file into one folder per function. - Fix the imports:
@/utilities→@/utils,./utilities→./utils.
How to check: find . -type d \( -name utilities -o -name helpers \) -not -path "*/node_modules/*" finds nothing, and grep -rn "utilities\"" --exclude-dir=node_modules . finds no imports.
6. Components live where they are used: no feature folders
Why: Grouping by feature and grouping by page meant two places for the same component. Now there is one rule. See Where components live.
| Used by | Lives in |
|---|---|
| Every project the same way: the Design System Components | components/ui/ |
| More than one page of this project | components/shared/ |
| A few pages in one section | app/(section)/components/ |
| One page only | app/page-name/components/ |
What to do:
- For each component in
components/features/orcomponents/layout/, find where it is imported (grep -rn "ComponentName").- Imported by one page → move it to that page's
components/folder. - Imported by pages in one section → move it to that section's route group:
app/(section)/components/. - Imported by pages in more than one section → move it to
components/shared/.
- Imported by one page → move it to that page's
- Move a feature's
utils/folder the same way, to wherever its functions are used. - Delete the empty
components/features/andcomponents/layout/folders. - Leave
components/ui/as it is.
How to check: components/features/ and components/layout/ do not exist, and the build passes.
7. The Component Reference is now Design System Components
Why: It is the list of standard components every project builds the same way, and "design system components" is what people call that.
What to do: In AGENTS.md and the project's docs, change "Component Reference" to "Design System Components", and change links from /template-components to /design-system-components.
How to check: grep -rn "Component Reference\|template-components" --exclude-dir=node_modules . finds nothing.
8. The Dictionary: short names that are words
Why: Some short names are words, not abbreviations. The Dictionary is the one list of them, so nobody has to guess.
What to do:
- The Dictionary has:
id,docs,props,utils,app,url,api,uat,pdf,html,css,json,readme. - Any other short name in the project's own code is written out in full. If one seems like a word, ask for it to be added to the Dictionary; do not just use it.
- Copy the new "Dictionary words stay short" bullet into
AGENTS.mdfrom the template.
How to check: pnpm lint passes with the new ESLint config.
9. Third-party names are used as they are
Why: The naming rule is only for names we write. Names from someone else's code keep their own names: everyone knows them by those names, and renaming them breaks the code.
What to do:
- Undo any rename of a third-party name. The common one is shadcn/ui's
cnfunction: it stayscn. - In our own code, give the value a full-word name as soon as it arrives:
const workingDirectory = process.cwd();, then{ cwd: workingDirectory }.
How to check: Nothing to search for. Review any renamed import from a package.
10. The ontology only holds code words
Why: The code standards define words about writing code. Each project defines the words for what it builds.
What to do:
- Keep the project's product words in
docs/product/ontology.md. - Check every word in it against the Ontology. If the project gives a code word a different meaning, remove that entry and use the code standards' meaning. Tell a person what changed.
- Make the project's ontology say that it adds to the code standards' ontology.
- Update the "Ontology" section of
AGENTS.mdfrom the template. It now points to both ontologies.
How to check: No term in docs/product/ontology.md is also defined in the code standards' Ontology.
11. Whether agents may commit is the project's choice
Why: Strict no commit is right for most projects, but a tool built to ship code with AI, such as Launchpad, needs agents to commit.
What to do: This is standard 22 in the list from change 2. If the person kept it, keep the template's "NEVER COMMIT CODE" lines for strict no commit. Otherwise, replace them with the project's own rule, saying exactly which agents may commit, push and open Pull Requests, and when. See A project can choose otherwise.
How to check: The top of AGENTS.md states the project's commit rule.
12. The live standards site
Why: The standards now have a real address: https://activatus-code-standards.vercel.app
What to do: Replace [code standards site URL] in AGENTS.md and docs/development/coding-standards.md with https://activatus-code-standards.vercel.app
How to check: grep -rn "code standards site URL" . finds nothing.
13. This Version History
What to do: Add **Code standards version:** 2 to AGENTS.md, under "Code standards".
No action needed
- The Setup guide is written for the agent: install the package, check the version, agree the standards, apply them. It never asks which framework the project uses, and never creates the app: the person does that.
- The migration guide has a Names stage.
Version 1
Released: 2026-10-15
The first numbered version: everything in the standards on this date. It included documentation/ for the docs folder, Properties at the end of prop types, utilities/ for reusable functions, components/features/, and the Component Reference. Version 2 replaced all five.
What to do in a project: follow the migration guide for everything.