Skip to main content

Documentation

Every project documents itself the same way, so a developer or an AI agent can open any Activatus project and find what they need in the same place.

This is the docs framework from Launchpad. It is used for Launchpad itself and for every other project.

How it worksโ€‹

PartWhat it isWhere it lives
The docsPlain Markdown files. Every one is a Decision Docdocs/ in the project root
The viewerA Docusaurus site that displays those filesdocsSite/ in the project root
The routeThe docs appear at /docs in the running projectSwitched on by the DOCS_URL environment variable

The docs are written once, as Markdown, and read two ways:

  • AI agents read the files in docs/ directly. No build, no server.
  • People browse them at /docs, with a menu, search and page contents โ€” the same layout as this site.

๐Ÿ“œ Every file is a Decision Docโ€‹

Every file in docs/ is a Decision Doc. There is no separate decisions/ folder.

A design system is a decision. A scope is a decision. A database choice is a decision. Each doc states one decision and why it was made, so anyone โ€” or any agent โ€” can trace the work back to the reason for it, and never has to invent a product decision to unblock themselves.

  • Tickets own the what. "Build GitHub authentication."
  • Decision Docs own the why. "Developers are our audience and we must sync directly to their repositories."

See the Ontology for the terms.


๐Ÿ“ The foldersโ€‹

Every project has the same folders. The folder is the menu group.

Reusable foldersโ€‹

These are the same in every project:

FolderHolds decisions aboutFiles
overview/What the project is and how to read its docswhat-is-it, how-it-works, writing-docs
ai/Agents, models and what they may doagent-harness, agents, architecture, context, model-routing, models, retrieval, tools
architecture/How the system is builtauthentication, backend, background-jobs, backups, caching, ci-cd, disaster-recovery, environments, frontend, hosting, observability, realtime, search, services, state-management, storage, system
business/The customer and the moneybuying-trigger, customer, customer-value, defensibility, growth, pricing, revenue-model, unit-economics
data/What is stored and howanalytics, audit, database, indexing, migrations, model, retention, schema
deployment/Getting work to productionapproval, live-verification, process, rollback
design/How it looks and behavesdesign-system, errors, feedback, interactions, layout, navigation, principles, responsive
development/How code is written and mergedbranching, code-quality, coding-standards, commits, merge-strategy, process, pull-requests
experience/What using it should feel likeaccessibility, aha-moment, anti-experience, friction, outcomes, principles, retention, sharing
pitch/Why the product should existalternatives, assumptions, audience, concept, differentiators, outcomes, problem, product-market-fit, success-criteria, validation, value-proposition, vision
scope/What is in, out and latercore, exclusions, future, launch, post-launch, priorities, quality-bar
security/Keeping it safeaudit, data-access, logging, privacy, secrets, threat-model
testing/How it is testedend-to-end, integration, regression, security, strategy, test-data, unit
uat/Checking we built the right thingchecks, strategy
users/Who uses it and what they may dopermissions, roles, user-types
workflows/How work movesassignment, bugfix, dependencies, deployment, development, planning, review, uat

Product-specific foldersโ€‹

Decisions that only make sense for this one product go in their own folders:

FolderHolds
product/What this product is and how its main parts work. Starts with ontology.md: the product's words and how it speaks
integrations/One file per outside service the product connects to
user-flows/One file per User Flow
A folder for the product's own subjectFor example, Launchpad has tickets/

Which folder?โ€‹

Before placing a doc in a reusable folder, ask: would this topic make sense as a standard decision category for a completely different software product?

  • Yes โ†’ a reusable folder.
  • No โ†’ a product-specific folder.

Keep these pairs distinct:

  • Authentication (who are you?) belongs in architecture/authentication.md. Permissions (what are you allowed to do?) belong in users/permissions.md.
  • data/model.md is the conceptual model, data/database.md is the technology and strategy, and data/schema.md is the concrete structure.

Every file exists from the startโ€‹

A new project starts with every reusable file already in place, each marked Status: Not started. Copy the doc templates.

  • When a decision is made, write it into the matching file.
  • Create a new file only when a topic has no home.

A Not started file is a visible gap: it shows a decision that has not been made yet.

Keep every folder and file, even ones that do not apply yet. They keep every project's docs laid out the same way, and a small project such as a one-page site grows into them. Never delete one to tidy the menu.

Decision Docs belong to the personโ€‹

Confirm with the person before creating, changing or deleting a Decision Doc, including deleting a folder. A decision is theirs to make, not the agent's. The exception is a project whose AGENTS.md names an agent that manages the docs, such as a planning agent: that agent follows its own instructions.


๐Ÿ“„ The doc formatโ€‹

