ADR 0026: Internal service documentation as source of truth
Ratification
This ADR sets rules for internal HTML under docs/internal/: what belongs there, how
pages relate to code and OpenAPI, and how we keep history. It works with
ADR 0025 (who reads what). Git history on
main is the record for this file.
- Discussion Issue: optional — link when opened with
[ADR]title - Merge PR: see git history for this file
- Accepted: 2026-04-14
- Record updated: 2026-04-15 — concrete
docs/internal/tree, hub-and-spoke HTTP docs, navigation ownership (internal-sidebar.js), contributor layout indocs/howto/internal-service-docs-layout.html(STRUCTURE.mdstub links there), and how-to index atdocs/howto/README.html. - Record updated: 2026-04-17 — standardized docs block semantics for how-to and ADR index pages: use
<section class="card">for content blocks (avoid extra<div class="card">wrappers) to keep visual hierarchy and HTML semantics consistent across docs.
Context
Why this matters: External clients use OpenAPI (ADR 0007). Inside the team we still need one place we can review for rules across layers: business invariants, when an error is operational vs validation, how we expect to monitor a flow, and how HTTP handlers map to services and async work. Without that, people fall back to “read the code,” which is slow and unclear when code and branches differ.
This ADR makes docs/internal/ the main engineering narrative for those topics.
It does not copy OpenAPI field lists; it explains them and ties them to behavior and
operations.
Decision
-
Location and format. Internal service documentation lives under
docs/internal/as static English HTML. All such pages use the shared site stylesheetdocs/assets/docs.cssand top navigationdocs/assets/docs-nav.js. Multi-page internal docs (API hubs, resource hubs, per-method pages) additionally usedocs/assets/internal-layout.cssanddocs/assets/internal-sidebar.jsfor the two-column shell and left navigation (see Navigation and chrome). In documentation indexes and guides (for example underdocs/howto/anddocs/adr/), use semantic<section class="card">blocks for major content groups, without nested<div class="card">wrappers. New or changed pages are added or updated in the same pull request as the behavior change when the change is user-visible or alters invariants described here. -
Source of truth for engineering disputes. For topics explicitly covered in
docs/internal/, the written page (onmainafter merge) is the normative reference for how the team intends the system to work. If code disagrees, either the code or the doc must be fixed in a follow-up; silent divergence is treated as a defect. Topics not yet documented remain governed by ADRs, runbooks, and code until a page exists. -
What to document (non-exhaustive). Pages should cover, as applicable:
- Capability or domain slice — purpose, boundaries, key entities.
- HTTP mapping — reference
operationIds or paths that implement the slice; link to OpenAPI rather than copying schemas. - Business rules — invariants, ordering assumptions, idempotency and conflict behavior (see also ADR 0006).
- Errors and observability — which codes or signals warrant alerts vs expected client mistakes; correlation with logs and metrics (see ADR 0003, ADR 0009, ADR 0023).
- Async and integration — message topics, consumers, outbox patterns, or external systems, when present.
-
History and traceability. Material pages include a short Page history
subsection (table: date, author optional, summary of change). Git history remains the full audit trail; the
table is for human scanning. Breaking behavioral changes also appear in root
CHANGELOG.mdper ADR 0013. -
Entry points.
docs/internal/README.htmlis the top-level index (teams, practices, links into HTTP docs). The directory tree and contributor checklist live indocs/howto/internal-service-docs-layout.html;docs/internal/STRUCTURE.mdis a short pointer to that page and should stay aligned when the tree changes. -
Relationship to ADRs. Cross-cutting policy (versioning, security, idempotency) stays in ADRs.
docs/internal/applies those policies to concrete flows and entities. If a new policy is needed, add or update an ADR; do not bury policy-only decisions in internal prose alone.
Repository layout under docs/internal/
Internal docs are organized so readers can move from product context → HTTP surface → one resource → one endpoint, without a single monolithic HTML file for everything.
Path (under docs/internal/) |
Role |
|---|---|
README.html |
Single entry: project context, service role and boundaries, contract and observability summary, links to ADRs and the HTTP API area. (Former service-overview.html content merged here.) |
STRUCTURE.md + docs/howto/internal-service-docs-layout.html |
Full tree outline and contributor checklist in the how-to HTML page; STRUCTURE.md links there.
Update both when adding resources or renaming paths. |
api/README.html |
HTTP API hub: lists documented resources and points to each resource hub. |
api/<resource>/index.html |
Single entry point per resource — business context, contract summary, governance, technical
depth (layers, idempotency, errors, logging, metrics, dependencies), endpoint index, and stable fragment
identifiers (e.g. #user-op-createUser) for deep links from per-method pages.
|
api/<resource>/operations/*.html |
One page per HTTP endpoint, named from method + path template (not from Python
operationId), focused on call semantics; each page links back to anchors on the resource
index.html. |
api/<resource>/… redirect stubs (optional) |
Legacy filenames may remain as minimal HTML that redirect to index.html with hash preserved,
so old bookmarks keep working. |
Navigation and chrome
-
Top nav.
docs/assets/docs-nav.jsprovides the global “pill” navigation acrossdocs/(ADRs, developer guides, internal docs, and so on). -
Left sidebar.
docs/assets/internal-sidebar.jsdefines the tree for internal pages in the arrayINTERNAL_SIDEBAR_NAV. Paths are expressed relative to thedocs/root (for exampleinternal/api/user/index.html). This array is the single source of truth for which internal pages appear in the sidebar; adding or renaming a page requires updating it (and the how-to layout page /STRUCTURE.mdstub) alongside the HTML. - Labels. Operation entries in the sidebar use short, HTTP-first labels (e.g. action + method). The full path belongs on the per-method page hero, not duplicated in the nav.
-
Markup. Pages that use the sidebar include the shell from existing internal templates: aside with
#internal-sidebar-mount, main column, and the scripts above. FollowREADME.htmlas a reference. -
In-page TOC rule. Keep only one in-page navigation block:
On this page from
docs-nav.js. Do not author a manualContentsblock on pages that load this script; if the mount is missing,docs-nav.jscreates it automatically.
Contributor workflow
When documenting or changing HTTP behavior:
- Keep OpenAPI (
docs/openapi/) and contract tests aligned per ADR 0007. - Update the resource
api/<resource>/index.htmlif business narrative, invariants, or cross-cutting technical behavior (idempotency, logging, metrics) change. - Update affected
operations/*.htmlpages when endpoint-specific detail changes. - Update
INTERNAL_SIDEBAR_NAVwhen adding routes or pages; add a row to Page history on substantive hub updates. - Reflect notable doc-tree changes in
docs/CHANGELOG.mdwhen the documentation set itself is part of the deliverable.
The checklist in
docs/howto/internal-service-docs-layout.html
mirrors this workflow for new resources (new folder under api/, naming convention for operation files,
link from api/README.html).
Scope
-
In scope: Structure, authority, minimum expectations, navigation ownership, and extension process
for
docs/internal/HTML (and the linked how-to /STRUCTURE.mdstub). -
Out of scope: Replacing OpenAPI, replacing pdoc under
docs/api/, or duplicating runbook procedures (link todocs/runbooks/instead).
Alternatives considered
-
Wiki or external tool.
- Pros: rich editors, permissions.
- Cons: not versioned with the same PR as code; harder to enforce review alongside behavior changes.
-
Markdown only under
docs/internal/.- Pros: faster to write.
- Cons: this site’s published docs are HTML-first; one stack keeps nav and styling consistent.
-
One very long HTML file per resource.
- Pros: single URL for everything.
- Cons: poor scanability; heavy merge conflicts; we instead use a hub plus per-method pages with reciprocal anchor links.
Consequences
Positive
- Engineers can resolve “what did we agree?” without inferring from code alone for documented areas.
- Onboarding gains a bounded reading path: internal README → API hub → resource hub → method page.
- Navigation has an explicit owner (
INTERNAL_SIDEBAR_NAV), reducing orphan pages.
Trade-offs
- Ongoing maintenance: hubs, operation pages, sidebar, and the how-to layout page (plus
STRUCTURE.md) must move together when the HTTP surface changes, or authority of the doc set erodes.
Page history
| Date | Change | Author |
|---|---|---|
| Added Page history section (repository baseline). | Ivan Boyarkin |