Skip to content

Decision Register

Consolidated decisions from 115 feature(s), 1 service(s), and 4 principle(s).

Summary

IDDecisionStatusSource
use-deps-not-inlineUse battle-tested CLI libraries, not hand-rolled inline helpersacceptedusm/cli-color-output
respect-no-colorHonor NO_COLOR env var and non-TTY stdout/stderr by emitting plain textacceptedusm/cli-color-output
palettegreen=✓, red=✗, yellow=⚠, cyan=→/info, dim=secondary valuesacceptedusm/cli-color-output
cjs-pin-versionsPin dependency versions to CJS-compatible majorsacceptedusm/cli-color-output
outputs-from-configAll generators read output paths from usmconfig.json outputs sectionacceptedusm/cli-config-outputs
generate-only-flagReplace generate:xxx commands with generate --only <target> flagacceptedusm/cli-config-outputs
scaffold-project-to-subcommandRename scaffold-project to 'scaffold project' (subcommand)acceptedusm/cli-config-outputs
no-dependencyImplement port probing with Node.js built-in net module — no third-party dependeacceptedusm/docs-serve-port-check
auto-port-opt-inAuto-port selection is opt-in via --auto-port flag, not the default.acceptedusm/docs-serve-port-check
lsof-best-effortProcess detection via lsof is best-effort only — graceful degradation if lsof isacceptedusm/docs-serve-port-check
pid-file-approachUse a PID file (.usm-workspace/.vitepress.pid) for already-serving detection, noacceptedusm/docs-serve-port-check
watch-in-processWatch-mode regeneration runs usm generate --only docs in-process (calling the geacceptedusm/docs-serve-port-check
debounce-500msWatch-mode debounce is 500ms — a pragmatic balance between responsiveness and avacceptedusm/docs-serve-port-check
vitepress-over-customUse VitePress as the rendering layer instead of building a custom serveracceptedusm/cli-docs
unified-docs-directoryAll generated docs write to a single docs/ directory instead of scattered .agentacceptedusm/cli-docs
sidebar-from-system-indexAuto-generate VitePress sidebar config from system.usm index + feature directoryacceptedusm/cli-docs
vitepress-optional-dependencyVitePress is an optional peer dependency, not bundled with USMacceptedusm/cli-docs
dont-commit-generated-docsGenerated docs are not committed to the repo — served locally and deployed via Cacceptedusm/cli-docs
internal-over-externalInternal (host-language) DSL, not a custom grammarusm/internal-dsl-builder
runtime-schema-validationEnforce constraints by validating at build() rather than type-level brandingusm/internal-dsl-builder
all-35-in-docsGenerate per-editor setup pages for all 35 editorsacceptedusm/mcp-setup-guides
top-10-logosMarketing site ToolLogos shows top 10 editors onlyacceptedusm/mcp-setup-guides
index-plus-per-editorIndex page plus per-editor pages (shadcn pattern)acceptedusm/mcp-setup-guides
rules-files-per-editorDocument rules/skills file installation on each editor page where supportedacceptedusm/mcp-setup-guides
stdio-config-patternUse the standard USM stdio pattern (usm mcp serve) for all editorsacceptedusm/mcp-setup-guides
manifest-based-detectionDetect services by language manifest filesacceptedusm/cli-multi-lang-scan
framework-specific-route-detectionDetect routes per-framework, not per-languageacceptedusm/cli-multi-lang-scan
configurable-in-usmconfigLanguage/framework detection rules are configurable in usmconfig.jsonacceptedusm/cli-multi-lang-scan
all-languages-from-startSupport all major languages from the initial implementationacceptedusm/cli-multi-lang-scan
detectors-directoryAdd .usm/detectors/*.yaml as a second auto-discovered extension surface alongsidacceptedusm/cli-multi-lang-scan
script-escape-hatchAllow routes.script in a detector to point at a .ts/.js file exporting extractRoacceptedusm/cli-multi-lang-scan
generalized-orchestratorGeneralize structural.ts service detection so any detector manifest drives it, racceptedusm/cli-multi-lang-scan
infrastructure-as-detectorTreat infrastructure (Terraform today) as a detector kind rather than a hardcodeacceptedusm/cli-multi-lang-scan
precedence-orderDetector precedence is built-in defaults < .usm/detectors/ files < usmconfig.jsoacceptedusm/cli-multi-lang-scan
tiny-grammar-not-sqlA ~200-line recursive-descent grammar, not SQL/jq subsetusm/query-layer
missing-field-is-falseUnknown/absent fields make predicates false instead of erroringusm/query-layer
capability-registry-patternCapabilities self-describe (detect + setup + introducedIn); upgrade orchestratesacceptedusm/upgrade
usm-version-field-for-alignmentUse system.usm.usm_version for USM-tool alignment, NOT the project's own versionacceptedusm/upgrade
schema-version-independenceSchema $version moves independently of the package version: additive schema chanacceptedusm/upgrade
setup-owned-by-capabilityEach registry entry owns its own setup function (interactive + default); upgradeacceptedusm/upgrade
interactive-with-flag-fallbackTTY prompts per capability; --apply uses defaults for CI/scripts; --check is repacceptedusm/upgrade
non-destructiveUpgrade only adds missing blocks and bumps usm_version; existing config is neveracceptedusm/upgrade
homepage-simplify-not-removeSimplify the homepage by removing marketing content, not by removing the homepagacceptedusm/vitepress-home-feedback-schema
feedback-as-generated-pageThe feedback page is a generated markdown page (docs/feedback.md), not a customacceptedusm/vitepress-home-feedback-schema
agent-feedback-via-existing-mcpThe agent feedback path reuses the existing usm_report_feedback MCP tool (from uacceptedusm/vitepress-home-feedback-schema
cross-links-in-generatorCross-links and next-steps sections are added in the markdown generator, not hanacceptedusm/vitepress-home-feedback-schema
schema-as-single-sourceThe schema reference is generated entirely from schema/v1.json; terse field descacceptedusm/vitepress-schema-polish
homepage-from-system-usmHomepage hero, tagline, principle cards, and CTAs are derived from system.usm (iacceptedusm/vitepress-schema-polish
mermaid-dark-mode-awareInject a small theme-bridge script so Mermaid follows VitePress isDark instead oacceptedusm/vitepress-schema-polish
declarative-sidebar-groupsSidebar group order/names are driven by a declarative group-to-paths mapping whiacceptedusm/vitepress-schema-polish
native-vitepress-containersUse VitePress native containers and Mermaid code fences for visuals — no customacceptedusm/vitepress-schema-polish
priority-orderingDeliver schema-reference and homepage/getting-started first; visuals/polish lastacceptedusm/vitepress-schema-polish
content-block-systemDefine a content-block schema (heading, paragraph, code, mermaid, tabs, callout,acceptedusm/gen-content-blocks
reference-pages-on-systemsystem.usm gains reference_pages[] for product-level reference pages (language sacceptedusm/gen-content-blocks
reference-blocks-on-featuresFeature specs gain an optional reference[] field for user-facing reference conteacceptedusm/gen-content-blocks
block-level-audience-filterThe help-doc filter operates at content-block granularity (keep audience: publicacceptedusm/gen-content-blocks
preserve-schema-reading-generatorsgenerateConfigReference and generateSchemaReference are preserved — they read JSacceptedusm/gen-content-blocks
delete-after-migrationHardcoded functions are deleted only after their content is migrated to specs anacceptedusm/gen-content-blocks
emit-only-when-populatedEmit a templated page only when its source section is populated; never write a pusm/docs-experience
togaf-as-architecture-sectionSurface togaf output as the Architecture section of the docs site, not a separatusm/docs-experience
css-not-config-for-widthWiden via generated theme CSS custom properties, not VitePress config knobsusm/docs-experience
feature-cards-as-navUse the VitePress features grid as homepage navigation rather than forcing a sidusm/docs-experience
voice-is-render-modeAudience is a render mode (voice), not just a filterusm/docs-experience
post-process-filteringGenerate full docs first, then filter for help audienceacceptedusm/gen-docs-split
visibility-field-optionalAdd optional visibility field (public/internal) to features and servicesacceptedusm/gen-docs-split
help-docs-simplified-featuresHelp docs show summary + intent + flows only, not contracts/tests/implementationacceptedusm/gen-docs-split
review-mode-separate-from-referenceAdd a review-oriented template alongside the existing overview templateacceptedusm/gen-feature-review
flows-as-numbered-stepsRender flows as numbered human-readable steps, not tablesacceptedusm/gen-feature-review
contracts-as-checklistRender contracts as a markdown checklist the human can mentally tickacceptedusm/gen-feature-review
tests-as-given-when-thenRender tests in given/when/then formatacceptedusm/gen-feature-review
default-upstream-hardcodedDefault upstream tracker is the canonical USM repo, overridable via feedback.upsusm/feedback-upstream-routing
classification-instructions-not-detectionTeach scope classification via instructions rather than automatic detection in tusm/feedback-upstream-routing
usage-in-feature-specAdd usage/options/prerequisites to FeatureUsm schema (optional fields)acceptedusm/gen-help-reference
config-reference-from-schemaGenerate usmconfig reference from usmconfig-v1.json schema descriptionsacceptedusm/gen-help-reference
schema-reference-from-v1-jsonGenerate schema reference from v1.json schema descriptionsacceptedusm/gen-help-reference
logo-carouselUse a clickable logo carousel, not tabsacceptedusm/mkt-language-tabs
simple-iconsUse Simple Icons (simpleicons.org) for language and framework logosacceptedusm/mkt-language-tabs
full-grid-in-docsFull language/framework reference grid goes in help docs, not marketing siteacceptedusm/mkt-language-tabs
reuse-real-doc-structureModel browser mock content on real generated feature docsacceptedusm/mkt-mock-interfaces-v2
file-explorer-syncIDE file explorer reveals .usm files in sync with agent file-write actionsacceptedusm/mkt-mock-interfaces-v2
reuse-cli-animation-patternReuse the line-by-line reveal pattern from CliAnimation for both mocksacceptedusm/mkt-mock-interfaces
two-command-cycleCycle two command/spec pairs: draft_feature then generateacceptedusm/mkt-mock-interfaces
responsive-stackSide-by-side on md+ screens, stacked on mobileacceptedusm/mkt-mock-interfaces
instructions-plus-skill-not-pluginUse opencode instructions array + skill, not a plugin with experimental.chat.sysusm/opencode-integration
short-dedicated-file-not-agents-mdGenerate a dedicated short instructions file rather than reusing AGENTS.mdusm/opencode-integration
skill-file-usm-ownedThe skill and instructions files are fully USM-owned (wholesale regeneration); ousm/opencode-integration
roadmap-links-to-featuresRoadmap items can optionally reference a feature $id via a feature fieldacceptedusm/gen-roadmap
shipped-in-optionalAdd optional shipped_in field to roadmap items for version trackingacceptedusm/gen-roadmap
sidebar-checks-file-existenceSidebar generation should only include links to files that existacceptedusm/gen-roadmap
mermaid-via-vitepress-pluginUse VitePress mermaid plugin instead of relying on native mermaid renderingacceptedusm/gen-roadmap
one-generator-multiple-outputsSingle generator produces all tool-specific files in one passacceptedusm/gen-rules-files
workflow-instructions-not-just-contextRules files contain behavioural instructions, not just system descriptionacceptedusm/gen-rules-files
smart-merge-for-rules-filesReuse the smart-merge strategy from AGENTS.md generatoracceptedusm/gen-rules-files
cursor-uses-mdc-formatGenerate .cursor/rules/usm.mdc in Cursor's rule formatacceptedusm/gen-rules-files
source-mapping-nameName the feature source mapping rather than code navigator or file tree, becauseacceptedusm/gen-source-mapping
reference-pages-sourcesSource mapping views are reference_pages sources on system.usm, rendered throughacceptedusm/gen-source-mapping
no-new-schema-fieldsUse existing spec fields only — service modules[] and feature implementation — nacceptedusm/gen-source-mapping
filesystem-walk-for-completenessThe generator walks the filesystem within service paths[] to enumerate actual fiacceptedusm/gen-source-mapping
json-import-firstImport the Structurizr workspace JSON format, not the DSL grammarusm/structurizr-bridge
features-as-componentsExport features as components inside their service containerusm/structurizr-bridge
generator-not-authorThe technical design document is a generator (structured renderer), not an authousm/gen-technical-design
design-pages-parallel-to-reference-pagessystem.usm gains a design_pages[] field parallel to reference_pages[]. Each entrusm/gen-technical-design
suppress-empty-sectionsSections with no data are suppressed entirely — no empty stub pages, no sidebarusm/gen-technical-design
five-group-sidebarThe sidebar is restructured into five groups: Getting Started, Design, Project Musm/gen-technical-design
nav-template-definitionThe nav template structure is the standard for all generated technical docs. Itusm/gen-technical-design
decision-register-consolidatedA Decision Register page consolidates all decisions from features, services, andusm/gen-technical-design
features-as-project-managementFeatures are grouped under Project Management, not Design. Features with flows/cusm/gen-technical-design
default-human-gatehuman-gate is the default policyusm/agent-feedback
feedback-as-first-class-usm-typeFeedback entries are $type: feedback .usm files in .usm/feedback, not a free-forusm/agent-feedback
one-shared-protocol-blockA single Feedback Protocol block is rendered into all four rules files from oneusm/agent-feedback
setup-asks-two-questionsusm init asks exactly two questions: gh auth presence and policy choiceusm/agent-feedback
mcp-tool-respects-policyusm_report_feedback consults system.feedback.policy before writingusm/agent-feedback
write-tools-as-mcp-not-cliImplement authoring as MCP tools rather than CLI commands onlyacceptedusm/mcp-write
draft-returns-previewdraft_feature returns both YAML and generated markdownacceptedusm/mcp-write
validate-before-writeAll write operations validate against the v1 schema before persistingacceptedusm/mcp-write
json-over-yamlUse JSON for usmconfig (not YAML) because it is machine-generated and machine-reusm/usm-config
structured-source-of-truthEvery system artifact is captured in YAML validated by a JSON Schema — no scatteacceptedsystem.principles
agent-firstUSM files are designed for AI agent consumption via MCP tools before human readaacceptedsystem.principles
idempotent-generationScan and generate are safe to run repeatedly; smart-merge preserves human edits.acceptedsystem.principles
one-source-many-outputsA single .usm/ directory generates markdown, Mermaid, OpenAPI, ArchiMate, TOGAF,acceptedsystem.principles

Details

Feature Decisions

use-deps-not-inline [accepted]

Source: usm/cli-color-output

Decision: Use battle-tested CLI libraries, not hand-rolled inline helpers

Rationale: Libraries like picocolors, ora, cli-progress, and update-notifier are tiny, battle-tested by millions of downloads, and handle edge cases (Windows terminals, truecolor detection, spinner cleanup) that inline code would miss. Less code to maintain, better UX, and they auto-install via npm with no user action needed.

Alternatives:

  • Strictly zero new deps (all inline) — rejected: More code to maintain, misses edge cases, reinvents wheels

Consequences: Adds ~5 small runtime dependencies to package.json, all pinned to CJS-compatible versions (ora@5, cli-progress@3, update-notifier@5 — newer majors are ESM-only and incompatible with the CJS package).

respect-no-color [accepted]

Source: usm/cli-color-output

Decision: Honor NO_COLOR env var and non-TTY stdout/stderr by emitting plain text

Rationale: Standard CLI convention (https://no-color.org) and CI hygiene. Piped/redirected output must stay plain so logs and grep work; colored escape codes in CI logs are noise. picocolors respects NO_COLOR natively; ora disables spinner in non-TTY.

Consequences: Color helpers short-circuit when process.stdout.isTTY is false or NO_COLOR is set. Spinner/progress bars suppressed in non-TTY.

palette [accepted]

Source: usm/cli-color-output

Decision: green=✓, red=✗, yellow=⚠, cyan=→/info, dim=secondary values

Rationale: Conventional semantic mapping that reads correctly in both light and dark terminals. Dim for file paths and counts keeps the primary message in foreground.

Consequences: Single palette in src/cli/colors.ts applied consistently

cjs-pin-versions [accepted]

Source: usm/cli-color-output

Decision: Pin dependency versions to CJS-compatible majors

Rationale: The package is CommonJS (no type field = module). Newer majors of ora (v6+) and update-notifier (v7+) are ESM-only and would break the CJS build. Pin to the last CJS-compatible major.

Consequences: package.json dependencies list exact major versions: picocolors (^1), ora (^5), cli-progress (^3), treeify (^0.1), update-notifier (^5).

outputs-from-config [accepted]

Source: usm/cli-config-outputs

Decision: All generators read output paths from usmconfig.json outputs section

Rationale: Users may want outputs in different locations (e.g. docs/ instead of .usm-workspace/docs/, or a custom build directory). Reading from config with sensible defaults means it works out of the box but is overridable.

Consequences: All generators need to call a shared getOutputPath() utility instead of hardcoding paths

generate-only-flag [accepted]

Source: usm/cli-config-outputs

Decision: Replace generate:xxx commands with generate --only <target> flag

Rationale: generate:help-docs, generate:togaf, generate:archimate use colon syntax which is inconsistent with docs serve, mcp serve (space syntax). Using --only flag keeps generate as a single command with a filter, which is simpler and more consistent.

Alternatives:

  • Keep colon commands (generate:togaf, generate:archimate) — rejected: Inconsistent with space-syntax subcommands, clutters --help output
  • Subcommands (generate togaf, generate archimate) — rejected: Makes generate a parent command, changes default behaviour, more complex Commander setup

Consequences: Breaking change — generate:xxx commands removed. Pre-public-release so acceptable.

scaffold-project-to-subcommand [accepted]

Source: usm/cli-config-outputs

Decision: Rename scaffold-project to 'scaffold project' (subcommand)

Rationale: Consistent with 'docs serve', 'mcp serve', 'scan infrastructure' — all use space syntax for subcommands.

Consequences: Breaking change — scaffold-project becomes 'scaffold project'

no-dependency [accepted]

Source: usm/docs-serve-port-check

Decision: Implement port probing with Node.js built-in net module — no third-party dependency like detect-port or chokidar.

Rationale: Keeps the dependency footprint zero. net.createServer().listen() for port probing and fs.watch for file watching are both built-in and sufficient.

auto-port-opt-in [accepted]

Source: usm/docs-serve-port-check

Decision: Auto-port selection is opt-in via --auto-port flag, not the default.

Rationale: Users expect --port 5173 to mean port 5173. Silently switching ports would break bookmarks, CORS configs, and automation scripts.

lsof-best-effort [accepted]

Source: usm/docs-serve-port-check

Decision: Process detection via lsof is best-effort only — graceful degradation if lsof is unavailable.

Rationale: lsof may not be installed in minimal Docker images or on Windows. The error message should still be clear without it, just omitting the process name.

pid-file-approach [accepted]

Source: usm/docs-serve-port-check

Decision: Use a PID file (.usm-workspace/.vitepress.pid) for already-serving detection, not port-sniffing alone.

Rationale: Port-sniffing can't distinguish between a VitePress server and another process on the same port. A PID file gives us certainty and enables usm docs stop.

watch-in-process [accepted]

Source: usm/docs-serve-port-check

Decision: Watch-mode regeneration runs usm generate --only docs in-process (calling the generator functions directly), not as a subprocess.

Rationale: Avoids the overhead and complexity of spawning a child process. The generator functions are already imported and callable.

debounce-500ms [accepted]

Source: usm/docs-serve-port-check

Decision: Watch-mode debounce is 500ms — a pragmatic balance between responsiveness and avoiding thrash on bulk writes.

Rationale: git checkout, usm scan, and editor auto-save can touch many files in rapid succession. 500ms is fast enough to feel instant but long enough to batch.

vitepress-over-custom [accepted]

Source: usm/cli-docs

Decision: Use VitePress as the rendering layer instead of building a custom server

Rationale: Building a custom markdown server with navigation, search, themes, and live reload is a whole separate product. VitePress is purpose-built for docs: markdown-native, Vite-powered HMR, built-in sidebar/search/dark mode, static build output. USM owns the content (generating markdown from .usm), VitePress owns the experience (rendering, nav, search). Clean separation of concerns. Used by Vue, Vitest, Pinia — battle-tested. Alternatives considered: custom Express+marked.js (rejected — reinventing the wheel, permanent maintenance burden), Docsify (rejected — client-side rendering, poor SEO), Nextra (rejected — heavyweight, pulls in Next.js).

unified-docs-directory [accepted]

Source: usm/cli-docs

Decision: All generated docs write to a single docs/ directory instead of scattered .agents-workspace/ paths

Rationale: Currently feature docs land in apps/<service>/.agents-workspace/docs/ and service docs in .agents-workspace/docs/shared-services/. This scattering makes navigation impossible and paths unpredictable. A unified docs/ directory with services/, features/, architecture/ subdirectories creates a clean hierarchy that maps 1:1 to the .usm file structure and works as VitePress input.

Source: usm/cli-docs

Decision: Auto-generate VitePress sidebar config from system.usm index + feature directory structure

Rationale: system.usm already has an index[] with feature id, name, ref, status, and tags. This is the authoritative source of what features exist and how they're grouped. Generating the sidebar from this index means the navigation is always in sync with the spec — no manual sidebar maintenance. Feature grouping (CLI, generators, mcp) comes from the directory structure of .usm/features/.

vitepress-optional-dependency [accepted]

Source: usm/cli-docs

Decision: VitePress is an optional peer dependency, not bundled with USM

Rationale: Not every USM user needs the docs server — some just want the CLI and MCP tools. Making VitePress optional keeps the core install lightweight. usm docs serve checks for VitePress and prints install instructions if missing, rather than failing at install time.

dont-commit-generated-docs [accepted]

Source: usm/cli-docs

Decision: Generated docs are not committed to the repo — served locally and deployed via CI

Rationale: Committing generated files creates diff noise and merge conflicts. The docs/ directory is gitignored. usm docs serve reads from the local generated output. CI runs usm generate && usm docs build for deployment. GitHub browsing links to the deployed site. This matches the current .gitignore strategy for .agents-workspace/.

internal-over-external [—]

Source: usm/internal-dsl-builder

Decision: Internal (host-language) DSL, not a custom grammar

Rationale: Fowler's cost test: inherit TS tooling and LLM fluency, skip parser maintenance forever. The semantic model (.usm) remains the asset.

runtime-schema-validation [—]

Source: usm/internal-dsl-builder

Decision: Enforce constraints by validating at build() rather than type-level branding

Rationale: Keeps the fluent API simple and the JSON Schema the single source of truth; builders cannot silently bypass the same validation MCP tools enforce.

all-35-in-docs [accepted]

Source: usm/mcp-setup-guides

Decision: Generate per-editor setup pages for all 35 editors

Rationale: Maximizes compatibility coverage. Any USM user using any MCP-ready editor can find their setup. Matches shadcn's coverage bar.

Consequences: 35 markdown files under .usm-workspace/docs/mcp-setup/ plus index.md

top-10-logos [accepted]

Source: usm/mcp-setup-guides

Decision: Marketing site ToolLogos shows top 10 editors only

Rationale: 35 logos would overwhelm the marketing hero area. Top 10 covers the most popular. Full 35 coverage lives in the docs.

Consequences: tool-logos.tsx updated with top 10, sorted by popularity

index-plus-per-editor [accepted]

Source: usm/mcp-setup-guides

Decision: Index page plus per-editor pages (shadcn pattern)

Rationale: Best UX. Users land on a grid, click their editor, get exact config. Matches the shadcn setup pattern users are familiar with.

Consequences: 1 index page + 35 editor pages = 36 markdown files

rules-files-per-editor [accepted]

Source: usm/mcp-setup-guides

Decision: Document rules/skills file installation on each editor page where supported

Rationale: The always-on rules file is a key USM differentiator. Editors that support always-on hooks (opencode, Claude Code, Cursor, Copilot, Codex) should document both the MCP config AND the rules file install.

Consequences: Per-editor pages include a Rules File section when the editor supports always-on hooks; pages for editors without hook support note this.

stdio-config-pattern [accepted]

Source: usm/mcp-setup-guides

Decision: Use the standard USM stdio pattern (usm mcp serve) for all editors

Rationale: USM's MCP server is stdio-based. Most editors accept the command/args JSON pattern. HTTP-only clients use mcp-remote as a bridge.

Consequences: Configs use command usm args mcp serve. HTTP-only editors get a mcp-remote bridge note.

manifest-based-detection [accepted]

Source: usm/cli-multi-lang-scan

Decision: Detect services by language manifest files

Rationale: Every language ecosystem has a standard manifest file. Detecting these files is reliable, language-specific, and doesn't require parsing source code. The manifest also tells us dependencies (framework detection).

Consequences: Scanner needs a manifest-to-language map. New languages added by adding a manifest entry.

framework-specific-route-detection [accepted]

Source: usm/cli-multi-lang-scan

Decision: Detect routes per-framework, not per-language

Rationale: Route patterns differ by framework, not language. Next.js uses app/page.tsx, FastAPI uses @app.get decorators, Spring uses @GetMapping annotations, Rails uses config/routes.rb, ASP.NET uses [HttpGet] attributes. Each framework needs its own route extractor.

Alternatives:

  • Generic AST parsing per language — rejected: Too complex, requires language-specific parsers, fragile
  • File-pattern matching only (no framework awareness) — rejected: Misses decorator/annotation-based routes (FastAPI, Spring, ASP.NET)

Consequences: Route detection is framework-by-framework. New frameworks need a route extractor.

configurable-in-usmconfig [accepted]

Source: usm/cli-multi-lang-scan

Decision: Language/framework detection rules are configurable in usmconfig.json

Rationale: Users can add custom manifest patterns, route patterns, and data model patterns for frameworks we don't support out of the box. Defaults cover common frameworks; advanced users can extend.

Consequences: usmconfig.json gains a 'detection' section with language/framework rules

all-languages-from-start [accepted]

Source: usm/cli-multi-lang-scan

Decision: Support all major languages from the initial implementation

Rationale: Rather than phasing, include all major languages (Python, Go, Rust, Java, Kotlin, C#, Ruby, PHP, Elixir, Swift, Scala, C/C++) from the start. The manifest detection is simple (file pattern matching) and route detection is regex-based per framework. The complexity is manageable.

Consequences: Larger initial implementation but no phased rollout complexity

detectors-directory [accepted]

Source: usm/cli-multi-lang-scan

Decision: Add .usm/detectors/*.yaml as a second auto-discovered extension surface alongside usmconfig.json detection

Rationale: usmconfig.json describes a single repo's shape and is per-project. Detector files are shareable across repos, versioned with the .usm source of truth, validatable like other USM artifacts, and agent-writable via MCP tools. Two surfaces with the same field shapes give users the cheap path (drop a file) and the config path (inline rules) without forcing one or the other.

Alternatives:

  • usmconfig.json detection only (original accepted decision) — rejected: Config is per-project and not shareable; cannot express convention-based frameworks that need code
  • .usm/detectors/ only, drop the config section — rejected: Reverses an accepted decision and removes the inline config ergonomics for small overrides

Consequences: Two surfaces to keep in sync; precedence rules must be deterministic. Built-in detectors migrate to the same shape as user detectors.

script-escape-hatch [accepted]

Source: usm/cli-multi-lang-scan

Decision: Allow routes.script in a detector to point at a .ts/.js file exporting extractRoutes(sourceDir, framework) for convention-based frameworks

Rationale: Regex cannot capture Next.js app/page.tsx convention routing, Remix, or SvelteKit file-based routes. These need real logic. A script field keeps the declarative detector file as the entry point but delegates extraction to code when needed. The script lives in the user's repo (not auto-loaded from third parties), bounding the trust surface.

Consequences: Detector files can reference local code; USM dynamically imports it. Only opt-in per detector; declarative regex remains the default.

generalized-orchestrator [accepted]

Source: usm/cli-multi-lang-scan

Decision: Generalize structural.ts service detection so any detector manifest drives it, removing the package.json hardcode

Rationale: Today a service directory without package.json is warned and skipped, making non-JS/TS stacks second-class. The whole point of multi-language scan is that a Go app (go.mod) or Zig app (build.zig.zon) is detected as a service without package.json. The orchestrator must ask detectors 'does your manifest match this directory' instead of assuming package.json.

Consequences: The Python pyproject.toml and Docker docker-compose special-case passes become built-in detectors. structural.ts shrinks to an orchestrator that iterates detectors.

infrastructure-as-detector [accepted]

Source: usm/cli-multi-lang-scan

Decision: Treat infrastructure (Terraform today) as a detector kind rather than a hardcoded separate subcommand

Rationale: infrastructure.ts only parses Terraform. Users with CloudFormation, Pulumi, or CDK get nothing. An infrastructure detector kind with the same manifest+pattern shape as other detectors makes IaC extensible without code, consistent with the rest of the plugin system.

Consequences: usm scan infrastructure subcommand continues to work (backed by the built-in Terraform detector); new formats add via detector files or detection.infrastructure in config.

precedence-order [accepted]

Source: usm/cli-multi-lang-scan

Decision: Detector precedence is built-in defaults < .usm/detectors/ files < usmconfig.json detection (last wins)

Rationale: Built-ins provide sane defaults out of the box. Detector files are project-level customizations shared across the team. usmconfig.json is the highest precedence so a user can override a detector file from config without editing the file (useful for one-off overrides). Deterministic order prevents merge ambiguity.

Consequences: Documented precedence; overrides by $id for detectors and by manifest pattern for config arrays.

tiny-grammar-not-sql [—]

Source: usm/query-layer

Decision: A ~200-line recursive-descent grammar, not SQL/jq subset

Rationale: Covers selector+predicate use cases agents actually have; keeps error messages precise and the surface teachable in one rules-file line.

missing-field-is-false [—]

Source: usm/query-layer

Decision: Unknown/absent fields make predicates false instead of erroring

Rationale: Queries like 'feedback where severity = high' must not explode on feature files that lack severity; absence is simply not a match.

capability-registry-pattern [accepted]

Source: usm/upgrade

Decision: Capabilities self-describe (detect + setup + introducedIn); upgrade orchestrates without hardcoding.

Rationale: Adding a future capability means one registry entry, zero changes to upgrade. Same extensibility principle as the generators target list.

usm-version-field-for-alignment [accepted]

Source: usm/upgrade

Decision: Use system.usm.usm_version for USM-tool alignment, NOT the project's own version field.

Rationale: The version field tracks the consuming project's own release (e.g. 5.2.0). Repurposing it would miscompare against the USM tool version. A dedicated usm_version field keeps project version and tool alignment separate and unambiguous.

Alternatives:

  • Reuse the existing version field for alignment — rejected: Overwrites the consuming project's own release version; a project at 5.2.0 would be misread as 'ahead of' USM 0.1.0 and never offered upgrades.
  • Use $version (schema format) for alignment — rejected: Conflates schema-format changes with feature additions; additive schema changes (like feedback) intentionally do not bump $version.

schema-version-independence [accepted]

Source: usm/upgrade

Decision: Schema $version moves independently of the package version: additive schema changes do NOT bump $version (still v1); breaking changes bump $version + CURRENT_SCHEMA_VERSION together and ship a migration in usm upgrade.

Rationale: Decouples the file format from the tool release cadence. A 0.2.0 release may still be schema v1. Additive changes (new optional fields) are backward compatible and must not force every consumer to migrate.

setup-owned-by-capability [accepted]

Source: usm/upgrade

Decision: Each registry entry owns its own setup function (interactive + default); upgrade just calls it.

Rationale: Avoids subprocess fragility (no need for the binary on PATH) and keeps each capability's setup logic co-located with its detection.

interactive-with-flag-fallback [accepted]

Source: usm/upgrade

Decision: TTY prompts per capability; --apply uses defaults for CI/scripts; --check is report-only.

Rationale: Composes with existing per-feature setup commands (e.g. usm feedback) while supporting non-interactive and CI use cases.

non-destructive [accepted]

Source: usm/upgrade

Decision: Upgrade only adds missing blocks and bumps usm_version; existing config is never overwritten.

Rationale: detect() returns true for configured capabilities → they are skipped. Safe to re-run idempotently.

homepage-simplify-not-remove [accepted]

Source: usm/vitepress-home-feedback-schema

Decision: Simplify the homepage by removing marketing content, not by removing the homepage entirely. Keep the spec-first diagram, quick stats, and quick start as the core reference content.

Rationale: The existing vitepress-schema-polish feature built a rich homepage. This feature refines it — removing the marketing duplication while keeping the technical reference value. The marketing content belongs on usm.dev (Next.js + shadcn), not docs.usm.dev (VitePress).

feedback-as-generated-page [accepted]

Source: usm/vitepress-home-feedback-schema

Decision: The feedback page is a generated markdown page (docs/feedback.md), not a custom Vue component. The nav bar link is added via VitePress themeConfig.nav.

Rationale: Keeps the build lightweight (no custom Vue components). The feedback page content is generated from system.usm (identity.repository for the issue tracker URL) and the USM version. The nav link is injected into the VitePress config during docs serve/build.

agent-feedback-via-existing-mcp [accepted]

Source: usm/vitepress-home-feedback-schema

Decision: The agent feedback path reuses the existing usm_report_feedback MCP tool (from usm/agent-feedback feature). The feedback page documents how agents should use it with page context.

Rationale: No new MCP tool needed. The existing agent-feedback protocol already handles structured feedback entries. The docs page teaches agents to include current page URL and .usm map context.

Source: usm/vitepress-home-feedback-schema

Decision: Cross-links and next-steps sections are added in the markdown generator, not hand-edited into output.

Rationale: Dogfooding: all content must be generated from .usm. The generator already has access to system.usm index for building link maps.

schema-as-single-source [accepted]

Source: usm/vitepress-schema-polish

Decision: The schema reference is generated entirely from schema/v1.json; terse field descriptions are enriched in-place in the schema so the reference is self-generating and never drifts.

Rationale: schema/v1.json is already the authoritative source for type/required/constraints. Co-locating rich descriptions avoids a second hand-maintained doc and honors 'generated from .usm/schema'.

Alternatives:

  • A separate .usm annotation file describing each field — rejected: duplicates the schema and will drift from it

homepage-from-system-usm [accepted]

Source: usm/vitepress-schema-polish

Decision: Homepage hero, tagline, principle cards, and CTAs are derived from system.usm (identity, summary, principles, repository) so every USM project gets a reflective homepage.

Rationale: Dogfooding: the generator must work for any project, not just USM's own docs.

mermaid-dark-mode-aware [accepted]

Source: usm/vitepress-schema-polish

Decision: Inject a small theme-bridge script so Mermaid follows VitePress isDark instead of the hardcoded 'default' theme.

Rationale: Hardcoded 'default' makes diagrams unreadable in dark mode.

declarative-sidebar-groups [accepted]

Source: usm/vitepress-schema-polish

Decision: Sidebar group order/names are driven by a declarative group-to-paths mapping while still only emitting links to pages that exist.

Rationale: Makes the requested group structure explicit and easy to evolve without breaking existence-checking that prevents dead links.

native-vitepress-containers [accepted]

Source: usm/vitepress-schema-polish

Decision: Use VitePress native containers and Mermaid code fences for visuals — no custom Vue components or heavy deps.

Rationale: Keeps the build lightweight and avoids custom-component maintenance.

priority-ordering [accepted]

Source: usm/vitepress-schema-polish

Decision: Deliver schema-reference and homepage/getting-started first; visuals/polish last.

Rationale: Schema reference + onboarding are the highest-leverage surfaces; polish is additive.

content-block-system [accepted]

Source: usm/gen-content-blocks

Decision: Define a content-block schema (heading, paragraph, code, mermaid, tabs, callout, table, steps, cards, badge, divider) that any .usm spec can use to express VitePress-rich content declaratively.

Rationale: The schema currently has no fields for reference content, onboarding prose, code snippets, Mermaid-as-doc, tabs, or callouts. When someone needed these, they hand-wrote them in generator code (LANGUAGE_SUPPORT, generateAgentSetupGuide, generateGettingStartedDoc prose). A content-block schema gives specs a structured way to express any VitePress content, making the generator a pure renderer and eliminating drift. This is the first-principles fix: make the schema expressive enough that docs content lives in specs.

Alternatives:

  • Keep bespoke generators but make them read from specs/registry — rejected: Still one function per content type; doesn't scale; every new page needs a new generator function.
  • Markdown-in-YAML (raw markdown strings in spec fields) — rejected: Not structured, not agent-writable via MCP tools, no audience filtering, defeats the point of structured source of truth.
  • A separate .md file per reference page committed alongside .usm — rejected: Not generated from source; duplicates the spec; will drift.

Consequences: Schema gains content-block types (a new $def in v1.json). Generators gain one renderContentBlocks function. Hardcoded functions deleted after migration.

reference-pages-on-system [accepted]

Source: usm/gen-content-blocks

Decision: system.usm gains reference_pages[] for product-level reference pages (language support, getting started, agent setup) with inline content or runtime sources (detectors, schema, config).

Rationale: Some reference pages document the product, not a single feature. system.usm is the right home. source: detectors lets the language-support page render from the detector registry at generation time — always in sync, no constant. Inline content lets getting-started/agent-setup pages carry their prose as structured content blocks. This replaces the hardcoded functions that currently own these pages.

Consequences: system.usm schema gains an optional reference_pages field. The generator iterates reference_pages and renders each.

reference-blocks-on-features [accepted]

Source: usm/gen-content-blocks

Decision: Feature specs gain an optional reference[] field for user-facing reference content that survives the help filter.

Rationale: Some feature docs need reference tables that are NOT acceptance criteria (e.g. the multi-lang-scan spec's supported-languages table is user reference, not a developer contract). Today these live in contracts[] which the help filter strips. A reference[] field with audience: public gives feature specs a place for user-facing content that survives into help docs.

Consequences: Feature schema gains an optional reference field. The help filter is updated to keep public reference blocks. generateFeatureMarkdown appends rendered reference blocks after standard sections.

block-level-audience-filter [accepted]

Source: usm/gen-content-blocks

Decision: The help-doc filter operates at content-block granularity (keep audience: public, drop audience: internal) instead of section-level granularity (strip ## Contracts by heading name).

Rationale: The current filter strips entire sections by heading name, which is coarse and breaks if headings change. Block-level audience filtering is precise — each block declares its audience and the filter keeps or drops it. This also lets a single page mix public and internal content.

Consequences: simplifyFeatureDoc in docs.ts is rewritten to filter content blocks by audience. Contracts/tests/implementation/decisions continue to be stripped (they are developer-only by definition).

preserve-schema-reading-generators [accepted]

Source: usm/gen-content-blocks

Decision: generateConfigReference and generateSchemaReference are preserved — they read JSON schema files which ARE the source of truth for config/schema fields.

Rationale: These two functions read usmconfig-v1.json and v1.json respectively. The JSON schemas are the structured source of truth for those fields — reading them at runtime is spec-driven in spirit. Converting them to content blocks would mean duplicating the schema's field descriptions into a spec, which would drift. They stay as-is but can be invoked via reference_pages[source: config|schema].

Consequences: These two functions are wrapped as source renderers callable from reference_pages, not deleted. Their internal logic is unchanged.

delete-after-migration [accepted]

Source: usm/gen-content-blocks

Decision: Hardcoded functions are deleted only after their content is migrated to specs and the replacement is proven by tests.

Rationale: Deleting first risks breaking docs with no fallback. Migrate content to reference_pages/reference blocks, prove the generated output matches (or corrects) the old output, then delete. This is the cleanup pass, not the first step.

Consequences: The delete-hardcoded-generators flow runs last. LANGUAGE_SUPPORT, generateLanguageSupportDoc, generateAgentSetupGuide, and hardcoded prose in generateGettingStartedDoc are removed only after tests confirm the replacement works.

emit-only-when-populated [—]

Source: usm/docs-experience

Decision: Emit a templated page only when its source section is populated; never write a placeholder

Rationale: A placeholder promises depth and delivers nothing — worse than the page not existing. Omission is honest.

togaf-as-architecture-section [—]

Source: usm/docs-experience

Decision: Surface togaf output as the Architecture section of the docs site, not a separate target

Rationale: The detailed design already exists; the failure was discoverability. Wiring it into the nav fixes both the empty stubs and the orphaned output in one move.

css-not-config-for-width [—]

Source: usm/docs-experience

Decision: Widen via generated theme CSS custom properties, not VitePress config knobs

Rationale: CSS custom properties are the documented override path and survive VitePress minor bumps; config knobs for layout do not exist.

feature-cards-as-nav [—]

Source: usm/docs-experience

Decision: Use the VitePress features grid as homepage navigation rather than forcing a sidebar onto layout home

Rationale: Cards are the canonical landing-page nav pattern and keep the hero; forcing a sidebar means abandoning the home layout entirely.

voice-is-render-mode [—]

Source: usm/docs-experience

Decision: Audience is a render mode (voice), not just a filter

Rationale: Filtering alone produced two identical sites; the brief needs two different documents from one source.

post-process-filtering [accepted]

Source: usm/gen-docs-split

Decision: Generate full docs first, then filter for help audience

Rationale: Rather than modifying the core markdown generator with audience-aware logic, generate the full docs as usual then post-process for the help audience. This keeps the generator simple and puts audience filtering in the VitePress integration layer (docs.ts) where it belongs.

Alternatives:

  • Audience-aware generator (pass audience through to all render functions) — rejected: Touches every render function, complex, hard to maintain
  • Separate generator functions for help vs developer — rejected: Duplicates rendering logic, drifts over time

Consequences: Full docs generated first (slight overhead), then filtered — but filtering is fast and the full docs are useful anyway

visibility-field-optional [accepted]

Source: usm/gen-docs-split

Decision: Add optional visibility field (public/internal) to features and services

Rationale: Default visibility is internal (safe — nothing leaks unless explicitly marked public). Features marked public appear in help docs. Features with no visibility field appear in help docs only if status is built.

Consequences: Authors must opt-in features to public docs by setting visibility: public

help-docs-simplified-features [accepted]

Source: usm/gen-docs-split

Decision: Help docs show summary + intent + flows only, not contracts/tests/implementation

Rationale: Visitors don't need acceptance criteria checklists or Given/When/Then test cases. They need to understand what the feature does (summary), why it exists (intent), and how it works (flows as steps). Contracts and tests are developer concerns.

Consequences: Help docs are shorter and more readable for non-developers

review-mode-separate-from-reference [accepted]

Source: usm/gen-feature-review

Decision: Add a review-oriented template alongside the existing overview template

Rationale: The existing overview.md is useful as a structural reference (what fields exist, where files are). The review template serves a different purpose: human approval before implementation. Keeping both lets the overview remain a reference while the review doc is the approval artifact.

flows-as-numbered-steps [accepted]

Source: usm/gen-feature-review

Decision: Render flows as numbered human-readable steps, not tables

Rationale: A table of {id, action, target} is hard to read as a narrative. Numbered steps like "1. Receive structured feature fields → 2. Validate against schema → 3. Generate YAML + markdown" read like a procedure a human can follow and approve.

contracts-as-checklist [accepted]

Source: usm/gen-feature-review

Decision: Render contracts as a markdown checklist the human can mentally tick

Rationale: Contracts are guarantees the implementation must satisfy. A checklist (- [ ] ...) frames them as acceptance criteria, which is how a human naturally reviews a spec: "does this cover what I expect?"

tests-as-given-when-then [accepted]

Source: usm/gen-feature-review

Decision: Render tests in given/when/then format

Rationale: The .usm test schema already has setup (given), implied action (when), and expect (then). Making this explicit in the markdown helps the human understand what test cases will verify the feature.

default-upstream-hardcoded [—]

Source: usm/feedback-upstream-routing

Decision: Default upstream tracker is the canonical USM repo, overridable via feedback.upstream_tracker

Rationale: Consumers should not need configuration to get correct routing; forks/rebrands can override. Optional schema field only — no breaking change.

classification-instructions-not-detection [—]

Source: usm/feedback-upstream-routing

Decision: Teach scope classification via instructions rather than automatic detection in the MCP tool

Rationale: Classification requires judgment about blast radius; the agent (or human at the gate) is better placed than a heuristic on the summary string. The tool only nudges when a USM tool bug seems likely.

usage-in-feature-spec [accepted]

Source: usm/gen-help-reference

Decision: Add usage/options/prerequisites to FeatureUsm schema (optional fields)

Rationale: The .usm spec is the source of truth. If CLI usage examples and flag references live in the .usm feature specs, they're available to both the help doc generator and MCP tools (agents can query usage). The alternative — parsing --help output at generation time — is brittle and produces terse output.

Alternatives:

  • Parse commander --help output at generation time — rejected: Terse output, brittle parsing, not available to MCP tools
  • Hand-write reference pages in markdown — rejected: Not generated from source, will drift, not generic

Consequences: Feature specs for CLI commands need updating with usage/options fields — one-time effort

config-reference-from-schema [accepted]

Source: usm/gen-help-reference

Decision: Generate usmconfig reference from usmconfig-v1.json schema descriptions

Rationale: The JSON schema already has descriptions for every field. Generating a human-readable reference from the schema means it's always in sync. No need for a separate .usm spec for the config file.

Consequences: Config reference page is only as good as the schema descriptions — may need enriching

schema-reference-from-v1-json [accepted]

Source: usm/gen-help-reference

Decision: Generate schema reference from v1.json schema descriptions

Rationale: Same principle — the v1.json schema has descriptions for every field. Generate a human-readable reference showing required vs optional, types, and descriptions for each .usm file type.

Consequences: Schema reference is comprehensive but may be dense — could add examples later

Source: usm/mkt-language-tabs

Decision: Use a clickable logo carousel, not tabs

Rationale: Tabs hide breadth (only one visible at a time). A logo row shows all 12 languages instantly. Clicking reveals frameworks + code example for that language. Compact, interactive, visual.

Alternatives:

  • Tabbed interface (one tab per language) — rejected: Hides breadth — visitor only sees one language at a time
  • Full grid (all languages and frameworks visible) — rejected: Too much information for marketing — better in docs

Consequences: Need language logos (Simple Icons) and a carousel component

simple-icons [accepted]

Source: usm/mkt-language-tabs

Decision: Use Simple Icons (simpleicons.org) for language and framework logos

Rationale: Simple Icons has 3000+ monochrome SVG brand icons including all 12 languages and ~18 of the 30+ frameworks. Free, open source, SVG (crisp at all sizes). Monochrome by default — perfect for dark theme.

Consequences: Less popular frameworks (chi, gin, Axum, Crow, etc.) won't have logos — show as text chips

full-grid-in-docs [accepted]

Source: usm/mkt-language-tabs

Decision: Full language/framework reference grid goes in help docs, not marketing site

Rationale: Marketing site should be concise (carousel). Help docs should be comprehensive (full grid with all 12 languages and all 30+ frameworks). This matches the docs vs marketing split.

Consequences: New help docs page: 'Language Support' with full grid

reuse-real-doc-structure [accepted]

Source: usm/mkt-mock-interfaces-v2

Decision: Model browser mock content on real generated feature docs

Rationale: The actual VitePress docs render H1 + Usage + How it works + Guarantees. Matching this makes the mock instantly recognizable as the real docs site.

Consequences: Browser scenes use structured content blocks matching real doc layout

file-explorer-sync [accepted]

Source: usm/mkt-mock-interfaces-v2

Decision: IDE file explorer reveals .usm files in sync with agent file-write actions

Rationale: Shows the spec-first workflow visually — file appears in explorer as agent writes it

Consequences: File explorer state tied to chat animation phase

reuse-cli-animation-pattern [accepted]

Source: usm/mkt-mock-interfaces

Decision: Reuse the line-by-line reveal pattern from CliAnimation for both mocks

Rationale: CliAnimation already has a working scene/line/delay system with typewriter reveal. Reusing it keeps the two new mocks consistent and low-risk.

Consequences: A new ChatMock and BrowserMock component, each with their own scenes array

two-command-cycle [accepted]

Source: usm/mkt-mock-interfaces

Decision: Cycle two command/spec pairs: draft_feature then generate

Rationale: Shows the spec-first write flow (draft to spec appears in browser) and the docs flow (generate to docs page appears). Two scenes cover both halves of the USM value prop.

Consequences: Each mock has 2 scenes that loop

responsive-stack [accepted]

Source: usm/mkt-mock-interfaces

Decision: Side-by-side on md+ screens, stacked on mobile

Rationale: Standard responsive pattern for two-column comparisons. Both mocks should be fully visible side-by-side on desktop for the synced effect.

Consequences: grid grid-cols-1 md:grid-cols-2 gap-6 layout

instructions-plus-skill-not-plugin [—]

Source: usm/opencode-integration

Decision: Use opencode instructions array + skill, not a plugin with experimental.chat.system.transform

Rationale: Plugin system-transform hooks are experimental and require TypeScript runtime code in the consumer repo — fragile distribution. instructions injection is stable config, and the skill description is visible every message anyway. Together they cover the every-message requirement without shipping code.

short-dedicated-file-not-agents-md [—]

Source: usm/opencode-integration

Decision: Generate a dedicated short instructions file rather than reusing AGENTS.md

Rationale: opencode already injects AGENTS.md every message — the drift problem is dilution by length. A ≤30-line iron-rules file stays salient where the full AGENTS.md does not.

skill-file-usm-owned [—]

Source: usm/opencode-integration

Decision: The skill and instructions files are fully USM-owned (wholesale regeneration); opencode.json is merge-touched only in instructions

Rationale: Skills are single-purpose; unlike CLAUDE.md there is no human content to preserve inside them. opencode.json is user-authored config and must be treated as hand-written.

Source: usm/gen-roadmap

Decision: Roadmap items can optionally reference a feature $id via a feature field

Rationale: Not every roadmap item is a feature (some are refactors, infra, etc.), but when a roadmap item corresponds to a feature, linking them lets the generated roadmap page cross-link to the feature spec. The feature field is optional — roadmap items without a feature link still work.

shipped-in-optional [accepted]

Source: usm/gen-roadmap

Decision: Add optional shipped_in field to roadmap items for version tracking

Rationale: When a roadmap item ships, recording which package version it shipped in gives a release history without complex versioning machinery. The CHANGELOG already tracks release contents — shipped_in just annotates the roadmap item with the version number.

Source: usm/gen-roadmap

Decision: Sidebar generation should only include links to files that exist

Rationale: The sidebar currently includes links to /risks and /roadmap even when those pages aren't generated (empty content suppressed). The sidebar generator should check if the target file exists before adding the link. Same for feature links — only include features whose docs files exist.

mermaid-via-vitepress-plugin [accepted]

Source: usm/gen-roadmap

Decision: Use VitePress mermaid plugin instead of relying on native mermaid rendering

Rationale: VitePress doesn't render mermaid code blocks by default — they show as raw text. The vitepress-plugin-mermaid plugin (or MermaidMarkdown plugin) enables mermaid rendering. This is configured in the VitePress config.

one-generator-multiple-outputs [accepted]

Source: usm/gen-rules-files

Decision: Single generator produces all tool-specific files in one pass

Rationale: The workflow instructions are largely the same across tools; only the file path and format conventions differ. A single generator with per-tool templates avoids drift between instruction files.

workflow-instructions-not-just-context [accepted]

Source: usm/gen-rules-files

Decision: Rules files contain behavioural instructions, not just system description

Rationale: The current AGENTS.md generator outputs structural context (what apps exist, what packages exist). That's useful but passive. The rules files must actively instruct: "Before implementing a feature, draft a .usm spec and show the human the review markdown. After implementation, update the feature status." Behavioural instructions are what make USM a workflow, not just a reference.

smart-merge-for-rules-files [accepted]

Source: usm/gen-rules-files

Decision: Reuse the smart-merge strategy from AGENTS.md generator

Rationale: Teams may add tool-specific instructions (e.g. "use pnpm not npm") that must survive regeneration. The USM:START/USM:END marker strategy preserves human content outside the generated block.

cursor-uses-mdc-format [accepted]

Source: usm/gen-rules-files

Decision: Generate .cursor/rules/usm.mdc in Cursor's rule format

Rationale: Cursor rules (.mdc files) support frontmatter with globs and descriptions that control when the rule is injected. Generating in .mdc format means the rule auto-activates when agents work in .usm/ directories.

source-mapping-name [accepted]

Source: usm/gen-source-mapping

Decision: Name the feature source mapping rather than code navigator or file tree, because the capability is building a bidirectional source-to-spec mapping and rendering multiple views from it.

Rationale: Code navigator describes one view (the file tree). Source mapping describes the capability — the underlying file-to-feature-to-service relationship that powers file-tree, coverage-matrix, and orphan-report views. The name should reflect the general capability, not one output.

Consequences: The feature id is usm/gen-source-mapping. Reference page sources are file-tree, coverage-matrix, orphan-report.

reference-pages-sources [accepted]

Source: usm/gen-source-mapping

Decision: Source mapping views are reference_pages sources on system.usm, rendered through the generic content-block renderer.

Rationale: The content-block system already supports source-based reference pages (detectors, schema, config). Adding file-tree, coverage-matrix, and orphan-report as new sources keeps the architecture uniform — one generator, one renderer, multiple sources. No bespoke generator functions.

Consequences: system.usm declares which views to generate. The referencePages generator dispatches to source-specific builders.

no-new-schema-fields [accepted]

Source: usm/gen-source-mapping

Decision: Use existing spec fields only — service modules[] and feature implementation — no new schema fields.

Rationale: The data already exists. Adding schema fields would be unnecessary. The generator reads service.usm modules[].paths[] for directory grouping and feature.usm implementation.primary for file ownership. The filesystem walk fills the gap between specs and actual files.

Consequences: No v1.json changes. No migration needed. The generator is purely additive.

filesystem-walk-for-completeness [accepted]

Source: usm/gen-source-mapping

Decision: The generator walks the filesystem within service paths[] to enumerate actual files, not just the files mentioned in specs.

Rationale: Specs point at implementation files but don't list every file in a directory. To build a complete file tree and detect orphans, the generator must walk the filesystem. Files not mentioned in any spec are orphans — that's the coverage signal.

Consequences: The generator needs filesystem access at generation time. Exclude patterns (node_modules, dist, .git) are respected.

json-import-first [—]

Source: usm/structurizr-bridge

Decision: Import the Structurizr workspace JSON format, not the DSL grammar

Rationale: Structurizr serialises workspaces to JSON; parsing that is tractable, while the DSL grammar would need a real parser — deferred until demand is demonstrated (Fowler threshold).

features-as-components [—]

Source: usm/structurizr-bridge

Decision: Export features as components inside their service container

Rationale: Preserves the C4 hierarchy (system, container, component) and gives Structurizr users feature-level detail.

generator-not-author [—]

Source: usm/gen-technical-design

Decision: The technical design document is a generator (structured renderer), not an authored doc. All content comes from .usm specs, schema, or declared content blocks.

Rationale: Follows the first principle established by gen-content-blocks: generators are renderers, not authors. The mapping from USM data to the 13 sections is deterministic.

design-pages-parallel-to-reference-pages [—]

Source: usm/gen-technical-design

Decision: system.usm gains a design_pages[] field parallel to reference_pages[]. Each entry can declare inline content blocks for a section, enriching the structured data.

Rationale: Some sections need prose that doesn't fit structured fields (e.g. disaster recovery narrative, testing philosophy). Content blocks provide that escape hatch while keeping the generator as the primary renderer.

suppress-empty-sections [—]

Source: usm/gen-technical-design

Decision: Sections with no data are suppressed entirely — no empty stub pages, no sidebar links to pages that don't exist.

Rationale: Follows the docs-experience principle: a CLI tool shouldn't have a Database Design page. The template adapts to the project.

five-group-sidebar [—]

Source: usm/gen-technical-design

Decision: The sidebar is restructured into five groups: Getting Started, Design, Project Management, Developers, Exports. This replaces the current fragmented groups (Core Concepts, Workflows, Architecture, Deployment, Contributing).

Rationale: The current sidebar mixes concerns — architecture phases next to reference pages next to feature groups. Five clean groups by audience and purpose: onboarding, holistic design, project governance, developer reference, alternative framework exports.

Source: usm/gen-technical-design

Decision: The nav template structure is the standard for all generated technical docs. It defines five top-level groups and the pages within each. The template is fixed in structure but adaptive in rendering — groups and pages only appear when data exists.

Getting Started
  Home
  Getting Started

Design
  Project Overview
    Project Name
    Project Description
    Stakeholders
    Assumptions
    Use-cases
  Requirements
    Functional Requirements
    Non-Functional Requirements
      Performance
      Scalability
      Security
      Reliability
      Maintainability
  System Architecture
    High-Level Diagram
    Technology Stack
      Frontend
      Backend
      Database
      Infrastructure
    System Components
  Module Design
    Module Name
    Purpose
    Inputs
    Outputs
    Dependencies
    Flow
  Database Design
    ER Diagram
    Schema Design
    Indexes
    Transactions
  API Design
    Endpoints
      HTTP Method (GET/POST/PUT/DELETE)
      URL Schemas / naming conventions
    Request/Response structure
    Authentication
    Authorization (roles, resources, permissions)
    Rate Limiting
    Error Handling
  Security Design
    Authentication / Authorization
    Data Encryption
    Security Auditing
    Vulnerabilities
    Security Stack
  Deployment Architecture
    Deployment Diagram
    Environment Setup
      Development
      Staging
      Production
    Scaling Strategy
    Monitoring Stack
  Testing Strategy
    Unit Testing
    Integration Testing
    Acceptance Testing
    Performance Testing
    Security Testing
    Automated Testing
  Maintenance & Monitoring
    Logging
    Alerting
    System Health Monitoring
    Error Tracking
  Backup & Recovery
    Backup Strategy
    Disaster Recovery
  Risks & Mitigation
    Technical Risks
    Mitigation Strategies
  Future Enhancements
    Roadmap
    Scalability Considerations

Project Management
  Roadmap
  Features (grouped by service/area)
  Decision Register

Developers
  Source Map
  Test Coverage
  Spec Coverage
  API Reference
  CLI Reference
  Configuration

Exports (collapsed)
  TOGAF Phases
  ArchiMate Model

Level 1 = sidebar group. Level 2 = sidebar page link. Level 3+ = page content sections (rendered as the page outline/TOC, not sidebar links). The sidebar is 2 levels deep (group > page). Groups and pages only appear when their data source exists — the template adapts to the project type.

Rationale: Capturing the full template structure as a decision ensures the generator has a single authoritative reference for what to render. The template is universal — it applies to any project type — but adapts by suppressing sections without data.

decision-register-consolidated [—]

Source: usm/gen-technical-design

Decision: A Decision Register page consolidates all decisions from features, services, and system principles into one page under Project Management.

Rationale: Decisions are currently scattered across feature docs and TOGAF Phase H. A single register is the project governance view — what was decided, why, and what alternatives were rejected.

features-as-project-management [—]

Source: usm/gen-technical-design

Decision: Features are grouped under Project Management, not Design. Features with flows/contracts/tests/status are work records, not design prose.

Rationale: Separating how the system is designed from how the project is managed is cleaner. Design is the system; Project Management is the work.

default-human-gate [—]

Source: usm/agent-feedback

Decision: human-gate is the default policy

Rationale: Matches USM's human-in-the-loop philosophy (ALWAYS show the human first). Prevents noisy autonomous issues and unreviewed files. Projects with confidence and gh auth can opt into direct modes.

feedback-as-first-class-usm-type [—]

Source: usm/agent-feedback

Decision: Feedback entries are $type: feedback .usm files in .usm/feedback, not a free-form markdown log

Rationale: Consistent with structured-source-of-truth principle. Validated, queryable via MCP, convertible to features/issues. Avoids the drift that made ad-hoc bugs.md useless.

one-shared-protocol-block [—]

Source: usm/agent-feedback

Decision: A single Feedback Protocol block is rendered into all four rules files from one code path

Rationale: Same rationale as the existing WORKFLOW_INSTRUCTIONS — one source, avoids drift between agent tools (Cursor/Claude/Codex/Copilot).

setup-asks-two-questions [—]

Source: usm/agent-feedback

Decision: usm init asks exactly two questions: gh auth presence and policy choice

Rationale: Minimal friction. gh_auth gates the direct-to-github option; policy drives everything else. Tracker and feedback_dir have sensible defaults.

mcp-tool-respects-policy [—]

Source: usm/agent-feedback

Decision: usm_report_feedback consults system.feedback.policy before writing

Rationale: In human-gate mode the tool returns a draft preview for the human instead of writing, keeping the agent honest even if it ignores the rules file.

write-tools-as-mcp-not-cli [accepted]

Source: usm/mcp-write

Decision: Implement authoring as MCP tools rather than CLI commands only

Rationale: Agents work through MCP in Cursor, Claude Desktop, and other tools. A CLI command requires the agent to shell out, parse output, and handle errors externally. An MCP tool returns structured JSON the agent can reason about directly. CLI wrappers can be added later for human use.

Alternatives:

  • CLI commands only (usm feature draft, usm feature update) — rejected: Agents must shell out, parse stdout, handle errors externally — MCP returns structured JSON directly
  • File system writes only (agent writes YAML directly) — rejected: No validation, no markdown preview, no status transition enforcement — agents would produce invalid .usm files

Consequences: MCP server gains write capability — needs careful validation to prevent corruption

draft-returns-preview [accepted]

Source: usm/mcp-write

Decision: draft_feature returns both YAML and generated markdown

Rationale: The human reviews the markdown, not the YAML. Returning both lets the agent show the human the readable preview immediately without a separate generate step. The agent can also iterate on the draft based on feedback before writing to disk.

Consequences: draft_feature is heavier than a pure YAML generator, but the review step is the whole point

validate-before-write [accepted]

Source: usm/mcp-write

Decision: All write operations validate against the v1 schema before persisting

Rationale: An invalid .usm file breaks downstream generators and MCP read tools. Validation at write time prevents corruption. The tool returns structured validation errors so the agent can fix them before retrying.

Consequences: Write operations are slower due to validation, but prevent corrupt .usm files from entering the system

Service Decisions

json-over-yaml [—]

Source: usm/usm-config

Decision: Use JSON for usmconfig (not YAML) because it is machine-generated and machine-read

Rationale: Config is produced by usm init and consumed by usm scan — no hand-editing required for initial creation

Architecture Principles (as decisions)

structured-source-of-truth

Decision: Every system artifact is captured in YAML validated by a JSON Schema — no scattered, stale docs.

agent-first

Decision: USM files are designed for AI agent consumption via MCP tools before human readability.

idempotent-generation

Decision: Scan and generate are safe to run repeatedly; smart-merge preserves human edits.

one-source-many-outputs

Decision: A single .usm/ directory generates markdown, Mermaid, OpenAPI, ArchiMate, TOGAF, AGENTS.md, and Vitest specs.