Every doc has the same shape:

# <Group> โ€” <Menu item>

> One line: what this doc decides.

**Status:** Not started | Under evaluation | Current plan | Decided ยท **Updated:** YYYY-MM-DD

## Decision

## Why

## Details (optional)

## Alternatives considered (optional)

## Current implementation (only when the code differs)

## Related

## Retro Feedback (added as tasks complete)

Statusโ€‹

StatusMeans
Not startedA template waiting for its first decision
Under evaluationNot decided. The options are recorded
Current planThe direction we are working to. It may change
DecidedAn intentional current decision. Change it only with a Decision Doc update

Sectionsโ€‹

SectionHolds
DecisionWhat was decided
WhyThe reasons
DetailsAnything needed to act on the decision
Alternatives consideredWhat was rejected, and why
Current implementationOnly when the code differs from the decision: describe the gap
RelatedLinks to related docs
Retro FeedbackWhat went well and what did not, added as tasks linked to this doc are completed

A doc states the decision, not the code. When the code does not match yet, the gap goes under Current implementation.

When a decision changesโ€‹

Update the doc, change the Updated date, and add a ## History entry: what changed, why, when, and which work was affected. Keep the original decision and its reason in the history.

The Updated date tracks the decision, not the file. Moving, renaming, reformatting or converting a doc to this format, with no decision changed, leaves the date as it is.


๐Ÿงญ One file, one menu item, one routeโ€‹

The docs menu is built straight from the docs/ folder, so every doc is in the menu and nothing else is.

RuleExample
The folder is the menu groupscope/ shows as Scope
The title after the โ€” is the menu item# Scope โ€” Future shows as Future
The file name is that title in kebab-case# Scope โ€” Future lives in scope/future.md
The route is the folder plus the file name, without .mdscope/future.md is served at /docs/scope/future
  • Overview comes first in the menu. Every other group is in alphabetical order, and items are alphabetical within their group.
  • Link between docs with relative .md paths โ€” [Future](../scope/future.md). The link works in the file and on the site.
  • Folder and file names are kebab-case, because they become the route. See Naming Conventions.

The viewer does this automatically. There is no menu file to maintain.


