ADR-021: Doc Generation Proof Of Concept
Purpose: Architecture decision record for Doc Generation Proof Of Concept
Overview
Section titled “Overview”| Property | Value |
|---|---|
| Status | superseded |
| Category | documentation |
| Phase | 27 |
Status: SUPERSEDED - This POC has been implemented. See:
- Convention-tagged decision records (ADR/PDR) with @libar-docs-convention tags
docs-generated/ANNOTATION-GUIDE.md- Comprehensive guide for fixing generated docs
This decision establishes the pattern for generating technical documentation from annotated source files. It serves as both the DECISION (why/how) and the PROOF OF CONCEPT (demonstrating the pattern works).
Context
Section titled “Context”Invariant: Documentation must be generated from annotated source code, never manually maintained as a separate artifact.
Rationale: Manual documentation drifts from source as the codebase evolves, creating stale references that mislead both humans and AI coding sessions.
| Document | Lines | Maintenance Burden |
|---|---|---|
| docs/PROCESS-GUARD.md | ~300 | High - duplicates code behavior |
| docs/METHODOLOGY.md | ~400 | Medium - conceptual, changes less |
| _claude-md/validation/*.md | ~50 each | High - must match detailed docs |
| CLAUDE.md | ~800 | Very High - aggregates everything |
| Gap | Impact | Solution |
|---|---|---|
| Shape extraction from TypeScript | High | New @extract-shapes tag |
| Convention-tagged content | Medium | Decision records as convention sources |
| Durable intro/context content | Medium | Decision Rule: Context sections |
The Problem:
Common technical documentation is the hardest part to maintain in a repository.The volume constantly grows, and AI coding sessions are drastically less effectiveat updating documentation compared to code. Documentation drifts from source.
Current state in this package:
**Root Causes:**
1. **Duplication** - Same information exists in code comments, feature files, and markdown docs. Changes require updating multiple places.
2. **No Single Source** - Documentation is authored separately from the code it describes. There's no compilation step to catch drift.
3. **Detail Level Mismatch** - Compact docs for AI context and detailed docs for humans are maintained separately despite sharing content.
**What We Have:**
The delivery-process package already has the required ingredients:- Pattern extraction from TypeScript JSDoc and Gherkin tags- Rich content support (DocStrings, tables, code blocks in features)- Multi-source aggregation via tag taxonomy- Progressive disclosure via codec detail levels- Relationship tags for cross-references
**What's Missing:**Decision
Section titled “Decision”Invariant: Each content type (intro/rationale, rules/examples, API types) is owned by exactly one source type (decision, behavior spec, or code).
Rationale: Shared ownership leads to conflicting updates and ambiguous authority over what the “correct” version is.
| Source Type | Durability | Content Ownership |
|---|---|---|
| Decision documents (ADR/PDR) | Permanent | Intro, context, rationale, conventions |
| Behavior specs (.feature) | Permanent | Rules, examples, acceptance criteria |
| Implementation code (.ts) | Compiled | API types, error messages, signatures |
| Rule Prefix | ADR Section | Doc Section |
|---|---|---|
Context... | context | ## Background / Introduction |
Decision... | decision | ## How It Works |
Consequence... | consequences | ## Trade-offs |
| Other rules | other (warning logged) | Custom sections |
| Target Document | Sources | Detail Level | Effect |
|---|---|---|---|
| docs/PROCESS-GUARD.md | This decision + behavior specs + code | detailed | All sections, full JSDoc |
| _claude-md/validation/process-guard.md | This decision + behavior specs + code | summary | Rules table, types only |
| Level | Content Included | Rendering Style |
|---|---|---|
| summary | Essential tables, type names only | Compact - lists vs code blocks |
| standard | Tables, types, key descriptions | Balanced |
| detailed | Everything including JSDoc, examples | Full - code blocks with JSDoc |
| Source | What’s Extracted | How |
|---|---|---|
| Decision Rule: Context | Intro/background section | Rule description text |
| Decision Rule: Decision | How it works section | Rule description text |
| Decision Rule: Consequences | Trade-offs section | Rule description text |
| Decision DocStrings | Code examples (Husky, API) | Fenced code blocks |
| Behavior spec Rules | Validation rules, business rules | Rule names + descriptions |
| Behavior spec Scenario Outlines | Decision tables, lookup tables | Examples tables |
| TypeScript @extract-shapes | API types, interfaces | AST extraction |
| TypeScript JSDoc | Implementation notes | Markdown in comments |
The Pattern:
Documentation is generated from three source types with different durability:
**Why Decisions Own Intro Content:**
Tier 1 specs (roadmap features) become clutter after implementation - theirdeliverables are done, status is completed, they pile up. Behavior specs staycurrent because tests must pass. But neither is appropriate for intro content.
Decisions (ADR/PDR) are durable by design - they remain valid until explicitlysuperseded. The `Rule: Context` section of a decision IS the background/introfor any documentation about that topic.
**Extends Existing ADR Codec:**
The doc-from-decision generator extends the existing `AdrDocumentCodec` whichalready parses Rule: prefixes via `partitionAdrRules()` (see adr.ts:627-663):
**Source Mapping Pattern:**
Each documentation decision declares its target documents and source mapping:
**Detail Level Mapping:**
Uses existing `DetailLevel` enum from `renderable/codecs/types/base.ts`:
**Extraction by Source Type:**
**The Generator Command:**generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' --typescript 'src/**/*.ts' --generators doc-from-decision --output docsInvariant: The source mapping table in a decision document defines how documentation sections are assembled from multiple source files.
Rationale: Without a declarative mapping, generators must hard-code source-to-section relationships, making the system brittle to new document types.
| Column | Purpose | Example |
|---|---|---|
| Section | Target section heading in generated doc | ”Intro & Context”, “API Types” |
| Source File | Path to source file or self-reference marker | ”src/types.ts”, “THIS DECISION” |
| Extraction Method | How to extract content from source | ”@extract-shapes”, “Rule blocks” |
| Marker | Meaning |
|---|---|
| THIS DECISION | Extract from the current decision document |
| THIS DECISION (Rule: X) | Extract specific Rule: block from current document |
| THIS DECISION (DocString) | Extract fenced code blocks from current document |
| Extraction Method | Source Type | Action |
|---|---|---|
| Decision rule description | Decision (.feature) | Extract Rule: block content (Invariant, Rationale) |
| @extract-shapes tag | TypeScript (.ts) | Invoke shape extractor for @libar-docs-extract-shapes |
| Rule blocks | Behavior spec (.feature) | Extract Rule: names and descriptions |
| Scenario Outline Examples | Behavior spec (.feature) | Extract Examples tables as markdown |
| JSDoc section | TypeScript (.ts) | Extract markdown from JSDoc comments |
| createViolation() patterns | TypeScript (.ts) | Extract error message literals |
| Fenced code block | Decision (.feature) | Extract DocString code blocks with language |
Table Format:
**Self-Reference Markers:**
**Extraction Method Dispatch:**
**Path Resolution:**
- Relative paths are resolved from project root- `THIS DECISION` resolves to the current decision document- Missing files produce warnings but generation continuesConsequences
Section titled “Consequences”Invariant: Decision documents remain the authoritative source for intro, rationale, and convention content until explicitly superseded.
Rationale: Without durable ownership, documentation sections lose their authoritative source and degrade into unattributed prose that no one updates.
| Benefit | How |
|---|---|
| Single source of truth | Each content type owned by one source |
| Always-current docs | Generated from tested/compiled sources |
| Reduced maintenance | Change source once, docs regenerate |
| Progressive disclosure | Same sources → compact + detailed outputs |
| Clear ownership | Decisions own “why”, code owns “what” |
| Trade-off | Mitigation |
|---|---|
| Decisions must be updated for fundamental changes | Appropriate - fundamentals ARE decisions |
| New @extract-shapes capability required | Spec created (shape-extraction.feature) |
| Initial annotation effort on existing code | One-time migration, then maintained |
| Generated docs in git history | Same as current manual approach |
| Content Type | Owner | Update Trigger |
|---|---|---|
| Intro, rationale, context | Decision document | Fundamental change to approach |
| Rules, examples, edge cases | Behavior specs | Behavior change (tests fail) |
| API types, signatures | Code with @extract-shapes | Interface change (compile fail) |
| Error messages | Code patterns | Message text change |
| Code examples | Decision DocStrings | Example needs update |
Benefits:
**Trade-offs:**
**Ownership Boundaries:**Invariant: Pre-implementation design stubs must reside in delivery-process/stubs/, never in src/.
Rationale: Stubs in src/ require ESLint exceptions, create confusion between production and design code, and risk accidental imports of unimplemented functions.
| Issue | Impact |
|---|---|
| ESLint exceptions needed | Rules relaxed for “not-yet-real” code |
| Confusion | What’s production vs. what’s design? |
| Pollution | Stubs mixed with implemented code |
| Import accidents | Other code might import unimplemented stubs |
| Maintenance burden | Must track which files are stubs |
| Location | Content | When Moved to src/ |
|---|---|---|
| delivery-process/stubs/{pattern}/*.ts | API shapes, interfaces, throw-not-implemented | Implementation session |
| src/*/.ts | Production code only | Already there |
| Benefit | How |
|---|---|
| No ESLint exceptions | Stubs aren’t in src/, no relaxation needed |
| Clear separation | delivery-process/stubs/ = design, src/ = production |
| Documentation source | Stubs with @extract-shapes generate API docs |
| Safe iteration | Can refine stub APIs without breaking anything |
| Implementation signal | Moving from delivery-process/stubs/ to src/ = implementation started |
| Document | Decision Source |
|---|---|
| docs/METHODOLOGY.md | ADR for delivery process methodology |
| docs/TAXONOMY.md | PDR-006 TypeScript Taxonomy (exists) |
| docs/VALIDATION.md | ADR for validation approach |
| docs/SESSION-GUIDES.md | ADR for session workflows |
| _claude-md/*/.md | Corresponding decisions with compact extraction |
The Problem:
Design stubs (pre-implementation API shapes) placed in `src/` cause issues:
Example of the anti-pattern (from monorepo eslint.config.js):// TODO: Delivery process design artifacts: Relax unused-vars { files: [ "**/packages/platform-core/src/durability/durableAppend.ts", "**/packages/platform-core/src/durability/intentCompletion.ts", // ... more stubs in src/ ... ], rules: { "@typescript-eslint/no-unused-vars": "off", }, }The Solution:
Design stubs live in `delivery-process/stubs/`:
**Design Stub Pattern:**/** * @libar-docs * @libar-docs-pattern ShapeExtractorStub * @libar-docs-status roadmap * * ## Shape Extractor - Design Stub * * API design for extracting TypeScript types from source files. */
export interface ExtractedShape { name: string; kind: 'interface' | 'type' | 'enum' | 'function'; sourceText: string;}
export function extractShapes( sourceCode: string, shapeNames: string[]): Map<string, ExtractedShape> { throw new Error('ShapeExtractor not yet implemented - roadmap pattern');}Benefits:
**Workflow:**
1. **Design session:** Create stub in `delivery-process/stubs/{pattern-name}/`2. **Iterate:** Refine API shapes, add JSDoc, test with docs generation3. **Implementation session:** Move/copy to `src/`, implement real logic4. **Stub becomes example:** Original stub stays as reference (optional)
**What This Enables:**
Once proven with Process Guard, the pattern applies to all documentation:Proof of Concept - Self-documentation validates the pattern
Section titled “Proof of Concept - Self-documentation validates the pattern”Invariant: The documentation generation pattern must be validated by generating documentation about itself from its own annotated sources.
Rationale: A self-referential proof of concept exposes extraction gaps and source mapping issues that synthetic test data would miss. This POC demonstrates the doc-from-decision pattern by generating docs about ITSELF. The DocGenerationProofOfConcept pattern produces:
| Output | Purpose | Detail Level |
|---|---|---|
| docs/DOC-GENERATION-PROOF-OF-CONCEPT.md | Detailed reference | detailed |
| _claude-md/generated/doc-generation-proof-of-concept.md | AI context | summary |
| Section | Source File | Extraction Method |
|---|---|---|
| Intro & Context | THIS DECISION (Rule: Context above) | Decision rule description |
| How It Works | THIS DECISION (Rule: Decision above) | Decision rule description |
| Validation Rules | tests/features/validation/process-guard.feature | Rule blocks |
| Protection Levels | delivery-process/specs/process-guard-linter.feature | Scenario Outline Examples |
| Valid Transitions | delivery-process/specs/process-guard-linter.feature | Scenario Outline Examples |
| API Types | src/lint/process-guard/types.ts | @extract-shapes tag |
| Decider API | src/lint/process-guard/decider.ts | @extract-shapes tag |
| CLI Options | src/cli/lint-process.ts | JSDoc section |
| Error Messages | src/lint/process-guard/decider.ts | createViolation() patterns |
| Pre-commit Setup | THIS DECISION (DocString) | Fenced code block |
| Programmatic API | THIS DECISION (DocString) | Fenced code block |
| Situation | Solution | Example |
|---|---|---|
| Fix bug in completed spec | Add unlock reason tag | @libar-docs-unlock-reason:'Fix-typo' |
| Modify outside session scope | Use ignore flag | lint-process --staged --ignore-session |
| CI treats warnings as errors | Use strict flag | lint-process --all --strict |
| Skip workflow (legacy import) | Multiple transitions | Set roadmap then completed in same commit |
Process Guard docs are generated separately from adr-006-process-guard.feature.
**Source Mapping for POC Self-Documentation:**
This source mapping demonstrates all extraction methods by extracting contentfrom this POC's own sources. The table serves as both documentation AND test data.
**Pre-commit Hook Setup:**
File: `.husky/pre-commit`npx lint-process --stagedPackage.json Scripts:
{ "scripts": { "lint:process": "lint-process --staged", "lint:process:ci": "lint-process --all --strict" }}Programmatic API Example:
import { deriveProcessState, detectStagedChanges, validateChanges, hasErrors, summarizeResult,} from '@libar-dev/delivery-process/lint';
// 1. Derive state from annotationsconst state = (await deriveProcessState({ baseDir: '.' })).value;
// 2. Detect changesconst changes = detectStagedChanges('.').value;
// 3. Validateconst { result } = validateChanges({ state, changes, options: { strict: false, ignoreSession: false },});
// 4. Handle resultsif (hasErrors(result)) { console.log(summarizeResult(result)); process.exit(1);}Escape Hatches:
Expected Output - Compact claude module structure
Section titled “Expected Output - Compact claude module structure”Invariant: Compact output must contain only essential tables and type names, with no JSDoc comments or implementation details.
Rationale: AI context windows are finite; including non-essential content displaces actionable information and degrades session effectiveness.
| Section | Content |
|---|---|
| Header + Intro | Pattern name, problem/solution summary |
| API Types | Core interface definitions (DeciderInput, ValidationResult) |
| 7 Validation Rules | Rule table with severity and description |
| Protection Levels | Status-to-protection mapping table |
| CLI | Essential command examples |
| Link | Reference to full documentation |
File: _claude-md/validation/process-guard.md
The compact module extracts only essential content for AI context.Output size depends on source mapping entries - there is no artificial line limit.
**Expected Sections:**
**Key Characteristics:**
- Summary detail level (essential tables only)- No JSDoc comments or implementation details- Tables for structured data (rules, protection levels)- Inline code blocks for CLI examples- Cross-reference to detailed documentation