Skip to main content

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 changesExampleMeans
First3.2.2 → 4.0.0A major update: it changes how most of a project's code is written, so every project reworks it
Middle3.2.2 → 3.3.0Files move: the update moves or renames files in a project. Plan it, and take a regression baseline first
Last3.2.2 → 3.2.3Changes 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.

  1. 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.
  2. 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 to minimumReleaseAgeExclude in pnpm-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 from node_modules/@activatus/code-standards/docs/version-history.md.
  3. 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.
  4. 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.md template, and the project's AGENTS.md records the standards in its own words instead, write the updated rule there in the project's own shape.
  5. 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.
  6. Run pnpm lint, pnpm format:check, pnpm test and the build after each change. format:check checks every file git does not ignore; never narrow it to changed files.
  7. Ask any Working with the agent question whose answer is not recorded in docs/development/coding-standards.md, under "Details".
  8. Run pnpm standards:check, and fix what it finds. See Checking a project.
  9. Set the version in AGENTS.md to 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 in package.json, then publish it to npm (npmjs.com) with pnpm publish. pnpm is the tool; npm is where the package lives, and projects install it from there with pnpm 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, set package.json to N.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.md template lives between the code-standards markers. A project copies only that section on an update, and pnpm standards:check compares only that section, so a rule outside the markers never reaches a project that is already set up.
  • pnpm devtest must pass before publishing. Run it, then pnpm 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 as docs: 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.mjs templates, so after pnpm format a project's copy no longer matched the template, and pnpm standards:check failed standard 6. pageLayout.visual.ts, the docs site's index.tsx, and the tables in the ontology.md and design-system.md templates changed the same way. Every template is now already formatted, and the code standards check this before each release, so pnpm format leaves a copied template as it is.

    What to do: copy the project's ESLint template over eslint.config.mjs again, and, if the project has a regression check, tests/visual/pageLayout.visual.ts. Run pnpm 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/, so pnpm lint:css also linted .next/, out/, build/, coverage/ and docsSite/, 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.mjs again. If the project added a .stylelintignore for those folders, delete it, and remove its line from "Unfinished changes" in docs/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:check now reports a missing Stylelint config. Standard 8 covers all hand-written CSS, and a Tailwind globals.css is hand-written CSS, but the check only looked at stylelint.config.mjs when there was one. A project with a .css or .scss file outside vendor/ and docsSite/, and no stylelint.config.mjs, now fails standard 8. See Stylelint.

    What to do: if the project has no stylelint.config.mjs:

    1. Install the packages from the Stylelint page.
    2. Copy static/templates/stylelint/css/stylelint.config.mjs, or the scss one for SCSS.
    3. Add the lint:css and lint:css:fix scripts from the Stylelint page, run pnpm lint:css, and fix what it finds.
    4. If docs/development/coding-standards.md says 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, both stylelint.config.mjs templates and the regression check's playwright.config.mjs started with a one-line /** @type … */ comment, which unicorn/single-line-block-comment-style rejects. 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's playwright.config.mjs and pageLayout.visual.ts had 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.mjs and tests/visual/pageLayout.visual.ts. If the project turned a lint rule off for one of them while waiting for this version, remove that from eslint.overrides.mjs and from docs/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, add vi.mock("server-only", () => ({})); to the Vitest setup file, and make sure vitest.config.mjs lists that file in setupFiles. Copy the Code standards section of AGENTS.md from 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.md are now inside the code-standards markers. They were above them, so copying the Code standards section on an update never brought them in, and pnpm standards:check never 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 about docsSite/ now starts "If the project has a docsSite/".

    What to do: copy the Code standards section of AGENTS.md from 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 own components/ 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:

    1. pnpm add -D @ianvs/prettier-plugin-sort-imports.
    2. Copy static/templates/prettier.config.mjs over the project's again. A project without Tailwind removes prettier-plugin-tailwindcss and tailwindStylesheet as before.
    3. Move each side-effect import, such as import "./globals.css";, below the file's other imports.
    4. 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/ and components/shared/ each have an index.ts, and code outside a category imports from it: import { Button } from "@/components/ui";. There is no components/index.ts, and pnpm standards:check now reports one. A category's index.ts exports 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 or next/headers, starts with import "server-only";, is left out of the index.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's index.ts, and passes once it is imported from its own folder. See Importing components.

    What to do:

    1. Delete any components/index.ts, and point its imports at the category: @/components/ui, @/components/layout or @/components/shared.
    2. Give each category an index.ts if it has none.
    3. For each component that reaches server-only code, add import "server-only"; at the top, take it out of its category's index.ts, and import it from its own folder.
    4. Copy the Code standards section of AGENTS.md from the template again, if not already done for the first fix above.
  • "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:check used to read any line under "Details" in docs/development/coding-standards.md that 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 shape N. Name — skipped: reason or N. 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 from static/templates/docs/development/coding-standards.md if 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. A for with 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.md and 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 .prettierignore template: see What Prettier skips.

    What to do: add these lines to the project's .prettierignore, then copy the Code standards section of AGENTS.md and the skills from the templates again, so Prettier's changes to them are undone:

    .prettierignore
    AGENTS.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 only ui/, layout/ and shared/, as Where components live already said. pnpm standards:check now reports any other folder, and any component file, directly in components/.

    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 the components/ folder of the page or section that uses it.

  • Only r, s and shared values go in lib/constants/. Every link still goes in r and every piece of text people see or hear in s, 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 in lib/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 new status.ts for statuses the app shares; only routes.ts, strings.ts and index.ts are required, and every file in it is exempt from max-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.md from the template again, and copy the project's ESLint template over eslint.config.mjs again: the max-lines block now reads lib/constants/*.ts. A value moved into lib/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 in unsortedValues.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:check reports 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.md section 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.md template, Docs and Regression Checks say so.

    What to do: read docs/testing/regression.md and any other doc that records a check. Remove any result, URL or commit for a check that has not actually run. Copied into AGENTS.md with 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 a node_modules that points outside the project folder. On macOS, cp -Rc makes 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 : number or : string only to follow 3.3.0: those can stay or go.

  • The readonly search also finds string[], number[] and boolean[] 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 the playwright.config.mjs template say the same; a project's frozen copy need not change.

    What to do: if the project's docs/testing/regression.md does not say where its baseline came from, add it: the production URL, or the commit and the date.

  • AGENTS.md: an as const that cannot be avoided is one of the allowed lint disables, beside let and 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's types.ts left empty is deleted.

  • Every as const value found outside lib/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 except r and s is typed where it is declared, with a type from lib/types/. Changed in 3.3.1: a single number or string needs no type of its own. "lib/constants/ never imports from lib/types/" is withdrawn: it only ever guarded against a loop of types, which import type removes at build, and it pushed projects into as const and into retyping values wherever they were read. lib/types/ now reads a constant's type with import type straight from its file, never through @/lib/constants. See Types for constants.

    What to do: change every import in lib/types/ from @/lib/constants to the constant's own file, as import type. Give every object and list constant except r and s a type from lib/types/, in files named by topic, and move the types it uses there with what they depend on. Point imports at @/lib/types, delete component types.ts files 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 type readonly, including lists inside an item. Copy the Code standards section of AGENTS.md from the template again.

  • No as const anywhere. A written type is the contract; when the code needs exact values, they go in the type. Each constant's list type is readonly. A list that must hold every id is checked with a Record or a test. ESLint now reports as const as an error: consistent-type-assertions always let it through, so the configs ban it with no-restricted-syntax. See No as const.

    What to do: copy the project's ESLint template over eslint.config.mjs again. Replace each as const with a real type where the value is declared. An as const value found outside lib/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.md into .agents/skills/ and .claude/commands/ again. Where one as const is 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 any as const with 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:** in AGENTS.md is now the full version last applied, such as 3.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:check reads the full version, and flags a project that recorded only 3, an older version, or a version newer than the installed package.

    What to do: if AGENTS.md records 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 of AGENTS.md from 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.md template 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:check no 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" in coding-standards.md. Changed in 3.3.2: only r, s and shared values go in lib/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 in animation.ts.

    What to do: move any CSS string out of lib/constants/ and components into globals.css.

  • s holds only text people see or hear. Internal strings, such as ids, keys, event names and log messages, are not s: 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.ts and artwork.ts are split by one question. Where something ends up is artwork; how and when it gets there is animation. Drawing data the code reads goes in artwork.ts; a static drawing nothing reads goes in an .svg file or an icon component. classNames.ts can 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:check reports 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 of AGENTS.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 in lib/constants/, moves into the seven standard files: one move per file. pnpm standards:check lists 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:check lists 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 in lib/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" in coding-standards.md, and the work carries on. In eslint.config.mjs, copy the template over it again: the max-lines block now reads lib/constants/{routes,strings,numbers,noData,animation,artwork,classNames}.ts. Copy the Constants rules into AGENTS.md from the template. Changed in 3.3.2: only r, s and shared values go in lib/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 from r: keyof typeof r.plans. Delete separate lookup objects that map a key to its link: r already is that lookup. See A list of items with links.

  • pnpm standards:check checks more of the How to check lists: sub-components sit flat in their root component's components/ folder; the homepage is in app/(homepage)/; docs/development/coding-standards.md records the agent answers under "Details"; docs/design/design-system.md has a filled-in Colour palette table; and lib/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" to package.json, run pnpm standards:check, and fix what it finds. It follows the exceptions recorded in docs/development/coding-standards.md. See Checking a project. Copy the Code standards section of AGENTS.md from 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 over eslint.config.mjs unchanged.
  • The writing-docs.md and coding-standards.md templates gained a ## Why section. 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.tsx and the homepage's components. Small.

  • The site frame moves into components/layout/: the navigation, header and footer. Small.

  • Every component constants.ts moves into lib/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.

  • r or s split into several files only to stay under 300 lines goes back into one file. Small.

  • routes.ts, strings.ts, numbers.ts and noData.ts can run past 300 lines. They hold data only, so max-lines no longer applies to them. Splitting r or s across files to stay under 300 lines only made labels harder to find. In eslint.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 split r or s into several files only to get under 300 lines, put each back into one file. Update the 300-line rule in AGENTS.md from 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.md does 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 into AGENTS.md from the template, under Code standards.

  • ESLint changes live in eslint.overrides.mjs. eslint.config.mjs is now an unchanged copy of the template, so an update can copy it straight over. Copy static/templates/eslint.overrides.mjs to the project root. Move every rule the project added or changed in eslint.config.mjs into it, each with a comment naming the standard's number, and check each has an entry in coding-standards.md. Ask the person about any that do not. Then copy the project's template from static/templates/eslint/ over eslint.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's components/ folder or components/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 in AGENTS.md from 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's utils/ folder.

  • The codeReview skill checks for these. Copy static/templates/skills/codeReview.md into .agents/skills/ and .claude/commands/ again.

  • The homepage is a route group, app/(homepage)/. It holds the homepage's page.tsx and its own components/ and utils/, and is still served at /. Move app/page.tsx there, and move the homepage's components out of app/components/ into app/(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 read r and s. Move those components there from components/shared/ or app/components/. Layout primitives such as Duplex stay in components/ui/.

  • A props-only component that is not on the Design System Components list stays in components/ui/. Record it, with its props, in docs/development/coding-standards.md, under a new "Components for the standards" heading (copy it from the template), and tell the person. Move back into components/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 in animation.ts and artwork.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: only r, s and shared values go in lib/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 in lib/constants/, even if one file uses it, so all the text is in one place for translation. Move any component's constants.ts into r, s and numbers.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: only r, s and shared values go in lib/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 r and s keys out by name, r.plans.starter, never r.plans[planId]. A list of items keeps its words in s and its links in r under 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 from lib/types/. Move any type it imports from there beside the constant. Withdrawn in 3.3.0: constants now take their types from lib/types/. Do the 3.3.0 step instead.

  • Create noData.ts only once the data's shape is decided. An empty noData.ts can 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's components/ folder.

  • The palette is listed in docs/design/design-system.md: each colour's name, value, class names and use. globals.css holds 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 into docs/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.md template if the project has none, and ask the person to review it. Fix the link in overview/writing-docs.md if what-is-it.md was renamed. Retitle how-it-works.md "Overview — How It Works". Delete any version 2 leftovers: _category_.json, the sidebar_position, sidebar_label, slug, status and lastReviewed front matter, and docs/index.md.

  • Decision Docs are confirmed with the person before an agent creates, changes or deletes one, unless AGENTS.md names an agent that manages them. A folder that cannot apply may be deleted with the person's yes, recorded in coding-standards.md. Withdrawn in 3.2.0: keep every folder. Moving or reformatting a doc does not change its Updated date.

  • Stylelint configs name their export: const config = { … }; export default config;. Change the project's stylelint.config.mjs to 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 as activatus-life, team ACT, 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. Use pnpm add -D @activatus/code-standards@latest, never a typed version number. package.json keeps whatever version pnpm writes. Nothing to change in the project.
  • minimumReleaseAgeExclude is the plain package name. In pnpm-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 in docs/development/coding-standards.md, under "Details". An agent still never copies a Linear workspace or ticket prefix from another project: if AGENTS.md does not name the Linear project, it asks before creating, linking or citing any ticket.

  • format and format:check get their files from git. Prettier reads the project's .gitignore and .prettierignore, but not .git/info/exclude or a person's global git ignore, so local tool files kept out of git that way, such as .claude/settings.local.json, made pnpm format:check fail on that machine only, and pnpm format rewrote them. Replace both scripts in package.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 . or prettier --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.json or .impeccable/*.local.json, remove them: git's ignore rules cover them now. Keep entries for files in git that a tool rewrites, such as pnpm-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 off docs/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.yaml is Prettier-ignored. pnpm rewrites it with single quotes on every install, so pnpm format:check failed after every update. Add pnpm-workspace.yaml to .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 from git remote get-url origin and 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". If AGENTS.md already 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-depth uses a named constant, MAXIMUM_NESTING_DEPTH.
  • The array is assigned to config before export 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 as durations.ts. There is no n object and no numbers.ts. If the project has a file of unrelated numbers, split it by topic. Copy the updated No magic numbers line from the AGENTS.md template. Run pnpm lint: a no-magic-numbers finding 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_PARAMETER list. Next.js: route handlers in app/**/route.ts may now take (request, context). If the project switched the rule off for them some other way, such as an override block or eslint-disable comments, 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/, as r, s and noData. r and s are 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's AGENTS.md or docs to the /docs/ address, such as https://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.md stays 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 Reference uses Ref, 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.md records 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-msg and .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 that pnpm lint passes 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 to lib/ with git mv, and fix the imports: @/library/ → @/lib/.
  • Rename every name ending in Reference to end in Ref, whatever it refers to: inputReference → inputRef, surfaceReference → surfaceRef. Rename repository back to repo where it is short for a Git repository, such as repositoryName → 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 } and allowList: { params: true, searchParams: true }.
  • Update the "Dictionary words stay short" line in AGENTS.md from the template, and remove lib/ → library/ and repo → repository from 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, .ts or .mts to .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.mjs and so on. Use git mv. Never convert a config to .ts or .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 the docsSite/ 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.mjs and .editorconfig. Prettier now has printWidth: 70 and useTabs: true, and keeps spaces for Markdown and YAML. EditorConfig indents with tabs, except Markdown, YAML and Python.
  • Run pnpm format once, 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. The docsSite/ 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 createClient becomes createProjectDatabaseClient, and Supabase's createClient keeps 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.

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/ with routes.ts, strings.ts, noData.ts and index.ts, as in Constants. If the project already has them somewhere else, such as app/constants/, move the folder with git mv and fix the imports: @/app/constants → @/lib/constants.
  • routes.ts exports r, with every link: the app's own pages, outside sites, and mailto:, tel: and sms: links. strings.ts exports s, with every piece of static text. Use named exports, export const r = { … }, never export default.
  • Move links and text written straight into components into r and s.
  • Rename each noData export to NO_DATA_ plus the thing it stands in for: noDataInsights → NO_DATA_INSIGHTS. Move dummy values, such as dummyHeading, out of s to the top of noData.ts as DUMMY_HEADING and the rest. Move anything in noData.ts that is real copy, such as a page's metadata, into s.
  • 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 r and s exception from the AGENTS.md template.
  • Add 12.1 to the project's standards: it is a new standard, so ask the person whether the project follows it. Record a no in docs/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.txt to 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's toEqual({ … }): 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 minimumReleaseAge when 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 in node_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 from node_modules/@activatus/code-standards/docs/. Copy the wording from the AGENTS.md template.

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/ and documentationSite/ exist, merge them into docsSite/: move each tracked file from documentationSite/ with git mv, keeping whichever copy is newer, then delete documentationSite/. 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_URL in next.config.mjs, .env.local, .env.example and the hosting provider's settings.
  • Rename the route /documentation → /docs. Copy the new rewrite from the next.config.mjs template.
  • Re-copy docsSite/docusaurus.config.ts from the docsSite/ template, keeping the project's name and theme. Version 2 changed it: baseUrl: '/docs/', path: '../docs', full-word variable names (rest → remainingArguments, doc → page), and static/img → static/images. Rename the project's docsSite/static/img/ to static/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 the docsSite/ template's .gitignore.
  • Update every mention in AGENTS.md, README.md and 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 own node_modules makes 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 Properties to end in Props: 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 properties to props, 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-replacements now has replacements: { docs: false, props: false, utils: false }.
  • 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 to utils/ with git mv, at every level: global, section and component.
  • Rename any helpers/ folder to utils/. Split a helpers.ts grab-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 byLives in
Every project the same way: the Design System Componentscomponents/ui/
More than one page of this projectcomponents/shared/
A few pages in one sectionapp/(section)/components/
One page onlyapp/page-name/components/

What to do:

  • For each component in components/features/ or components/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/.
  • Move a feature's utils/ folder the same way, to wherever its functions are used.
  • Delete the empty components/features/ and components/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.md from 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 cn function: it stays cn.
  • 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.md from 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.