โœ๏ธ Rules for writingโ€‹

  • Use the Ontology. Use its terms for code, and the project's own ontology for product words.
  • One decision per file.
  • Plain Markdown (.md). No JSX or imports, so every file reads the same for an agent as it does on the page.
  • One # Heading per file, at the top, in the form # Group โ€” Menu item.
  • Draw diagrams with Mermaid in a ```mermaid code block, so the diagram is text and stays in the file.
  • Write for someone who has never seen the project. Full sentences, full English words, no shorthand.

Not finished until the docs are updatedโ€‹

A change is not finished until the docs match it. If a change makes a doc wrong, the doc is fixed in the same change โ€” the same rule as test files.

This applies to everyone writing code, including AI agents. An agent that changes how something works updates the Decision Doc that describes it.


๐ŸŒ The /docs routeโ€‹

The docs appear at /docs inside the running project โ€” http://localhost:3000/docs โ€” in the same layout as this site.

They are switched on by one environment variable:

DOCS_URLWhat happens at /docs
Set (http://localhost:3001)The docs are shown
Not set404 โ€” the route does not exist

โ›” Never in productionโ€‹

DOCS_URL is never set in production. With it unset, the project has no /docs route at all, so internal docs cannot be reached on a live site.

  • Set it in .env.local for local development.
  • Set it on a staging or preview environment only if the team wants the docs there.
  • Do not add it to the production environment.

The docs site also sets noIndex, so search engines are told not to index it wherever it is running.

What goes in Gitโ€‹

The docs are committed. They are not published. Committing a file to the repository and serving it on the live site are two different things.

ItemIn Git?Why
docs/โœ… CommittedIt is the team's shared record. Developers and AI agents on every machine need it, and it changes in the same pull request as the code
docsSite/ config filesโœ… CommittedSo everyone gets the same viewer
docsSite/node_modules/, docsSite/build/, docsSite/.docusaurus/โŒ IgnoredInstalled and generated files. The docsSite/.gitignore from the template covers them
.env.local (holds DOCS_URL)โŒ IgnoredLocal settings are never committed

Never add docs/ to .gitignore. Ignored docs exist on one machine only: the rest of the team, and every agent working from a fresh copy of the repo, would have none.

Nothing in docs/ is shipped with the app. The build only includes files the app imports, and the app never imports from docs/.

Public repositories

Anyone who can read the repository can read its docs. Keep internal projects in a private repository.

How the route is wiredโ€‹

The project's next.config.mjs forwards /docs to the docs site, but only when DOCS_URL is set:

next.config.mjs
const DOCS_URL = process.env.DOCS_URL;

/**
@type {import("next").NextConfig}
*/
const nextConfig = {
async rewrites() {
// The docs are only served when DOCS_URL is set. It is never set in production.
if (!DOCS_URL) {
return [];
}

return [
{ source: "/docs", destination: `${DOCS_URL}/docs/` },
{
source: "/docs/:path*",
destination: `${DOCS_URL}/docs/:path*`,
},
];
},
};

export default nextConfig;

๐Ÿ”„ Converting existing docsโ€‹

How to turn a project's existing documentation into Decision Docs. Every word is kept: converting changes where things live and how they are laid out, never what they say. Confirm the plan with the person before starting.

Outside material, such as client briefsโ€‹

A brief says what to build rather than recording a decision, but it is still a decision: to build what the brief says.

  1. Give the product its own folder, such as docs/homepage/, with one doc per part of the product.
  2. Decision: "Build it as the brief below says."
  3. Why: the client's reason, and which brief wins where two disagree.
  4. Details: the brief's full text, unchanged.
  5. A superseded brief goes under ## History, marked "Reference only".
  6. The table of which brief wins goes in scope/core.md.

Build decisions made along the wayโ€‹

One decision per file. A decision made while building a part, such as "the hero cards become the rail's cards", gets its own doc beside that part's doc, in the same product folder. It is never folded into the part's doc.

Headingsโ€‹

A doc has one # heading, its title. Push every heading in the old content down by the smallest step that frees #: if the old doc's top level is #, every heading moves down one level. Skip headings inside code blocks. If that pushes any heading past ######, split the doc into one doc per top-level part instead. The site's menu on the right shows levels 2 and 3 only, so keep the important structure there.

Imagesโ€‹

Images go in docs/assets/, in a folder per product area, such as docs/assets/homepage/. Link each image from the doc that uses it, with a relative path. Refer to a folder of images as a code path, docs/assets/homepage/, never a link: the docs site cannot open a folder, so the link breaks the build.

Old viewer leftoversโ€‹

Delete every _category_.json, the sidebar_position, sidebar_label, slug, status and lastReviewed front matter, and docs/index.md once its content has moved to overview/.

The Updated dateโ€‹

Converting a doc does not change its decision, so the Updated date stays as it was. Use the date of the last real decision, from the old doc or the git history.


๐Ÿš€ Setting it upโ€‹

  1. Copy the docs. Copy the docs/ templates into the project's docs/ folder. Converting docs from version 2 or earlier? Delete the old viewer's leftovers: every _category_.json, the sidebar_position, sidebar_label, slug, status and lastReviewed front matter, and docs/index.md once its content has moved to overview/. Rename overview/what-is-it.md after the project (what-is-launchpad.md), update its title, and update the link to it in overview/writing-docs.md. The docs build fails on a broken link.

  2. Copy the viewer. Copy the docsSite/ template into the project root. Rename package.json.txt to package.json and gitignore.txt to .gitignore, set PROJECT_NAME in docusaurus.config.mjs, and point the home redirect in src/pages/index.tsx at the renamed overview file.

  3. Add the route. Add the rewrite above to next.config.mjs.

  4. Add the scripts to the project's package.json:

    {
    "scripts": {
    "docs:install": "pnpm --dir docsSite install",
    "docs:dev": "pnpm --dir docsSite start",
    "docs:build": "pnpm --dir docsSite build"
    }
    }
  5. Set the variable in .env.local:

    DOCS_URL=http://localhost:3001
  6. Run it:

    pnpm docs:install # Once
    pnpm docs:dev # Starts the docs site on port 3001
    pnpm dev # Starts the project on port 3000

    Open http://localhost:3000/docs.

  7. Ask the person to review docs/product/ontology.md before work starts: the product's words and how it speaks. Every agent reads it, so a wrong word spreads. Tell them to ask their AI to make any changes.

caution
Do not name the script docs

pnpm docs is a built-in pnpm command, so a script called docs never runs. Use docs:dev.


๐Ÿค– For AI agentsโ€‹

  • Read docs/ before starting work. Start with the three overview/ docs, then the docs for the area being changed.
  • Read the files directly. Do not start the docs site to read them.
  • Check the Status. Decided is settled. Current plan may change. Under evaluation and Not started are not decisions โ€” do not treat them as one, and do not invent one. Ask.
  • Update the docs in the same change as the code, and change the Updated date.
  • Record a check only after it has run. Never write 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.
  • Write into the matching file. Create a new file only when a topic has no home, in kebab-case, using the doc format.
  • Do not edit anything in docsSite/ unless the task is about the docs viewer itself.