Documentation Maintenance
This document gathers the maintenance procedures for all parts of the documentation portal. Use as a reference when adding, modifying, or removing content.
Build Flow Overview
docs:rtm → docs:openapi → docs:typedoc → vitepress build
│ │ │ │
│ │ │ └── final site
│ │ │ at docs/.vitepress/dist/
│ │ │
│ │ └── docs/reference/typedoc/
│ │ └── docs/public/reference/typedoc/
│ │
│ └── docs/reference/api/openapi.json
│ └── docs/public/reference/api/openapi.json
│
└── docs/quality/rtm.mdPhase 1 — VitePress Portal
How to add a new page to the portal
- Create the
.mdfile in the corresponding folder (e.g.docs/frontend/my-page.md) - Add YAML frontmatter (see pattern below)
- Add the page to the sidebar in
docs/.vitepress/config.mts:- pt-BR: in the
ptSidebarsection (links without/en/prefix) - en-US: in the
enSidebarsection (links with${enPrefix}/prefix)
- pt-BR: in the
- Run
pnpm docs:build— VitePress validates internal links
Standard frontmatter
Every markdown document in the portal must start with:
---
title: Page Name
description: One-line summary of the page's purpose.
status: published | draft
owner: engineering | product | design
lastReviewed: 2026-06-10
---How to change navigation
- Top navbar: edit
themeConfig.navinsidelocales.root.themeConfig(pt-BR) andlocales.en.themeConfig(en-US) inconfig.mts - Sidebar: edit the
ptSidebarorenSidebararrays in the same file - Footer: edit
themeConfig.footerwithin each locale
i18n Locale
The portal has two locales: root (pt-BR, default) and en (en-US).
docs/en/is a hand-maintained English translation layer — every.mdthere is a real translation, not a byte copy ofdocs/. There is no copy script;scripts/copy-docs-en.shwas retired because itrm -rf'ddocs/en/and would have destroyed the hand-written translations.- When a root page changes, update its
docs/en/counterpart in the same change, then refresh the freshness pin withpnpm docs:i18n:commit -- <paths>(see "Source Language Policy" and "en mirror policy" below). - To add locale-specific content, create the file only in the desired locale folder.
Source Language Policy
- The root docs (
docs/) are the canonical pt-BR source. Theenlocale is a derived translation layer, never a co-author. default_lang_commitsemantics: eachdocs/en/page records in its frontmatter the commit hash of the root (pt-BR) page it was translated from. An en page is drifted when its pin is older than the current commit of the root file; refreshing the pin (pnpm docs:i18n:commit -- <paths>) declares the translation is in sync again. A# patchedmarker allows mechanical fixes (link retargets, build fixes) without a content resync.- Documented exceptions: root pages whose body is still authored in English, pending pt-BR conversion (tracked in the v1 i18n backlog):
docs/index.md,docs/maintenance.md(this guide),docs/community/security-policy.md,docs/community/code-of-conduct.md. These pages are exempt from the "root = pt-BR" rule until converted.
en mirror policy
The docs/en mirror follows the no-partial policy (Kubernetes/MDN model, see spec #588), enforced by scripts/check-i18n.sh in the CI docs job and in pnpm docs:check:
- Orphan — an en page without a root counterpart fails (delete the en page).
- Stub — an en page byte-identical to its root counterpart fails: it is an untranslated pt-BR page served under
/en(translate it or delete it). - Missing — a root page without an en counterpart fails, unless it is exempted by a glob in
docs/en/.i18n-exempt(internal-only docs:analysis/**,research/**,roadmap/**,big-fat-refactor-01/**, individual ADRs and SDD specs, internal audits/reference pages). - Pin — an en page without
default_lang_commitin its frontmatter fails. - Drift — an en page whose pin is older than the root file's current commit is reported as drifted (non-blocking warning) and gets
drifted_from_default: trueset in its frontmatter, which exempts it from the lychee dead-link check until resynced.
Workflow when a root page changes:
- Update the translation in
docs/en/. - Refresh its pin:
pnpm docs:i18n:commit -- product/prd(or all pages:pnpm docs:i18n:commit). pnpm docs:checkmust pass locally before pushing (CI enforces the same gate).
Adding a new page to the portal in pt-BR? Add the English translation in the same change — the missing check will block otherwise. Adding an internal-only pt-BR doc? Add a glob to docs/en/.i18n-exempt instead of a translation.
Commands
| Command | Action |
|---|---|
pnpm docs:dev | Development server (hot-reload) |
pnpm docs:build | Full production build |
pnpm docs:preview | Build preview |
pnpm docs:lint | markdownlint on all docs (incl. docs/en) |
pnpm docs:i18n | en-mirror policy check (check-i18n.sh) |
pnpm docs:i18n:commit | Refresh default_lang_commit pins (check-i18n.sh commit HEAD) |
pnpm docs:links | lychee dead-link check on the built site (drifted pages exempt) |
pnpm docs:staleness | lastReviewed staleness warning (incl. docs/en) |
pnpm docs:check | lint + i18n + build (what the CI docs job runs, plus lychee/staleness) |
Phase 2 — Editorial Taxonomy
Folder structure
docs/
├── index.md ← Portal landing page
├── getting-started/ ← Onboarding for new contributors
│ ├── overview.md
│ ├── quickstart.md
│ ├── repository-structure.md
│ └── contributor-workflow.md
├── product/ ← Business vision and product requirements
│ ├── index.md
│ ├── prd.md
│ ├── business-rules.md
│ ├── roles-and-permissions.md
│ ├── scope.md
│ └── success-criteria.md
├── architecture/ ← Architecture and design
│ ├── index.md
│ ├── srs.md
│ ├── sdd.md
│ ├── context-map.md
│ ├── domain-model.md
│ ├── sync-architecture.md
│ ├── auth-architecture.md
│ └── adr/ ← Architectural decisions (individual files)
│ ├── index.md
│ ├── ADR-0001.md
│ └── ...
├── frontend/ ← Frontend SPA documentation
│ ├── index.md
│ ├── app-overview.md
│ ├── offline-mode.md
│ ├── import-export.md
│ ├── reports.md
│ └── ui-flows.md
├── workers/ ← Backend documentation
│ ├── index.md
│ ├── workers-overview.md
│ ├── api-overview.md
│ ├── api-guide.md ← Guide on how to add routes
│ ├── auth.md
│ ├── idempotency.md
│ ├── d1-schema.md
│ └── legacy-fastify.md
├── operations/ ← Operations and deployment
│ ├── index.md
│ ├── environments.md
│ ├── deployment.md
│ ├── migrations.md
│ ├── seed.md
│ ├── backup-restore.md
│ ├── security.md
│ ├── lgpd.md
│ └── troubleshooting.md
├── quality/ ← Quality and testing
│ ├── index.md
│ ├── mtp.md
│ ├── rtm.md ← Automatically generated (csv → md)
│ ├── production-readiness-gates.md
│ ├── hardening-backlog.md
│ ├── test-execution-handbook.md
│ └── testing-patterns.md
├── reference/ ← Technical reference
│ ├── index.md
│ ├── api/
│ │ ├── index.md
│ │ ├── api-contract.md
│ │ └── openapi.json ← Automatically generated
│ ├── typedoc/ ← Automatically generated (gitignored)
│ ├── schemas.md
│ ├── permissions.md
│ ├── data-dictionary.md
│ └── extending-student-fields.md
├── community/ ← Community documents
│ ├── index.md
│ ├── contributing.md
│ ├── security-policy.md
│ ├── code-of-conduct.md
│ └── changelog.md
├── agents/ ← Internal Reasonix workflow docs (outside portal)
├── archive/ ← Preserved obsolete documents
└── RTM.csv ← Source for rtm.md generationRules
- Each section must have an
index.mdwith overview and link list - Links between documents use relative markdown paths (
../product/prd.md) - The VitePress sidebar is the source of truth for navigation — keep it in sync
docs/agents/contains internal Reasonix workflow documentation — do not link in the portaldocs/archive/contains obsolete documents preserved for historical reference
How to move or rename a document
- Move/rename the file
- Update the sidebar in
docs/.vitepress/config.mts(in bothptSidebarandenSidebarsections) - Update internal links in other documents that pointed to the old path
- Update REDIRECT.md or third-party links (README.md, CONTEXT-MAP.md, AGENTS.md, REASONIX.md)
- Run
pnpm docs:buildto check for dead links
Phase 3 — OpenAPI / Live API
See the full guide in API Guide.
Summary flow
- Add the handler in
workers/src/routes/ - Import and register in
workers/src/router.ts(viaroute()helper with declarative middlewares) - Add entry in
scripts/openapi-registry.ts(with Zod schema, example, auth) - Run
pnpm docs:openapito validate coverage and generateopenapi.json
Automatic validation
The scripts/generate-openapi.ts script verifies that every route in router.ts has a corresponding entry in the registry. If missing, the build fails with:
Route POST /api/v1/customers is NOT documented in openapi-registry.tsFiles
| File | Purpose | Editable? |
|---|---|---|
scripts/openapi-registry.ts | Registry with schemas, examples, and metadata for each route | ✅ Yes |
scripts/generate-openapi.ts | OpenAPI 3.1 generator | Rarely |
docs/reference/api/openapi.json | Generated spec (source) | ❌ Generated |
docs/public/reference/api/openapi.json | Spec for VitePress build | ❌ Generated |
Phase 4 — TypeDoc
How to add a new entry point
- Edit
typedoc.json, add the file path toentryPoints - Edit
typedoc.tsconfig.json, add toinclude - Run
pnpm docs:typedocto generate - If you want a sidebar link, add it in
docs/.vitepress/config.mtsunder Reference > TypeDoc (sub-item)
Common error resolution
| Error | Cause | Solution |
|---|---|---|
TS2322: Uint8Array not assignable to BufferSource | Workers-specific runtime type | Exclude the file from entry points |
TS2339: timingSafeEqual does not exist | Workers API not available in standard TypeScript | Exclude the file from entry points |
Module has no exported member | Path alias not resolved | Add to paths in typedoc.tsconfig.json |
Files
| File | Purpose | Editable? |
|---|---|---|
typedoc.json | TypeDoc config (entry points, output) | ✅ Yes |
typedoc.tsconfig.json | TSConfig for TypeDoc to resolve workspace packages | Rarely |
scripts/generate-typedoc.sh | Script that generates + copies to docs/public/ | Rarely |
docs/reference/typedoc/ | Generated HTML | ❌ Generated (gitignored) |
docs/public/reference/typedoc/ | Copy for VitePress build | ❌ Generated (gitignored) |
Current entry points
| Entry Point | Content |
|---|---|
packages/permissions/index.ts | PERMISSIONS, UserRole, Permission |
packages/schemas/src/index.ts | All Zod schemas + enums + utils (HttpError, sanitizePayload) |
workers/src/modules/auth/sessionTokens.ts | signAccessToken, verifyAccessToken, createRefreshToken, hashRefreshToken |
workers/src/modules/idempotency.ts | payloadHash, isSamePayloadHash |
workers/src/schemas.ts | HTTP API schemas (auth, student, attendance, sync) |
Phase 5 — RTM (Requirement Traceability Matrix)
How to update requirements
- Edit
docs/RTM.csv(spreadsheet with 6 columns:RequirementID,Category,RequirementSummary,PrimarySuite,TestCaseRefs,CoverageStatus) - Run
pnpm docs:rtmto regeneratedocs/quality/rtm.md - Run
pnpm docs:buildto verify
CSV fields
| Column | Example | Required |
|---|---|---|
RequirementID | FR-001 | Yes |
Category | Functional | Yes |
RequirementSummary | Support all defined roles | Yes |
PrimarySuite | RBAC | Yes |
TestCaseRefs | TC-RBAC-001;TC-RBAC-002 | Yes (separated by ;) |
CoverageStatus | Planned, Implemented, Passed, Failed | Yes |
New requirements
- IDs follow the format
<PREFIX>-<NUMBER>:FR-,NFR-,DR-,SR-,SCR- - Insert at the end of the corresponding section or reorder freely (the generator preserves CSV order)
- When changing
TestCaseRefs, use;as separator — no spaces - For coverage status, use
Planned(not tested),Implemented(code exists),Passed(tests pass),Failed(tests break)
Files
| File | Purpose | Editable? |
|---|---|---|
docs/RTM.csv | Data source | ✅ Yes (manual editing or spreadsheet) |
scripts/generate-rtm.ts | CSV → Markdown generator | Rarely |
docs/quality/rtm.md | Generated page | ❌ Generated |
Script Summary
| Script | Phase | Generates | Validates | Frequency |
|---|---|---|---|---|
| — | 1 | — | docs:build validates dead links | Always on build |
scripts/generate-openapi.ts + openapi-registry.ts | 2 | openapi.json | ✅ Coverage against router.ts | New route |
scripts/generate-typedoc.sh | 3 | docs/reference/typedoc/ | — | New entry point |
scripts/generate-rtm.ts | 4 | docs/quality/rtm.md | — | New requirement |
Checklist for New Content
- [ ]
.mdfile created in the correct folder - [ ] YAML frontmatter present (title, description, status, owner, lastReviewed)
- [ ] Sidebar updated in
docs/.vitepress/config.mts(pt-BR + en-US) - [ ] If it's a new route: handler → router.ts (via
route()helper with declarative middlewares) → openapi-registry.ts - [ ] If it's a new requirement: RTM.csv →
pnpm docs:rtm - [ ] If it's a new TypeDoc entry point: typedoc.json + typedoc.tsconfig.json
- [ ]
pnpm docs:buildpasses without errors