@neottia/design-docs
v0.2.1
Published
Repository-local versioned design documents with strict Markdown/YAML and SQLite FTS5 search
Readme
@neottia/design-docs
Repository-local, versioned design documents for Neottia. Canonical Markdown is the sole authority; a domain-owned SQLite FTS5 database is a disposable search cache.
pnpm add @neottia/design-docsRead the Design Docs user guide and library reference.
import { DesignDocumentStore, loadDesignDocsConfig } from "@neottia/design-docs";
const cwd = process.cwd();
const store = await DesignDocumentStore.fromConfig(loadDesignDocsConfig(cwd, { enabled: true }), cwd);
const document = await store.create({
title: "Deployment status page",
kind: "hld",
created_by: "user:owner",
});
console.log(document.id, document.revision);Configuration
Enable the strict canonical shard in .neottia/config.yml:
version: 1
modules:
design_docs:
enabled: true
root: .neottia/design-docs
retrieval:
limit: 20
snippet_bytes: 512
all_versions: false
cache:
max_age_ms: 300000
stale_policy: prompt # prompt | rebuild | failroot must be a safe project-relative path. Portable Unicode directory names are valid. Actual .neottia/cache and .neottia/repository-store paths remain reserved. Environment overrides include NEOTTIA_DESIGN_DOCS_ENABLED, NEOTTIA_DESIGN_DOCS_ROOT, NEOTTIA_DESIGN_DOCS_RETRIEVAL_LIMIT, NEOTTIA_DESIGN_DOCS_SNIPPET_BYTES, NEOTTIA_DESIGN_DOCS_ALL_VERSIONS, NEOTTIA_DESIGN_DOCS_CACHE_MAX_AGE_MS, and NEOTTIA_DESIGN_DOCS_CACHE_STALE_POLICY. Shared resolution applies defaults, global configuration, project configuration, the selected profile, environment values, and explicit runtime overrides in that order. Security limits are documented by the published config.schema.json.
The exported designDocsConfigContribution composes Design Docs into a shared @neottia/config registry. loadDesignDocsConfig() remains available for standalone use and returns the same typed configuration. Its deprecated compatibility mode accepts skills.design_docs, NEOTTIA_DESIGN_DOCS_CONFIG_FILE, and NEOTTIA_CONFIG_DESIGN_DOCS_PATH; new shared hosts should use modules.design_docs and resolve one snapshot for all participating domains. See the unified configuration guide for file discovery, profiles, precedence, provenance, diagnostics, and migration.
Authority and authoring
Files live under .neottia/design-docs/; archived lineages live under its archive/ directory. New IDs are doc- plus an uppercase Crockford ULID. Existing doc-00001-style IDs remain readable and importable but are never allocated. Filenames are deterministic: <id>-<title-slug>-v<N>.md.
Frontmatter has only id, title, kind, status, version, created_at, updated_at, optional created_by, and optional JSON metadata. Kinds are hld, lld, design-overview, and gdd. Bodies begin with exactly # <title> and a blank line and contain no other H1. Input must be canonical UTF-8/LF; ambiguous YAML, aliases, anchors, tags, directives, unknown fields, controls, and configured limit violations are rejected. Revisions are v1:<sha256-of-exact-bytes>.
Review, approval, and versions
document_create always creates v1 in draft. document_update edits only the latest active draft or review and requires expected_revision. document_transition permits draft → review, review → draft, and review → approved; it requires an actor, explicit intent, and evidence whose source is human-ui, caller-attestation, or policy. The domain records the assertion and never manufactures human confirmation.
Approval is terminal for that version. Approved bytes cannot be updated. document_version requires the latest approved exact revision and creates the contiguous successor as draft, preserving every prior byte, ID, and creation time.
Search, archive, links, and migration
document_search uses FTS5/BM25 and supports kind, status, location, ID, current/all-version, and result-limit filters. Hits are deterministically ordered and hydrated from the same valid canonical snapshot; use document_get for full content. The cache at .neottia/cache/design-docs.sqlite can always be rebuilt and is never authority.
Archive and restore require the latest revision and durably move the complete lineage in one recoverable repository-store batch. There is no physical-delete tool. Stable issue references use {kind: design-doc, id, version?}: omitted version resolves latest, pinned versions resolve exactly, and archived versions remain addressable. document_validate with cross_domain: true checks links from active and archived canonical Issues. Shipped MCP, Pi, and OpenCode hosts install @neottia/issues-design-docs by default; library users can inject its linkValidator or a custom validator. Both capabilities must be enabled. resolveAddressesUnderLease is the cycle-free composition seam; Design Docs does not import or mutate Issues.
document_export produces a deterministic, digested bundle containing exact canonical bytes. document_import defaults to preview and reports additions, conflicts, unsupported records, warnings, and legacy-to-Neottia path mappings before mutation. For format: harnessctl-v2, supply an explicit JSON {version: 1, format: "harnessctl-v2-design-documents", documents: [{path, content, location?}]} bundle of canonical legacy sources; the package never scans or mutates .harnessctl or .specs.
Concurrency, recovery, and troubleshooting
All reads and mutations use the repository authority lease. Mutations validate a complete proposed snapshot before publication; rename and lineage moves use exact-revision durable batches. Interrupted journals recover at the next lease. Symlinks, hard links, portable path collisions, stale revisions, split/gapped lineages, and operator-modified recovery artifacts fail closed.
Every shared tool error contains category, code, message, paths, and retryable, plus optional bounded details. Repository-store retryability and diagnostic evidence survive translation to this contract. Tool results are measured as compact UTF-8 JSON after schema validation and rejected when they exceed security.limits.max_result_bytes; a successfully published mutation is not rolled back merely because its response is too large.
If search reports a missing, corrupt, incompatible, stale, or contradictory cache, choose rebuild; canonical validation must pass first. If a revision mismatch occurs, fetch the latest record and intentionally retry. Never hand-edit an approved file or cache database.
Delivery surfaces
Use @neottia/design-docs-mcp for generic stdio MCP, @neottia/pi-design-docs for Pi, or @neottia/opencode-design-docs for OpenCode. All consume the same thirteen Zod-derived input/output/error contracts.
