diff --git a/_claude-md/CLAUDE-MODULES.md b/_claude-md/CLAUDE-MODULES.md index a5691c8d..d63d3c39 100644 --- a/_claude-md/CLAUDE-MODULES.md +++ b/_claude-md/CLAUDE-MODULES.md @@ -4,6 +4,6 @@ 1 module(s) generated from annotated behavior specs. -| Module | Section | Source Pattern | Content | -| --- | --- | --- | --- | +| Module | Section | Source Pattern | Content | +| ----------------- | -------- | ------------------------- | ------- | | session-workflows | workflow | SessionGuidesModuleSource | 9 rules | diff --git a/_claude-md/workflow/session-workflows.md b/_claude-md/workflow/session-workflows.md new file mode 100644 index 00000000..f8f8b4c6 --- /dev/null +++ b/_claude-md/workflow/session-workflows.md @@ -0,0 +1,97 @@ +### SessionGuidesModuleSource + +#### SESSION-GUIDES.md is the authoritative public human reference + +**Invariant:** `docs/SESSION-GUIDES.md` exists and is not deleted, shortened, or replaced with a redirect. Its comprehensive checklists, CLI command examples, and session decision trees serve developers on libar.dev. + +**Rationale:** Session workflow guidance requires two formats for two audiences. Public developers need comprehensive checklists with full examples. AI sessions need compact invariants they can apply without reading 389 lines. + +#### CLAUDE.md session workflow content is derived, not hand-authored + +**Invariant:** After Phase 39 generation deliverables complete, the "Session Workflows" section in CLAUDE.md contains no manually-authored content. It is composed from generated `_claude-md/workflow/` modules. + +**Rationale:** A hand-maintained CLAUDE.md session section creates two copies of session workflow guidance with no synchronization mechanism. Regeneration from annotated source eliminates drift. + +#### Session type determines artifacts and FSM changes + +**Invariant:** Four session types exist, each with defined input, output, and FSM impact. Mixing outputs across session types (e.g., writing code in a planning session) violates session discipline. + +**Rationale:** Session type confusion causes wasted work — a design mistake discovered mid-implementation wastes the entire session. Clear contracts prevent scope bleeding between session types. + +| Session | Input | Output | FSM Change | +| ----------------- | ------------------- | --------------------------- | ------------------------------ | +| Planning | Pattern brief | Roadmap spec (.feature) | Creates roadmap | +| Design | Complex requirement | Decision specs + code stubs | None | +| Implementation | Roadmap spec | Code + tests | roadmap to active to completed | +| Planning + Design | Pattern brief | Spec + stubs | Creates roadmap | + +#### Planning sessions produce roadmap specs only + +**Invariant:** A planning session creates a roadmap spec with metadata, deliverables table, Rule: blocks with invariants, and scenarios. It must not produce implementation code, transition to active, or prompt for implementation readiness. + +**Rationale:** Planning is the cheapest session type — it produces .feature file edits, no compilation needed. Mixing implementation into planning defeats the cost advantage and introduces untested code without a locked scope. + +| Do | Do NOT | +| --------------------------------------------------- | -------------------------- | +| Extract metadata from pattern brief | Create .ts implementation | +| Create spec file with proper tags | Transition to active | +| Add deliverables table in Background | Ask Ready to implement | +| Convert constraints to Rule: blocks | Write full implementations | +| Add scenarios: 1 happy-path + 1 validation per Rule | | + +#### Design sessions produce decisions and stubs only + +**Invariant:** A design session makes architectural decisions and creates code stubs with interfaces. It must not produce implementation code. Context gathering via the Process Data API must precede any explore agent usage. + +**Rationale:** Design sessions resolve ambiguity before implementation begins. Code stubs in delivery-process/stubs/ live outside src/ to avoid TypeScript compilation and ESLint issues, making them zero-risk artifacts. + +| Use Design Session | Skip Design Session | +| -------------------------- | ------------------- | +| Multiple valid approaches | Single obvious path | +| New patterns/capabilities | Bug fix | +| Cross-context coordination | Clear requirements | + +#### Implementation sessions follow FSM-enforced execution order + +**Invariant:** Implementation sessions must follow a strict 5-step execution order. Transition to active must happen before any code changes. Transition to completed must happen only when ALL deliverables are done. Skipping steps causes Process Guard rejection at commit time. + +**Rationale:** The execution order ensures FSM state accurately reflects work state at every point. Writing code before transitioning to active means Process Guard sees changes to a roadmap spec (no scope protection). Marking completed with incomplete work creates a hard-locked state that requires unlock-reason to fix. + +| Do NOT | Why | +| ----------------------------------- | --------------------------------------- | +| Add new deliverables to active spec | Scope-locked state prevents scope creep | +| Mark completed with incomplete work | Hard-locked state cannot be undone | +| Skip FSM transitions | Process Guard will reject | +| Edit generated docs directly | Regenerate from source | + +#### FSM errors have documented fixes + +**Invariant:** Every Process Guard error code has a defined cause and fix. The error codes, causes, and fixes form a closed set — no undocumented error states exist. + +**Rationale:** Undocumented FSM errors cause session-blocking confusion. A lookup table from error code to fix eliminates guesswork and prevents workarounds that bypass process integrity. + +| Error | Cause | Fix | +| ------------------------- | ---------------------------------------------- | ------------------------------------------- | +| completed-protection | File has completed status but no unlock tag | Add libar-docs-unlock-reason tag | +| invalid-status-transition | Skipped FSM state (e.g., roadmap to completed) | Follow path: roadmap to active to completed | +| scope-creep | Added deliverable to active spec | Remove deliverable OR revert to roadmap | +| session-scope (warning) | Modified file outside session scope | Add to scope OR use --ignore-session | +| session-excluded | Modified excluded pattern during session | Remove from exclusion OR override | + +| 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 | + +#### Handoff captures session-end state for continuity + +**Invariant:** Multi-session work requires handoff documentation generated from the Process Data API. Handoff output always reflects actual annotation state, not manual notes. + +**Rationale:** Manual session notes drift from actual deliverable state. The handoff command derives state from annotations, ensuring the next session starts from ground truth rather than stale notes. + +#### ClaudeModuleGeneration is the generation mechanism + +**Invariant:** Phase 39 depends on ClaudeModuleGeneration (Phase 25). Adding `@libar-docs-claude-module` and `@libar-docs-claude-section:workflow` tags to this spec will cause ClaudeModuleGeneration to produce `_claude-md/workflow/` output files. The hand-written `_claude-md/workflow/` files are deleted after successful verified generation. + +**Rationale:** The annotation work (Rule blocks in this spec) is immediately useful — queryable via `pnpm process:query -- rules`. Generation deliverables cannot complete until Phase 25 ships the ClaudeModuleCodec. This sequencing is intentional: the annotation investment has standalone value regardless of whether the codec exists yet. diff --git a/delivery-process.config.ts b/delivery-process.config.ts index 3eb27b64..60259b84 100644 --- a/delivery-process.config.ts +++ b/delivery-process.config.ts @@ -339,6 +339,9 @@ export default defineConfig({ index: { outputDirectory: 'docs-live', }, + 'design-review': { + outputDirectory: 'delivery-process', + }, }, codecOptions: { index: { diff --git a/delivery-process/design-reviews/setup-command.md b/delivery-process/design-reviews/setup-command.md new file mode 100644 index 00000000..e03eeeb0 --- /dev/null +++ b/delivery-process/design-reviews/setup-command.md @@ -0,0 +1,188 @@ +# Design Review: SetupCommand + +**Purpose:** Auto-generated design review with sequence and component diagrams +**Detail Level:** Design review artifact from sequence annotations + +--- + +**Pattern:** SetupCommand | **Phase:** Phase 45 | **Status:** roadmap | **Orchestrator:** init-cli | **Steps:** 6 | **Participants:** 8 + +**Source:** `delivery-process/specs/setup-command.feature` + +--- + +## Annotation Convention + +This design review is generated from the following annotations: + +| Tag | Level | Format | Purpose | +| --------------------- | -------- | ------ | ---------------------------------- | +| sequence-orchestrator | Feature | value | Identifies the coordinator module | +| sequence-step | Rule | number | Explicit execution ordering | +| sequence-module | Rule | csv | Maps Rule to deliverable module(s) | +| sequence-error | Scenario | flag | Marks scenario as error/alt path | + +Description markers: `**Input:**` and `**Output:**` in Rule descriptions define data flow types for sequence diagram call arrows and component diagram edges. + +--- + +## Sequence Diagram — Runtime Interaction Flow + +Generated from: `@libar-docs-sequence-step`, `@libar-docs-sequence-module`, `@libar-docs-sequence-error`, `**Input:**`/`**Output:**` markers, and `@libar-docs-sequence-orchestrator` on the Feature. + +```mermaid +sequenceDiagram + participant User + participant init_cli as "init-cli.ts" + participant detect_context as "detect-context.ts" + participant prompts as "prompts.ts" + participant generate_config as "generate-config.ts" + participant augment_package_json as "augment-package-json.ts" + participant scaffold_dirs as "scaffold-dirs.ts" + participant generate_example as "generate-example.ts" + participant validate_setup as "validate-setup.ts" + + User->>init_cli: invoke + + Note over init_cli: Rule 1 — The init command reads the target directory for package.json, tsconfig.json, delivery-process.config.ts (or .js), and monorepo markers before prompting or generating any files. Detection results determine which steps are skipped. + + init_cli->>+detect_context: targetDir: string + detect_context-->>-init_cli: ProjectContext + + alt Fails gracefully when no package.json exists + init_cli-->>User: error + init_cli->>init_cli: exit(1) + end + + Note over init_cli: Rule 2 — The init command prompts for preset selection from the three available presets (generic, libar-generic, ddd-es-cqrs) with descriptions, and for source glob paths with defaults inferred from project structure. The —yes flag skips non-destructive selection prompts and uses defaults. Destructive overwrites require an explicit —force flag; otherwise init exits without modifying existing files. + + init_cli->>+prompts: ProjectContext + prompts-->>-init_cli: InitConfig + + alt Non-interactive mode refuses to overwrite existing config + init_cli-->>User: error + init_cli->>init_cli: exit(1) + end + + Note over init_cli: Rule 3 — The generated delivery-process.config.ts (or .js) imports defineConfig from the correct path, uses the selected preset, and includes configured source globs. An existing config file is never overwritten without confirmation. + + init_cli->>+generate_config: InitConfig + generate_config-->>-init_cli: delivery-process.config.ts written to targetDir + + alt Existing config file is not overwritten without confirmation + init_cli-->>User: error + init_cli->>init_cli: exit(1) + end + + Note over init_cli: Rule 4 — Injected scripts reference bin names (process-api, generate-docs) resolved via node_modules/.bin, not dist paths. Existing scripts are preserved. The package.json "type" field is preserved. ESM migration is an explicit opt-in via —esm flag. + + init_cli->>+augment_package_json: InitConfig + augment_package_json-->>-init_cli: package.json updated with process and docs scripts + + Note over init_cli: Rule 5 — The init command creates directories for configured source globs and generates one example annotated TypeScript file with the minimum annotation set (opt-in marker, pattern tag, status, category, description). + + init_cli->>+scaffold_dirs: InitConfig + scaffold_dirs-->>-init_cli: directories created for source globs, example annotated .ts file + init_cli->>+generate_example: InitConfig + generate_example-->>-init_cli: directories created for source globs, example annotated .ts file + + Note over init_cli: Rule 6 — After all files are generated, init runs process-api overview and reports whether the pipeline detected the example pattern. Success prints a summary and next steps. Failure prints diagnostic information. + + init_cli->>+validate_setup: targetDir: string + validate_setup-->>-init_cli: SetupResult + + alt Failed validation prints diagnostic information + init_cli-->>User: error + init_cli->>init_cli: exit(1) + end + +``` + +--- + +## Component Diagram — Types and Data Flow + +Generated from: `@libar-docs-sequence-module` (nodes), `**Input:**`/`**Output:**` (edges and type shapes), deliverables table (locations), and `sequence-step` (grouping). + +```mermaid +graph LR + subgraph phase_1["Phase 1: targetDir: string"] + phase_1_detect_context["detect-context.ts"] + end + + subgraph phase_2["Phase 2: ProjectContext"] + phase_2_prompts["prompts.ts"] + end + + subgraph phase_3["Phase 3: InitConfig"] + phase_3_generate_config["generate-config.ts"] + phase_3_augment_package_json["augment-package-json.ts"] + phase_3_scaffold_dirs["scaffold-dirs.ts"] + phase_3_generate_example["generate-example.ts"] + end + + subgraph phase_4["Phase 4: targetDir: string"] + phase_4_validate_setup["validate-setup.ts"] + end + + subgraph orchestrator["Orchestrator"] + init_cli["init-cli.ts"] + end + + subgraph types["Key Types"] + ProjectContext{{"ProjectContext\n-----------\npackageJsonPath\npackageJson\ntsconfigExists\ntsconfigModuleResolution\nexistingConfigPath\nisMonorepo\nhasEsmType"}} + InitConfig{{"InitConfig\n-----------\ntargetDir\npreset\nsources\nforce\ncontext"}} + SetupResult{{"SetupResult\n-----------\nsuccess\npatternCount\ndiagnostics"}} + end + + phase_1_detect_context -->|"ProjectContext"| init_cli + phase_2_prompts -->|"InitConfig"| init_cli + phase_4_validate_setup -->|"SetupResult"| init_cli + init_cli -->|"targetDir: string"| phase_1_detect_context + init_cli -->|"ProjectContext"| phase_2_prompts + init_cli -->|"InitConfig"| phase_3_generate_config + init_cli -->|"InitConfig"| phase_3_augment_package_json + init_cli -->|"InitConfig"| phase_3_scaffold_dirs + init_cli -->|"InitConfig"| phase_3_generate_example + init_cli -->|"targetDir: string"| phase_4_validate_setup +``` + +--- + +## Key Type Definitions + +| Type | Fields | Produced By | Consumed By | +| ---------------- | ------------------------------------------------------------------------------------------------------------------ | -------------- | ---------------------------------------------------------------------- | +| `ProjectContext` | packageJsonPath, packageJson, tsconfigExists, tsconfigModuleResolution, existingConfigPath, isMonorepo, hasEsmType | detect-context | prompts | +| `InitConfig` | targetDir, preset, sources, force, context | prompts | generate-config, augment-package-json, scaffold-dirs, generate-example | +| `SetupResult` | success, patternCount, diagnostics | validate-setup | | + +--- + +## Design Questions + +Verify these design properties against the diagrams above: + +| # | Question | Auto-Check | Diagram | +| ---- | ------------------------------------ | ------------------------------- | --------- | +| DQ-1 | Is the execution ordering correct? | 6 steps in monotonic order | Sequence | +| DQ-2 | Are all interfaces well-defined? | 3 distinct types across 6 steps | Component | +| DQ-3 | Is error handling complete? | 4 error paths identified | Sequence | +| DQ-4 | Is data flow unidirectional? | Review component diagram edges | Component | +| DQ-5 | Does validation prove the full path? | Review final step | Both | + +--- + +## Findings + +Record design observations from reviewing the diagrams above. Each finding should reference which diagram revealed it and its impact on the spec. + +| # | Finding | Diagram Source | Impact on Spec | +| --- | ------------------------------------------- | -------------- | -------------- | +| F-1 | (Review the diagrams and add findings here) | — | — | + +--- + +## Summary + +The SetupCommand design review covers 6 sequential steps across 8 participants with 3 key data types and 4 error paths. diff --git a/delivery-process/specs/design-review-generation.feature b/delivery-process/specs/design-review-generation.feature new file mode 100644 index 00000000..d4048854 --- /dev/null +++ b/delivery-process/specs/design-review-generation.feature @@ -0,0 +1,141 @@ +@libar-docs +@libar-docs-pattern:DesignReviewGeneration +@libar-docs-status:active +@libar-docs-phase:46 +@libar-docs-product-area:Generation +@libar-docs-effort:2d +@libar-docs-priority:high +@libar-docs-depends-on:MermaidDiagramUtils +Feature: Design Review Diagram Generation + + **Problem:** + Design reviews require manual creation of sequence and component diagrams that + duplicate information already captured in spec annotations. When the spec changes, + the diagrams drift. When a new pattern needs a design review, the author must + copy-paste from a reference document and manually adapt. This process takes 30-45 + minutes per pattern and produces artifacts that are stale by the next spec edit. + + **Solution:** + A generation pipeline that reads sequence annotations (libar-docs-sequence-*) from + feature files and produces design review documents with Mermaid sequence and component + diagrams. The pipeline fits into the existing codec/generator architecture: sequence + data is pre-computed in a SequenceIndex view on MasterDataset, then a standalone + DesignReviewCodec renders the diagrams and supporting tables. Output goes to + delivery-process/design-reviews/. + + Background: Deliverables + Given the following deliverables: + | Deliverable | Status | Location | + | SequenceIndex schema | complete | src/validation-schemas/master-dataset.ts | + | Sequence transform utilities | complete | src/generators/pipeline/sequence-utils.ts | + | Transform dataset integration | complete | src/generators/pipeline/transform-dataset.ts | + | BusinessRule errorScenarioNames | complete | src/validation-schemas/extracted-pattern.ts | + | DesignReviewCodec | complete | src/renderable/codecs/design-review.ts | + | Design review generator | complete | src/generators/built-in/design-review-generator.ts | + | Process API sequence subcommand | complete | src/cli/process-api.ts | + | Config wiring | complete | delivery-process.config.ts | + + Rule: SequenceIndex pre-computes ordered steps from rule-level tags + + **Invariant:** The MasterDataset sequenceIndex contains one entry per pattern + that has libar-docs-sequence-orchestrator and at least one rule with + libar-docs-sequence-step. Steps are sorted by stepNumber. Participants are + deduplicated and ordered with orchestrator first. + + **Rationale:** Pre-computing in the transform pass avoids repeated parsing + in the codec. ADR-006 mandates the MasterDataset as the sole read model. + Downstream consumers (codec, process API) read structured data, not raw tags. + + **Verified by:** SequenceIndex populated for annotated pattern, + Patterns without sequence annotations excluded + + @acceptance-criteria @happy-path + Scenario: SequenceIndex populated for annotated pattern + Given a pattern with sequence-orchestrator and 6 sequence-step rules + When the pattern is transformed to MasterDataset + Then sequenceIndex contains an entry for the pattern + And the entry has 6 ordered steps with modules and data flow types + + @acceptance-criteria @validation + Scenario: Patterns without sequence annotations excluded + Given a pattern with no sequence-orchestrator tag + When the pattern is transformed to MasterDataset + Then sequenceIndex does not contain an entry for the pattern + + Rule: DesignReviewCodec generates sequence diagrams from ordered steps + + **Invariant:** The sequence diagram contains participants derived from + sequence-module tags, Note blocks from Rule names, call arrows from + Input/Output markers, and alt blocks from sequence-error scenarios. + Participant order follows step order with User and orchestrator first. + + **Rationale:** Sequence diagrams verify interaction ordering and error + handling completeness. Auto-generation ensures diagrams stay synchronized + with spec annotations. Manual diagrams drift within days of a spec edit. + + **Verified by:** Sequence diagram has correct participants, + Error scenarios produce alt blocks + + @acceptance-criteria @happy-path + Scenario: Sequence diagram has correct participants + Given a pattern with orchestrator and 7 distinct modules + When the design review is generated + Then the sequence diagram declares 9 participants + And participant order matches step order + + @acceptance-criteria @validation + Scenario: Error scenarios produce alt blocks + Given a step with 2 scenarios tagged sequence-error + When the design review is generated + Then the sequence diagram contains 2 alt blocks for that step + + Rule: DesignReviewCodec generates component diagrams from data flow types + + **Invariant:** The component diagram groups modules into subgraphs by + shared Input type, renders distinct Output types as hexagon nodes with + field lists, and draws directed edges showing data flow through the + orchestrator. No circular edges exist in the generated diagram. + + **Rationale:** Component diagrams verify unidirectional data flow and + interface completeness. Type hexagon nodes make the central contracts + visible, informing stub creation with exact field lists. + + **Verified by:** Modules grouped by shared input type, + Type nodes rendered as hexagons with fields + + @acceptance-criteria @happy-path + Scenario: Modules grouped by shared input type + Given 4 modules that all take InitConfig as input + When the design review is generated + Then those 4 modules appear in the same subgraph + + @acceptance-criteria @happy-path + Scenario: Type nodes rendered as hexagons with fields + Given a step with Output annotation containing type name and fields + When the design review is generated + Then the component diagram contains a hexagon node with the type name and fields + + Rule: Process API exposes sequence data via subcommand + + **Invariant:** The sequence subcommand with no args lists all patterns + with sequence annotations. With a pattern name, it returns the full + SequenceIndexEntry including steps, participants, and data flow types. + + **Rationale:** The Process API is the primary query interface for AI + sessions. Exposing sequence data enables design review analysis without + regenerating the full document or reading generated markdown. + + **Verified by:** Sequence subcommand lists annotated patterns, + Sequence subcommand returns entry for named pattern + + @acceptance-criteria @happy-path + Scenario: Sequence subcommand lists annotated patterns + Given SetupCommand has sequence annotations + When querying sequence with no args + Then the response lists SetupCommand + + @acceptance-criteria @happy-path + Scenario: Sequence subcommand returns entry for named pattern + Given SetupCommand has sequence annotations + When querying sequence SetupCommand + Then the response contains 6 steps and orchestrator init-cli diff --git a/delivery-process/specs/setup-command.feature b/delivery-process/specs/setup-command.feature index 036db37b..19baa824 100644 --- a/delivery-process/specs/setup-command.feature +++ b/delivery-process/specs/setup-command.feature @@ -7,6 +7,7 @@ @libar-docs-priority:high @libar-docs-depends-on:ConfigLoader @libar-docs-business-value:reduce-first-project-setup-from-55-minutes-to-under-2-minutes +@libar-docs-sequence-orchestrator:init-cli Feature: Interactive Setup Command **Problem:** @@ -44,6 +45,8 @@ Feature: Interactive Setup Command | Init CLI entry point | pending | src/cli/init.ts | | Bin entry registration | pending | package.json | + @libar-docs-sequence-step:1 + @libar-docs-sequence-module:detect-context Rule: Init detects existing project context before making changes **Invariant:** The init command reads the target directory for package.json, @@ -54,6 +57,11 @@ Feature: Interactive Setup Command working setups. Context detection enables safe adoption into existing projects by skipping steps that are already complete. + **Input:** targetDir: string + + **Output:** ProjectContext -- packageJsonPath, packageJson, tsconfigExists, + tsconfigModuleResolution, existingConfigPath, isMonorepo, hasEsmType + **Verified by:** Detects existing package.json and skips creation, Fails gracefully when run outside a project directory @@ -66,13 +74,15 @@ Feature: Interactive Setup Command And the command does not modify tsconfig.json And the command proceeds to preset selection - @acceptance-criteria @validation + @acceptance-criteria @validation @libar-docs-sequence-error Scenario: Fails gracefully when no package.json exists Given an empty directory with no package.json When running the init command Then the command prints "No package.json found. Run npm init first." And exits with code 1 + @libar-docs-sequence-step:2 + @libar-docs-sequence-module:prompts Rule: Interactive prompts configure preset and source paths with smart defaults **Invariant:** The init command prompts for preset selection from the three @@ -86,6 +96,10 @@ Feature: Interactive Setup Command to use. Smart defaults reduce decisions to confirmations. The --yes flag enables scripted adoption in CI. + **Input:** ProjectContext + + **Output:** InitConfig -- targetDir, preset, sources, force, context + **Verified by:** Preset selection shows all three presets, Non-interactive mode uses defaults without prompting @@ -108,13 +122,15 @@ Feature: Interactive Setup Command And preset defaults to "libar-generic" And source globs use sensible defaults - @acceptance-criteria @validation + @acceptance-criteria @validation @libar-docs-sequence-error Scenario: Non-interactive mode refuses to overwrite existing config Given a directory with an existing delivery-process config file When running the init command with --yes flag Then the command prints a message requiring --force to overwrite And exits with code 1 + @libar-docs-sequence-step:3 + @libar-docs-sequence-module:generate-config Rule: Generated config file uses defineConfig with correct imports **Invariant:** The generated delivery-process.config.ts (or .js) imports @@ -126,6 +142,10 @@ Feature: Interactive Setup Command import path or malformed glob causes every subsequent command to fail. The overwrite guard prevents destroying custom configuration. + **Input:** InitConfig + + **Output:** delivery-process.config.ts written to targetDir + **Verified by:** Generated config is valid TypeScript, Existing config is not overwritten without confirmation @@ -138,13 +158,15 @@ Feature: Interactive Setup Command And contains defineConfig with the selected preset And contains the configured source globs - @acceptance-criteria @validation + @acceptance-criteria @validation @libar-docs-sequence-error Scenario: Existing config file is not overwritten without confirmation Given a directory with an existing delivery-process config file When running the init command Then the command prompts for overwrite confirmation And answering "no" preserves the existing file + @libar-docs-sequence-step:4 + @libar-docs-sequence-module:augment-package-json Rule: Npm scripts are injected using bin command names **Invariant:** Injected scripts reference bin names (process-api, generate-docs) @@ -155,6 +177,10 @@ Feature: Interactive Setup Command **Rationale:** The tutorial uses long fragile dist paths. Bin commands are the stable public API. Setting type:module ensures ESM imports work for the config. + **Input:** InitConfig + + **Output:** package.json updated with process and docs scripts + **Verified by:** Injected scripts use bin names, Existing scripts are preserved @@ -173,6 +199,8 @@ Feature: Interactive Setup Command Then existing scripts are unchanged And new process and docs scripts are added alongside them + @libar-docs-sequence-step:5 + @libar-docs-sequence-module:scaffold-dirs,generate-example Rule: Directory structure and example annotation enable immediate first run **Invariant:** The init command creates directories for configured source globs @@ -182,6 +210,10 @@ Feature: Interactive Setup Command **Rationale:** Empty source globs produce a confusing "0 patterns" result. An example file proves the pipeline works and teaches annotation syntax by example. + **Input:** InitConfig + + **Output:** directories created for source globs, example annotated .ts file + **Verified by:** Directories created for configured globs, Example file is detected by the scanner @@ -191,6 +223,8 @@ Feature: Interactive Setup Command When running process-api overview Then the output shows 1 pattern detected + @libar-docs-sequence-step:6 + @libar-docs-sequence-module:validate-setup Rule: Init validates the complete setup by running the pipeline **Invariant:** After all files are generated, init runs process-api overview and @@ -201,6 +235,10 @@ Feature: Interactive Setup Command Running the pipeline as the final step proves config, globs, directories, and the example annotation all work together. + **Input:** targetDir: string + + **Output:** SetupResult -- success, patternCount, diagnostics + **Verified by:** Successful setup prints summary, Failed validation prints diagnostics @@ -212,7 +250,7 @@ Feature: Interactive Setup Command And prints next steps for annotating files and generating docs And exits with code 0 - @acceptance-criteria @validation + @acceptance-criteria @validation @libar-docs-sequence-error Scenario: Failed validation prints diagnostic information Given init completed but the example file has an invalid glob match When the validation step detects 0 patterns diff --git a/docs-live/ARCHITECTURE.md b/docs-live/ARCHITECTURE.md index d07a7e25..e102a628 100644 --- a/docs-live/ARCHITECTURE.md +++ b/docs-live/ARCHITECTURE.md @@ -7,11 +7,11 @@ ## Overview -This diagram was auto-generated from 155 annotated source files across 11 bounded contexts. +This diagram was auto-generated from 158 annotated source files across 11 bounded contexts. | Metric | Count | | ---------------- | ----- | -| Total Components | 155 | +| Total Components | 158 | | Bounded Contexts | 11 | | Component Roles | 5 | @@ -74,7 +74,6 @@ graph TB Document_Extractor["Document Extractor[service]"] end subgraph generator["Generator BC"] - FileCache["FileCache[infrastructure]"] WarningCollector["WarningCollector"] GeneratorTypes["GeneratorTypes"] SourceMappingValidator["SourceMappingValidator"] @@ -83,13 +82,16 @@ graph TB Documentation_Generation_Orchestrator["Documentation Generation Orchestrator[service]"] ContentDeduplicator["ContentDeduplicator[infrastructure]"] CodecBasedGenerator["CodecBasedGenerator[service]"] + FileCache["FileCache[infrastructure]"] TransformDataset["TransformDataset[service]"] + SequenceTransformUtils["SequenceTransformUtils[service]"] MergePatterns["MergePatterns"] PipelineModule["PipelineModule"] PipelineFactory["PipelineFactory"] ReferenceGeneratorRegistration["ReferenceGeneratorRegistration"] ProcessApiReferenceGenerator["ProcessApiReferenceGenerator"] BuiltInGenerators["BuiltInGenerators"] + DesignReviewGenerator["DesignReviewGenerator[service]"] DecisionDocGenerator["DecisionDocGenerator[service]"] CodecGeneratorRegistration["CodecGeneratorRegistration"] CliRecipeGenerator["CliRecipeGenerator"] @@ -127,6 +129,7 @@ graph TB IndexCodec["IndexCodec"] RichContentHelpers["RichContentHelpers"] MermaidDiagramUtils["MermaidDiagramUtils"] + DesignReviewCodec["DesignReviewCodec[projection]"] DecisionDocCodec["DecisionDocCodec[projection]"] CompositeCodec["CompositeCodec[projection]"] ClaudeModuleCodec["ClaudeModuleCodec"] @@ -176,6 +179,7 @@ graph TB FSMModule["FSMModule"] end subgraph shared["Shared Infrastructure"] + Convention_Annotation_Example___DD_3_Decision["Convention Annotation Example — DD-3 Decision[decider]"] WorkflowConfigSchema["WorkflowConfigSchema"] Tag_Registry_Configuration["Tag Registry Configuration"] OutputSchemas["OutputSchemas"] @@ -194,11 +198,11 @@ graph TB FormatTypes["FormatTypes"] DeliverableStatusTaxonomy["DeliverableStatusTaxonomy"] CategoryDefinition["CategoryDefinition"] - DoDValidationTypes["DoDValidationTypes"] - ValidationModule["ValidationModule"] - RenderableUtils["RenderableUtils"] - SectionBlock["SectionBlock"] - RenderableDocumentModel_RDM_["RenderableDocumentModel(RDM)"] + LintModule["LintModule"] + WarningCollector["WarningCollector"] + GeneratorTypes["GeneratorTypes"] + SourceMappingValidator["SourceMappingValidator"] + GeneratorRegistry["GeneratorRegistry"] ShapeExtractor["ShapeExtractor"] LayerInference["LayerInference"] CLIVersionHelper["CLIVersionHelper"] @@ -208,17 +212,25 @@ graph TB TagTaxonomyCLI["TagTaxonomyCLI"] Documentation_Generator_CLI["Documentation Generator CLI"] CLIErrorHandler["CLIErrorHandler"] - WarningCollector["WarningCollector"] - GeneratorTypes["GeneratorTypes"] - SourceMappingValidator["SourceMappingValidator"] - GeneratorRegistry["GeneratorRegistry"] ProcessStateTypes["ProcessStateTypes"] StubResolverImpl["StubResolverImpl"] RulesQueryModule["RulesQueryModule"] APIModule["APIModule"] - LintModule["LintModule"] - Convention_Annotation_Example___DD_3_Decision["Convention Annotation Example — DD-3 Decision[decider]"] - FSMModule["FSMModule"] + RenderableUtils["RenderableUtils"] + SectionBlock["SectionBlock"] + RenderableDocumentModel_RDM_["RenderableDocumentModel(RDM)"] + DoDValidationTypes["DoDValidationTypes"] + ValidationModule["ValidationModule"] + ProcessGuardTypes["ProcessGuardTypes"] + ProcessGuardModule["ProcessGuardModule"] + DetectChanges["DetectChanges"] + DeriveProcessState["DeriveProcessState"] + MergePatterns["MergePatterns"] + PipelineModule["PipelineModule"] + PipelineFactory["PipelineFactory"] + ReferenceGeneratorRegistration["ReferenceGeneratorRegistration"] + BuiltInGenerators["BuiltInGenerators"] + CodecGeneratorRegistration["CodecGeneratorRegistration"] ValidationRulesCodec["ValidationRulesCodec"] TimelineCodec["TimelineCodec"] TaxonomyCodec["TaxonomyCodec"] @@ -234,17 +246,13 @@ graph TB ClaudeModuleCodec["ClaudeModuleCodec"] BusinessRulesCodec["BusinessRulesCodec"] AdrDocumentCodec["AdrDocumentCodec"] - MergePatterns["MergePatterns"] - PipelineModule["PipelineModule"] - PipelineFactory["PipelineFactory"] - ReferenceGeneratorRegistration["ReferenceGeneratorRegistration"] - BuiltInGenerators["BuiltInGenerators"] - CodecGeneratorRegistration["CodecGeneratorRegistration"] - ProcessGuardTypes["ProcessGuardTypes"] - ProcessGuardModule["ProcessGuardModule"] - DetectChanges["DetectChanges"] - DeriveProcessState["DeriveProcessState"] + FSMModule["FSMModule"] CodecBaseOptions["CodecBaseOptions"] + ADR006SingleReadModelArchitecture["ADR006SingleReadModelArchitecture"] + ADR005CodecBasedMarkdownRendering["ADR005CodecBasedMarkdownRendering"] + ADR003SourceFirstPatternArchitecture["ADR003SourceFirstPatternArchitecture"] + ADR002GherkinOnlyTesting["ADR002GherkinOnlyTesting"] + ADR001TaxonomyCanonicalValues["ADR001TaxonomyCanonicalValues"] ValidatorReadModelConsolidation["ValidatorReadModelConsolidation"] StepDefinitionCompletion["StepDefinitionCompletion"] SessionGuidesModuleSource["SessionGuidesModuleSource"] @@ -256,19 +264,14 @@ graph TB EffortVarianceTracking["EffortVarianceTracking"] ConfigBasedWorkflowDefinition["ConfigBasedWorkflowDefinition"] CliBehaviorTesting["CliBehaviorTesting"] - ADR006SingleReadModelArchitecture["ADR006SingleReadModelArchitecture"] - ADR005CodecBasedMarkdownRendering["ADR005CodecBasedMarkdownRendering"] - ADR003SourceFirstPatternArchitecture["ADR003SourceFirstPatternArchitecture"] - ADR002GherkinOnlyTesting["ADR002GherkinOnlyTesting"] - ADR001TaxonomyCanonicalValues["ADR001TaxonomyCanonicalValues"] StringUtils["StringUtils"] ProcessGuardTesting["ProcessGuardTesting"] + ResultMonad["ResultMonad"] + ErrorFactories["ErrorFactories"] SessionHandoffs["SessionHandoffs"] SessionFileLifecycle["SessionFileLifecycle"] KebabCaseSlugs["KebabCaseSlugs"] ErrorHandlingUnification["ErrorHandlingUnification"] - ResultMonad["ResultMonad"] - ErrorFactories["ErrorFactories"] end ExtractedPatternSchema --> DocDirectiveSchema DualSourceSchemas ..-> MvpWorkflowImplementation @@ -278,10 +281,19 @@ graph TB CategoryDefinition ..-> CategoryDefinitions GherkinScanner --> GherkinASTParser TypeScript_AST_Parser --> DocDirectiveSchema - DoDValidator --> DoDValidationTypes - DoDValidator --> DualSourceExtractor - AntiPatternDetector --> DoDValidationTypes - SectionBlock ..-> RenderableDocument + LintModule --> LintRules + LintModule --> LintEngine + LintEngine --> LintRules + LintEngine --> CodecUtils + SourceMapper -.-> DecisionDocCodec + SourceMapper -.-> ShapeExtractor + SourceMapper -.-> GherkinASTParser + GeneratorRegistry --> GeneratorTypes + Documentation_Generation_Orchestrator --> Pattern_Scanner + GherkinExtractor --> GherkinASTParser + DualSourceExtractor --> GherkinExtractor + DualSourceExtractor --> GherkinScanner + Document_Extractor --> Pattern_Scanner WorkflowLoader --> WorkflowConfigSchema WorkflowLoader --> CodecUtils ConfigResolver --> ProjectConfigTypes @@ -299,10 +311,6 @@ graph TB DefineConfig --> ProjectConfigTypes ConfigLoader --> DeliveryProcessFactory ConfigLoader --> ConfigurationTypes - GherkinExtractor --> GherkinASTParser - DualSourceExtractor --> GherkinExtractor - DualSourceExtractor --> GherkinScanner - Document_Extractor --> Pattern_Scanner ValidatePatternsCLI --> GherkinScanner ValidatePatternsCLI --> GherkinExtractor ValidatePatternsCLI --> MasterDataset @@ -319,11 +327,6 @@ graph TB LintPatternsCLI --> LintEngine LintPatternsCLI --> LintRules TagTaxonomyCLI --> ConfigLoader - SourceMapper -.-> DecisionDocCodec - SourceMapper -.-> ShapeExtractor - SourceMapper -.-> GherkinASTParser - GeneratorRegistry --> GeneratorTypes - Documentation_Generation_Orchestrator --> Pattern_Scanner PatternSummarizerImpl --> ProcessStateAPI StubResolverImpl --> ProcessStateAPI ScopeValidatorImpl --> ProcessStateAPI @@ -346,14 +349,18 @@ graph TB ContextAssemblerImpl --> StubResolverImpl ArchQueriesImpl --> ProcessStateAPI ArchQueriesImpl --> MasterDataset - LintModule --> LintRules - LintModule --> LintEngine - LintEngine --> LintRules - LintEngine --> CodecUtils - FSMValidator --> FSMTransitions - FSMValidator --> FSMStates - ArchitectureCodec --> MasterDataset + SectionBlock ..-> RenderableDocument + DoDValidator --> DoDValidationTypes + DoDValidator --> DualSourceExtractor + AntiPatternDetector --> DoDValidationTypes + DetectChanges --> DeriveProcessState + DeriveProcessState --> GherkinScanner + DeriveProcessState --> FSMValidator + ProcessGuardDecider --> FSMValidator + ProcessGuardDecider --> DeriveProcessState + ProcessGuardDecider --> DetectChanges TransformDataset --> MasterDataset + SequenceTransformUtils --> MasterDataset MergePatterns --> PatternHelpers MergePatterns ..-> OrchestratorPipelineFactoryMigration PipelineModule --> TransformDataset @@ -363,14 +370,21 @@ graph TB PipelineFactory ..-> ProcessAPILayeredExtraction BuiltInGenerators --> GeneratorRegistry BuiltInGenerators --> CodecBasedGenerator + DesignReviewGenerator --> DesignReviewCodec + DesignReviewGenerator --> MasterDataset DecisionDocGenerator -.-> DecisionDocCodec DecisionDocGenerator -.-> SourceMapper - DetectChanges --> DeriveProcessState - DeriveProcessState --> GherkinScanner - DeriveProcessState --> FSMValidator - ProcessGuardDecider --> FSMValidator - ProcessGuardDecider --> DeriveProcessState - ProcessGuardDecider --> DetectChanges + CodecGeneratorRegistration --> DesignReviewGenerator + CodecGeneratorRegistration --> DecisionDocGenerator + CodecGeneratorRegistration --> ProcessApiReferenceGenerator + CodecGeneratorRegistration --> CliRecipeGenerator + DesignReviewCodec --> MasterDataset + DesignReviewCodec --> MermaidDiagramUtils + ArchitectureCodec --> MasterDataset + FSMValidator --> FSMTransitions + FSMValidator --> FSMStates + ADR006SingleReadModelArchitecture -.-> ADR005CodecBasedMarkdownRendering + ADR003SourceFirstPatternArchitecture -.-> ADR001TaxonomyCanonicalValues ValidatorReadModelConsolidation -.-> ADR006SingleReadModelArchitecture StepDefinitionCompletion -.-> ADR002GherkinOnlyTesting SessionFileCleanup -.-> SessionFileLifecycle @@ -380,8 +394,6 @@ graph TB EffortVarianceTracking -.-> MvpWorkflowImplementation ConfigBasedWorkflowDefinition -.-> MvpWorkflowImplementation CliBehaviorTesting -.-> ADR002GherkinOnlyTesting - ADR006SingleReadModelArchitecture -.-> ADR005CodecBasedMarkdownRendering - ADR003SourceFirstPatternArchitecture -.-> ADR001TaxonomyCanonicalValues ProcessGuardTesting -.-> AntiPatternDetector KebabCaseSlugs -.-> StringUtils ErrorHandlingUnification -.-> ResultMonad @@ -443,7 +455,9 @@ All components with architecture annotations: | ✅ Source Mapper | generator | infrastructure | infrastructure | src/generators/source-mapper.ts | | ✅ Codec Based Generator | generator | service | application | src/generators/codec-based.ts | | ✅ Decision Doc Generator | generator | service | application | src/generators/built-in/decision-doc-generator.ts | +| 🚧 Design Review Generator | generator | service | application | src/generators/built-in/design-review-generator.ts | | ✅ Documentation Generation Orchestrator | generator | service | application | src/generators/orchestrator.ts | +| 🚧 Sequence Transform Utils | generator | service | application | src/generators/pipeline/sequence-utils.ts | | ✅ Transform Dataset | generator | service | application | src/generators/pipeline/transform-dataset.ts | | 🚧 Process Guard Decider | lint | decider | application | src/lint/process-guard/decider.ts | | ✅ Lint Engine | lint | service | application | src/lint/engine.ts | @@ -453,6 +467,7 @@ All components with architecture annotations: | ✅ Architecture Codec | renderer | projection | application | src/renderable/codecs/architecture.ts | | 🚧 Composite Codec | renderer | projection | application | src/renderable/codecs/composite.ts | | ✅ Decision Doc Codec | renderer | projection | application | src/renderable/codecs/decision-doc.ts | +| 🚧 Design Review Codec | renderer | projection | application | src/renderable/codecs/design-review.ts | | ✅ Patterns Codec | renderer | projection | application | src/renderable/codecs/patterns.ts | | ✅ Session Codec | renderer | projection | application | src/renderable/codecs/session.ts | | ✅ Renderable Document | renderer | read-model | domain | src/renderable/schema.ts | @@ -507,7 +522,7 @@ All components with architecture annotations: | ✅ Generator Types | - | - | - | src/generators/types.ts | | ✅ Hierarchy Levels | - | - | - | src/taxonomy/hierarchy-levels.ts | | ✅ Index Codec | - | - | - | src/renderable/codecs/index-codec.ts | -| 📋 Kebab Case Slugs | - | - | - | tests/features/behavior/kebab-case-slugs.feature | +| ✅ Kebab Case Slugs | - | - | - | tests/features/behavior/kebab-case-slugs.feature | | ✅ Layer Inference | - | - | - | src/extractor/layer-inference.ts | | ✅ Layer Types | - | - | - | src/taxonomy/layer-types.ts | | ✅ Lint Module | - | - | - | src/lint/index.ts | diff --git a/docs-live/BUSINESS-RULES.md b/docs-live/BUSINESS-RULES.md index 87440f51..2a212aca 100644 --- a/docs-live/BUSINESS-RULES.md +++ b/docs-live/BUSINESS-RULES.md @@ -5,7 +5,7 @@ --- -**Domain constraints and invariants extracted from feature specifications. 579 rules from 124 features across 7 product areas.** +**Domain constraints and invariants extracted from feature specifications. 592 rules from 126 features across 7 product areas.** --- @@ -17,7 +17,7 @@ | [Configuration](business-rules/configuration.md) | 7 | 32 | 32 | | [Core Types](business-rules/core-types.md) | 5 | 22 | 22 | | [Data API](business-rules/data-api.md) | 21 | 89 | 89 | -| [Generation](business-rules/generation.md) | 58 | 287 | 287 | +| [Generation](business-rules/generation.md) | 60 | 300 | 300 | | [Process](business-rules/process.md) | 2 | 7 | 7 | | [Validation](business-rules/validation.md) | 11 | 54 | 54 | diff --git a/docs-live/CHANGELOG-GENERATED.md b/docs-live/CHANGELOG-GENERATED.md index a894815e..18502240 100644 --- a/docs-live/CHANGELOG-GENERATED.md +++ b/docs-live/CHANGELOG-GENERATED.md @@ -20,9 +20,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Project Config Schema**: Zod validation schema for `DeliveryProcessProjectConfig`. - **Source Merger**: Computes effective sources for a specific generator by applying per-generator overrides to the base resolved sources. - **Define Config**: Identity function for type-safe project configuration. -- **Process API CLI Impl**: Exposes ProcessStateAPI methods as CLI subcommands with JSON output. -- **Output Pipeline Impl**: Post-processing pipeline that transforms raw API results into shaped CLI output. -- **Lint Process CLI**: Validates git changes against delivery process rules. - **File Cache**: Simple Map-based cache for file contents during a single generation run. - **Process State Types**: :MasterDataset Type definitions for the ProcessStateAPI query interface. - **Pattern Summarizer Impl**: Projects the full ExtractedPattern (~3.5KB per pattern) down to a PatternSummary (~100 bytes) for list queries. @@ -35,32 +32,41 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Context Formatter Impl**: First plain-text formatter in the codebase. - **Context Assembler Impl**: Pure function composition over MasterDataset. - **Arch Queries Impl**: Pure functions over MasterDataset for deep architecture exploration. +- **Process API CLI Impl**: Exposes ProcessStateAPI methods as CLI subcommands with JSON output. +- **Output Pipeline Impl**: Post-processing pipeline that transforms raw API results into shaped CLI output. +- **Lint Process CLI**: Validates git changes against delivery process rules. - **FSM Validator**: :PDR005MvpWorkflow Pure validation functions following the Decider pattern: - No I/O, no side effects - Return... - **FSM Transitions**: :PDR005MvpWorkflow Defines valid transitions between FSM states per PDR-005: ``` roadmap ──→ active ──→ completed │ ... - **FSM States**: :PDR005MvpWorkflow Defines the 4-state FSM from PDR-005 MVP Workflow: - roadmap: Planned work (fully editable) -... - **FSM Module**: :PDR005MvpWorkflow Central export for the 4-state FSM defined in PDR-005: ``` roadmap ──→ active ──→ completed │ ... - **Reference Document Codec**: :Generation A single codec factory that creates reference document codecs from configuration objects. +- **Design Review Codec**: :Generation Transforms MasterDataset into a RenderableDocument containing design review artifacts: sequence diagrams,... - **Composite Codec**: :Generation Assembles reference documents from multiple codec outputs by concatenating RenderableDocument sections. - **Claude Module Codec**: :Generation Transforms MasterDataset into RenderableDocuments for CLAUDE.md module generation. +- **Reference Generator Registration**: Registers all reference document generators. +- **Design Review Generator**: :Generation Generates design review documents for patterns with sequence annotations. +- **Sequence Transform Utils**: :Generation Builds pre-computed SequenceIndexEntry objects from patterns that have sequence diagram annotations. - **Process Guard Types**: :FSMValidator Defines types for the process guard linter including: - Process state derived from file annotations -... - **Process Guard Module**: :FSMValidator,DeriveProcessState,DetectChanges,ProcessGuardDecider Enforces delivery process rules by validating... - **Detect Changes**: Detects changes from git diff including: - Modified, added, deleted files - Status transitions (@libar-docs-status... - **Derive Process State**: :GherkinScanner,FSMValidator Derives process state from @libar-docs-\* annotations in files. - **Process Guard Decider**: :FSMValidator,DeriveProcessState,DetectChanges Pure function that validates changes against process rules. -- **Reference Generator Registration**: Registers all reference document generators. +- **Design Review Generation**: Design reviews require manual creation of sequence and component diagrams that duplicate information already captured... - **Load Preamble Parser**: The parseMarkdownToBlocks function converts raw markdown content into a readonly SectionBlock[] array using a 5-state... +- **Design Review Generation Tests**: Tests the full design review generation pipeline: sequence annotations are extracted from patterns with business... +- **Design Review Generator Lifecycle Tests**: The design review generator cleans up stale markdown files when annotated patterns are renamed or removed from the... - **Architecture Doc Refactoring Testing**: Validates that ARCHITECTURE.md retains its full reference content and that generated documents in docs-live/ coexist... -- **Uses Tag Testing**: Tests extraction and processing of @libar-docs-uses and @libar-docs-used-by relationship tags from TypeScript files. -- **Depends On Tag Testing**: Tests extraction of @libar-docs-depends-on and @libar-docs-enables relationship tags from Gherkin files. -- **Stub Taxonomy Tag Tests**: Stub metadata (target path, design session) was stored as plain text in JSDoc descriptions, invisible to structured... -- **Stub Resolver Tests**: Design session stubs need structured discovery and resolution to determine which stubs have been implemented and... +- **Context Formatter Tests**: Tests for formatContextBundle(), formatDepTree(), formatFileReadingList(), and formatOverview() plain text rendering... +- **Context Assembler Tests**: Tests for assembleContext(), buildDepTree(), buildFileReadingList(), and buildOverview() pure functions that operate... +- **Arch Queries Test** - **Pattern Summarize Tests**: Validates that summarizePattern() projects ExtractedPattern (~3.5KB) to PatternSummary (~100 bytes) with the correct... - **Pattern Helpers Tests** - **Output Pipeline Tests**: Validates the output pipeline transforms: summarization, modifiers, list filters, empty stripping, and format output. - **Fuzzy Match Tests**: Validates tiered fuzzy matching: exact > prefix > substring > Levenshtein. -- **Arch Queries Test** -- **Context Formatter Tests**: Tests for formatContextBundle(), formatDepTree(), formatFileReadingList(), and formatOverview() plain text rendering... -- **Context Assembler Tests**: Tests for assembleContext(), buildDepTree(), buildFileReadingList(), and buildOverview() pure functions that operate... +- **Stub Taxonomy Tag Tests**: Stub metadata (target path, design session) was stored as plain text in JSDoc descriptions, invisible to structured... +- **Stub Resolver Tests**: Design session stubs need structured discovery and resolution to determine which stubs have been implemented and... +- **Uses Tag Testing**: Tests extraction and processing of @libar-docs-uses and @libar-docs-used-by relationship tags from TypeScript files. +- **Depends On Tag Testing**: Tests extraction of @libar-docs-depends-on and @libar-docs-enables relationship tags from Gherkin files. --- @@ -82,13 +88,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - **Public API**: Main entry point for the @libar-dev/delivery-process package. -- **Index Preamble Configuration — DD-3, DD-4 Decisions**: Decision DD-3 (Audience paths: preamble vs annotation-derived): Use full preamble for audience reading paths. -- **IndexCodec Factory — DD-1 Implementation Stub**: Creates the IndexCodec as a Zod codec (MasterDataset -> RenderableDocument). -- **IndexCodecOptions — DD-1, DD-5 Decisions**: Decision DD-1 (New IndexCodec vs extend existing): Create a new IndexCodec registered in CodecRegistry, NOT a... -- **DoD Validation Types**: Types and schemas for Definition of Done (DoD) validation and anti-pattern detection. -- **Validation Module**: Barrel export for validation module providing: - Definition of Done (DoD) validation for completed phases -... -- **DoD Validator**: Validates that completed phases meet Definition of Done criteria: 1. -- **Anti Pattern Detector**: Detects violations of the dual-source documentation architecture and process hygiene issues that lead to... - **Workflow Config Schema**: Zod schemas for validating workflow configuration files that define status models, phase definitions, and artifact... - **Tag Registry Configuration**: Defines the structure and validation for tag taxonomy configuration. - **Output Schemas**: Zod schemas for JSON output formats used by CLI tools. @@ -100,6 +99,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Codec Utils**: Provides factory functions for creating type-safe JSON parsing and serialization pipelines using Zod schemas. - **Result Monad Types**: Explicit error handling via discriminated union. - **Error Factory Types**: Structured, discriminated error types with factory functions. +- **DoD Validation Types**: Types and schemas for Definition of Done (DoD) validation and anti-pattern detection. +- **Validation Module**: Barrel export for validation module providing: - Definition of Done (DoD) validation for completed phases -... +- **DoD Validator**: Validates that completed phases meet Definition of Done criteria: 1. +- **Anti Pattern Detector**: Detects violations of the dual-source documentation architecture and process hygiene issues that lead to... - **Status Values**: THE single source of truth for FSM state values in the monorepo (per PDR-005 FSM). - **Risk Levels**: Three-tier risk classification for roadmap planning. - **Tag Registry Builder**: Constructs a complete TagRegistry from TypeScript constants. @@ -108,14 +111,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Hierarchy Levels**: Three-level hierarchy for organizing work: - epic: Multi-quarter strategic initiatives - phase: Standard work units... - **Format Types**: Defines how tag values are parsed and validated. - **Category Definitions**: Categories are used to classify patterns and organize documentation. +- **String Utilities**: Provides shared utilities for string manipulation used across the delivery-process package, including slugification... +- **Utils Module**: Common helper functions used across the delivery-process package. +- **Pattern Id Generator**: Generates unique, deterministic pattern IDs based on file path and line number. +- **Collection Utilities**: Provides shared utilities for working with arrays and collections, such as grouping items by a key function. - **Renderable Utils**: Utility functions for document codecs. - **Renderable Document**: Universal intermediate format for all generated documentation. - **Universal Renderer**: Converts RenderableDocument to output strings. - **Renderable Document Model(RDM)**: Unified document generation using codecs and a universal renderer. - **Document Generator**: Simplified document generation using codecs. -- **Lint Rules**: Defines lint rules that check @libar-docs-\* directives for completeness and quality. -- **Lint Module**: Provides lint rules and engine for pattern annotation quality checking. -- **Lint Engine**: Orchestrates lint rule execution against parsed directives. +- **Pattern Scanner**: Discovers TypeScript files matching glob patterns and filters to only those with `@libar-docs` opt-in. +- **Gherkin Scanner**: Scans .feature files for pattern metadata encoded in Gherkin tags. +- **Gherkin AST Parser**: Parses Gherkin feature files using @cucumber/gherkin and extracts structured data including feature metadata, tags,... +- **TypeScript AST Parser**: Parses TypeScript source files using @typescript-eslint/typescript-estree to extract @libar-docs-\* directives with... - **Warning Collector**: Provides a unified system for capturing, categorizing, and reporting non-fatal issues during document generation. - **Generator Types**: Minimal interface for pluggable generators that produce documentation from patterns. - **Source Mapping Validator**: Performs pre-flight checks on source mapping tables before extraction begins. @@ -124,11 +132,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Documentation Generation Orchestrator**: Invariant: The orchestrator is the integration boundary for full docs generation: it delegates dataset construction... - **Content Deduplicator**: Identifies and merges duplicate sections extracted from multiple sources. - **Codec Based Generator**: Adapts the new RenderableDocument Model (RDM) codec system to the existing DocumentGenerator interface. -- **Shape Extractor**: Extracts TypeScript type definitions (interfaces, type aliases, enums, function signatures) from source files for... -- **Layer Inference**: Infers feature file layer (timeline, domain, integration, e2e, component) from directory path patterns. -- **Gherkin Extractor**: Transforms scanned Gherkin feature files into ExtractedPattern objects for inclusion in generated documentation. -- **Dual Source Extractor**: Extracts pattern metadata from both TypeScript code stubs (@libar-docs-_) and Gherkin feature files (@libar-docs-_),... -- **Document Extractor**: Converts scanned file data into complete ExtractedPattern objects with unique IDs, inferred names, categories, and... - **Workflow Loader**: Provides the default 6-phase workflow as an inline constant and loads custom workflow overrides from JSON files via... - **Configuration Types**: Type definitions for the delivery process configuration system. - **Regex Builders**: Type-safe regex factory functions for tag detection and normalization. @@ -136,23 +139,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Delivery Process Factory**: Main factory function for creating configured delivery process instances. - **Configuration Defaults**: Centralized default constants for the delivery-process package. - **Config Loader**: Discovers and loads `delivery-process.config.ts` files for hierarchical configuration. +- **Shape Extractor**: Extracts TypeScript type definitions (interfaces, type aliases, enums, function signatures) from source files for... +- **Layer Inference**: Infers feature file layer (timeline, domain, integration, e2e, component) from directory path patterns. +- **Gherkin Extractor**: Transforms scanned Gherkin feature files into ExtractedPattern objects for inclusion in generated documentation. +- **Dual Source Extractor**: Extracts pattern metadata from both TypeScript code stubs (@libar-docs-_) and Gherkin feature files (@libar-docs-_),... +- **Document Extractor**: Converts scanned file data into complete ExtractedPattern objects with unique IDs, inferred names, categories, and... +- **Lint Rules**: Defines lint rules that check @libar-docs-\* directives for completeness and quality. +- **Lint Module**: Provides lint rules and engine for pattern annotation quality checking. +- **Lint Engine**: Orchestrates lint rule execution against parsed directives. +- **Scope Validator Impl**: Pure function composition over ProcessStateAPI and MasterDataset. +- **Rules Query Module**: Pure query function for business rules extracted from Gherkin Rule: blocks. +- **Handoff Generator Impl**: Pure function that assembles a handoff document from ProcessStateAPI and MasterDataset. - **CLI Version Helper**: Reads package version from package.json for CLI --version flag. - **Validate Patterns CLI**: Cross-validates TypeScript patterns vs Gherkin feature files. - **Lint Patterns CLI**: Validates pattern annotations for quality and completeness. - **Documentation Generator CLI**: Replaces multiple specialized CLIs with one unified interface that supports multiple generators in a single run. - **CLI Error Handler**: Provides type-safe error handling for all CLI commands using the DocError discriminated union pattern. - **CLI Schema**: :DataAPI Declarative schema defining all CLI options for the process-api command. -- **String Utilities**: Provides shared utilities for string manipulation used across the delivery-process package, including slugification... -- **Utils Module**: Common helper functions used across the delivery-process package. -- **Pattern Id Generator**: Generates unique, deterministic pattern IDs based on file path and line number. -- **Collection Utilities**: Provides shared utilities for working with arrays and collections, such as grouping items by a key function. -- **Scope Validator Impl**: Pure function composition over ProcessStateAPI and MasterDataset. -- **Rules Query Module**: Pure query function for business rules extracted from Gherkin Rule: blocks. -- **Handoff Generator Impl**: Pure function that assembles a handoff document from ProcessStateAPI and MasterDataset. -- **Pattern Scanner**: Discovers TypeScript files matching glob patterns and filters to only those with `@libar-docs` opt-in. -- **Gherkin Scanner**: Scans .feature files for pattern metadata encoded in Gherkin tags. -- **Gherkin AST Parser**: Parses Gherkin feature files using @cucumber/gherkin and extracts structured data including feature metadata, tags,... -- **TypeScript AST Parser**: Parses TypeScript source files using @typescript-eslint/typescript-estree to extract @libar-docs-\* directives with... +- **Index Preamble Configuration — DD-3, DD-4 Decisions**: Decision DD-3 (Audience paths: preamble vs annotation-derived): Use full preamble for audience reading paths. +- **IndexCodec Factory — DD-1 Implementation Stub**: Creates the IndexCodec as a Zod codec (MasterDataset -> RenderableDocument). +- **IndexCodecOptions — DD-1, DD-5 Decisions**: Decision DD-1 (New IndexCodec vs extend existing): Create a new IndexCodec registered in CodecRegistry, NOT a... - **Validation Rules Codec**: :Generation Transforms MasterDataset into a RenderableDocument for Process Guard validation rules reference. - **Timeline Codec**: :Generation Purpose: Development roadmap organized by phase with progress tracking. - **Taxonomy Codec**: :Generation Transforms MasterDataset into a RenderableDocument for taxonomy reference output. @@ -171,14 +177,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Business Rules Codec**: :Generation Transforms MasterDataset into a RenderableDocument for business rules output. - **Architecture Codec**: :Generation Transforms MasterDataset into a RenderableDocument containing architecture diagrams (Mermaid) generated... - **Adr Document Codec**: :Generation Transforms MasterDataset into RenderableDocument for Architecture Decision Records. -- **Transform Dataset**: Transforms raw extracted patterns into a MasterDataset with all pre-computed views. -- **Merge Patterns**: Merges patterns from TypeScript and Gherkin sources with conflict detection. -- **Pipeline Module**: Barrel export for the unified transformation pipeline components. -- **Pipeline Factory**: Invariant: `buildMasterDataset()` is the shared factory for Steps 1-8 of the architecture pipeline and returns... - **Process Api Reference Generator**: :Generation Generates `PROCESS-API-REFERENCE.md` from the declarative CLI schema. - **Built In Generators**: Registers all codec-based generators on import using the RDM (RenderableDocument Model) architecture. - **Decision Doc Generator**: Orchestrates the full pipeline for generating documentation from decision documents (ADR/PDR in .feature format): 1. - **Codec Generator Registration**: Registers codec-based generators for the RenderableDocument Model (RDM) system. +- **Transform Dataset**: Transforms raw extracted patterns into a MasterDataset with all pre-computed views. +- **Merge Patterns**: Merges patterns from TypeScript and Gherkin sources with conflict detection. +- **Pipeline Module**: Barrel export for the unified transformation pipeline components. +- **Pipeline Factory**: Invariant: `buildMasterDataset()` is the shared factory for Steps 1-8 of the architecture pipeline and returns... - **Codec Base Options**: Shared types, interfaces, and utilities for all document codecs. - **ADR 006 Single Read Model Architecture**: The delivery-process package applies event sourcing to itself: git is the event store, annotated source files are... - **ADR 005 Codec Based Markdown Rendering**: The documentation generator needs to transform structured pattern data (MasterDataset) into markdown files. @@ -186,6 +192,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Validator Read Model Consolidation**: `validate-patterns.ts` is the only feature consumer that bypasses the MasterDataset. - **Universal Doc Generator Robustness**: This feature transforms the PoC document generator into a production-ready universal generator capable of operating... - **Step Lint Vitest Cucumber**: Hours are lost debugging vitest-cucumber-specific issues that only surface at test runtime. +- **Step Lint Extended Rules**: The initial lint-steps CLI catches 8 vitest-cucumber traps, but 4 documented traps from... - **Shape Extraction**: Documentation comments duplicate type definitions that exist in the same file. - **Session Guides Module Source**: CLAUDE.md contains a "Session Workflows" section (~160 lines) that is hand-maintained with no link to any annotated... - **Scoped Architectural View**: Full architecture diagrams show every annotated pattern in the project. @@ -203,10 +210,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Error Guide Codec**: `docs/PROCESS-GUARD.md` (341 lines) is manually maintained with per-error-code diagnosis guides, escape hatch... - **Enhanced Index Generation**: `docs/INDEX.md` (354 lines) is a manually maintained navigation hub with audience-based reading orders, per-document... - **Docs Live Consolidation**: `docs-generated/` mixes production reference documents (ARCHITECTURE-CODECS.md, ARCHITECTURE-TYPES.md at 19 KB and 14... +- **Docs Consolidation Strategy**: 14 manually-maintained docs (~5,400 lines in `docs/`) duplicate information that already exists in annotated source... - **Doc Generation Proof Of Concept**: Status: SUPERSEDED - This POC has been implemented. - **Declaration Level Shape Tagging**: The current shape extraction system operates at file granularity. - **Data API Stub Integration**: Design sessions produce code stubs in `delivery-process/stubs/` with rich metadata: `@target` (destination file... - **Data API Design Session Support**: Starting a design or implementation session requires manually compiling elaborate context prompts. +- **Data API Platform Integration**: The process-api CLI requires subprocess invocation for every query, adding shell overhead and preventing stateful... - **Data API Output Shaping**: The ProcessStateAPI CLI returns raw `ExtractedPattern` objects via `JSON.stringify`. - **Data API Context Assembly**: Starting a Claude Code design or implementation session requires assembling 30-100KB of curated, multi-source context... - **Data API Architecture Queries**: The current `arch` subcommand provides basic queries (roles, context, layer, graph) but lacks deeper analysis needed... @@ -216,6 +225,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Cli Recipe Codec**: `docs/PROCESS-API.md` (~509 lines) retains ~460 lines of editorial prose after Phase 43 (ProcessApiHybridGeneration)... - **Claude Module Generation**: Problem: CLAUDE.md modules are hand-written markdown files that drift from source code over time. - **Architecture Doc Refactoring**: ARCHITECTURE.md is 1,287 lines of manually-maintained documentation covering 14 sections. +- **Architecture Diagram Core**: Problem: Architecture documentation requires manually maintaining mermaid diagrams that duplicate information already... +- **Architecture Diagram Advanced**: Problem: Core diagram generation (see ArchitectureDiagramCore) produces component-level diagrams from `arch-*` tags. +- **String Utils**: String utilities provide consistent text transformations across the codebase. - **Status Transition Detection Testing**: Tests for the detectStatusTransitions function that parses git diff output. - **Process Guard Testing**: Pure validation functions for enforcing delivery process rules per PDR-005. - **FSM Validator Testing**: Pure validation functions for the 4-state FSM defined in PDR-005. @@ -223,14 +235,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Detect Changes Testing**: Tests for the detectDeliverableChanges function that parses git diff output. - **Config Schema Validation**: Configuration schemas validate scanner and generator inputs with security constraints to prevent path traversal... - **Anti Pattern Detector Testing**: Detects violations of the dual-source documentation architecture and process hygiene issues that lead to... -- **Result Monad**: The Result type provides explicit error handling via a discriminated union. -- **Error Factories**: Error factories create structured, discriminated error types with consistent message formatting. - **Gherkin Ast Parser**: The Gherkin AST parser extracts feature metadata, scenarios, and steps from .feature files for timeline generation... - **File Discovery**: The file discovery system uses glob patterns to find TypeScript files for documentation extraction. - **Doc String Media Type**: DocString language hints (mediaType) should be preserved through the parsing pipeline from feature files to rendered... - **Ast Parser Relationships Edges**: The AST Parser extracts @libar-docs-\* directives from TypeScript source files using the TypeScript compiler API. - **Ast Parser Metadata**: The AST Parser extracts @libar-docs-\* directives from TypeScript source files using the TypeScript compiler API. - **Ast Parser Exports**: The AST Parser extracts @libar-docs-\* directives from TypeScript source files using the TypeScript compiler API. +- **Result Monad**: The Result type provides explicit error handling via a discriminated union. +- **Error Factories**: Error factories create structured, discriminated error types with consistent message formatting. - **Rule Keyword Po C**: This feature tests whether vitest-cucumber supports the Rule keyword for organizing scenarios under business rules. - **Lint Rule Individual Testing**: Individual lint rules that check parsed directives for completeness. - **Lint Rule Advanced Testing**: Complex lint rule logic and collection-level behavior. @@ -247,6 +259,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Extraction Pipeline Enhancements Testing**: Validates extraction pipeline capabilities for ReferenceDocShowcase: function signature surfacing, full... - **Dual Source Extractor Testing**: Extracts and combines pattern metadata from both TypeScript code stubs (@libar-docs-) and Gherkin feature files... - **Declaration Level Shape Tagging Testing**: Tests the discoverTaggedShapes function that scans TypeScript source code for declarations annotated with the... +- **Warning Collector Testing**: The warning collector provides a unified system for capturing, categorizing, and reporting non-fatal issues during... +- **Validation Rules Codec Testing**: Validates the Validation Rules Codec that transforms MasterDataset into a RenderableDocument for Process Guard... +- **Taxonomy Codec Testing**: Validates the Taxonomy Codec that transforms MasterDataset into a RenderableDocument for tag taxonomy reference... +- **Source Mapping Validator Testing**: Context: Source mappings reference files that may not exist, use invalid extraction methods, or have incompatible... +- **Source Mapper Testing**: The Source Mapper aggregates content from multiple source files based on source mapping tables parsed from decision... +- **Robustness Integration**: Context: Document generation pipeline needs validation, deduplication, and warning collection to work together... +- **Poc Integration**: End-to-end integration tests that exercise the full documentation generation pipeline using the actual POC decision... +- **Decision Doc Generator Testing**: The Decision Doc Generator orchestrates the full documentation generation pipeline from decision documents (ADR/PDR in . +- **Decision Doc Codec Testing**: Validates the Decision Doc Codec that parses decision documents (ADR/PDR in .feature format) and extracts content for... +- **Content Deduplication**: Context: Multiple sources may extract identical content, leading to duplicate sections in generated documentation. - **Source Merging**: mergeSourcesForGenerator computes effective sources for a specific generator by applying per-generator overrides to... - **Project Config Loader**: loadProjectConfig loads and resolves configuration from file, supporting both new-style defineConfig and legacy... - **Preset System**: Presets provide pre-configured taxonomies for different project types. @@ -262,10 +284,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Lint Patterns Cli**: Command-line interface for validating pattern annotation quality. - **Generate Tag Taxonomy Cli**: Command-line interface for generating TAG_TAXONOMY.md from tag registry configuration. - **Generate Docs Cli**: Command-line interface for generating documentation from annotated TypeScript. +- **Process State API Testing**: Programmatic interface for querying delivery process state. - **Transform Dataset Testing**: The transformToMasterDataset function transforms raw extracted patterns into a MasterDataset with all pre-computed... - **Session Handoffs**: The delivery process supports mid-phase handoffs between sessions and coordination across multiple developers through... - **Session File Lifecycle**: Orphaned session files are automatically cleaned up during generation, maintaining a clean docs-living/sessions/... - **Scanner Core**: The scanPatterns function orchestrates file discovery, directive detection, and AST parsing to extract documentation... +- **Rich Content Helpers Testing**: As a document codec author I need helpers to render Gherkin rich content So that DataTables, DocStrings, and... - **Renderer Output Formats**: The universal renderer converts RenderableDocument to markdown. - **Renderer Block Types**: The universal renderer converts RenderableDocument to markdown. - **Remaining Work Summary Accuracy**: Summary totals in REMAINING-WORK.md must match the sum of phase table rows. @@ -274,6 +298,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Patterns Codec Testing**: The PatternsDocumentCodec transforms MasterDataset into a RenderableDocument for generating PATTERNS.md and category... - **Pattern Tag Extraction**: The extractPatternTags function parses Gherkin feature tags into structured metadata objects for pattern processing. - **Layer Inference Testing**: The layer inference module classifies feature files into testing layers (timeline, domain, integration, e2e,... +- **Kebab Case Slugs**: As a documentation generator I need to generate readable, URL-safe slugs from pattern names So that generated file... - **Implementation Link Path Normalization**: Links to implementation files in generated pattern documents should have correct relative paths. - **Extract Summary**: The extractSummary function transforms multi-line pattern descriptions into concise, single-line summaries suitable... - **Error Handling Unification**: All CLI commands and extractors should use the DocError discriminated union pattern for consistent, structured error... @@ -282,23 +307,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Description Header Normalization**: Pattern descriptions should not create duplicate headers when rendered. - **Context Inference**: Patterns in standard directories (src/validation/, src/scanner/) should automatically receive architecture context... - **Zod Codec Migration**: All JSON parsing and serialization uses type-safe Zod codec pattern, replacing raw JSON.parse/stringify with... -- **Process State API Testing**: Programmatic interface for querying delivery process state. -- **Warning Collector Testing**: The warning collector provides a unified system for capturing, categorizing, and reporting non-fatal issues during... -- **Validation Rules Codec Testing**: Validates the Validation Rules Codec that transforms MasterDataset into a RenderableDocument for Process Guard... -- **Taxonomy Codec Testing**: Validates the Taxonomy Codec that transforms MasterDataset into a RenderableDocument for tag taxonomy reference... -- **Source Mapping Validator Testing**: Context: Source mappings reference files that may not exist, use invalid extraction methods, or have incompatible... -- **Source Mapper Testing**: The Source Mapper aggregates content from multiple source files based on source mapping tables parsed from decision... -- **Robustness Integration**: Context: Document generation pipeline needs validation, deduplication, and warning collection to work together... -- **Poc Integration**: End-to-end integration tests that exercise the full documentation generation pipeline using the actual POC decision... -- **Decision Doc Generator Testing**: The Decision Doc Generator orchestrates the full documentation generation pipeline from decision documents (ADR/PDR in . -- **Decision Doc Codec Testing**: Validates the Decision Doc Codec that parses decision documents (ADR/PDR in .feature format) and extracts content for... -- **Content Deduplication**: Context: Multiple sources may extract identical content, leading to duplicate sections in generated documentation. -- **String Utils**: String utilities provide consistent text transformations across the codebase. -- **Mermaid Relationship Rendering**: Tests for rendering all relationship types in Mermaid dependency graphs with distinct visual styles per relationship... -- **Linter Validation Testing**: Tests for lint rules that validate relationship integrity, detect conflicts, and ensure bidirectional traceability... -- **Implements Tag Processing**: Tests for the @libar-docs-implements tag which links implementation files to their corresponding roadmap pattern... -- **Extends Tag Testing**: Tests for the @libar-docs-extends tag which establishes generalization relationships between patterns (pattern... -- **Process Api Reference Tests**: Verifies that the declarative CLI schema drives reference table generation and stays in sync with the parser... +- **Scope Validator Tests**: Starting an implementation or design session without checking prerequisites wastes time when blockers are discovered... +- **Handoff Generator Tests**: Multi-session work loses critical state between sessions when handoff documentation is manual or forgotten. - **Timeline Codec Testing**: The timeline codecs (RoadmapDocumentCodec, CompletedMilestonesCodec, CurrentWorkCodec) transform MasterDataset into... - **Shape Selector Testing**: Tests the filterShapesBySelectors function that provides fine-grained shape selection via structural discriminated... - **Shape Matcher Testing**: Matches file paths against glob patterns for TypeScript shape extraction. @@ -317,12 +327,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Dedent Helper**: The dedent helper function normalizes indentation in code blocks extracted from DocStrings. - **Convention Extractor Testing**: Extracts convention content from MasterDataset decision records tagged with @libar-docs-convention. - **Composite Codec Testing**: Assembles reference documents from multiple codec outputs by concatenating RenderableDocument sections. -- **Scope Validator Tests**: Starting an implementation or design session without checking prerequisites wastes time when blockers are discovered... -- **Handoff Generator Tests**: Multi-session work loses critical state between sessions when handoff documentation is manual or forgotten. +- **Process Api Reference Tests**: Verifies that the declarative CLI schema drives reference table generation and stays in sync with the parser... - **Layered Diagram Generation**: As a documentation generator I want to generate layered architecture diagrams from metadata So that system... - **Arch Generator Registration**: As a CLI user I want an architecture generator registered in the generator registry So that I can run pnpm... - **Component Diagram Generation**: As a documentation generator I want to generate component diagrams from architecture metadata So that system... - **Arch Tag Extraction**: As a documentation generator I want architecture tags extracted from source code So that I can generate accurate... - **Arch Index Dataset**: As a documentation generator I want an archIndex built during dataset transformation So that I can efficiently look... +- **Mermaid Relationship Rendering**: Tests for rendering all relationship types in Mermaid dependency graphs with distinct visual styles per relationship... +- **Linter Validation Testing**: Tests for lint rules that validate relationship integrity, detect conflicts, and ensure bidirectional traceability... +- **Implements Tag Processing**: Tests for the @libar-docs-implements tag which links implementation files to their corresponding roadmap pattern... +- **Extends Tag Testing**: Tests for the @libar-docs-extends tag which establishes generalization relationships between patterns (pattern... --- diff --git a/docs-live/DECISIONS.md b/docs-live/DECISIONS.md index 08f6c4c9..adf543f9 100644 --- a/docs-live/DECISIONS.md +++ b/docs-live/DECISIONS.md @@ -7,14 +7,14 @@ ## Summary -| Metric | Value | -| --- | --- | -| Total ADRs | 7 | -| Accepted | 5 | -| Proposed | 0 | -| Deprecated | 0 | -| Superseded | 1 | -| Categories | 4 | +| Metric | Value | +| ---------- | ----- | +| Total ADRs | 7 | +| Accepted | 5 | +| Proposed | 0 | +| Deprecated | 0 | +| Superseded | 1 | +| Categories | 4 | --- @@ -24,9 +24,9 @@ 3 decisions -| ADR | Title | Status | -| --- | --- | --- | -| [ADR-004](decisions/adr-004-session-workflow-commands.md) | PDR 001 Session Workflow Commands | accepted | +| ADR | Title | Status | +| -------------------------------------------------------------- | -------------------------------------- | -------- | +| [ADR-004](decisions/adr-004-session-workflow-commands.md) | PDR 001 Session Workflow Commands | accepted | | [ADR-005](decisions/adr-005-codec-based-markdown-rendering.md) | ADR 005 Codec Based Markdown Rendering | accepted | | [ADR-006](decisions/adr-006-single-read-model-architecture.md) | ADR 006 Single Read Model Architecture | proposed | @@ -36,8 +36,8 @@ 1 decisions -| ADR | Title | Status | -| --- | --- | --- | +| ADR | Title | Status | +| --------------------------------------------------------------- | ------------------------------- | ---------- | | [ADR-021](decisions/adr-021-doc-generation-proof-of-concept.md) | Doc Generation Proof Of Concept | superseded | --- @@ -46,9 +46,9 @@ 2 decisions -| ADR | Title | Status | -| --- | --- | --- | -| [ADR-001](decisions/adr-001-taxonomy-canonical-values.md) | ADR 001 Taxonomy Canonical Values | accepted | +| ADR | Title | Status | +| ----------------------------------------------------------------- | ----------------------------------------- | -------- | +| [ADR-001](decisions/adr-001-taxonomy-canonical-values.md) | ADR 001 Taxonomy Canonical Values | accepted | | [ADR-003](decisions/adr-003-source-first-pattern-architecture.md) | ADR 003 Source First Pattern Architecture | accepted | --- @@ -57,22 +57,22 @@ 1 decisions -| ADR | Title | Status | -| --- | --- | --- | +| ADR | Title | Status | +| ---------------------------------------------------- | ---------------------------- | -------- | | [ADR-002](decisions/adr-002-gherkin-only-testing.md) | ADR 002 Gherkin Only Testing | accepted | --- ## ADR Index -| ADR | Title | Status | Category | -| --- | --- | --- | --- | -| [ADR-001](decisions/adr-001-taxonomy-canonical-values.md) | ADR 001 Taxonomy Canonical Values | accepted | process | -| [ADR-002](decisions/adr-002-gherkin-only-testing.md) | ADR 002 Gherkin Only Testing | accepted | testing | -| [ADR-003](decisions/adr-003-source-first-pattern-architecture.md) | ADR 003 Source First Pattern Architecture | accepted | process | -| [ADR-004](decisions/adr-004-session-workflow-commands.md) | PDR 001 Session Workflow Commands | accepted | architecture | -| [ADR-005](decisions/adr-005-codec-based-markdown-rendering.md) | ADR 005 Codec Based Markdown Rendering | accepted | architecture | -| [ADR-006](decisions/adr-006-single-read-model-architecture.md) | ADR 006 Single Read Model Architecture | proposed | architecture | -| [ADR-021](decisions/adr-021-doc-generation-proof-of-concept.md) | Doc Generation Proof Of Concept | superseded | documentation | +| ADR | Title | Status | Category | +| ----------------------------------------------------------------- | ----------------------------------------- | ---------- | ------------- | +| [ADR-001](decisions/adr-001-taxonomy-canonical-values.md) | ADR 001 Taxonomy Canonical Values | accepted | process | +| [ADR-002](decisions/adr-002-gherkin-only-testing.md) | ADR 002 Gherkin Only Testing | accepted | testing | +| [ADR-003](decisions/adr-003-source-first-pattern-architecture.md) | ADR 003 Source First Pattern Architecture | accepted | process | +| [ADR-004](decisions/adr-004-session-workflow-commands.md) | PDR 001 Session Workflow Commands | accepted | architecture | +| [ADR-005](decisions/adr-005-codec-based-markdown-rendering.md) | ADR 005 Codec Based Markdown Rendering | accepted | architecture | +| [ADR-006](decisions/adr-006-single-read-model-architecture.md) | ADR 006 Single Read Model Architecture | proposed | architecture | +| [ADR-021](decisions/adr-021-doc-generation-proof-of-concept.md) | Doc Generation Proof Of Concept | superseded | documentation | --- diff --git a/docs-live/INDEX.md b/docs-live/INDEX.md index 52edff89..7b68426e 100644 --- a/docs-live/INDEX.md +++ b/docs-live/INDEX.md @@ -10,7 +10,7 @@ | ----------------- | ----------------------------------------------------- | | **Package** | @libar-dev/delivery-process | | **Purpose** | Code-first documentation and delivery process toolkit | -| **Patterns** | 355 tracked (249 completed, 47 active, 59 planned) | +| **Patterns** | 364 tracked (256 completed, 53 active, 55 planned) | | **Product Areas** | 7 | | **License** | MIT | @@ -147,41 +147,41 @@ | Area | Patterns | Completed | Active | Planned | Progress | | ------------- | -------- | --------- | ------ | ------- | -------------------------- | | Annotation | 26 | 23 | 2 | 1 | [███████░] 23/26 88% | -| Configuration | 9 | 8 | 0 | 1 | [███████░] 8/9 89% | -| CoreTypes | 7 | 6 | 0 | 1 | [███████░] 6/7 86% | -| DataAPI | 35 | 22 | 9 | 4 | [█████░░░] 22/35 63% | -| Generation | 91 | 77 | 2 | 12 | [███████░] 77/91 85% | +| Configuration | 11 | 8 | 0 | 3 | [██████░░] 8/11 73% | +| CoreTypes | 7 | 7 | 0 | 0 | [████████] 7/7 100% | +| DataAPI | 36 | 23 | 9 | 4 | [█████░░░] 23/36 64% | +| Generation | 94 | 81 | 5 | 8 | [███████░] 81/94 86% | | Process | 11 | 4 | 0 | 7 | [███░░░░░] 4/11 36% | -| Validation | 22 | 15 | 0 | 7 | [█████░░░] 15/22 68% | -| **Total** | **201** | **155** | **13** | **33** | **[██████░░] 155/201 77%** | +| Validation | 22 | 16 | 0 | 6 | [██████░░] 16/22 73% | +| **Total** | **207** | **162** | **16** | **29** | **[██████░░] 162/207 78%** | --- ## Phase Progress -**355** patterns total: **249** completed (70%), **47** active, **59** planned. [██████████████░░░░░░] 249/355 +**364** patterns total: **256** completed (70%), **53** active, **55** planned. [██████████████░░░░░░] 256/364 | Status | Count | Percentage | | --------- | ----- | ---------- | -| Completed | 249 | 70% | -| Active | 47 | 13% | -| Planned | 59 | 17% | +| Completed | 256 | 70% | +| Active | 53 | 15% | +| Planned | 55 | 15% | ### By Phase | Phase | Patterns | Completed | Progress | | --------- | -------- | --------- | -------- | | Phase 18 | 1 | 0 | 0% | -| Phase 23 | 2 | 0 | 0% | +| Phase 23 | 2 | 2 | 100% | | Phase 24 | 2 | 2 | 100% | -| Phase 25 | 10 | 6 | 60% | +| Phase 25 | 10 | 7 | 70% | | Phase 26 | 2 | 2 | 100% | | Phase 27 | 3 | 3 | 100% | | Phase 28 | 2 | 2 | 100% | | Phase 30 | 1 | 1 | 100% | | Phase 31 | 1 | 1 | 100% | | Phase 32 | 1 | 1 | 100% | -| Phase 35 | 5 | 4 | 80% | +| Phase 35 | 5 | 5 | 100% | | Phase 36 | 1 | 1 | 100% | | Phase 37 | 1 | 1 | 100% | | Phase 38 | 1 | 1 | 100% | @@ -190,11 +190,13 @@ | Phase 41 | 1 | 1 | 100% | | Phase 42 | 1 | 1 | 100% | | Phase 43 | 1 | 1 | 100% | -| Phase 44 | 2 | 0 | 0% | +| Phase 44 | 2 | 2 | 100% | +| Phase 45 | 1 | 0 | 0% | +| Phase 46 | 2 | 0 | 0% | | Phase 50 | 1 | 1 | 100% | -| Phase 51 | 1 | 0 | 0% | +| Phase 51 | 1 | 1 | 100% | | Phase 99 | 9 | 5 | 56% | -| Phase 100 | 15 | 4 | 27% | +| Phase 100 | 16 | 4 | 25% | | Phase 101 | 2 | 1 | 50% | | Phase 102 | 1 | 0 | 0% | | Phase 103 | 1 | 0 | 0% | diff --git a/docs-live/PRODUCT-AREAS.md b/docs-live/PRODUCT-AREAS.md index bd68135f..9957302a 100644 --- a/docs-live/PRODUCT-AREAS.md +++ b/docs-live/PRODUCT-AREAS.md @@ -21,7 +21,7 @@ The annotation system is the ingestion boundary — it transforms annotated Type Configuration is the entry boundary — it transforms a user-authored `delivery-process.config.ts` file into a fully resolved `DeliveryProcessInstance` that powers the entire pipeline. The flow is: `defineConfig()` provides type-safe authoring (Vite convention, zero validation), `ConfigLoader` discovers and loads the file, `ProjectConfigSchema` validates via Zod, `ConfigResolver` applies defaults and merges stubs into sources, and `DeliveryProcessFactory` builds the final instance with `TagRegistry` and `RegexBuilders`. Three presets define escalating taxonomy complexity — from 3 categories (`generic`, `libar-generic`) to 21 (`ddd-es-cqrs`). `SourceMerger` computes per-generator source overrides, enabling generators like changelog to pull from different feature sets than the base config. -**9 patterns** — 8 completed, 0 active, 1 planned +**11 patterns** — 8 completed, 0 active, 3 planned **Key patterns:** DeliveryProcessFactory, ConfigLoader, ConfigResolver, DefineConfig, ConfigurationPresets, SourceMerger @@ -31,7 +31,7 @@ Configuration is the entry boundary — it transforms a user-authored `delivery- The generation pipeline transforms annotated source code into markdown documents through a four-stage architecture: Scanner discovers files, Extractor produces `ExtractedPattern` objects, Transformer builds MasterDataset with pre-computed views, and Codecs render to markdown via RenderableDocument IR. Nine specialized codecs handle reference docs, planning, session, reporting, timeline, ADRs, business rules, taxonomy, and composite output — each supporting three detail levels (detailed, standard, summary). The Orchestrator runs generators in registration order, producing both detailed `docs-live/` references and compact `_claude-md/` summaries. -**91 patterns** — 77 completed, 2 active, 12 planned +**94 patterns** — 81 completed, 5 active, 8 planned **Key patterns:** ADR005CodecBasedMarkdownRendering, CodecDrivenReferenceGeneration, CrossCuttingDocumentInclusion, ArchitectureDiagramGeneration, ScopedArchitecturalView, CompositeCodec, RenderableDocument, ProductAreaOverview @@ -41,7 +41,7 @@ The generation pipeline transforms annotated source code into markdown documents Validation is the enforcement boundary — it ensures that every change to annotated source files respects the delivery lifecycle rules defined by the FSM, protection levels, and scope constraints. The system operates in three layers: the FSM validator checks status transitions against a 4-state directed graph, the Process Guard orchestrates commit-time validation using a Decider pattern (state derived from annotations, not stored separately), and the lint engine provides pluggable rule execution with pretty and JSON output. Anti-pattern detection enforces dual-source ownership boundaries — `@libar-docs-uses` belongs on TypeScript, `@libar-docs-depends-on` belongs on Gherkin — preventing cross-domain tag confusion that causes documentation drift. Definition of Done validation ensures completed patterns have all deliverables marked done and at least one acceptance-criteria scenario. -**22 patterns** — 15 completed, 0 active, 7 planned +**22 patterns** — 16 completed, 0 active, 6 planned **Key patterns:** ProcessGuardLinter, PhaseStateMachineValidation, DoDValidation, StepLintVitestCucumber, ProgressiveGovernance @@ -51,7 +51,7 @@ Validation is the enforcement boundary — it ensures that every change to annot The Data API provides direct terminal access to delivery process state. It replaces reading generated markdown or launching explore agents — targeted queries use 5-10x less context. The `context` command assembles curated bundles tailored to session type (planning, design, implement). -**35 patterns** — 22 completed, 9 active, 4 planned +**36 patterns** — 23 completed, 9 active, 4 planned **Key patterns:** DataAPIContextAssembly, ProcessStateAPICLI, DataAPIDesignSessionSupport, DataAPIRelationshipGraph, DataAPIOutputShaping @@ -61,7 +61,7 @@ The Data API provides direct terminal access to delivery process state. It repla CoreTypes provides the foundational type system used across all other areas. Three pillars enforce discipline at compile time: the Result monad replaces try/catch with explicit error handling — functions return `Result.ok(value)` or `Result.err(error)` instead of throwing. The DocError discriminated union provides structured error context with type, file, line, and reason fields, enabling exhaustive pattern matching in error handlers. Branded types create nominal typing from structural TypeScript — `PatternId`, `CategoryName`, and `SourceFilePath` are compile-time distinct despite all being strings. String utilities handle slugification and case conversion with acronym-aware title casing. -**7 patterns** — 6 completed, 0 active, 1 planned +**7 patterns** — 7 completed, 0 active, 0 planned **Key patterns:** ResultMonad, ErrorHandlingUnification, ErrorFactories, StringUtils, KebabCaseSlugs @@ -82,13 +82,13 @@ Process defines the USDP-inspired session workflow that governs how work moves t | Area | Patterns | Completed | Active | Planned | | ----------------------------------------------- | -------- | --------- | ------ | ------- | | [Annotation](product-areas/ANNOTATION.md) | 26 | 23 | 2 | 1 | -| [Configuration](product-areas/CONFIGURATION.md) | 9 | 8 | 0 | 1 | -| [Generation](product-areas/GENERATION.md) | 91 | 77 | 2 | 12 | -| [Validation](product-areas/VALIDATION.md) | 22 | 15 | 0 | 7 | -| [DataAPI](product-areas/DATA-API.md) | 35 | 22 | 9 | 4 | -| [CoreTypes](product-areas/CORE-TYPES.md) | 7 | 6 | 0 | 1 | +| [Configuration](product-areas/CONFIGURATION.md) | 11 | 8 | 0 | 3 | +| [Generation](product-areas/GENERATION.md) | 94 | 81 | 5 | 8 | +| [Validation](product-areas/VALIDATION.md) | 22 | 16 | 0 | 6 | +| [DataAPI](product-areas/DATA-API.md) | 36 | 23 | 9 | 4 | +| [CoreTypes](product-areas/CORE-TYPES.md) | 7 | 7 | 0 | 0 | | [Process](product-areas/PROCESS.md) | 11 | 4 | 0 | 7 | -| **Total** | **201** | **155** | **13** | **33** | +| **Total** | **207** | **162** | **16** | **29** | --- diff --git a/docs-live/TAXONOMY.md b/docs-live/TAXONOMY.md index bb0413d8..8564b12e 100644 --- a/docs-live/TAXONOMY.md +++ b/docs-live/TAXONOMY.md @@ -7,14 +7,14 @@ ## Overview -**3 categories** | **56 metadata tags** | **3 aggregation tags** +**3 categories** | **60 metadata tags** | **3 aggregation tags** Current configuration uses `@libar-docs-` prefix with `@libar-docs` file opt-in. | Component | Count | Description | | ---------------- | ----- | ------------------------------------ | | Categories | 3 | Pattern classification by domain | -| Metadata Tags | 56 | Pattern enrichment and relationships | +| Metadata Tags | 60 | Pattern enrichment and relationships | | Aggregation Tags | 3 | Document routing | --- @@ -122,6 +122,10 @@ Tags for enriching patterns with additional metadata. | `claude-module` | value | Module identifier for CLAUDE.md module generation (becomes filename) | No | `@libar-docs-claude-module process-guard` | | `claude-section` | enum | Target section directory in \_claude-md/ for module output | No | `@libar-docs-claude-section delivery-process` | | `claude-tags` | csv | Variation filtering tags for modular-claude-md inclusion | No | `@libar-docs-claude-tags core-mandatory, delivery-process` | +| `sequence-orchestrator` | value | Identifies the coordinator module for sequence diagram generation | No | `@libar-docs-sequence-orchestrator:init-cli` | +| `sequence-step` | number | Explicit execution ordering number for sequence diagram steps | No | `@libar-docs-sequence-step:1` | +| `sequence-module` | csv | Maps Rule to deliverable module(s) for sequence diagram participants | No | `@libar-docs-sequence-module:detect-context` | +| `sequence-error` | flag | Marks scenario as error/alternative path in sequence diagram | No | `@libar-docs-sequence-error` | [Full metadata tag reference](taxonomy/metadata-tags.md) diff --git a/docs-live/_claude-md/annotation/annotation-overview.md b/docs-live/_claude-md/annotation/annotation-overview.md index 830e8f08..2acd5259 100644 --- a/docs-live/_claude-md/annotation/annotation-overview.md +++ b/docs-live/_claude-md/annotation/annotation-overview.md @@ -9,19 +9,18 @@ - Pipeline data preservation: Gherkin `Rule:` blocks, deliverables, scenarios, and all metadata flow through scanner → extractor → `ExtractedPattern` → generators without data loss - Dual-source merge with conflict detection: Same pattern name in both TypeScript and Gherkin produces a merge conflict error. Phase mismatches between sources produce validation errors - **Components:** Extractor (GherkinExtractor, DualSourceExtractor, Document Extractor), Scanner (Pattern Scanner, GherkinScanner, GherkinASTParser, TypeScript AST Parser) #### API Types -| Type | Kind | -| --- | --- | -| TagRegistry | interface | +| Type | Kind | +| -------------------------------- | --------- | +| TagRegistry | interface | | MetadataTagDefinitionForRegistry | interface | -| CategoryDefinition | interface | -| TagDefinition | type | -| CategoryTag | type | -| buildRegistry | function | -| METADATA_TAGS_BY_GROUP | const | -| CATEGORIES | const | -| CATEGORY_TAGS | const | +| CategoryDefinition | interface | +| TagDefinition | type | +| CategoryTag | type | +| buildRegistry | function | +| METADATA_TAGS_BY_GROUP | const | +| CATEGORIES | const | +| CATEGORY_TAGS | const | diff --git a/docs-live/_claude-md/architecture/architecture-codecs.md b/docs-live/_claude-md/architecture/architecture-codecs.md index e7dbc399..812899d8 100644 --- a/docs-live/_claude-md/architecture/architecture-codecs.md +++ b/docs-live/_claude-md/architecture/architecture-codecs.md @@ -127,6 +127,8 @@ | includeDocumentInventory | boolean | true | Unified document inventory | | includePackageMetadata | boolean | true | Package metadata header | +#### DesignReviewCodec + #### CompositeCodec #### ClaudeModuleCodec diff --git a/docs-live/_claude-md/architecture/architecture-types.md b/docs-live/_claude-md/architecture/architecture-types.md index 1f7a468a..a360563b 100644 --- a/docs-live/_claude-md/architecture/architecture-types.md +++ b/docs-live/_claude-md/architecture/architecture-types.md @@ -2,37 +2,31 @@ #### API Types -| Type | Kind | -| --- | --- | -| MasterDatasetSchema | const | -| StatusGroupsSchema | const | -| StatusCountsSchema | const | -| PhaseGroupSchema | const | -| SourceViewsSchema | const | -| RelationshipEntrySchema | const | -| RuntimeMasterDataset | interface | -| RawDataset | interface | -| PipelineOptions | interface | -| PipelineResult | interface | - +| Type | Kind | +| ----------------------- | --------- | +| MasterDatasetSchema | const | +| StatusGroupsSchema | const | +| StatusCountsSchema | const | +| PhaseGroupSchema | const | +| SourceViewsSchema | const | +| RelationshipEntrySchema | const | +| RuntimeMasterDataset | interface | +| RawDataset | interface | +| PipelineOptions | interface | +| PipelineResult | interface | #### Orchestrator Pipeline Responsibilities **Invariant:** The orchestrator is the integration boundary for full docs generation: it delegates dataset construction to the shared pipeline, then executes codecs and writes files. - #### Steps 1-8 via buildMasterDataset() - #### Steps 9-10: Codec Execution and File Writing - #### Shared Pipeline Factory Responsibilities **Invariant:** `buildMasterDataset()` is the shared factory for Steps 1-8 of the architecture pipeline and returns `Result` without process-level side effects. - #### 8-Step Dataset Build Flow - #### Consumer Architecture and PipelineOptions Differentiation diff --git a/docs-live/_claude-md/architecture/reference-sample.md b/docs-live/_claude-md/architecture/reference-sample.md index fedf0005..74126574 100644 --- a/docs-live/_claude-md/architecture/reference-sample.md +++ b/docs-live/_claude-md/architecture/reference-sample.md @@ -117,15 +117,6 @@ ##### DefineConfig -##### ConfigBasedWorkflowDefinition - -| Rule | Description | -| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| Default workflow is built from an inline constant | **Invariant:** `loadDefaultWorkflow()` returns a `LoadedWorkflow` without
file system access. It cannot fail. The... | -| Custom workflow files still work via --workflow flag | **Invariant:** `loadWorkflowFromPath()` remains available for projects
that need custom workflow definitions. The... | -| FSM validation and Process Guard are not affected | **Invariant:** The FSM transition matrix, protection levels, and Process
Guard rules remain hardcoded in... | -| Workflow as a configurable preset field is deferred | **Invariant:** The inline default workflow constant is the only workflow source until preset integration is... | - ##### ADR005CodecBasedMarkdownRendering | Rule | Description | @@ -150,6 +141,15 @@ | Canonical phase definitions (6-phase USDP standard) | **Invariant:** The default workflow defines exactly 6 phases in fixed
order. These are the canonical phase names... | | Deliverable status canonical values | **Invariant:** Deliverable status (distinct from pattern FSM status)
uses exactly 6 values, enforced by Zod... | +##### ConfigBasedWorkflowDefinition + +| Rule | Description | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Default workflow is built from an inline constant | **Invariant:** `loadDefaultWorkflow()` returns a `LoadedWorkflow` without
file system access. It cannot fail. The... | +| Custom workflow files still work via --workflow flag | **Invariant:** `loadWorkflowFromPath()` remains available for projects
that need custom workflow definitions. The... | +| FSM validation and Process Guard are not affected | **Invariant:** The FSM transition matrix, protection levels, and Process
Guard rules remain hardcoded in... | +| Workflow as a configurable preset field is deferred | **Invariant:** The inline default workflow constant is the only workflow source until preset integration is... | + ##### ProcessGuardTesting | Rule | Description | diff --git a/docs-live/_claude-md/configuration/configuration-overview.md b/docs-live/_claude-md/configuration/configuration-overview.md index 4a8fd860..3c573a63 100644 --- a/docs-live/_claude-md/configuration/configuration-overview.md +++ b/docs-live/_claude-md/configuration/configuration-overview.md @@ -9,30 +9,29 @@ - Stubs merged at resolution time: Stub directory globs are appended to typescript sources, making stubs transparent to the downstream pipeline - Source override composition: SourceMerger applies per-generator overrides (`replaceFeatures`, `additionalFeatures`, `additionalInput`) to base sources. Exclude is always inherited from base - **Components:** Config (WorkflowLoader, ConfigurationTypes, ConfigResolver, RegexBuilders, ProjectConfigTypes, ProjectConfigSchema, ConfigurationPresets, SourceMerger, DeliveryProcessFactory, DefineConfig, ConfigurationDefaults, ConfigLoader) #### API Types -| Type | Kind | -| --- | --- | -| DeliveryProcessConfig | interface | -| DeliveryProcessInstance | interface | -| RegexBuilders | interface | +| Type | Kind | +| ---------------------------- | --------- | +| DeliveryProcessConfig | interface | +| DeliveryProcessInstance | interface | +| RegexBuilders | interface | | DeliveryProcessProjectConfig | interface | -| SourcesConfig | interface | -| OutputConfig | interface | -| GeneratorSourceOverride | interface | -| ResolvedProjectConfig | interface | -| ResolvedSourcesConfig | interface | +| SourcesConfig | interface | +| OutputConfig | interface | +| GeneratorSourceOverride | interface | +| ResolvedProjectConfig | interface | +| ResolvedSourcesConfig | interface | | CreateDeliveryProcessOptions | interface | -| ConfigDiscoveryResult | interface | -| ConfigLoadError | interface | -| ResolvedConfig | type | -| PresetName | type | -| ConfigLoadResult | type | -| createRegexBuilders | function | -| createDeliveryProcess | function | -| findConfigFile | function | -| loadConfig | function | -| formatConfigError | function | +| ConfigDiscoveryResult | interface | +| ConfigLoadError | interface | +| ResolvedConfig | type | +| PresetName | type | +| ConfigLoadResult | type | +| createRegexBuilders | function | +| createDeliveryProcess | function | +| findConfigFile | function | +| loadConfig | function | +| formatConfigError | function | diff --git a/docs-live/_claude-md/data-api/data-api-overview.md b/docs-live/_claude-md/data-api/data-api-overview.md index 35e48cff..e02e25c9 100644 --- a/docs-live/_claude-md/data-api/data-api-overview.md +++ b/docs-live/_claude-md/data-api/data-api-overview.md @@ -8,7 +8,6 @@ - Session type tailoring: `planning` (~500B, brief + deps), `design` (~1.5KB, spec + stubs + deps), `implement` (deliverables + FSM + tests) - Direct API queries replace doc reading: JSON output is 5-10x smaller than generated docs - #### Contents - [Key Invariants](#key-invariants) @@ -17,28 +16,24 @@ - [Consumer Architecture and PipelineOptions Differentiation](#consumer-architecture-and-pipelineoptions-differentiation) - [API Types](#api-types) - #### Shared Pipeline Factory Responsibilities **Invariant:** `buildMasterDataset()` is the shared factory for Steps 1-8 of the architecture pipeline and returns `Result` without process-level side effects. - #### 8-Step Dataset Build Flow - #### Consumer Architecture and PipelineOptions Differentiation - #### API Types -| Type | Kind | -| --- | --- | -| PipelineOptions | interface | -| PipelineResult | interface | -| MasterDatasetSchema | const | -| StatusGroupsSchema | const | -| StatusCountsSchema | const | -| PhaseGroupSchema | const | -| SourceViewsSchema | const | -| RelationshipEntrySchema | const | -| ArchIndexSchema | const | +| Type | Kind | +| ----------------------- | --------- | +| PipelineOptions | interface | +| PipelineResult | interface | +| MasterDatasetSchema | const | +| StatusGroupsSchema | const | +| StatusCountsSchema | const | +| PhaseGroupSchema | const | +| SourceViewsSchema | const | +| RelationshipEntrySchema | const | +| ArchIndexSchema | const | diff --git a/docs-live/_claude-md/validation/validation-overview.md b/docs-live/_claude-md/validation/validation-overview.md index b24b3404..b927aa87 100644 --- a/docs-live/_claude-md/validation/validation-overview.md +++ b/docs-live/_claude-md/validation/validation-overview.md @@ -9,30 +9,29 @@ - Decider pattern: All validation is (state, changes, options) → result. State is derived from annotations, not maintained separately - Dual-source ownership: Anti-pattern detection enforces tag boundaries — `uses` on TypeScript (runtime deps), `depends-on`/`quarter`/`team` on Gherkin (planning metadata). Violations are flagged before they cause documentation drift - **Components:** Lint (LintRules, LintEngine, ProcessGuardDecider), Validation (DoDValidator, AntiPatternDetector, FSMValidator, FSMTransitions, FSMStates) #### API Types -| Type | Kind | -| --- | --- | -| AntiPatternDetectionOptions | interface | -| LintRule | interface | -| LintContext | interface | -| ProtectionLevel | type | -| isDeliverableComplete | function | -| hasAcceptanceCriteria | function | -| extractAcceptanceCriteriaScenarios | function | -| validateDoDForPhase | function | -| validateDoD | function | -| formatDoDSummary | function | -| detectAntiPatterns | function | -| detectProcessInCode | function | -| detectMagicComments | function | -| detectScenarioBloat | function | -| detectMegaFeature | function | -| formatAntiPatternReport | function | -| toValidationIssues | function | -| filterRulesBySeverity | function | -| isValidTransition | function | -| getValidTransitionsFrom | function | +| Type | Kind | +| ---------------------------------- | --------- | +| AntiPatternDetectionOptions | interface | +| LintRule | interface | +| LintContext | interface | +| ProtectionLevel | type | +| isDeliverableComplete | function | +| hasAcceptanceCriteria | function | +| extractAcceptanceCriteriaScenarios | function | +| validateDoDForPhase | function | +| validateDoD | function | +| formatDoDSummary | function | +| detectAntiPatterns | function | +| detectProcessInCode | function | +| detectMagicComments | function | +| detectScenarioBloat | function | +| detectMegaFeature | function | +| formatAntiPatternReport | function | +| toValidationIssues | function | +| filterRulesBySeverity | function | +| isValidTransition | function | +| getValidTransitionsFrom | function | diff --git a/docs-live/business-rules/generation.md b/docs-live/business-rules/generation.md index dbedb327..34cd9fde 100644 --- a/docs-live/business-rules/generation.md +++ b/docs-live/business-rules/generation.md @@ -4,7 +4,7 @@ --- -**287 rules** from 58 features. 287 rules have explicit invariants. +**300 rules** from 60 features. 300 rules have explicit invariants. --- @@ -1466,6 +1466,179 @@ _- CamelCase pattern names (e.g., "RemainingWorkEnhancement") are hard to read_ _description-quality-foundation.feature_ +### Design Review Generation Tests + +_Tests the full design review generation pipeline: sequence annotations are_ + +--- + +#### SequenceIndex pre-computes ordered steps from annotated rules + +> **Invariant:** buildSequenceIndexEntry produces a SequenceIndexEntry with steps sorted by stepNumber, participants deduplicated with orchestrator first, and data flow types collected from Input/Output annotations. +> +> **Rationale:** Pre-computing in the transform pass avoids repeated parsing in the codec. ADR-006 mandates the MasterDataset as the sole read model. + +**Verified by:** + +- SequenceIndex populated for annotated pattern +- Steps sorted by step number +- Patterns without sequence annotations have no entry + +--- + +#### Participants are deduplicated with orchestrator first + +> **Invariant:** The participants array starts with the orchestrator, followed by module names in first-appearance step order, with no duplicates. +> +> **Rationale:** Sequence diagram participant declarations must be ordered and unique. The orchestrator is always the first participant as the entry point. + +**Verified by:** + +- Participants ordered with orchestrator first + +--- + +#### Data flow types are extracted from Input and Output annotations + +> **Invariant:** The dataFlowTypes array contains distinct type names parsed from Input and Output annotation strings using the "TypeName -- fields" format. +> +> **Rationale:** Data flow types are used by the component diagram to render hexagon nodes and by the type definitions table to show producers and consumers. + +**Verified by:** + +- Data flow types collected from annotations +- Prose outputs are excluded from data flow types + +--- + +#### DesignReviewCodec produces sequence diagram with correct participant count + +> **Invariant:** The rendered sequence diagram participant list includes User plus all participants from the SequenceIndexEntry. The count equals 1 (User) plus the number of unique participants. +> +> **Rationale:** Correct participant count proves the codec reads SequenceIndex data correctly and maps it to Mermaid syntax. + +**Verified by:** + +- Sequence diagram has correct participant count + +--- + +#### Error scenarios produce alt blocks in sequence diagrams + +> **Invariant:** Each error scenario name from a step's errorScenarios array produces an alt block in the Mermaid sequence diagram with the scenario name as the condition text. +> +> **Rationale:** Alt blocks make error handling visible in the sequence diagram, enabling design review verification of error path completeness. + +**Verified by:** + +- Error scenarios produce alt blocks in output + +--- + +#### Component diagram groups modules by shared input type + +> **Invariant:** Contiguous steps sharing the same Input type annotation are grouped into a single subgraph in the component diagram. Non-contiguous steps with the same input become separate subgraphs. +> +> **Rationale:** Grouping by input type reveals natural phase boundaries in the orchestration flow, making data flow architecture visible. + +**Verified by:** + +- Modules with same input grouped together + +--- + +#### Component diagram module nodes are scoped per phase + +> **Invariant:** Repeated modules in non-contiguous phases render with distinct Mermaid node IDs, while repeated use of the same module inside one phase reuses a single declaration. +> +> **Rationale:** Mermaid node IDs are global across the diagram. Reusing raw module IDs causes later phases to collapse into earlier declarations and misrepresent the orchestration flow. + +**Verified by:** + +- Repeated module in non-contiguous phases gets distinct node ids +- Repeated module in one phase is declared once + +--- + +#### Type hexagons show field definitions from Output annotations + +> **Invariant:** Output annotations with the "TypeName -- field1, field2" format produce hexagon nodes in the component diagram containing the type name and field names separated by newlines. +> +> **Rationale:** Type hexagons make central data contracts visible, enabling design reviewers to verify interface completeness. + +**Verified by:** + +- Type hexagon rendered with fields + +--- + +#### Mermaid-sensitive text is escaped across rendered labels + +> **Invariant:** Participant aliases, subgraph labels, type hexagon text, and edge labels escape Mermaid-sensitive characters such as quotes, pipes, and comment markers before rendering. +> +> **Rationale:** Design review diagrams are generated directly from annotations. Valid annotation text must not break Mermaid parsing when rendered into different label positions. + +**Verified by:** + +- Mermaid-sensitive text is escaped in rendered markdown + +--- + +#### Design questions table includes auto-computed metrics + +> **Invariant:** The Design Questions section contains a table with auto-computed step count, type count, and error path count drawn from the SequenceIndexEntry data. +> +> **Rationale:** Auto-computed metrics reduce manual counting during design reviews and highlight coverage gaps (e.g., 0 error paths). + +**Verified by:** + +- Design questions table has correct metrics + +--- + +#### Invalid sequence annotations are skipped with validation warnings + +> **Invariant:** Patterns with ambiguous sequence-step numbering or empty sequence-module tags are excluded from sequenceIndex and reported through malformedPatterns. +> +> **Rationale:** Design reviews should never render misleading diagrams from malformed annotations. The transform pass is the correct place to validate and suppress bad sequence entries. + +**Verified by:** + +- Duplicate step numbers are reported as malformed +- Sequence step without modules is reported as malformed + +--- + +#### Process API sequence lookup resolves pattern names case-insensitively + +> **Invariant:** The sequence subcommand resolves pattern names with the same case-insensitive matching behavior as other pattern-oriented process-api queries. +> +> **Rationale:** Design review consumers should not need exact display-name casing when querying sequence data from the CLI. + +**Verified by:** + +- Sequence lookup accepts lowercase pattern name + +_design-review.feature_ + +### Design Review Generator Lifecycle Tests + +_The design review generator cleans up stale markdown files when annotated_ + +--- + +#### Orphaned design review files are scheduled for deletion + +> **Invariant:** Existing markdown files in design-reviews/ that no longer map to the current sequenceIndex must be returned in filesToDelete, while current patterns remain preserved. +> +> **Rationale:** Renaming or removing sequence-annotated patterns otherwise leaves stale design review documents behind, which misleads readers and downstream tooling. + +**Verified by:** + +- Renamed pattern schedules stale design review for deletion + +_design-review-generator.feature_ + ### Documentation Orchestrator _Tests the orchestrator's pattern merging, conflict detection, and generator_ diff --git a/docs-live/business-rules/validation.md b/docs-live/business-rules/validation.md index eec62b88..b25d73f0 100644 --- a/docs-live/business-rules/validation.md +++ b/docs-live/business-rules/validation.md @@ -719,6 +719,7 @@ _- Completed specs modified without explicit unlock reason_ - Valid transitions pass validation - Invalid transitions fail validation +- Existing file with unlock-reason bypasses FSM check --- diff --git a/docs-live/decisions/adr-001-taxonomy-canonical-values.md b/docs-live/decisions/adr-001-taxonomy-canonical-values.md index cd418218..68f55110 100644 --- a/docs-live/decisions/adr-001-taxonomy-canonical-values.md +++ b/docs-live/decisions/adr-001-taxonomy-canonical-values.md @@ -6,10 +6,10 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | accepted | -| Category | process | +| Property | Value | +| -------- | -------- | +| Status | accepted | +| Category | process | **Context:** The annotation system requires well-defined canonical values for taxonomy @@ -24,11 +24,11 @@ These are the durable constants of the delivery process. **Consequences:** -| Type | Impact | -| --- | --- | -| Positive | Generated docs group into coherent sections | -| Positive | FSM enforcement has clear, auditable state definitions | -| Positive | Source ownership prevents cross-domain tag confusion | +| Type | Impact | +| -------- | ------------------------------------------------------------- | +| Positive | Generated docs group into coherent sections | +| Positive | FSM enforcement has clear, auditable state definitions | +| Positive | Source ownership prevents cross-domain tag confusion | | Negative | Migration effort for existing specs with non-canonical values | ## Rules @@ -39,15 +39,15 @@ These are the durable constants of the delivery process. **Rationale:** Without canonical values, organic drift (e.g., Generator vs Generators) produces inconsistent grouping in generated documentation and fragmented product area pages. -| Value | Reader Question | Covers | -| --- | --- | --- | -| Annotation | How do I annotate code? | Scanning, extraction, tag parsing, dual-source | -| Configuration | How do I configure the tool? | Config loading, presets, resolution | -| Generation | How does code become docs? | Codecs, generators, rendering, diagrams | -| Validation | How is the workflow enforced? | FSM, DoD, anti-patterns, process guard, lint | -| DataAPI | How do I query process state? | Process state API, stubs, context assembly, CLI | -| CoreTypes | What foundational types exist? | Result monad, error factories, string utils | -| Process | How does the session workflow work? | Session lifecycle, handoffs, conventions | +| Value | Reader Question | Covers | +| ------------- | ----------------------------------- | ----------------------------------------------- | +| Annotation | How do I annotate code? | Scanning, extraction, tag parsing, dual-source | +| Configuration | How do I configure the tool? | Config loading, presets, resolution | +| Generation | How does code become docs? | Codecs, generators, rendering, diagrams | +| Validation | How is the workflow enforced? | FSM, DoD, anti-patterns, process guard, lint | +| DataAPI | How do I query process state? | Process state API, stubs, context assembly, CLI | +| CoreTypes | What foundational types exist? | Result monad, error factories, string utils | +| Process | How does the session workflow work? | Session lifecycle, handoffs, conventions | **Verified by:** @@ -59,12 +59,12 @@ These are the durable constants of the delivery process. **Rationale:** Unbounded category values prevent meaningful grouping of architecture decisions and make cross-cutting queries unreliable. -| Value | Purpose | -| --- | --- | -| architecture | System structure, component design, data flow | -| process | Workflow, conventions, annotation rules | -| testing | Test strategy, verification approach | -| documentation | Documentation generation, content structure | +| Value | Purpose | +| ------------- | --------------------------------------------- | +| architecture | System structure, component design, data flow | +| process | Workflow, conventions, annotation rules | +| testing | Test strategy, verification approach | +| documentation | Documentation generation, content structure | **Verified by:** @@ -76,12 +76,12 @@ These are the durable constants of the delivery process. **Rationale:** Without protection levels, active specs accumulate scope creep and completed specs get silently modified, undermining delivery process integrity. -| Status | Protection | Can Add Deliverables | Allowed Actions | -| --- | --- | --- | --- | -| roadmap | None | Yes | Full editing | -| active | Scope-locked | No | Edit existing deliverables only | -| completed | Hard-locked | No | Requires unlock-reason tag | -| deferred | None | Yes | Full editing | +| Status | Protection | Can Add Deliverables | Allowed Actions | +| --------- | ------------ | -------------------- | ------------------------------- | +| roadmap | None | Yes | Full editing | +| active | Scope-locked | No | Edit existing deliverables only | +| completed | Hard-locked | No | Requires unlock-reason tag | +| deferred | None | Yes | Full editing | **Verified by:** @@ -93,21 +93,20 @@ These are the durable constants of the delivery process. **Rationale:** Allowing arbitrary transitions (e.g., roadmap to completed) bypasses the active phase where scope-lock and deliverable tracking provide quality assurance. -| From | To | Trigger | -| --- | --- | --- | -| roadmap | active | Start work | -| roadmap | deferred | Postpone | -| active | completed | All deliverables done | -| active | roadmap | Blocked/regressed | -| deferred | roadmap | Resume planning | +| From | To | Trigger | +| -------- | --------- | --------------------- | +| roadmap | active | Start work | +| roadmap | deferred | Postpone | +| active | completed | All deliverables done | +| active | roadmap | Blocked/regressed | +| deferred | roadmap | Resume planning | **Verified by:** - Canonical values are enforced - - Completed is a terminal state. Modifications require - `@libar-docs-unlock-reason` escape hatch. + Completed is a terminal state. Modifications require + `@libar-docs-unlock-reason` escape hatch. ### Tag format types @@ -115,14 +114,14 @@ These are the durable constants of the delivery process. **Rationale:** Without explicit format types, parsers must guess value structure, leading to silent data corruption when CSV values are treated as single strings or numbers are treated as text. -| Format | Parsing | Example | -| --- | --- | --- | -| flag | Boolean presence, no value | @libar-docs-core | -| value | Simple string | @libar-docs-pattern MyPattern | -| enum | Constrained to predefined list | @libar-docs-status completed | -| csv | Comma-separated values | @libar-docs-uses A, B, C | -| number | Numeric value | @libar-docs-phase 15 | -| quoted-value | Preserves spaces | @libar-docs-brief:'Multi word' | +| Format | Parsing | Example | +| ------------ | ------------------------------ | ------------------------------ | +| flag | Boolean presence, no value | @libar-docs-core | +| value | Simple string | @libar-docs-pattern MyPattern | +| enum | Constrained to predefined list | @libar-docs-status completed | +| csv | Comma-separated values | @libar-docs-uses A, B, C | +| number | Numeric value | @libar-docs-phase 15 | +| quoted-value | Preserves spaces | @libar-docs-brief:'Multi word' | **Verified by:** @@ -134,12 +133,12 @@ These are the durable constants of the delivery process. **Rationale:** Cross-domain tag placement (e.g., runtime dependencies in Gherkin) creates conflicting sources of truth and breaks the dual-source architecture ownership model. -| Tag | Correct Source | Wrong Source | Rationale | -| --- | --- | --- | --- | -| uses | TypeScript | Feature files | TS owns runtime dependencies | -| depends-on | Feature files | TypeScript | Gherkin owns planning dependencies | -| quarter | Feature files | TypeScript | Gherkin owns timeline metadata | -| team | Feature files | TypeScript | Gherkin owns ownership metadata | +| Tag | Correct Source | Wrong Source | Rationale | +| ---------- | -------------- | ------------- | ---------------------------------- | +| uses | TypeScript | Feature files | TS owns runtime dependencies | +| depends-on | Feature files | TypeScript | Gherkin owns planning dependencies | +| quarter | Feature files | TypeScript | Gherkin owns timeline metadata | +| team | Feature files | TypeScript | Gherkin owns ownership metadata | **Verified by:** @@ -161,14 +160,14 @@ These are the durable constants of the delivery process. **Rationale:** Ad-hoc phase names and ordering produce inconsistent roadmap grouping across packages and make cross-package progress tracking impossible. -| Order | Phase | Purpose | -| --- | --- | --- | -| 1 | Inception | Problem framing, scope definition | -| 2 | Elaboration | Design decisions, architecture exploration | -| 3 | Session | Planning and design session work | -| 4 | Construction | Implementation, testing, integration | -| 5 | Validation | Verification, acceptance criteria confirmation | -| 6 | Retrospective | Review, lessons learned, documentation | +| Order | Phase | Purpose | +| ----- | ------------- | ---------------------------------------------- | +| 1 | Inception | Problem framing, scope definition | +| 2 | Elaboration | Design decisions, architecture exploration | +| 3 | Session | Planning and design session work | +| 4 | Construction | Implementation, testing, integration | +| 5 | Validation | Verification, acceptance criteria confirmation | +| 6 | Retrospective | Review, lessons learned, documentation | **Verified by:** @@ -180,14 +179,14 @@ These are the durable constants of the delivery process. **Rationale:** Freeform status strings bypass Zod validation and break DoD checks, which rely on terminal status classification to determine pattern completeness. -| Value | Meaning | -| --- | --- | -| complete | Work is done | -| in-progress | Work is ongoing | -| pending | Work has not started | -| deferred | Work postponed | -| superseded | Replaced by another | -| n/a | Not applicable | +| Value | Meaning | +| ----------- | -------------------- | +| complete | Work is done | +| in-progress | Work is ongoing | +| pending | Work has not started | +| deferred | Work postponed | +| superseded | Replaced by another | +| n/a | Not applicable | **Verified by:** diff --git a/docs-live/decisions/adr-002-gherkin-only-testing.md b/docs-live/decisions/adr-002-gherkin-only-testing.md index 9b68b76b..fa73e7f7 100644 --- a/docs-live/decisions/adr-002-gherkin-only-testing.md +++ b/docs-live/decisions/adr-002-gherkin-only-testing.md @@ -6,10 +6,10 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | accepted | -| Category | testing | +| Property | Value | +| -------- | -------- | +| Status | accepted | +| Category | testing | **Context:** A package that generates documentation from `.feature` files had dual @@ -18,19 +18,20 @@ This undermined the core thesis that Gherkin IS sufficient for all testing. **Decision:** Enforce strict Gherkin-only testing for the delivery-process package: + - All tests must be `.feature` files with step definitions - No new `.test.ts` files - Edge cases use Scenario Outline with Examples tables **Consequences:** -| Type | Impact | -| --- | --- | -| Positive | Single source of truth for tests AND documentation | +| Type | Impact | +| -------- | -------------------------------------------------------------------------- | +| Positive | Single source of truth for tests AND documentation | | Positive | Demonstrates Gherkin sufficiency -- the package practices what it preaches | -| Positive | Living documentation always matches test coverage | -| Positive | Forces better scenario design with Examples tables | -| Negative | Scenario Outline syntax more verbose than parameterized tests | +| Positive | Living documentation always matches test coverage | +| Positive | Forces better scenario design with Examples tables | +| Negative | Scenario Outline syntax more verbose than parameterized tests | ## Rules @@ -40,12 +41,12 @@ Enforce strict Gherkin-only testing for the delivery-process package: **Rationale:** Parallel `.test.ts` files create a hidden test layer invisible to the documentation pipeline, undermining the single source of truth principle this package enforces. -| Artifact | Without Gherkin-Only | With Gherkin-Only | -| --- | --- | --- | -| Tests | .test.ts (hidden from docs) | .feature (visible in docs) | -| Business rules | Manually maintained | Extracted from Rule blocks | -| Acceptance criteria | Implicit in test code | Explicit @acceptance-criteria tags | -| Traceability | Manual cross-referencing | @libar-docs-implements links | +| Artifact | Without Gherkin-Only | With Gherkin-Only | +| ------------------- | --------------------------- | ---------------------------------- | +| Tests | .test.ts (hidden from docs) | .feature (visible in docs) | +| Business rules | Manually maintained | Extracted from Rule blocks | +| Acceptance criteria | Implicit in test code | Explicit @acceptance-criteria tags | +| Traceability | Manual cross-referencing | @libar-docs-implements links | **Verified by:** diff --git a/docs-live/decisions/adr-003-source-first-pattern-architecture.md b/docs-live/decisions/adr-003-source-first-pattern-architecture.md index 68b62050..6a59b141 100644 --- a/docs-live/decisions/adr-003-source-first-pattern-architecture.md +++ b/docs-live/decisions/adr-003-source-first-pattern-architecture.md @@ -6,10 +6,10 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | accepted | -| Category | process | +| Property | Value | +| -------- | -------- | +| Status | accepted | +| Category | process | **Context:** The original annotation architecture assumed pattern definitions live @@ -26,14 +26,14 @@ artifacts are annotated source code, executable specs, and decision specs. **Consequences:** -| Type | Impact | -| --- | --- | -| Positive | Pattern identity travels with code from stub through production | -| Positive | Eliminates stale tier 1 spec maintenance burden | +| Type | Impact | +| -------- | ------------------------------------------------------------------- | +| Positive | Pattern identity travels with code from stub through production | +| Positive | Eliminates stale tier 1 spec maintenance burden | | Positive | Executable specs become the living specification (richer, verified) | -| Positive | Retroactive annotation works without merge conflicts | -| Negative | Migration effort for existing tier 1 specs | -| Negative | Requires updating CLAUDE.md annotation ownership guidance | +| Positive | Retroactive annotation works without merge conflicts | +| Negative | Migration effort for existing tier 1 specs | +| Negative | Requires updating CLAUDE.md annotation ownership guidance | ## Rules @@ -43,15 +43,14 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** If pattern identity lives in tier 1 specs, it becomes stale after implementation and diverges from the code that actually realizes the pattern. -| Phase | Location | Status | -| --- | --- | --- | -| Design | `delivery-process/stubs/pattern-name/` | roadmap | -| Implementation | `src/path/to/module.ts` | active | -| Completed | `src/path/to/module.ts` | completed | +| Phase | Location | Status | +| -------------- | -------------------------------------- | --------- | +| Design | `delivery-process/stubs/pattern-name/` | roadmap | +| Implementation | `src/path/to/module.ts` | active | +| Completed | `src/path/to/module.ts` | completed | **Pattern Definition Lifecycle:** - Exception: Patterns with no TypeScript implementation (pure process or workflow concerns) may be defined in decision specs. The constraint is: one definition per pattern, regardless of source type. @@ -66,11 +65,11 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** Treating tier 1 specs as durable creates a maintenance burden — at scale only 39% maintain traceability, and duplicated Rules/Scenarios average 200-400 stale lines. -| Phase | Planning Value | Documentation Value | -| --- | --- | --- | -| roadmap | High | None (not yet built) | -| active | Medium (deliverable tracking) | Low (stale snapshot) | -| completed | None | None (executable specs are better) | +| Phase | Planning Value | Documentation Value | +| --------- | ----------------------------- | ---------------------------------- | +| roadmap | High | None (not yet built) | +| active | Medium (deliverable tracking) | Low (stale snapshot) | +| completed | None | None (executable specs are better) | **Value by lifecycle phase:** @@ -84,11 +83,11 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** Without a clear boundary between durable and ephemeral artifacts, teams maintain redundant documents that inevitably drift from the source of truth. -| Artifact | Purpose | Owns | -| --- | --- | --- | -| Annotated TypeScript | Pattern identity, architecture graph | Name, status, uses, categories | -| Executable specs | Behavior verification, invariants | Rules, rationale, acceptance criteria | -| Decision specs (ADR/PDR) | Architectural choices, rationale | Why decisions were made | +| Artifact | Purpose | Owns | +| ------------------------ | ------------------------------------ | ------------------------------------- | +| Annotated TypeScript | Pattern identity, architecture graph | Name, status, uses, categories | +| Executable specs | Behavior verification, invariants | Rules, rationale, acceptance criteria | +| Decision specs (ADR/PDR) | Architectural choices, rationale | Why decisions were made | **Verified by:** @@ -100,10 +99,10 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** Without many-to-one realization, cross-cutting patterns that span multiple files cannot be traced back to a single canonical definition. -| Relationship | Tag | Cardinality | -| --- | --- | --- | -| Definition | `@libar-docs-pattern` | Exactly one per pattern | -| Realization | `@libar-docs-implements` | Many-to-one | +| Relationship | Tag | Cardinality | +| ------------ | ------------------------ | ----------------------- | +| Definition | `@libar-docs-pattern` | Exactly one per pattern | +| Realization | `@libar-docs-implements` | Many-to-one | **Verified by:** @@ -115,12 +114,12 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** Duplicate pattern definitions cause merge conflicts in the MasterDataset and produce ambiguous ownership in generated documentation. -| Current State | Resolution | -| --- | --- | -| Pattern in both TS and feature | Keep TS definition, feature uses `@implements` | -| Pattern only in tier 1 spec | Move definition to TS stub, archive tier 1 spec | -| Pattern only in TS | Already correct | -| Pattern only in executable spec | Valid if no TS implementation exists | +| Current State | Resolution | +| ------------------------------- | ----------------------------------------------- | +| Pattern in both TS and feature | Keep TS definition, feature uses `@implements` | +| Pattern only in tier 1 spec | Move definition to TS stub, archive tier 1 spec | +| Pattern only in TS | Already correct | +| Pattern only in executable spec | Valid if no TS implementation exists | **Migration path for existing conflicts:** @@ -134,10 +133,10 @@ artifacts are annotated source code, executable specs, and decision specs. **Rationale:** Forward links in tier 1 specs go stale when specs are archived, while reverse links in test files are self-maintaining because the test cannot run without the implementation. -| Mechanism | Usage | Reliability | -| --- | --- | --- | -| `@implements` (reverse) | 14 patterns (32%) | Self-maintaining, lives in test | -| `@executable-specs` (forward) | 9 patterns (20%) | Requires tier 1 spec maintenance | +| Mechanism | Usage | Reliability | +| ----------------------------- | ----------------- | -------------------------------- | +| `@implements` (reverse) | 14 patterns (32%) | Self-maintaining, lives in test | +| `@executable-specs` (forward) | 9 patterns (20%) | Requires tier 1 spec maintenance | **Verified by:** diff --git a/docs-live/decisions/adr-004-session-workflow-commands.md b/docs-live/decisions/adr-004-session-workflow-commands.md index 4df3b26c..19439cf3 100644 --- a/docs-live/decisions/adr-004-session-workflow-commands.md +++ b/docs-live/decisions/adr-004-session-workflow-commands.md @@ -6,9 +6,9 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | accepted | +| Property | Value | +| -------- | ------------ | +| Status | accepted | | Category | architecture | **Context:** @@ -31,10 +31,11 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. - scope-validate outputs structured text - Both scope-validate and handoff return string from the router + Both scope-validate and handoff return string from the router + - using - === SECTION === markers. Follows the dual output path where text - commands bypass JSON.stringify. + === SECTION === markers. Follows the dual output path where text + commands bypass JSON.stringify. ### DD-2 - Git integration is opt-in via --git flag @@ -46,9 +47,9 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. - N/A — no-shell constraint verified by code review (no exec/spawn calls in domain logic) - The handoff command accepts an optional --git flag. The CLI handler - calls git diff and passes file list to the pure generator function. - No shell dependency in domain logic. + The handoff command accepts an optional --git flag. The CLI handler + calls git diff and passes file list to the pure generator function. + No shell dependency in domain logic. ### DD-3 - Session type inferred from FSM status @@ -56,19 +57,19 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. **Rationale:** Ambiguous or missing inference forces users to always specify --session manually, defeating the ergonomic benefit of status-based defaults. -| Status | Inferred Session | -| --- | --- | -| roadmap | design | -| active | implement | -| completed | review | -| deferred | design | +| Status | Inferred Session | +| --------- | ---------------- | +| roadmap | design | +| active | implement | +| completed | review | +| deferred | design | **Verified by:** - N/A — full mapping table (4 statuses) verified by code review; active→implement example in "Active pattern infers implement session" - Handoff infers session type from pattern's current FSM status. - An explicit --session flag overrides inference. + Handoff infers session type from pattern's current FSM status. + An explicit --session flag overrides inference. ### DD-4 - Severity levels match Process Guard model @@ -76,20 +77,19 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. **Rationale:** Divergent severity models cause confusion when the same violation appears in both systems with different severity classifications. -| Severity | Meaning | -| --- | --- | -| PASS | Check passed | -| BLOCKED | Hard prerequisite missing | -| WARN | Recommendation not met | +| Severity | Meaning | +| -------- | ------------------------- | +| PASS | Check passed | +| BLOCKED | Hard prerequisite missing | +| WARN | Recommendation not met | **Verified by:** - N/A — three severity levels defined as type-system enum; verified by code review - Scope validation uses three severity levels: - + Scope validation uses three severity levels: - The --strict flag promotes WARN to BLOCKED. + The --strict flag promotes WARN to BLOCKED. ### DD-5 - Current date only for handoff @@ -101,7 +101,7 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. - N/A — no --date flag by design; verified by code review and CLI arg inventory - Handoff always uses the current date. No --date flag. + Handoff always uses the current date. No --date flag. ### DD-6 - Both positional and flag forms for scope type @@ -113,8 +113,8 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. - N/A — dual-form acceptance verified by code review (both positional and --type flag parsed in CLI handler) - scope-validate accepts scope type as both positional argument - and --type flag. + scope-validate accepts scope type as both positional argument + and --type flag. ### DD-7 - Co-located formatter functions @@ -126,10 +126,11 @@ Seven design decisions (DD-1 through DD-7) captured as Rules below. - N/A — co-location is a module structure decision; verified by code review of scope-validator.ts and handoff-generator.ts exports - Each module (scope-validator.ts + Each module (scope-validator.ts + - handoff-generator.ts) exports - both the data builder and the text formatter. Simpler than the - context-assembler/context-formatter split. + both the data builder and the text formatter. Simpler than the + context-assembler/context-formatter split. --- diff --git a/docs-live/decisions/adr-005-codec-based-markdown-rendering.md b/docs-live/decisions/adr-005-codec-based-markdown-rendering.md index ef8c485e..cbe9827e 100644 --- a/docs-live/decisions/adr-005-codec-based-markdown-rendering.md +++ b/docs-live/decisions/adr-005-codec-based-markdown-rendering.md @@ -6,9 +6,9 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | accepted | +| Property | Value | +| -------- | ------------ | +| Status | accepted | | Category | architecture | **Context:** @@ -30,23 +30,23 @@ include) from formatting (how it looks) from serialization (markdown syntax). **Consequences:** -| Type | Impact | -| --- | --- | -| Positive | Codecs are pure functions: dataset in, document out -- trivially testable | +| Type | Impact | +| -------- | --------------------------------------------------------------------------------- | +| Positive | Codecs are pure functions: dataset in, document out -- trivially testable | | Positive | RenderableDocument is an inspectable IR -- tests assert on structure, not strings | -| Positive | Composable via CompositeCodec -- reference docs assemble from child codecs | -| Positive | Same dataset can produce different outputs (full doc, compact doc, AI context) | -| Negative | Extra abstraction layer between data and output | -| Negative | RenderableDocument vocabulary must cover all needed output patterns | +| Positive | Composable via CompositeCodec -- reference docs assemble from child codecs | +| Positive | Same dataset can produce different outputs (full doc, compact doc, AI context) | +| Negative | Extra abstraction layer between data and output | +| Negative | RenderableDocument vocabulary must cover all needed output patterns | **Benefits:** -| Benefit | Before (String Concat) | After (Codec) | -| --- | --- | --- | -| Testability | Assert on markdown strings | Assert on typed section blocks | -| Composability | Copy-paste between generators | CompositeCodec assembles children | -| Format variants | Duplicate generator logic | Same codec, different renderer | -| Progressive disclosure | Manual heading management | Heading depth auto-calculated | +| Benefit | Before (String Concat) | After (Codec) | +| ---------------------- | ----------------------------- | --------------------------------- | +| Testability | Assert on markdown strings | Assert on typed section blocks | +| Composability | Copy-paste between generators | CompositeCodec assembles children | +| Format variants | Duplicate generator logic | Same codec, different renderer | +| Progressive disclosure | Manual heading management | Heading depth auto-calculated | ## Rules @@ -60,8 +60,8 @@ include) from formatting (how it looks) from serialization (markdown syntax). ```typescript interface DocumentCodec { - decode(dataset: MasterDataset): RenderableDocument; - } + decode(dataset: MasterDataset): RenderableDocument; +} ``` **Verified by:** @@ -75,15 +75,15 @@ interface DocumentCodec { **Rationale:** A typed IR decouples codecs from rendering. Codecs express intent ("this is a table with these rows") and the renderer handles syntax ("pipe-delimited markdown with separator row"). This means switching output format (e.g., HTML instead of markdown) requires only a new renderer, not changes to every codec. -| Block Type | Purpose | Markdown Output | -| --- | --- | --- | -| heading | Section title with depth | ## Title (depth-adjusted) | -| paragraph | Prose text | Plain text with blank lines | -| table | Structured data | Pipe-delimited table | -| code | Code sample with language | Fenced code block | -| list | Ordered or unordered items | - item or 1. item | -| separator | Visual break between sections | --- | -| metaRow | Key-value metadata | **Key:** Value | +| Block Type | Purpose | Markdown Output | +| ---------- | ----------------------------- | --------------------------- | +| heading | Section title with depth | ## Title (depth-adjusted) | +| paragraph | Prose text | Plain text with blank lines | +| table | Structured data | Pipe-delimited table | +| code | Code sample with language | Fenced code block | +| list | Ordered or unordered items | - item or 1. item | +| separator | Visual break between sections | --- | +| metaRow | Key-value metadata | **Key:** Value | **Section block types:** @@ -102,14 +102,14 @@ interface DocumentCodec { ```typescript const referenceDoc = CompositeCodec.create({ - title: 'Architecture Reference', - codecs: [ - behaviorCodec, // patterns with rules - conventionCodec, // decision records - shapeCodec, // type definitions - diagramCodec, // mermaid diagrams - ], - }); + title: 'Architecture Reference', + codecs: [ + behaviorCodec, // patterns with rules + conventionCodec, // decision records + shapeCodec, // type definitions + diagramCodec, // mermaid diagrams + ], +}); ``` **Verified by:** @@ -123,9 +123,9 @@ const referenceDoc = CompositeCodec.create({ **Rationale:** Early ADRs used name prefixes like "Context - ..." and "Decision - ..." on Rule blocks to structure content. Later ADRs placed Context, Decision, and Consequences as bold-annotated prose in the Feature description, reserving Rule: blocks for invariants and design rules. Both conventions are valid. The ADR codec must handle both because the codebase contains ADRs authored in each style. The Feature description lives in pattern.directive.description. If the codec only renders Rules (via partitionRulesByPrefix), then Feature description content is silently dropped -- no error, no warning. This caused confusion across two repos where ADR content appeared in the feature file but was missing from generated docs. The fix renders pattern.directive.description in buildSingleAdrDocument between the Overview metadata table and the partitioned Rules section, using renderFeatureDescription() which walks content linearly and handles prose, tables, and DocStrings with correct interleaving. -| Source | Location | Example | Rendered Via | -| --- | --- | --- | --- | -| Rule prefix | Rule: Context - ... | ADR-001 (taxonomy) | partitionRulesByPrefix() | +| Source | Location | Example | Rendered Via | +| ------------------- | ----------------------------------- | ------------------------- | -------------------------- | +| Rule prefix | Rule: Context - ... | ADR-001 (taxonomy) | partitionRulesByPrefix() | | Feature description | **Context:** prose in Feature block | ADR-005 (codec rendering) | renderFeatureDescription() | **Verified by:** diff --git a/docs-live/decisions/adr-006-single-read-model-architecture.md b/docs-live/decisions/adr-006-single-read-model-architecture.md index 56c1a7f1..a762123e 100644 --- a/docs-live/decisions/adr-006-single-read-model-architecture.md +++ b/docs-live/decisions/adr-006-single-read-model-architecture.md @@ -6,9 +6,9 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | proposed | +| Property | Value | +| -------- | ------------ | +| Status | proposed | | Category | architecture | **Context:** @@ -38,14 +38,14 @@ consume the same pre-computed read model. **Consequences:** -| Type | Impact | -| --- | --- | -| Positive | Relationship resolution happens once — no consumer re-derives implements, uses, or dependsOn | -| Positive | Eliminates lossy local types that discard fields from canonical ExtractedPattern | -| Positive | Validation rules automatically benefit from new MasterDataset views and indices | +| Type | Impact | +| -------- | ---------------------------------------------------------------------------------------------- | +| Positive | Relationship resolution happens once — no consumer re-derives implements, uses, or dependsOn | +| Positive | Eliminates lossy local types that discard fields from canonical ExtractedPattern | +| Positive | Validation rules automatically benefit from new MasterDataset views and indices | | Positive | Aligns with the monorepo's own ADR-006: projections for all reads, never query aggregate state | -| Negative | Validators that today only need stage 1-2 data will import the transformer | -| Negative | MasterDataset schema changes affect more consumers | +| Negative | Validators that today only need stage 1-2 data will import the transformer | +| Negative | MasterDataset schema changes affect more consumers | ## Rules @@ -55,20 +55,20 @@ consume the same pre-computed read model. **Rationale:** Bypassing the read model forces consumers to re-derive data that the MasterDataset already computes, creating duplicate logic and divergent behavior when the pipeline evolves. -| Layer | May Import | Examples | -| --- | --- | --- | -| Pipeline Orchestration | scanner/, extractor/, pipeline/ | orchestrator.ts, process-api.ts pipeline setup | -| Feature Consumption | MasterDataset, relationshipIndex | codecs, ProcessStateAPI, validators, query handlers | +| Layer | May Import | Examples | +| ---------------------- | -------------------------------- | --------------------------------------------------- | +| Pipeline Orchestration | scanner/, extractor/, pipeline/ | orchestrator.ts, process-api.ts pipeline setup | +| Feature Consumption | MasterDataset, relationshipIndex | codecs, ProcessStateAPI, validators, query handlers | **Verified by:** - Feature consumers import from MasterDataset not from raw pipeline stages + Exception: `lint-patterns.ts` is a pure stage-1 consumer. It validates + annotation syntax on scanned files. No relationships - Exception: `lint-patterns.ts` is a pure stage-1 consumer. It validates - annotation syntax on scanned files. No relationships - no cross-source - resolution. Direct scanner consumption is correct for that use case. + resolution. Direct scanner consumption is correct for that use case. ### No lossy local types @@ -96,10 +96,10 @@ consume the same pre-computed read model. **Rationale:** Without named anti-patterns, violations appear as one-off style issues rather than systematic architectural drift, making them harder to detect and communicate in code review. -| Anti-Pattern | Detection Signal | -| --- | --- | -| Parallel Pipeline | Feature consumer imports from scanner/ or extractor/ | -| Lossy Local Type | Local interface with subset of ExtractedPattern fields + dedicated extraction function | +| Anti-Pattern | Detection Signal | +| ----------------------- | ---------------------------------------------------------------------------------------- | +| Parallel Pipeline | Feature consumer imports from scanner/ or extractor/ | +| Lossy Local Type | Local interface with subset of ExtractedPattern fields + dedicated extraction function | | Re-derived Relationship | Building Map or Set from pattern.implementsPatterns, uses, or dependsOn in consumer code | **Good vs Bad** @@ -128,9 +128,8 @@ consume the same pre-computed read model. - Feature consumers import from MasterDataset not from raw pipeline stages - - Naming them makes them visible in code review — including AI-assisted - sessions where the default proposal is often "add a helper function." + Naming them makes them visible in code review — including AI-assisted + sessions where the default proposal is often "add a helper function." --- diff --git a/docs-live/decisions/adr-021-doc-generation-proof-of-concept.md b/docs-live/decisions/adr-021-doc-generation-proof-of-concept.md index 287cc688..a241b133 100644 --- a/docs-live/decisions/adr-021-doc-generation-proof-of-concept.md +++ b/docs-live/decisions/adr-021-doc-generation-proof-of-concept.md @@ -6,13 +6,14 @@ ## Overview -| Property | Value | -| --- | --- | -| Status | superseded | +| Property | Value | +| -------- | ------------- | +| Status | superseded | | Category | documentation | -| Phase | 27 | +| 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 @@ -26,18 +27,18 @@ the PROOF OF CONCEPT (demonstrating the pattern works). **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 | +| 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 | +| 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:** @@ -79,40 +80,40 @@ the PROOF OF CONCEPT (demonstrating the pattern works). **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 | +| 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:** @@ -162,31 +163,30 @@ generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' - **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 | +| 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:** @@ -209,32 +209,31 @@ generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' - **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 | +| 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" | +| Clear ownership | Decisions own "why", code owns "what" | -| Trade-off | Mitigation | -| --- | --- | +| 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 | +| 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:** @@ -248,34 +247,34 @@ generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' - **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 | +| 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 | +| 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:** @@ -307,33 +306,32 @@ generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' - ```typescript // delivery-process/stubs/shape-extractor/shape-extractor.ts - /** - * @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 { - throw new Error('ShapeExtractor not yet implemented - roadmap 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 { + throw new Error('ShapeExtractor not yet implemented - roadmap pattern'); +} ``` **Benefits:** - **Workflow:** 1. **Design session:** Create stub in `delivery-process/stubs/{pattern-name}/` @@ -358,31 +356,31 @@ generate-docs --decisions 'specs/**/*.feature' --features 'tests/**/*.feature' - **Rationale:** A self-referential proof of concept exposes extraction gaps and source mapping issues that synthetic test data would miss. -| 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 | +| 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`.** @@ -404,42 +402,42 @@ npx lint-process --staged ```json { - "scripts": { - "lint:process": "lint-process --staged", - "lint:process:ci": "lint-process --all --strict" - } - } + "scripts": { + "lint:process": "lint-process --staged", + "lint:process:ci": "lint-process --all --strict" + } +} ``` **Programmatic API Example:** ```typescript import { - deriveProcessState, - detectStagedChanges, - validateChanges, - hasErrors, - summarizeResult, - } from '@libar-dev/delivery-process/lint'; - - // 1. Derive state from annotations - const state = (await deriveProcessState({ baseDir: '.' })).value; - - // 2. Detect changes - const changes = detectStagedChanges('.').value; - - // 3. Validate - const { result } = validateChanges({ - state, - changes, - options: { strict: false, ignoreSession: false }, - }); - - // 4. Handle results - if (hasErrors(result)) { - console.log(summarizeResult(result)); - process.exit(1); - } + deriveProcessState, + detectStagedChanges, + validateChanges, + hasErrors, + summarizeResult, +} from '@libar-dev/delivery-process/lint'; + +// 1. Derive state from annotations +const state = (await deriveProcessState({ baseDir: '.' })).value; + +// 2. Detect changes +const changes = detectStagedChanges('.').value; + +// 3. Validate +const { result } = validateChanges({ + state, + changes, + options: { strict: false, ignoreSession: false }, +}); + +// 4. Handle results +if (hasErrors(result)) { + console.log(summarizeResult(result)); + process.exit(1); +} ``` **Escape Hatches:** @@ -448,8 +446,8 @@ import { - Full pipeline generates documentation from decision documents - This POC demonstrates the doc-from-decision pattern by generating docs - about ITSELF. The DocGenerationProofOfConcept pattern produces: + This POC demonstrates the doc-from-decision pattern by generating docs + about ITSELF. The DocGenerationProofOfConcept pattern produces: ### Expected Output - Compact claude module structure @@ -457,14 +455,14 @@ import { **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 | +| 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` diff --git a/docs-live/product-areas/ANNOTATION.md b/docs-live/product-areas/ANNOTATION.md index 09060a57..22fd5e1b 100644 --- a/docs-live/product-areas/ANNOTATION.md +++ b/docs-live/product-areas/ANNOTATION.md @@ -123,15 +123,15 @@ interface TagRegistry { } ``` -| Property | Description | -| --- | --- | -| version | Schema version for forward/backward compatibility checking | -| categories | Category definitions for classifying patterns by domain (e.g., core, api, ddd) | -| metadataTags | Metadata tag definitions with format, purpose, and validation rules | -| aggregationTags | Aggregation tag definitions for document-level grouping | -| formatOptions | Available format options for documentation output | -| tagPrefix | Prefix for all tags (e.g., "@libar-docs-") | -| fileOptInTag | File-level opt-in marker tag (e.g., "@libar-docs") | +| Property | Description | +| --------------- | ------------------------------------------------------------------------------ | +| version | Schema version for forward/backward compatibility checking | +| categories | Category definitions for classifying patterns by domain (e.g., core, api, ddd) | +| metadataTags | Metadata tag definitions with format, purpose, and validation rules | +| aggregationTags | Aggregation tag definitions for document-level grouping | +| formatOptions | Available format options for documentation output | +| tagPrefix | Prefix for all tags (e.g., "@libar-docs-") | +| fileOptInTag | File-level opt-in marker tag (e.g., "@libar-docs") | ### MetadataTagDefinitionForRegistry (interface) @@ -160,18 +160,18 @@ interface MetadataTagDefinitionForRegistry { } ``` -| Property | Description | -| --- | --- | -| tag | Tag name without prefix (e.g., "pattern", "status", "phase") | -| format | Value format type determining parsing rules (flag, value, enum, csv, number, quoted-value) | -| purpose | Human-readable description of the tag's purpose and usage | -| required | Whether this tag must be present for valid patterns | -| repeatable | Whether this tag can appear multiple times on a single pattern | -| values | Valid values for enum-type tags (undefined for non-enum formats) | -| default | Default value applied when tag is not specified | -| example | Example usage showing tag syntax (e.g., "@libar-docs-pattern MyPattern") | -| metadataKey | Maps tag name to metadata object property name (defaults to kebab-to-camelCase) | -| transform | Post-parse value transformer applied after format-based parsing | +| Property | Description | +| ----------- | ------------------------------------------------------------------------------------------ | +| tag | Tag name without prefix (e.g., "pattern", "status", "phase") | +| format | Value format type determining parsing rules (flag, value, enum, csv, number, quoted-value) | +| purpose | Human-readable description of the tag's purpose and usage | +| required | Whether this tag must be present for valid patterns | +| repeatable | Whether this tag can appear multiple times on a single pattern | +| values | Valid values for enum-type tags (undefined for non-enum formats) | +| default | Default value applied when tag is not specified | +| example | Example usage showing tag syntax (e.g., "@libar-docs-pattern MyPattern") | +| metadataKey | Maps tag name to metadata object property name (defaults to kebab-to-camelCase) | +| transform | Post-parse value transformer applied after format-based parsing | ### CategoryDefinition (interface) @@ -190,13 +190,13 @@ interface CategoryDefinition { } ``` -| Property | Description | -| --- | --- | -| tag | Category tag name without prefix (e.g., "core", "api", "ddd", "saga") | -| domain | Human-readable domain name for display (e.g., "Strategic DDD", "Event Sourcing") | -| priority | Display order priority - lower values appear first in sorted output | -| description | Brief description of the category's purpose and typical patterns | -| aliases | Alternative tag names that map to this category (e.g., "es" for "event-sourcing") | +| Property | Description | +| ----------- | --------------------------------------------------------------------------------- | +| tag | Category tag name without prefix (e.g., "core", "api", "ddd", "saga") | +| domain | Human-readable domain name for display (e.g., "Strategic DDD", "Event Sourcing") | +| priority | Display order priority - lower values appear first in sorted output | +| description | Brief description of the category's purpose and typical patterns | +| aliases | Alternative tag names that map to this category (e.g., "es" for "event-sourcing") | ### TagDefinition (type) @@ -301,7 +301,13 @@ METADATA_TAGS_BY_GROUP = { stub: ['target', 'since'] as const, convention: ['convention'] as const, claude: ['claude-module', 'claude-section', 'claude-tags'] as const, -} as const + sequence: [ + 'sequence-orchestrator', + 'sequence-step', + 'sequence-module', + 'sequence-error', + ] as const, +} as const; ``` ### CATEGORIES (const) @@ -325,7 +331,7 @@ const CATEGORIES: readonly CategoryDefinition[]; ``` ```typescript -CATEGORY_TAGS = CATEGORIES.map((c) => c.tag) as readonly CategoryTag[] +CATEGORY_TAGS = CATEGORIES.map((c) => c.tag) as readonly CategoryTag[]; ``` --- @@ -336,243 +342,243 @@ CATEGORY_TAGS = CATEGORIES.map((c) => c.tag) as readonly CategoryTag[] ### Ast Parser Exports -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Export types are correctly identified from TypeScript declarations | Every exported TypeScript declaration type (function, type, interface, const, class, enum, abstract class, arrow function, async function, generic function, default export, re-export) is correctly classified. | Export type classification drives how codecs render API documentation — misclassifying a function as a const (or vice versa) produces incorrect signatures and misleading docs. | ### Ast Parser Metadata -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Metadata is correctly extracted from JSDoc comments | Examples, multi-line descriptions, line numbers, function signatures, and standard JSDoc tags are all correctly parsed and separated. | Downstream codecs render each metadata field independently — incorrect parsing causes examples to leak into descriptions or signatures to be lost in generated documentation. | -| Tags are extracted only from the directive section, not from description or examples | Only tags appearing in the directive section (before the description) are extracted. Tags mentioned in description prose or example code blocks are ignored. | Tags control taxonomy classification and pattern routing — extracting them from prose or examples would create phantom patterns and corrupt the registry. | -| When to Use sections are extracted in all supported formats | When to Use content is extracted from heading format with bullet points, inline bold format, and asterisk bullet format. When no When to Use section exists, the field is undefined. | Generated pattern documentation includes a When to Use section — failing to recognize any supported format means valid guidance silently disappears from output. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Metadata is correctly extracted from JSDoc comments | Examples, multi-line descriptions, line numbers, function signatures, and standard JSDoc tags are all correctly parsed and separated. | Downstream codecs render each metadata field independently — incorrect parsing causes examples to leak into descriptions or signatures to be lost in generated documentation. | +| Tags are extracted only from the directive section, not from description or examples | Only tags appearing in the directive section (before the description) are extracted. Tags mentioned in description prose or example code blocks are ignored. | Tags control taxonomy classification and pattern routing — extracting them from prose or examples would create phantom patterns and corrupt the registry. | +| When to Use sections are extracted in all supported formats | When to Use content is extracted from heading format with bullet points, inline bold format, and asterisk bullet format. When no When to Use section exists, the field is undefined. | Generated pattern documentation includes a When to Use section — failing to recognize any supported format means valid guidance silently disappears from output. | ### Ast Parser Relationships Edges -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Relationship tags extract uses and usedBy dependencies | The uses and usedBy relationship arrays are populated from directive tags, not from description content. When no relationship tags exist, the fields are undefined. | Relationship data drives dependency diagrams and impact analysis — extracting from prose would produce false edges from incidental mentions. | -| Edge cases and malformed input are handled gracefully | The parser never crashes on invalid input. Files without directives return empty results. Malformed TypeScript returns a structured error with the file path. | The scanner processes hundreds of files in bulk — a single malformed file must not abort the entire pipeline or produce an undiagnosable crash. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| Relationship tags extract uses and usedBy dependencies | The uses and usedBy relationship arrays are populated from directive tags, not from description content. When no relationship tags exist, the fields are undefined. | Relationship data drives dependency diagrams and impact analysis — extracting from prose would produce false edges from incidental mentions. | +| Edge cases and malformed input are handled gracefully | The parser never crashes on invalid input. Files without directives return empty results. Malformed TypeScript returns a structured error with the file path. | The scanner processes hundreds of files in bulk — a single malformed file must not abort the entire pipeline or produce an undiagnosable crash. | ### Context Inference -| Rule | Invariant | Rationale | -| --- | --- | --- | -| matchPattern supports recursive wildcard ** | The `**` wildcard matches files at any nesting depth below the specified directory prefix. | Directory hierarchies vary in depth; recursive matching ensures all nested files inherit context. | -| matchPattern supports single-level wildcard /* | The `/*` wildcard matches only direct children of the specified directory, not deeper nested files. | Some contexts apply only to a specific directory level, not its entire subtree. | -| matchPattern supports prefix matching | A trailing slash pattern matches any file whose path starts with that directory prefix. | Without prefix matching, users would need separate wildcard patterns for each nesting depth, making rule configuration verbose and error-prone. | -| inferContext returns undefined when no rules match | When no inference rule matches a file path, the pattern receives no inferred context and is excluded from the byContext index. | Unmatched files must not receive a spurious context assignment; absence of context is a valid state. | -| inferContext applies first matching rule | When multiple rules could match a file path, only the first matching rule determines the inferred context. | Deterministic ordering prevents ambiguous context assignment when rules overlap. | -| Explicit archContext is not overridden | A pattern with an explicitly annotated archContext retains that value regardless of matching inference rules. | Explicit annotations represent intentional developer decisions that must not be silently overwritten by automation. | -| Inference works independently of archLayer | Context inference operates on file path alone; the presence or absence of archLayer does not affect context assignment. | Coupling context inference to archLayer would prevent context-based queries from finding patterns that lack explicit layer annotations. | -| Default rules map standard directories | Each standard source directory (validation, scanner, extractor, etc.) maps to a well-known bounded context name via the default rule set. | Convention-based mapping eliminates the need for explicit context annotations on every file in standard directories. | +| Rule | Invariant | Rationale | +| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| matchPattern supports recursive wildcard \*\* | The `**` wildcard matches files at any nesting depth below the specified directory prefix. | Directory hierarchies vary in depth; recursive matching ensures all nested files inherit context. | +| matchPattern supports single-level wildcard /\* | The `/*` wildcard matches only direct children of the specified directory, not deeper nested files. | Some contexts apply only to a specific directory level, not its entire subtree. | +| matchPattern supports prefix matching | A trailing slash pattern matches any file whose path starts with that directory prefix. | Without prefix matching, users would need separate wildcard patterns for each nesting depth, making rule configuration verbose and error-prone. | +| inferContext returns undefined when no rules match | When no inference rule matches a file path, the pattern receives no inferred context and is excluded from the byContext index. | Unmatched files must not receive a spurious context assignment; absence of context is a valid state. | +| inferContext applies first matching rule | When multiple rules could match a file path, only the first matching rule determines the inferred context. | Deterministic ordering prevents ambiguous context assignment when rules overlap. | +| Explicit archContext is not overridden | A pattern with an explicitly annotated archContext retains that value regardless of matching inference rules. | Explicit annotations represent intentional developer decisions that must not be silently overwritten by automation. | +| Inference works independently of archLayer | Context inference operates on file path alone; the presence or absence of archLayer does not affect context assignment. | Coupling context inference to archLayer would prevent context-based queries from finding patterns that lack explicit layer annotations. | +| Default rules map standard directories | Each standard source directory (validation, scanner, extractor, etc.) maps to a well-known bounded context name via the default rule set. | Convention-based mapping eliminates the need for explicit context annotations on every file in standard directories. | ### Cross Source Validation -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pattern names must be consistent across sources | A pattern name referenced in one source must resolve to the same canonical name in all other sources. | Typos or inconsistencies between TypeScript and Gherkin pattern names cause silent data loss — the pattern appears as two unrelated entries instead of a unified cross-source record. | -| Circular dependencies are detected | The dependency graph must be a directed acyclic graph (DAG) with no cycles. | Circular dependencies create unresolvable ordering — no pattern in the cycle can be completed first, blocking the entire chain from progressing. | -| Dependency references must resolve | Every `@depends-on` reference must resolve to an existing pattern in the registry. | Dangling dependency references produce incomplete ordering and missing relationship edges in generated documentation, hiding actual inter-pattern constraints. | +| Circular dependencies are detected | The dependency graph must be a directed acyclic graph (DAG) with no cycles. | Circular dependencies create unresolvable ordering — no pattern in the cycle can be completed first, blocking the entire chain from progressing. | +| Dependency references must resolve | Every `@depends-on` reference must resolve to an existing pattern in the registry. | Dangling dependency references produce incomplete ordering and missing relationship edges in generated documentation, hiding actual inter-pattern constraints. | ### Declaration Level Shape Tagging -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Declarations opt in via libar-docs-shape tag | Only declarations with the libar-docs-shape tag in their immediately preceding JSDoc are collected as tagged shapes. Declarations without the tag are ignored even if they are exported. The tag value is optional -- bare libar-docs-shape opts in without a group, while libar-docs-shape group-name assigns the declaration to a named group. Tagged non-exported declarations are included (DD-7). | Explicit opt-in prevents over-extraction of internal helpers. Unlike auto-discovery mode (extract-shapes *) which grabs all exports, declaration-level tagging gives precise control. This matches how TypeDoc uses public/internal tags -- the annotation lives next to the code it describes, surviving refactors without breaking extraction. | -| Reference doc configs select shapes via shapeSelectors | shapeSelectors provides three selection modes: by source path + specific names (DD-6 source+names variant), by group tag (DD-6 group variant), or by source path alone (DD-6 source-only variant). shapeSources remains for backward compatibility. When both are present, shapeSources provides the coarse file-level filter and shapeSelectors adds fine-grained name/group filtering on top. | The reference doc system composes focused documents from cherry-picked content. Every other content axis (conventions, behaviors, diagrams) has content-level filtering. shapeSources was the only axis limited to file-level granularity. shapeSelectors closes this gap with the same explicitness as conventionTags. | -| Discovery uses existing estree parser with JSDoc comment scanning | The discoverTaggedShapes function uses the existing typescript-estree parse() and extractPrecedingJsDoc() approach. It does not require the TypeScript compiler API, ts-morph, or parseAndGenerateServices. Tag detection is a regex match on the JSDoc comment text already extracted by the existing infrastructure. The tag regex pattern is: /libar-docs-shape(?:\s+(\S+))?/ where capture group 1 is the optional group name. | The shape extractor already traverses declarations and extracts their JSDoc. Adding libar-docs-shape detection is a string search on content that is already available -- approximately 15 lines of new logic. Switching parsers would introduce churn with no benefit for the v1 use case of tag detection on top-level declarations. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Declarations opt in via libar-docs-shape tag | Only declarations with the libar-docs-shape tag in their immediately preceding JSDoc are collected as tagged shapes. Declarations without the tag are ignored even if they are exported. The tag value is optional -- bare libar-docs-shape opts in without a group, while libar-docs-shape group-name assigns the declaration to a named group. Tagged non-exported declarations are included (DD-7). | Explicit opt-in prevents over-extraction of internal helpers. Unlike auto-discovery mode (extract-shapes \*) which grabs all exports, declaration-level tagging gives precise control. This matches how TypeDoc uses public/internal tags -- the annotation lives next to the code it describes, surviving refactors without breaking extraction. | +| Reference doc configs select shapes via shapeSelectors | shapeSelectors provides three selection modes: by source path + specific names (DD-6 source+names variant), by group tag (DD-6 group variant), or by source path alone (DD-6 source-only variant). shapeSources remains for backward compatibility. When both are present, shapeSources provides the coarse file-level filter and shapeSelectors adds fine-grained name/group filtering on top. | The reference doc system composes focused documents from cherry-picked content. Every other content axis (conventions, behaviors, diagrams) has content-level filtering. shapeSources was the only axis limited to file-level granularity. shapeSelectors closes this gap with the same explicitness as conventionTags. | +| Discovery uses existing estree parser with JSDoc comment scanning | The discoverTaggedShapes function uses the existing typescript-estree parse() and extractPrecedingJsDoc() approach. It does not require the TypeScript compiler API, ts-morph, or parseAndGenerateServices. Tag detection is a regex match on the JSDoc comment text already extracted by the existing infrastructure. The tag regex pattern is: /libar-docs-shape(?:\s+(\S+))?/ where capture group 1 is the optional group name. | The shape extractor already traverses declarations and extracts their JSDoc. Adding libar-docs-shape detection is a string search on content that is already available -- approximately 15 lines of new logic. Switching parsers would introduce churn with no benefit for the v1 use case of tag detection on top-level declarations. | ### Declaration Level Shape Tagging Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Declarations opt in via libar-docs-shape tag | Only declarations with the libar-docs-shape tag in their immediately preceding JSDoc are collected as tagged shapes. | Extracting shapes without an explicit opt-in tag would surface internal implementation details in generated API documentation, violating information hiding. | -| Discovery uses existing estree parser with JSDoc comment scanning | The discoverTaggedShapes function uses the existing typescript-estree parse() and extractPrecedingJsDoc() approach. | Introducing a second parser would create divergent AST behavior — reusing the established parser ensures consistent JSDoc handling and avoids subtle extraction differences. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Declarations opt in via libar-docs-shape tag | Only declarations with the libar-docs-shape tag in their immediately preceding JSDoc are collected as tagged shapes. | Extracting shapes without an explicit opt-in tag would surface internal implementation details in generated API documentation, violating information hiding. | +| Discovery uses existing estree parser with JSDoc comment scanning | The discoverTaggedShapes function uses the existing typescript-estree parse() and extractPrecedingJsDoc() approach. | Introducing a second parser would create divergent AST behavior — reusing the established parser ensures consistent JSDoc handling and avoids subtle extraction differences. | ### Depends On Tag Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Depends-on tag is defined in taxonomy registry | The depends-on and enables tags must exist in the taxonomy registry with CSV format. | Without registry definitions, the data-driven AST parser cannot discover or extract these planning dependency tags from source files. | -| Depends-on tag is extracted from Gherkin files | The Gherkin parser must extract depends-on values from feature file tags, including CSV multi-value lists. | Missing dependency extraction causes the dependency tree and blocking-pattern queries to return incomplete results. | -| Depends-on in TypeScript triggers anti-pattern warning | The depends-on tag must only appear in Gherkin files; its presence in TypeScript is an anti-pattern. | Depends-on represents planning dependencies owned by Gherkin specs, not runtime dependencies owned by TypeScript. | -| Enables tag is extracted from Gherkin files | The Gherkin parser must extract enables values from feature file tags, including CSV multi-value lists. | Missing enables extraction breaks forward-looking dependency queries, hiding which patterns are unblocked when a prerequisite completes. | -| Planning dependencies are stored in relationship index | The relationship index must store dependsOn and enables relationships extracted from pattern metadata. | Omitting planning dependencies from the index causes blocking-pattern and critical-path queries to return incomplete results. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Depends-on tag is defined in taxonomy registry | The depends-on and enables tags must exist in the taxonomy registry with CSV format. | Without registry definitions, the data-driven AST parser cannot discover or extract these planning dependency tags from source files. | +| Depends-on tag is extracted from Gherkin files | The Gherkin parser must extract depends-on values from feature file tags, including CSV multi-value lists. | Missing dependency extraction causes the dependency tree and blocking-pattern queries to return incomplete results. | +| Depends-on in TypeScript triggers anti-pattern warning | The depends-on tag must only appear in Gherkin files; its presence in TypeScript is an anti-pattern. | Depends-on represents planning dependencies owned by Gherkin specs, not runtime dependencies owned by TypeScript. | +| Enables tag is extracted from Gherkin files | The Gherkin parser must extract enables values from feature file tags, including CSV multi-value lists. | Missing enables extraction breaks forward-looking dependency queries, hiding which patterns are unblocked when a prerequisite completes. | +| Planning dependencies are stored in relationship index | The relationship index must store dependsOn and enables relationships extracted from pattern metadata. | Omitting planning dependencies from the index causes blocking-pattern and critical-path queries to return incomplete results. | ### Directive Detection -| Rule | Invariant | Rationale | -| --- | --- | --- | -| hasDocDirectives detects @libar-docs-* section directives | hasDocDirectives must return true if and only if the source contains at least one @libar-docs-{suffix} directive (case-sensitive, @ required, suffix required). | This is the first-pass filter in the scanner pipeline; false negatives cause patterns to be silently missed, while false positives only waste AST parsing time. | -| hasFileOptIn detects file-level @libar-docs marker | hasFileOptIn must return true if and only if the source contains a bare @libar-docs tag (not followed by a hyphen) inside a JSDoc block comment; line comments and @libar-docs-* suffixed tags must not match. | File-level opt-in is the gate for including a file in the scanner pipeline; confusing @libar-docs-core (a section tag) with @libar-docs (file opt-in) would either miss files or over-include them. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| hasDocDirectives detects @libar-docs-\* section directives | hasDocDirectives must return true if and only if the source contains at least one @libar-docs-{suffix} directive (case-sensitive, @ required, suffix required). | This is the first-pass filter in the scanner pipeline; false negatives cause patterns to be silently missed, while false positives only waste AST parsing time. | +| hasFileOptIn detects file-level @libar-docs marker | hasFileOptIn must return true if and only if the source contains a bare @libar-docs tag (not followed by a hyphen) inside a JSDoc block comment; line comments and @libar-docs-\* suffixed tags must not match. | File-level opt-in is the gate for including a file in the scanner pipeline; confusing @libar-docs-core (a section tag) with @libar-docs (file opt-in) would either miss files or over-include them. | ### Doc String Media Type -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Parser preserves DocString mediaType during extraction | The Gherkin parser must retain the mediaType annotation from DocString delimiters through to the parsed AST; DocStrings without a mediaType have undefined mediaType. | Losing the mediaType causes downstream renderers to apply incorrect escaping or default language hints, corrupting code block output. | -| MediaType is used when rendering code blocks | The rendered code block language must match the DocString mediaType; when mediaType is absent, the renderer falls back to a caller-specified default language. | Using the wrong language hint causes syntax highlighters to misrender code blocks, and losing mediaType entirely can trigger incorrect escaping (e.g., asterisks in JSDoc). | -| renderDocString handles both string and object formats | renderDocString accepts both plain string and object DocString formats; when an object has a mediaType, it takes precedence over the caller-supplied language parameter. | Legacy callers pass raw strings while newer code passes structured objects — the renderer must handle both without breaking existing usage. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Parser preserves DocString mediaType during extraction | The Gherkin parser must retain the mediaType annotation from DocString delimiters through to the parsed AST; DocStrings without a mediaType have undefined mediaType. | Losing the mediaType causes downstream renderers to apply incorrect escaping or default language hints, corrupting code block output. | +| MediaType is used when rendering code blocks | The rendered code block language must match the DocString mediaType; when mediaType is absent, the renderer falls back to a caller-specified default language. | Using the wrong language hint causes syntax highlighters to misrender code blocks, and losing mediaType entirely can trigger incorrect escaping (e.g., asterisks in JSDoc). | +| renderDocString handles both string and object formats | renderDocString accepts both plain string and object DocString formats; when an object has a mediaType, it takes precedence over the caller-supplied language parameter. | Legacy callers pass raw strings while newer code passes structured objects — the renderer must handle both without breaking existing usage. | ### Dual Source Extractor Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Process metadata is extracted from feature tags | A feature file must have both @pattern and @phase tags to produce valid process metadata; missing either yields null. | Pattern name and phase are the minimum identifiers for placing a pattern in the roadmap — without both, the pattern cannot be tracked. | -| Deliverables are extracted from Background tables | Deliverables are sourced exclusively from Background tables; features without a Background produce an empty deliverable list. | The Background table is the single source of truth for deliverable tracking — extracting from other locations would create ambiguity. | -| Code and feature patterns are combined into dual-source patterns | A combined pattern is produced only when both a code stub and a feature file exist for the same pattern name; unmatched sources are tracked separately as code-only or feature-only. | Dual-source combination ensures documentation reflects both implementation intent (code) and specification (Gherkin) — mismatches signal inconsistency. | -| Dual-source results are validated for consistency | Cross-source validation reports errors for metadata mismatches and warnings for orphaned patterns that are still in roadmap status. | Inconsistencies between code stubs and feature files indicate drift — errors catch conflicts while warnings surface missing counterparts that may be intentional. | -| Include tags are extracted from Gherkin feature tags | Include tags are parsed as comma-separated values; absence of the tag means the pattern has no includes. | Include tags control which patterns appear in scoped diagrams — incorrect parsing drops patterns from diagrams or includes unrelated ones. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Process metadata is extracted from feature tags | A feature file must have both @pattern and @phase tags to produce valid process metadata; missing either yields null. | Pattern name and phase are the minimum identifiers for placing a pattern in the roadmap — without both, the pattern cannot be tracked. | +| Deliverables are extracted from Background tables | Deliverables are sourced exclusively from Background tables; features without a Background produce an empty deliverable list. | The Background table is the single source of truth for deliverable tracking — extracting from other locations would create ambiguity. | +| Code and feature patterns are combined into dual-source patterns | A combined pattern is produced only when both a code stub and a feature file exist for the same pattern name; unmatched sources are tracked separately as code-only or feature-only. | Dual-source combination ensures documentation reflects both implementation intent (code) and specification (Gherkin) — mismatches signal inconsistency. | +| Dual-source results are validated for consistency | Cross-source validation reports errors for metadata mismatches and warnings for orphaned patterns that are still in roadmap status. | Inconsistencies between code stubs and feature files indicate drift — errors catch conflicts while warnings surface missing counterparts that may be intentional. | +| Include tags are extracted from Gherkin feature tags | Include tags are parsed as comma-separated values; absence of the tag means the pattern has no includes. | Include tags control which patterns appear in scoped diagrams — incorrect parsing drops patterns from diagrams or includes unrelated ones. | ### Extends Tag Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Extends tag is defined in taxonomy registry | The extends tag must exist in the taxonomy registry with single-value format. | Without a registry definition, the data-driven AST parser cannot discover or extract the extends tag from source files. | -| Patterns can extend exactly one base pattern | A pattern may extend at most one base pattern, enforced by single-value tag format. | Single inheritance avoids diamond-problem ambiguity in pattern generalization hierarchies. | -| Transform builds extendedBy reverse lookup | The transform must compute an extendedBy reverse index so base patterns know which patterns extend them. | Without the reverse index, base patterns cannot discover their extensions, breaking generalization hierarchy navigation in generated docs. | -| Linter detects circular inheritance chains | Circular inheritance chains (direct or transitive) must be detected and reported as errors. | Circular extends relationships create infinite resolution loops and undefined behavior. | +| Rule | Invariant | Rationale | +| -------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Extends tag is defined in taxonomy registry | The extends tag must exist in the taxonomy registry with single-value format. | Without a registry definition, the data-driven AST parser cannot discover or extract the extends tag from source files. | +| Patterns can extend exactly one base pattern | A pattern may extend at most one base pattern, enforced by single-value tag format. | Single inheritance avoids diamond-problem ambiguity in pattern generalization hierarchies. | +| Transform builds extendedBy reverse lookup | The transform must compute an extendedBy reverse index so base patterns know which patterns extend them. | Without the reverse index, base patterns cannot discover their extensions, breaking generalization hierarchy navigation in generated docs. | +| Linter detects circular inheritance chains | Circular inheritance chains (direct or transitive) must be detected and reported as errors. | Circular extends relationships create infinite resolution loops and undefined behavior. | ### Extraction Pipeline Enhancements Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Function signatures surface full parameter types in ExportInfo | ExportInfo.signature shows full parameter types and return type instead of the placeholder value. | Reference documentation renders signatures verbatim — placeholder values instead of real types make the API docs unusable for consumers. | -| Property-level JSDoc preserves full multi-line content | Property-level JSDoc preserves full multi-line content without first-line truncation. | Truncated property descriptions lose important behavioral details (defaults, units, constraints) that consumers rely on when integrating with the API. | -| Param returns and throws tags are extracted from function JSDoc | JSDoc param, returns, and throws tags are extracted and stored on ExtractedShape for function-kind shapes. | Function reference docs require parameter, return, and exception documentation — missing extraction means consumers must read source code instead of generated docs. | -| Auto-shape discovery extracts all exported types via wildcard | When extract-shapes tag value is the wildcard character, all exported declarations are extracted without listing names. | Requiring explicit names for every export creates maintenance burden and stale annotations — wildcard discovery keeps shape docs in sync as exports are added or removed. | +| Rule | Invariant | Rationale | +| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Function signatures surface full parameter types in ExportInfo | ExportInfo.signature shows full parameter types and return type instead of the placeholder value. | Reference documentation renders signatures verbatim — placeholder values instead of real types make the API docs unusable for consumers. | +| Property-level JSDoc preserves full multi-line content | Property-level JSDoc preserves full multi-line content without first-line truncation. | Truncated property descriptions lose important behavioral details (defaults, units, constraints) that consumers rely on when integrating with the API. | +| Param returns and throws tags are extracted from function JSDoc | JSDoc param, returns, and throws tags are extracted and stored on ExtractedShape for function-kind shapes. | Function reference docs require parameter, return, and exception documentation — missing extraction means consumers must read source code instead of generated docs. | +| Auto-shape discovery extracts all exported types via wildcard | When extract-shapes tag value is the wildcard character, all exported declarations are extracted without listing names. | Requiring explicit names for every export creates maintenance burden and stale annotations — wildcard discovery keeps shape docs in sync as exports are added or removed. | ### File Discovery -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Glob patterns match TypeScript source files | findFilesToScan must return absolute paths for all files matching the configured glob patterns. | Downstream pipeline stages (AST parser, extractor) require absolute paths to read file contents; relative paths would break when baseDir differs from cwd. | -| Default exclusions filter non-source files | node_modules, dist, .test.ts, .spec.ts, and .d.ts files must be excluded by default without explicit configuration. | Scanning generated output (dist), third-party code (node_modules), or test files would produce false positives in the pattern registry and waste processing time. | -| Custom configuration extends discovery behavior | User-provided exclude patterns must be applied in addition to (not replacing) the default exclusions. | Replacing defaults with custom patterns would silently re-include node_modules and dist, causing false positives in the pattern registry. | +| Rule | Invariant | Rationale | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Glob patterns match TypeScript source files | findFilesToScan must return absolute paths for all files matching the configured glob patterns. | Downstream pipeline stages (AST parser, extractor) require absolute paths to read file contents; relative paths would break when baseDir differs from cwd. | +| Default exclusions filter non-source files | node_modules, dist, .test.ts, .spec.ts, and .d.ts files must be excluded by default without explicit configuration. | Scanning generated output (dist), third-party code (node_modules), or test files would produce false positives in the pattern registry and waste processing time. | +| Custom configuration extends discovery behavior | User-provided exclude patterns must be applied in addition to (not replacing) the default exclusions. | Replacing defaults with custom patterns would silently re-include node_modules and dist, causing false positives in the pattern registry. | ### Gherkin Ast Parser -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Successful feature file parsing extracts complete metadata | A valid feature file must produce a ParsedFeature with name, description, language, tags, and all nested scenarios with their steps. | Downstream generators (timeline, business rules) depend on complete AST extraction; missing fields cause silent gaps in generated documentation. | -| Invalid Gherkin produces structured errors | Malformed or incomplete Gherkin input must return a Result.err with the source file path and a descriptive error message. | The scanner processes many feature files in batch; structured errors allow graceful degradation and per-file error reporting rather than aborting the entire scan. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Successful feature file parsing extracts complete metadata | A valid feature file must produce a ParsedFeature with name, description, language, tags, and all nested scenarios with their steps. | Downstream generators (timeline, business rules) depend on complete AST extraction; missing fields cause silent gaps in generated documentation. | +| Invalid Gherkin produces structured errors | Malformed or incomplete Gherkin input must return a Result.err with the source file path and a descriptive error message. | The scanner processes many feature files in batch; structured errors allow graceful degradation and per-file error reporting rather than aborting the entire scan. | ### Gherkin Rules Support -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Rules flow through the entire pipeline without data loss | Rule data (name, description, tags, scenarios) must be preserved through every pipeline stage from parser to ExtractedPattern. | Any data loss at an intermediate stage makes rule content invisible to all downstream generators, silently producing incomplete documentation. The @cucumber/gherkin parser extracts Rules natively. Our pipeline must preserve this data through scanner, extractor, and into ExtractedPattern so generators can access rule names, descriptions, and nested scenarios. | -| Generators can render rules as business documentation | Rules must render as human-readable Business Rules sections, not as raw Given/When/Then syntax. | Business stakeholders cannot interpret Gherkin step definitions; without rendering transformation, feature files remain developer-only artifacts. Business stakeholders see rule names and descriptions as "Business Rules" sections, not Given/When/Then syntax. This enables human-readable PRDs from the same files used for test execution. | -| Custom content blocks render in acceptance criteria | DataTables and DocStrings attached to steps must appear in generated documentation as Markdown tables and fenced code blocks respectively. | Without rendering custom content blocks, acceptance criteria lose the structured data and code examples that make them self-contained and verifiable. DataTables and DocStrings in steps should appear in generated documentation, providing structured data and code examples alongside step descriptions. | -| vitest-cucumber executes scenarios inside Rules | Scenarios nested inside Rule blocks must be executable by vitest-cucumber using the Rule() and RuleScenario() API. | If Rule-scoped scenarios cannot execute, adding Rule blocks to feature files would break the test suite, forcing a choice between documentation structure and test coverage. Test execution must work for scenarios inside Rule blocks. Use Rule() function with RuleScenario() instead of Scenario(). | +| Rule | Invariant | Rationale | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Rules flow through the entire pipeline without data loss | Rule data (name, description, tags, scenarios) must be preserved through every pipeline stage from parser to ExtractedPattern. | Any data loss at an intermediate stage makes rule content invisible to all downstream generators, silently producing incomplete documentation. The @cucumber/gherkin parser extracts Rules natively. Our pipeline must preserve this data through scanner, extractor, and into ExtractedPattern so generators can access rule names, descriptions, and nested scenarios. | +| Generators can render rules as business documentation | Rules must render as human-readable Business Rules sections, not as raw Given/When/Then syntax. | Business stakeholders cannot interpret Gherkin step definitions; without rendering transformation, feature files remain developer-only artifacts. Business stakeholders see rule names and descriptions as "Business Rules" sections, not Given/When/Then syntax. This enables human-readable PRDs from the same files used for test execution. | +| Custom content blocks render in acceptance criteria | DataTables and DocStrings attached to steps must appear in generated documentation as Markdown tables and fenced code blocks respectively. | Without rendering custom content blocks, acceptance criteria lose the structured data and code examples that make them self-contained and verifiable. DataTables and DocStrings in steps should appear in generated documentation, providing structured data and code examples alongside step descriptions. | +| vitest-cucumber executes scenarios inside Rules | Scenarios nested inside Rule blocks must be executable by vitest-cucumber using the Rule() and RuleScenario() API. | If Rule-scoped scenarios cannot execute, adding Rule blocks to feature files would break the test suite, forcing a choice between documentation structure and test coverage. Test execution must work for scenarios inside Rule blocks. Use Rule() function with RuleScenario() instead of Scenario(). | ### Implements Tag Processing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Implements tag is defined in taxonomy registry | The implements tag must exist in the taxonomy registry with CSV format. | Without a registry definition, the data-driven AST parser cannot discover or extract the implements tag from source files. | -| Files can implement a single pattern | The AST parser must extract a single implements value and preserve it through the extraction pipeline. | Lost implements values sever the link between implementation files and their roadmap specs, breaking traceability. | -| Files can implement multiple patterns using CSV format | The AST parser must split CSV implements values into individual pattern references with whitespace trimming. | Unsplit or untrimmed CSV values produce invalid pattern references that fail relationship index lookups. | -| Transform builds implementedBy reverse lookup | The transform must compute an implementedBy reverse index so spec patterns know which files implement them. | Without the reverse index, roadmap specs cannot discover their implementation files, breaking traceability and DoD validation. | -| Schemas validate implements field correctly | The Zod schemas must accept implements and implementedBy fields with correct array-of-string types. | Schema rejection of valid implements/implementedBy values causes runtime parse failures that silently drop traceability links. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | +| Implements tag is defined in taxonomy registry | The implements tag must exist in the taxonomy registry with CSV format. | Without a registry definition, the data-driven AST parser cannot discover or extract the implements tag from source files. | +| Files can implement a single pattern | The AST parser must extract a single implements value and preserve it through the extraction pipeline. | Lost implements values sever the link between implementation files and their roadmap specs, breaking traceability. | +| Files can implement multiple patterns using CSV format | The AST parser must split CSV implements values into individual pattern references with whitespace trimming. | Unsplit or untrimmed CSV values produce invalid pattern references that fail relationship index lookups. | +| Transform builds implementedBy reverse lookup | The transform must compute an implementedBy reverse index so spec patterns know which files implement them. | Without the reverse index, roadmap specs cannot discover their implementation files, breaking traceability and DoD validation. | +| Schemas validate implements field correctly | The Zod schemas must accept implements and implementedBy fields with correct array-of-string types. | Schema rejection of valid implements/implementedBy values causes runtime parse failures that silently drop traceability links. | ### Layer Inference Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Timeline layer is detected from /timeline/ directory segments | Any feature file path containing a /timeline/ directory segment is classified as timeline layer. | Timeline features track phased delivery progress and must be grouped separately for roadmap generation and phase filtering. | -| Domain layer is detected from business context directory segments | Feature files in /deciders/, /orders/, or /inventory/ directories are classified as domain layer. | Domain features define core business rules and must be distinguished from infrastructure tests for accurate coverage reporting. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Timeline layer is detected from /timeline/ directory segments | Any feature file path containing a /timeline/ directory segment is classified as timeline layer. | Timeline features track phased delivery progress and must be grouped separately for roadmap generation and phase filtering. | +| Domain layer is detected from business context directory segments | Feature files in /deciders/, /orders/, or /inventory/ directories are classified as domain layer. | Domain features define core business rules and must be distinguished from infrastructure tests for accurate coverage reporting. | | Integration layer is detected and takes priority over domain directories | Paths containing /integration-features/ or /integration/ are classified as integration, even when they also contain domain directory names. | Integration tests nested under domain directories (e.g., /integration/orders/) would be misclassified as domain without explicit priority, skewing layer coverage metrics. | -| E2E layer is detected from /e2e/ directory segments | Any feature file path containing an /e2e/ directory segment is classified as e2e layer. | E2E tests require separate execution infrastructure and longer timeouts; misclassification would mix them into faster test suites. | -| Component layer is detected from tool-specific directory segments | Feature files in /scanner/ or /lint/ directories are classified as component layer. | Tool-specific features test internal pipeline stages and must be isolated from business domain and integration layers in documentation grouping. | -| Unknown layer is the fallback for unclassified paths | Any feature file path that does not match a known layer pattern is classified as unknown. | Silently dropping unclassified features would create invisible gaps in test coverage; the unknown fallback ensures every feature is accounted for. | -| Path normalization handles cross-platform and case differences | Layer inference produces correct results regardless of path separators, case, or absolute vs relative paths. | The consumer monorepo runs on multiple platforms; platform-dependent classification would produce inconsistent documentation across developer machines and CI. | -| FEATURE_LAYERS constant provides validated layer enumeration | FEATURE_LAYERS is a readonly array containing exactly all 6 valid layer values. | Consumers iterate over FEATURE_LAYERS for exhaustive layer handling; a mutable or incomplete array would cause missed layers at runtime. | +| E2E layer is detected from /e2e/ directory segments | Any feature file path containing an /e2e/ directory segment is classified as e2e layer. | E2E tests require separate execution infrastructure and longer timeouts; misclassification would mix them into faster test suites. | +| Component layer is detected from tool-specific directory segments | Feature files in /scanner/ or /lint/ directories are classified as component layer. | Tool-specific features test internal pipeline stages and must be isolated from business domain and integration layers in documentation grouping. | +| Unknown layer is the fallback for unclassified paths | Any feature file path that does not match a known layer pattern is classified as unknown. | Silently dropping unclassified features would create invisible gaps in test coverage; the unknown fallback ensures every feature is accounted for. | +| Path normalization handles cross-platform and case differences | Layer inference produces correct results regardless of path separators, case, or absolute vs relative paths. | The consumer monorepo runs on multiple platforms; platform-dependent classification would produce inconsistent documentation across developer machines and CI. | +| FEATURE_LAYERS constant provides validated layer enumeration | FEATURE_LAYERS is a readonly array containing exactly all 6 valid layer values. | Consumers iterate over FEATURE_LAYERS for exhaustive layer handling; a mutable or incomplete array would cause missed layers at runtime. | ### Pattern Relationship Model -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Code files declare pattern realization via implements tag | Files with `@libar-docs-implements:PatternName,OtherPattern` are linked to the specified patterns without causing conflicts. Pattern definitions remain in roadmap specs; implementation files provide supplementary metadata. Multiple files can implement the same pattern, and one file can implement multiple patterns. | This mirrors UML's "realization" relationship where a class implements an interface. Code realizes the specification. Direction is code→spec (backward link). CSV format allows a single implementation file to realize multiple patterns when implementing a pattern family (e.g., durability primitives). | -| Pattern inheritance uses extends relationship tag | Files with `@libar-docs-extends:BasePattern` declare that they extend another pattern's capabilities. This is a generalization relationship where the extending pattern is a specialization of the base pattern. | Pattern families exist where specialized patterns build on base patterns. For example, `ReactiveProjections` extends `ProjectionCategories`. The extends relationship enables inheritance-based documentation and validates pattern hierarchy. | -| Technical dependencies use directed relationship tags | `@libar-docs-uses` declares outbound dependencies (what this pattern depends on). `@libar-docs-used-by` declares inbound dependencies (what depends on this pattern). Both are CSV format. | These represent technical coupling between patterns. The distinction matters for impact analysis: changing a pattern affects its `used-by` consumers but not its `uses` dependencies. | -| Roadmap sequencing uses ordering relationship tags | `@libar-docs-depends-on` declares what must be completed first (roadmap sequencing). `@libar-docs-enables` declares what this unlocks when completed. These are planning relationships, not technical dependencies. | Sequencing is about order of work, not runtime coupling. A pattern may depend on another being complete without using its code. | -| Cross-tier linking uses traceability tags (PDR-007) | `@libar-docs-executable-specs` on roadmap specs points to test locations. `@libar-docs-roadmap-spec` on package specs points back to the pattern. These create bidirectional traceability. | Two-tier architecture (PDR-007) separates planning specs from executable tests. Traceability tags maintain the connection for navigation and completeness checking. | -| Epic/Phase/Task hierarchy uses parent-child relationships | `@libar-docs-level` declares the hierarchy tier (epic, phase, task). `@libar-docs-parent` links to the containing pattern. This enables rollup progress tracking. | Large initiatives decompose into phases and tasks. The hierarchy allows progress aggregation (e.g., "Epic 80% complete based on child phases"). | -| All relationships appear in generated documentation | The PATTERNS.md dependency graph renders all relationship types with distinct visual styles. Pattern detail pages list all related artifacts grouped by relationship type. | Visualization makes the relationship model accessible. Different arrow styles distinguish relationship semantics at a glance. | -| Linter detects relationship violations | The pattern linter validates that all relationship targets exist, implements files don't have pattern tags, and bidirectional links are consistent. | Broken relationships cause confusion and incorrect generated docs. Early detection during linting prevents propagation of errors. | +| Pattern inheritance uses extends relationship tag | Files with `@libar-docs-extends:BasePattern` declare that they extend another pattern's capabilities. This is a generalization relationship where the extending pattern is a specialization of the base pattern. | Pattern families exist where specialized patterns build on base patterns. For example, `ReactiveProjections` extends `ProjectionCategories`. The extends relationship enables inheritance-based documentation and validates pattern hierarchy. | +| Technical dependencies use directed relationship tags | `@libar-docs-uses` declares outbound dependencies (what this pattern depends on). `@libar-docs-used-by` declares inbound dependencies (what depends on this pattern). Both are CSV format. | These represent technical coupling between patterns. The distinction matters for impact analysis: changing a pattern affects its `used-by` consumers but not its `uses` dependencies. | +| Roadmap sequencing uses ordering relationship tags | `@libar-docs-depends-on` declares what must be completed first (roadmap sequencing). `@libar-docs-enables` declares what this unlocks when completed. These are planning relationships, not technical dependencies. | Sequencing is about order of work, not runtime coupling. A pattern may depend on another being complete without using its code. | +| Cross-tier linking uses traceability tags (PDR-007) | `@libar-docs-executable-specs` on roadmap specs points to test locations. `@libar-docs-roadmap-spec` on package specs points back to the pattern. These create bidirectional traceability. | Two-tier architecture (PDR-007) separates planning specs from executable tests. Traceability tags maintain the connection for navigation and completeness checking. | +| Epic/Phase/Task hierarchy uses parent-child relationships | `@libar-docs-level` declares the hierarchy tier (epic, phase, task). `@libar-docs-parent` links to the containing pattern. This enables rollup progress tracking. | Large initiatives decompose into phases and tasks. The hierarchy allows progress aggregation (e.g., "Epic 80% complete based on child phases"). | +| All relationships appear in generated documentation | The PATTERNS.md dependency graph renders all relationship types with distinct visual styles. Pattern detail pages list all related artifacts grouped by relationship type. | Visualization makes the relationship model accessible. Different arrow styles distinguish relationship semantics at a glance. | +| Linter detects relationship violations | The pattern linter validates that all relationship targets exist, implements files don't have pattern tags, and bidirectional links are consistent. | Broken relationships cause confusion and incorrect generated docs. Early detection during linting prevents propagation of errors. | ### Pattern Tag Extraction -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Single value tags produce scalar metadata fields | Each single-value tag (pattern, phase, status, brief) maps to exactly one metadata field with the correct type. | Incorrect type coercion (e.g., phase as string instead of number) causes downstream pipeline failures in filtering and sorting. | -| Array value tags accumulate into list metadata fields | Tags for depends-on and enables split comma-separated values and accumulate across multiple tag occurrences. | Missing a dependency value silently breaks the dependency graph, causing incorrect build ordering and orphaned pattern references. | -| Category tags are colon-free tags filtered against known non-categories | Tags without colons become categories, except known non-category tags (acceptance-criteria, happy-path) and the libar-docs opt-in marker. | Including test-control tags (acceptance-criteria, happy-path) as categories pollutes the pattern taxonomy with non-semantic values. | -| Complex tag lists produce fully populated metadata | All tag types (scalar, array, category) are correctly extracted from a single mixed tag list. | Real feature files combine many tag types; extraction must handle all types simultaneously without interference between parsers. | -| Edge cases produce safe defaults | Empty or invalid inputs produce empty metadata or omit invalid fields rather than throwing errors. | Throwing on malformed tags would abort extraction for the entire file, losing valid metadata from well-formed tags. | -| Convention tags support CSV values with whitespace trimming | Convention tags split comma-separated values and trim whitespace from each value. | Untrimmed whitespace creates distinct values for the same convention, causing false negatives in convention-based filtering and validation. | +| Rule | Invariant | Rationale | +| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Single value tags produce scalar metadata fields | Each single-value tag (pattern, phase, status, brief) maps to exactly one metadata field with the correct type. | Incorrect type coercion (e.g., phase as string instead of number) causes downstream pipeline failures in filtering and sorting. | +| Array value tags accumulate into list metadata fields | Tags for depends-on and enables split comma-separated values and accumulate across multiple tag occurrences. | Missing a dependency value silently breaks the dependency graph, causing incorrect build ordering and orphaned pattern references. | +| Category tags are colon-free tags filtered against known non-categories | Tags without colons become categories, except known non-category tags (acceptance-criteria, happy-path) and the libar-docs opt-in marker. | Including test-control tags (acceptance-criteria, happy-path) as categories pollutes the pattern taxonomy with non-semantic values. | +| Complex tag lists produce fully populated metadata | All tag types (scalar, array, category) are correctly extracted from a single mixed tag list. | Real feature files combine many tag types; extraction must handle all types simultaneously without interference between parsers. | +| Edge cases produce safe defaults | Empty or invalid inputs produce empty metadata or omit invalid fields rather than throwing errors. | Throwing on malformed tags would abort extraction for the entire file, losing valid metadata from well-formed tags. | +| Convention tags support CSV values with whitespace trimming | Convention tags split comma-separated values and trim whitespace from each value. | Untrimmed whitespace creates distinct values for the same convention, causing false negatives in convention-based filtering and validation. | | Registry-driven extraction handles enums, transforms, and value constraints | Tags defined in the registry use data-driven extraction with enum validation, CSV accumulation, value transforms, and constraint checking. | Hard-coded if/else branches for each tag type cannot scale; registry-driven extraction ensures new tags are supported by configuration, not code changes. | ### Scanner Core -| Rule | Invariant | Rationale | -| --- | --- | --- | -| scanPatterns extracts directives from TypeScript files | Every file with a valid opt-in marker and JSDoc directives produces a complete ScannedFile with tags, description, examples, and exports. | Downstream generators depend on complete directive data; partial extraction causes silent documentation gaps across the monorepo. | -| scanPatterns collects errors without aborting | A parse failure in one file never prevents other files from being scanned; the result is always Ok with errors collected separately. | In a monorepo with hundreds of files, a single syntax error must not block the entire documentation pipeline. | -| Pattern matching and exclusion filtering | Glob patterns control file discovery and exclusion patterns remove matched files before scanning. | Without exclusion filtering, internal directories and generated files would pollute the pattern registry with false positives and slow down scanning. | -| File opt-in requirement gates scanning | Only files containing a standalone @libar-docs marker (not @libar-docs-*) are eligible for directive extraction. | Without opt-in gating, every TypeScript file in the monorepo would be parsed, wasting processing time on files that have no documentation directives. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| scanPatterns extracts directives from TypeScript files | Every file with a valid opt-in marker and JSDoc directives produces a complete ScannedFile with tags, description, examples, and exports. | Downstream generators depend on complete directive data; partial extraction causes silent documentation gaps across the monorepo. | +| scanPatterns collects errors without aborting | A parse failure in one file never prevents other files from being scanned; the result is always Ok with errors collected separately. | In a monorepo with hundreds of files, a single syntax error must not block the entire documentation pipeline. | +| Pattern matching and exclusion filtering | Glob patterns control file discovery and exclusion patterns remove matched files before scanning. | Without exclusion filtering, internal directories and generated files would pollute the pattern registry with false positives and slow down scanning. | +| File opt-in requirement gates scanning | Only files containing a standalone @libar-docs marker (not @libar-docs-\*) are eligible for directive extraction. | Without opt-in gating, every TypeScript file in the monorepo would be parsed, wasting processing time on files that have no documentation directives. | ### Shape Extraction -| Rule | Invariant | Rationale | -| --- | --- | --- | -| extract-shapes tag is defined in registry | The `extract-shapes` tag must exist with CSV format to list multiple type names for extraction. | Without a CSV-format tag, there is no mechanism to specify which type names to extract, and the extractor cannot discover shapes from source files. | -| Interfaces are extracted from TypeScript AST | When `@libar-docs-extract-shapes` lists an interface name, the extractor must find and extract the complete interface definition including JSDoc comments, generics, and extends clauses. | Partial extraction (missing generics, JSDoc, or extends clauses) produces incomplete API documentation that misleads consumers about a type's actual contract. | -| Type aliases are extracted from TypeScript AST | Type aliases (including union types, intersection types, and mapped types) are extracted when listed in extract-shapes. | Type aliases define domain vocabulary (status enums, branded types, utility types) that consumers need to understand API signatures; omitting them leaves gaps in generated documentation. | -| Enums are extracted from TypeScript AST | Both string and numeric enums are extracted with their complete member definitions. | Enum members define the closed set of valid values for a type; extracting only the enum name without its members provides no useful information to documentation consumers. | -| Function signatures are extracted (body omitted) | When a function name is listed in extract-shapes, only the signature (parameters, return type, generics) is extracted. The function body is replaced with ellipsis for documentation purposes. | Including implementation bodies in generated documentation exposes internal logic, bloats output size, and creates a maintenance burden when internals change without API changes. | -| Multiple shapes are extracted in specified order | When multiple shapes are listed, they appear in the documentation in the order specified in the tag, not source order. | Authors intentionally order shapes for progressive disclosure (e.g., output before input); using source order would break the author's intended documentation narrative. | -| Extracted shapes render as fenced code blocks | Codecs render extracted shapes as TypeScript fenced code blocks, grouped under an "API Types" or similar section. | Rendering shapes as plain text or without language hints prevents syntax highlighting and makes API types unreadable in generated markdown documentation. | -| Shapes can reference types from imports | Extracted shapes may reference types from imports. The extractor does NOT resolve imports - it extracts the shape as-is. Consumers understand that referenced types are defined elsewhere. | Resolving imports would require full dependency graph traversal across the codebase, dramatically increasing complexity and coupling the extractor to the project's module structure. | -| Overloaded function signatures are all extracted | When a function has multiple overload signatures, all signatures are extracted together as they represent the complete API. | Extracting only one overload signature hides valid call patterns from consumers, leading to incorrect usage assumptions about the function's capabilities. | -| Shape rendering supports grouping options | Codecs can render shapes grouped in a single code block or as separate code blocks, depending on detail level. | Compact claude-md modules need grouped blocks to minimize token usage, while detailed human docs benefit from separate blocks with individual JSDoc annotations. | +| Rule | Invariant | Rationale | +| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| extract-shapes tag is defined in registry | The `extract-shapes` tag must exist with CSV format to list multiple type names for extraction. | Without a CSV-format tag, there is no mechanism to specify which type names to extract, and the extractor cannot discover shapes from source files. | +| Interfaces are extracted from TypeScript AST | When `@libar-docs-extract-shapes` lists an interface name, the extractor must find and extract the complete interface definition including JSDoc comments, generics, and extends clauses. | Partial extraction (missing generics, JSDoc, or extends clauses) produces incomplete API documentation that misleads consumers about a type's actual contract. | +| Type aliases are extracted from TypeScript AST | Type aliases (including union types, intersection types, and mapped types) are extracted when listed in extract-shapes. | Type aliases define domain vocabulary (status enums, branded types, utility types) that consumers need to understand API signatures; omitting them leaves gaps in generated documentation. | +| Enums are extracted from TypeScript AST | Both string and numeric enums are extracted with their complete member definitions. | Enum members define the closed set of valid values for a type; extracting only the enum name without its members provides no useful information to documentation consumers. | +| Function signatures are extracted (body omitted) | When a function name is listed in extract-shapes, only the signature (parameters, return type, generics) is extracted. The function body is replaced with ellipsis for documentation purposes. | Including implementation bodies in generated documentation exposes internal logic, bloats output size, and creates a maintenance burden when internals change without API changes. | +| Multiple shapes are extracted in specified order | When multiple shapes are listed, they appear in the documentation in the order specified in the tag, not source order. | Authors intentionally order shapes for progressive disclosure (e.g., output before input); using source order would break the author's intended documentation narrative. | +| Extracted shapes render as fenced code blocks | Codecs render extracted shapes as TypeScript fenced code blocks, grouped under an "API Types" or similar section. | Rendering shapes as plain text or without language hints prevents syntax highlighting and makes API types unreadable in generated markdown documentation. | +| Shapes can reference types from imports | Extracted shapes may reference types from imports. The extractor does NOT resolve imports - it extracts the shape as-is. Consumers understand that referenced types are defined elsewhere. | Resolving imports would require full dependency graph traversal across the codebase, dramatically increasing complexity and coupling the extractor to the project's module structure. | +| Overloaded function signatures are all extracted | When a function has multiple overload signatures, all signatures are extracted together as they represent the complete API. | Extracting only one overload signature hides valid call patterns from consumers, leading to incorrect usage assumptions about the function's capabilities. | +| Shape rendering supports grouping options | Codecs can render shapes grouped in a single code block or as separate code blocks, depending on detail level. | Compact claude-md modules need grouped blocks to minimize token usage, while detailed human docs benefit from separate blocks with individual JSDoc annotations. | ### Shape Extraction Rendering Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Multiple shapes are extracted in specified order | Extracted shapes appear in the order specified by the tag list, not in source file declaration order. | Documentation consumers rely on tag-specified ordering for consistent, predictable layout regardless of how source files are organized. | -| Extracted shapes render as fenced code blocks | Every extracted shape renders as a fenced TypeScript code block in the markdown output. | Fenced code blocks provide syntax highlighting and preserve type definition formatting, which is essential for readable API documentation. | -| Imported and re-exported shapes are tracked separately | Shapes resolved via import or re-export statements are classified distinctly from locally declared shapes. | Silently extracting imported types as if they were local declarations would produce duplicate or misleading documentation, since the canonical definition lives in another file. | -| Invalid TypeScript produces error result | Malformed TypeScript source returns an error Result instead of throwing or producing partial shapes. | Unhandled parse exceptions would crash the pipeline mid-run, preventing all subsequent files from being processed. | -| Shape rendering supports grouping options | The `groupInSingleBlock` option controls whether shapes render in one combined code fence or in separate per-shape code fences. | Different documentation layouts require different grouping strategies; a single block provides compact overviews while separate blocks allow per-type commentary. | -| Annotation tags are stripped from extracted JSDoc while preserving standard tags | Extracted shapes never contain @libar-docs-* annotation lines in their jsDoc field. | Shape JSDoc is rendered in documentation output. Annotation tags are metadata for the extraction pipeline, not user-visible documentation content. | -| Large source files are rejected to prevent memory exhaustion | Source files exceeding 5MB are rejected before parsing begins. | Feeding unbounded input to the TypeScript parser risks out-of-memory crashes that would halt the entire extraction pipeline. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Multiple shapes are extracted in specified order | Extracted shapes appear in the order specified by the tag list, not in source file declaration order. | Documentation consumers rely on tag-specified ordering for consistent, predictable layout regardless of how source files are organized. | +| Extracted shapes render as fenced code blocks | Every extracted shape renders as a fenced TypeScript code block in the markdown output. | Fenced code blocks provide syntax highlighting and preserve type definition formatting, which is essential for readable API documentation. | +| Imported and re-exported shapes are tracked separately | Shapes resolved via import or re-export statements are classified distinctly from locally declared shapes. | Silently extracting imported types as if they were local declarations would produce duplicate or misleading documentation, since the canonical definition lives in another file. | +| Invalid TypeScript produces error result | Malformed TypeScript source returns an error Result instead of throwing or producing partial shapes. | Unhandled parse exceptions would crash the pipeline mid-run, preventing all subsequent files from being processed. | +| Shape rendering supports grouping options | The `groupInSingleBlock` option controls whether shapes render in one combined code fence or in separate per-shape code fences. | Different documentation layouts require different grouping strategies; a single block provides compact overviews while separate blocks allow per-type commentary. | +| Annotation tags are stripped from extracted JSDoc while preserving standard tags | Extracted shapes never contain @libar-docs-\* annotation lines in their jsDoc field. | Shape JSDoc is rendered in documentation output. Annotation tags are metadata for the extraction pipeline, not user-visible documentation content. | +| Large source files are rejected to prevent memory exhaustion | Source files exceeding 5MB are rejected before parsing begins. | Feeding unbounded input to the TypeScript parser risks out-of-memory crashes that would halt the entire extraction pipeline. | ### Shape Extraction Types Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| extract-shapes tag exists in registry with CSV format | The `extract-shapes` tag must be registered with CSV format so multiple shape names can be specified in a single annotation. | Without CSV format registration, the tag parser cannot split comma-separated shape lists, causing only the first shape to be extracted. | -| Interfaces are extracted from TypeScript AST | Every named interface declaration in a TypeScript source file must be extractable as a shape with kind `interface`, including generics, extends clauses, and JSDoc. | Interfaces are the primary API surface for TypeScript libraries; failing to extract them leaves the most important type contracts undocumented. | -| Property-level JSDoc is extracted for interface properties | Property-level JSDoc must be attributed only to the immediately adjacent property, never inherited from the parent interface declaration. | Misattributing interface-level JSDoc to the first property produces incorrect per-field documentation and misleads consumers about individual property semantics. The extractor uses strict adjacency (gap = 1 line) to prevent interface-level JSDoc from being misattributed to the first property. | -| Type aliases are extracted from TypeScript AST | Union types, mapped types, and conditional types must all be extractable as shapes with kind `type`, preserving their full type expression. | Type aliases encode domain constraints (e.g., discriminated unions, mapped utilities) that are essential for API documentation; omitting them hides the type-level design. | -| Enums are extracted from TypeScript AST | Both regular and const enums must be extractable as shapes with kind `enum`, including their member values. | Enums define finite value sets used in validation and serialization; missing them from documentation forces consumers to read source code to discover valid values. | -| Function signatures are extracted with body omitted | Extracted function shapes must include the full signature (name, parameters, return type, async modifier) but never the implementation body. | Including function bodies in documentation exposes implementation details, inflates output size, and creates a maintenance burden when internals change without signature changes. | -| Const declarations are extracted from TypeScript AST | Const declarations must be extractable as shapes with kind `const`, whether or not they carry an explicit type annotation. | Constants define configuration defaults, version strings, and sentinel values that consumers depend on; excluding them creates documentation gaps for public API surface. | -| Non-exported shapes are extractable | Shape extraction must succeed for declarations regardless of export status, with the `exported` flag accurately reflecting visibility. | Internal types often define the core domain model; restricting extraction to exports only would omit types that are essential for understanding module internals. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| extract-shapes tag exists in registry with CSV format | The `extract-shapes` tag must be registered with CSV format so multiple shape names can be specified in a single annotation. | Without CSV format registration, the tag parser cannot split comma-separated shape lists, causing only the first shape to be extracted. | +| Interfaces are extracted from TypeScript AST | Every named interface declaration in a TypeScript source file must be extractable as a shape with kind `interface`, including generics, extends clauses, and JSDoc. | Interfaces are the primary API surface for TypeScript libraries; failing to extract them leaves the most important type contracts undocumented. | +| Property-level JSDoc is extracted for interface properties | Property-level JSDoc must be attributed only to the immediately adjacent property, never inherited from the parent interface declaration. | Misattributing interface-level JSDoc to the first property produces incorrect per-field documentation and misleads consumers about individual property semantics. The extractor uses strict adjacency (gap = 1 line) to prevent interface-level JSDoc from being misattributed to the first property. | +| Type aliases are extracted from TypeScript AST | Union types, mapped types, and conditional types must all be extractable as shapes with kind `type`, preserving their full type expression. | Type aliases encode domain constraints (e.g., discriminated unions, mapped utilities) that are essential for API documentation; omitting them hides the type-level design. | +| Enums are extracted from TypeScript AST | Both regular and const enums must be extractable as shapes with kind `enum`, including their member values. | Enums define finite value sets used in validation and serialization; missing them from documentation forces consumers to read source code to discover valid values. | +| Function signatures are extracted with body omitted | Extracted function shapes must include the full signature (name, parameters, return type, async modifier) but never the implementation body. | Including function bodies in documentation exposes implementation details, inflates output size, and creates a maintenance burden when internals change without signature changes. | +| Const declarations are extracted from TypeScript AST | Const declarations must be extractable as shapes with kind `const`, whether or not they carry an explicit type annotation. | Constants define configuration defaults, version strings, and sentinel values that consumers depend on; excluding them creates documentation gaps for public API surface. | +| Non-exported shapes are extractable | Shape extraction must succeed for declarations regardless of export status, with the `exported` flag accurately reflecting visibility. | Internal types often define the core domain model; restricting extraction to exports only would omit types that are essential for understanding module internals. | ### Uses Tag Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Uses tag is defined in taxonomy registry | The uses and used-by tags must be registered in the taxonomy with CSV format and dependency-related purpose descriptions. | Without registry definitions, the data-driven AST parser cannot discover or extract these tags from source files. | -| Uses tag is extracted from TypeScript files | The AST parser must extract single and comma-separated uses values from TypeScript JSDoc annotations. | Missing or malformed uses extraction breaks runtime dependency tracking and produces incomplete relationship diagrams. | -| Used-by tag is extracted from TypeScript files | The AST parser must extract single and comma-separated used-by values from TypeScript JSDoc annotations. | Missing used-by extraction prevents reverse dependency lookups, leaving consumers unable to discover which patterns depend on them. | -| Uses relationships are stored in relationship index | All declared uses and usedBy relationships must be stored in the relationship index as explicitly declared entries. | Omitting relationships from the index causes dependency diagrams and impact-analysis queries to silently miss connections. | -| Schemas validate uses field correctly | DocDirective and RelationshipEntry schemas must accept uses and usedBy fields as valid CSV string values. | Schema rejection of valid uses/usedBy values causes runtime parse failures that silently drop relationship data. | +| Rule | Invariant | Rationale | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Uses tag is defined in taxonomy registry | The uses and used-by tags must be registered in the taxonomy with CSV format and dependency-related purpose descriptions. | Without registry definitions, the data-driven AST parser cannot discover or extract these tags from source files. | +| Uses tag is extracted from TypeScript files | The AST parser must extract single and comma-separated uses values from TypeScript JSDoc annotations. | Missing or malformed uses extraction breaks runtime dependency tracking and produces incomplete relationship diagrams. | +| Used-by tag is extracted from TypeScript files | The AST parser must extract single and comma-separated used-by values from TypeScript JSDoc annotations. | Missing used-by extraction prevents reverse dependency lookups, leaving consumers unable to discover which patterns depend on them. | +| Uses relationships are stored in relationship index | All declared uses and usedBy relationships must be stored in the relationship index as explicitly declared entries. | Omitting relationships from the index causes dependency diagrams and impact-analysis queries to silently miss connections. | +| Schemas validate uses field correctly | DocDirective and RelationshipEntry schemas must accept uses and usedBy fields as valid CSV string values. | Schema rejection of valid uses/usedBy values causes runtime parse failures that silently drop relationship data. | --- diff --git a/docs-live/product-areas/CONFIGURATION.md b/docs-live/product-areas/CONFIGURATION.md index 415ed7df..8f582385 100644 --- a/docs-live/product-areas/CONFIGURATION.md +++ b/docs-live/product-areas/CONFIGURATION.md @@ -155,13 +155,13 @@ interface DeliveryProcessConfig { } ```` -| Property | Description | -| --- | --- | -| tagPrefix | Tag prefix for directives (e.g., "@docs-" or "@libar-docs-") | -| fileOptInTag | File-level opt-in tag (e.g., "@docs" or "@libar-docs") | -| categories | Category definitions for pattern classification | -| metadataTags | Optional metadata tag definitions | -| contextInferenceRules | Optional context inference rules for auto-inferring bounded context from file paths. When provided, these rules are merged with the default rules. User-provided rules take precedence over defaults (applied first in the rule list). ```typescript contextInferenceRules: [ { pattern: 'packages/orders/**', context: 'orders' }, { pattern: 'packages/inventory/**', context: 'inventory' }, ] ``` | +| Property | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| tagPrefix | Tag prefix for directives (e.g., "@docs-" or "@libar-docs-") | +| fileOptInTag | File-level opt-in tag (e.g., "@docs" or "@libar-docs") | +| categories | Category definitions for pattern classification | +| metadataTags | Optional metadata tag definitions | +| contextInferenceRules | Optional context inference rules for auto-inferring bounded context from file paths. When provided, these rules are merged with the default rules. User-provided rules take precedence over defaults (applied first in the rule list). `typescript contextInferenceRules: [ { pattern: 'packages/orders/**', context: 'orders' }, { pattern: 'packages/inventory/**', context: 'inventory' }, ] ` | ### DeliveryProcessInstance (interface) @@ -180,10 +180,10 @@ interface DeliveryProcessInstance { } ``` -| Property | Description | -| --- | --- | -| registry | The fully configured tag registry | -| regexBuilders | Regex builders for tag detection | +| Property | Description | +| ------------- | --------------------------------- | +| registry | The fully configured tag registry | +| regexBuilders | Regex builders for tag detection | ### RegexBuilders (interface) @@ -211,9 +211,9 @@ interface RegexBuilders { } ``` -| Property | Description | -| --- | --- | -| fileOptInPattern | Pattern to match file-level opt-in (e.g., /** @docs *\/) | +| Property | Description | +| ---------------- | --------------------------------------------------------------- | +| fileOptInPattern | Pattern to match file-level opt-in (e.g., /\*_ @docs _\/) | | directivePattern | Pattern to match directives (e.g., @docs-pattern, @docs-status) | ### DeliveryProcessProjectConfig (interface) @@ -308,20 +308,20 @@ interface DeliveryProcessProjectConfig { } ``` -| Property | Description | -| --- | --- | -| preset | Use a preset taxonomy configuration | -| tagPrefix | Custom tag prefix (overrides preset, e.g., '@docs-') | -| fileOptInTag | Custom file opt-in tag (overrides preset, e.g., '@docs') | -| categories | Custom categories (replaces preset categories entirely) | -| sources | Source file glob configuration | -| output | Output configuration for generated docs | -| generators | Default generator names to run when CLI doesn't specify --generators | -| generatorOverrides | Per-generator source and output overrides | -| contextInferenceRules | Rules for auto-inferring bounded context from file paths | -| workflowPath | Path to custom workflow config JSON (relative to config file) | -| codecOptions | Per-codec options for fine-tuning document generation. Keys match codec names (e.g., 'business-rules', 'patterns'). Passed through to codec factories at generation time. | -| referenceDocConfigs | Reference document configurations for convention-based doc generation. Each config defines one reference document's content composition via convention tags, shape sources, behavior categories, and diagram scopes. When not specified, no reference generators are registered. Import `LIBAR_REFERENCE_CONFIGS` from the generators module to use the built-in set. | +| Property | Description | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| preset | Use a preset taxonomy configuration | +| tagPrefix | Custom tag prefix (overrides preset, e.g., '@docs-') | +| fileOptInTag | Custom file opt-in tag (overrides preset, e.g., '@docs') | +| categories | Custom categories (replaces preset categories entirely) | +| sources | Source file glob configuration | +| output | Output configuration for generated docs | +| generators | Default generator names to run when CLI doesn't specify --generators | +| generatorOverrides | Per-generator source and output overrides | +| contextInferenceRules | Rules for auto-inferring bounded context from file paths | +| workflowPath | Path to custom workflow config JSON (relative to config file) | +| codecOptions | Per-codec options for fine-tuning document generation. Keys match codec names (e.g., 'business-rules', 'patterns'). Passed through to codec factories at generation time. | +| referenceDocConfigs | Reference document configurations for convention-based doc generation. Each config defines one reference document's content composition via convention tags, shape sources, behavior categories, and diagram scopes. When not specified, no reference generators are registered. Import `LIBAR_REFERENCE_CONFIGS` from the generators module to use the built-in set. | ### SourcesConfig (interface) @@ -355,12 +355,12 @@ interface SourcesConfig { } ``` -| Property | Description | -| --- | --- | -| typescript | Glob patterns for TypeScript source files (replaces --input) | -| features | Glob patterns for Gherkin feature files (replaces --features). Includes both `.feature` and `.feature.md` files. | -| stubs | Glob patterns for design stub files. Stubs are TypeScript files that live outside `src/` (e.g., `delivery-process/stubs/`). Merged into TypeScript sources at resolution time. | -| exclude | Glob patterns to exclude from all scanning | +| Property | Description | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| typescript | Glob patterns for TypeScript source files (replaces --input) | +| features | Glob patterns for Gherkin feature files (replaces --features). Includes both `.feature` and `.feature.md` files. | +| stubs | Glob patterns for design stub files. Stubs are TypeScript files that live outside `src/` (e.g., `delivery-process/stubs/`). Merged into TypeScript sources at resolution time. | +| exclude | Glob patterns to exclude from all scanning | ### OutputConfig (interface) @@ -379,10 +379,10 @@ interface OutputConfig { } ``` -| Property | Description | -| --- | --- | +| Property | Description | +| --------- | ------------------------------------------------------------------ | | directory | Output directory for generated docs (default: 'docs/architecture') | -| overwrite | Overwrite existing files (default: false) | +| overwrite | Overwrite existing files (default: false) | ### GeneratorSourceOverride (interface) @@ -430,12 +430,12 @@ interface GeneratorSourceOverride { } ``` -| Property | Description | -| --- | --- | -| additionalFeatures | Additional feature file globs appended to base features | -| additionalInput | Additional TypeScript globs appended to base TypeScript sources | -| replaceFeatures | Feature globs used INSTEAD of base features. Mutually exclusive with non-empty `additionalFeatures`. | -| outputDirectory | Override output directory for this generator | +| Property | Description | +| ------------------ | ---------------------------------------------------------------------------------------------------- | +| additionalFeatures | Additional feature file globs appended to base features | +| additionalInput | Additional TypeScript globs appended to base TypeScript sources | +| replaceFeatures | Feature globs used INSTEAD of base features. Mutually exclusive with non-empty `additionalFeatures`. | +| outputDirectory | Override output directory for this generator | ### ResolvedProjectConfig (interface) @@ -466,16 +466,16 @@ interface ResolvedProjectConfig { } ``` -| Property | Description | -| --- | --- | -| sources | Resolved source globs (stubs merged, defaults applied) | -| output | Resolved output config with all defaults | -| generators | Default generator names | -| generatorOverrides | Per-generator source overrides | +| Property | Description | +| --------------------- | ---------------------------------------------------------- | +| sources | Resolved source globs (stubs merged, defaults applied) | +| output | Resolved output config with all defaults | +| generators | Default generator names | +| generatorOverrides | Per-generator source overrides | | contextInferenceRules | Context inference rules (user rules prepended to defaults) | -| workflowPath | Workflow config path (null if not specified) | -| codecOptions | Per-codec options for document generation (empty if none) | -| referenceDocConfigs | Reference document configurations (empty array if none) | +| workflowPath | Workflow config path (null if not specified) | +| codecOptions | Per-codec options for document generation (empty if none) | +| referenceDocConfigs | Reference document configurations (empty array if none) | ### ResolvedSourcesConfig (interface) @@ -496,11 +496,11 @@ interface ResolvedSourcesConfig { } ``` -| Property | Description | -| --- | --- | +| Property | Description | +| ---------- | ----------------------------------------------- | | typescript | TypeScript source globs (includes merged stubs) | -| features | Gherkin feature file globs | -| exclude | Glob patterns to exclude from scanning | +| features | Gherkin feature file globs | +| exclude | Glob patterns to exclude from scanning | ### CreateDeliveryProcessOptions (interface) @@ -523,12 +523,12 @@ interface CreateDeliveryProcessOptions { } ``` -| Property | Description | -| --- | --- | -| preset | Use a preset configuration | -| tagPrefix | Custom tag prefix (overrides preset) | -| fileOptInTag | Custom file opt-in tag (overrides preset) | -| categories | Custom categories (replaces preset categories entirely) | +| Property | Description | +| ------------ | ------------------------------------------------------- | +| preset | Use a preset configuration | +| tagPrefix | Custom tag prefix (overrides preset) | +| fileOptInTag | Custom file opt-in tag (overrides preset) | +| categories | Custom categories (replaces preset categories entirely) | ### ConfigDiscoveryResult (interface) @@ -551,12 +551,12 @@ interface ConfigDiscoveryResult { } ``` -| Property | Description | -| --- | --- | -| found | Whether a config file was found | -| path | Absolute path to the config file (if found) | -| instance | The loaded configuration instance | -| isDefault | Whether the default configuration was used | +| Property | Description | +| --------- | ------------------------------------------- | +| found | Whether a config file was found | +| path | Absolute path to the config file (if found) | +| instance | The loaded configuration instance | +| isDefault | Whether the default configuration was used | ### ConfigLoadError (interface) @@ -579,12 +579,12 @@ interface ConfigLoadError { } ``` -| Property | Description | -| --- | --- | -| type | Discriminant for error type identification | -| path | Absolute path to the config file that failed to load | -| message | Human-readable error description | -| cause | The underlying error that caused the failure (if any) | +| Property | Description | +| -------- | ----------------------------------------------------- | +| type | Discriminant for error type identification | +| path | Absolute path to the config file that failed to load | +| message | Human-readable error description | +| cause | The underlying error that caused the failure (if any) | ### ResolvedConfig (type) @@ -692,10 +692,10 @@ type ConfigLoadResult = function createRegexBuilders(tagPrefix: string, fileOptInTag: string): RegexBuilders; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| tagPrefix | | The tag prefix (e.g., "@docs-" or "@libar-docs-") | -| fileOptInTag | | The file opt-in tag (e.g., "@docs" or "@libar-docs") | +| Parameter | Type | Description | +| ------------ | ---- | ---------------------------------------------------- | +| tagPrefix | | The tag prefix (e.g., "@docs-" or "@libar-docs-") | +| fileOptInTag | | The file opt-in tag (e.g., "@docs" or "@libar-docs") | **Returns:** RegexBuilders instance with pattern matching methods @@ -743,14 +743,12 @@ function createRegexBuilders(tagPrefix: string, fileOptInTag: string): RegexBuil ```` ```typescript -function createDeliveryProcess( - options: CreateDeliveryProcessOptions = {} -): DeliveryProcessInstance; +function createDeliveryProcess(options: CreateDeliveryProcessOptions = {}): DeliveryProcessInstance; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| options | | Configuration options | +| Parameter | Type | Description | +| --------- | ---- | --------------------- | +| options | | Configuration options | **Returns:** Configured delivery process instance @@ -769,9 +767,9 @@ function createDeliveryProcess( async function findConfigFile(startDir: string): Promise; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| startDir | | Directory to start searching from | +| Parameter | Type | Description | +| --------- | ---- | --------------------------------- | +| startDir | | Directory to start searching from | **Returns:** Path to config file or null if not found @@ -810,9 +808,9 @@ async function findConfigFile(startDir: string): Promise; async function loadConfig(baseDir: string): Promise; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| baseDir | | Directory to start searching from (usually cwd or project root) | +| Parameter | Type | Description | +| --------- | ---- | --------------------------------------------------------------- | +| baseDir | | Directory to start searching from (usually cwd or project root) | **Returns:** Result with loaded configuration or error @@ -831,9 +829,9 @@ async function loadConfig(baseDir: string): Promise; function formatConfigError(error: ConfigLoadError): string; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| error | | Config load error | +| Parameter | Type | Description | +| --------- | ---- | ----------------- | +| error | | Config load error | **Returns:** Formatted error message @@ -885,7 +883,7 @@ GENERIC_PRESET = { aliases: ['infrastructure'], }, ] as const satisfies readonly CategoryDefinition[], -} as const satisfies DeliveryProcessConfig +} as const satisfies DeliveryProcessConfig; ``` ### LIBAR_GENERIC_PRESET (const) @@ -943,7 +941,7 @@ LIBAR_GENERIC_PRESET = { aliases: ['infrastructure'], }, ] as const satisfies readonly CategoryDefinition[], -} as const satisfies DeliveryProcessConfig +} as const satisfies DeliveryProcessConfig; ``` ### DDD_ES_CQRS_PRESET (const) @@ -973,7 +971,7 @@ DDD_ES_CQRS_PRESET = { fileOptInTag: DEFAULT_FILE_OPT_IN_TAG, categories: CATEGORIES, metadataTags: buildRegistry().metadataTags, -} as const satisfies DeliveryProcessConfig +} as const satisfies DeliveryProcessConfig; ``` ### PRESETS (const) @@ -1001,82 +999,103 @@ const PRESETS: Record; ## Business Rules -8 patterns, 36 rules with invariants (36 total) +10 patterns, 47 rules with invariants (47 total) ### Config Based Workflow Definition -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Default workflow is built from an inline constant | `loadDefaultWorkflow()` returns a `LoadedWorkflow` without file system access. It cannot fail. The default workflow constant uses only canonical status values from `src/taxonomy/status-values.ts`. | The file-based loading path (`catalogue/workflows/`) has been dead code since monorepo extraction. Both callers (orchestrator, process-api) already handle the failure gracefully, proving the system works without it. Making the function synchronous and infallible removes the try-catch ceremony and the warning noise. | -| Custom workflow files still work via --workflow flag | `loadWorkflowFromPath()` remains available for projects that need custom workflow definitions. The `--workflow ` CLI flag and `workflowPath` config field continue to work. | The inline default replaces file-based *default* loading, not file-based *custom* loading. Projects may define custom phases or additional statuses via JSON files. | -| FSM validation and Process Guard are not affected | The FSM transition matrix, protection levels, and Process Guard rules remain hardcoded in `src/validation/fsm/` and `src/lint/process-guard/`. They do not read from `LoadedWorkflow`. | FSM and workflow are separate concerns. FSM enforces status transitions (4-state model from PDR-005). Workflow defines phase structure (6-phase USDP). The workflow JSON declared `transitionsTo` on its statuses, but no code ever read those values — the FSM uses its own `VALID_TRANSITIONS` constant. This separation is correct and intentional. Blast radius analysis confirmed zero workflow imports in: - src/validation/fsm/ (4 files) - src/lint/process-guard/ (5 files) - src/taxonomy/ (all files) | -| Workflow as a configurable preset field is deferred | The inline default workflow constant is the only workflow source until preset integration is implemented. No preset or project config field exposes workflow customization. | Coupling workflow into the preset/config system before the inline fix ships would widen the blast radius and risk type regressions across all config consumers. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Default workflow is built from an inline constant | `loadDefaultWorkflow()` returns a `LoadedWorkflow` without file system access. It cannot fail. The default workflow constant uses only canonical status values from `src/taxonomy/status-values.ts`. | The file-based loading path (`catalogue/workflows/`) has been dead code since monorepo extraction. Both callers (orchestrator, process-api) already handle the failure gracefully, proving the system works without it. Making the function synchronous and infallible removes the try-catch ceremony and the warning noise. | +| Custom workflow files still work via --workflow flag | `loadWorkflowFromPath()` remains available for projects that need custom workflow definitions. The `--workflow ` CLI flag and `workflowPath` config field continue to work. | The inline default replaces file-based _default_ loading, not file-based _custom_ loading. Projects may define custom phases or additional statuses via JSON files. | +| FSM validation and Process Guard are not affected | The FSM transition matrix, protection levels, and Process Guard rules remain hardcoded in `src/validation/fsm/` and `src/lint/process-guard/`. They do not read from `LoadedWorkflow`. | FSM and workflow are separate concerns. FSM enforces status transitions (4-state model from PDR-005). Workflow defines phase structure (6-phase USDP). The workflow JSON declared `transitionsTo` on its statuses, but no code ever read those values — the FSM uses its own `VALID_TRANSITIONS` constant. This separation is correct and intentional. Blast radius analysis confirmed zero workflow imports in: - src/validation/fsm/ (4 files) - src/lint/process-guard/ (5 files) - src/taxonomy/ (all files) | +| Workflow as a configurable preset field is deferred | The inline default workflow constant is the only workflow source until preset integration is implemented. No preset or project config field exposes workflow customization. | Coupling workflow into the preset/config system before the inline fix ships would widen the blast radius and risk type regressions across all config consumers. | ### Config Loader Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Config files are discovered by walking up directories | The config loader must search for configuration files starting from the current directory and walking up parent directories until a match is found or the filesystem root is reached. | Projects may run CLI commands from subdirectories — upward traversal ensures the nearest config file is always found regardless of working directory. | -| Config discovery stops at repo root | Directory traversal must stop at repository root markers (e.g., .git directory) and not search beyond them. | Searching beyond the repo root could find unrelated config files from parent projects, producing confusing cross-project behavior. | -| Config is loaded and validated | Loaded config files must have a valid default export matching the expected configuration schema, with appropriate error messages for invalid formats. | Invalid configurations produce cryptic downstream errors — early validation with clear messages prevents debugging wasted on malformed config. | -| Config errors are formatted for display | Configuration loading errors must be formatted as human-readable messages including the file path and specific error description. | Raw error objects are not actionable — developers need the config file path and a clear description to diagnose and fix configuration issues. | +| Config discovery stops at repo root | Directory traversal must stop at repository root markers (e.g., .git directory) and not search beyond them. | Searching beyond the repo root could find unrelated config files from parent projects, producing confusing cross-project behavior. | +| Config is loaded and validated | Loaded config files must have a valid default export matching the expected configuration schema, with appropriate error messages for invalid formats. | Invalid configurations produce cryptic downstream errors — early validation with clear messages prevents debugging wasted on malformed config. | +| Config errors are formatted for display | Configuration loading errors must be formatted as human-readable messages including the file path and specific error description. | Raw error objects are not actionable — developers need the config file path and a clear description to diagnose and fix configuration issues. | ### Config Resolution -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Default config provides sensible fallbacks | A config created without user input must have isDefault=true and empty source collections. | Downstream consumers need a safe starting point when no config file exists. | -| Preset creates correct taxonomy instance | Each preset must produce a taxonomy with the correct number of categories and tag prefix. | Presets are the primary user-facing configuration — wrong category counts break downstream scanning. | -| Stubs are merged into typescript sources | Stub glob patterns must appear in resolved typescript sources alongside original globs. | Stubs extend the scanner's source set without requiring users to manually list them. | -| Output defaults are applied | Missing output configuration must resolve to "docs/architecture" with overwrite=false. | Consistent defaults prevent accidental overwrites and establish a predictable output location. | -| Generator defaults are applied | A config with no generators specified must default to the "patterns" generator. | Patterns is the most commonly needed output — defaulting to it reduces boilerplate. | -| Context inference rules are prepended | User-defined inference rules must appear before built-in defaults in the resolved array. | Prepending gives user rules priority during context matching without losing defaults. | -| Config path is carried from options | The configPath from resolution options must be preserved unchanged in resolved config. | Downstream tools need the original config file location for error reporting and relative path resolution. | +| Rule | Invariant | Rationale | +| ------------------------------------------ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | +| Default config provides sensible fallbacks | A config created without user input must have isDefault=true and empty source collections. | Downstream consumers need a safe starting point when no config file exists. | +| Preset creates correct taxonomy instance | Each preset must produce a taxonomy with the correct number of categories and tag prefix. | Presets are the primary user-facing configuration — wrong category counts break downstream scanning. | +| Stubs are merged into typescript sources | Stub glob patterns must appear in resolved typescript sources alongside original globs. | Stubs extend the scanner's source set without requiring users to manually list them. | +| Output defaults are applied | Missing output configuration must resolve to "docs/architecture" with overwrite=false. | Consistent defaults prevent accidental overwrites and establish a predictable output location. | +| Generator defaults are applied | A config with no generators specified must default to the "patterns" generator. | Patterns is the most commonly needed output — defaulting to it reduces boilerplate. | +| Context inference rules are prepended | User-defined inference rules must appear before built-in defaults in the resolved array. | Prepending gives user rules priority during context matching without losing defaults. | +| Config path is carried from options | The configPath from resolution options must be preserved unchanged in resolved config. | Downstream tools need the original config file location for error reporting and relative path resolution. | ### Configuration API -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Factory creates configured instances with correct defaults | The configuration factory must produce a fully initialized instance for any supported preset, with the libar-generic preset as the default when no arguments are provided. | A sensible default preset eliminates boilerplate for the common case while still supporting specialized presets (ddd-es-cqrs) for advanced monorepo configurations. | -| Custom prefix configuration works correctly | Custom tag prefix and file opt-in tag overrides must be applied to the configuration instance, replacing the preset defaults. | Consuming projects may use different annotation prefixes — custom prefixes enable the toolkit to work with any tag convention without forking presets. | -| Preset categories replace base categories entirely | When a preset defines its own category set, it must fully replace (not merge with) the base categories. | Category sets are curated per-preset — merging would include irrelevant categories (e.g., DDD categories in a generic project) that pollute taxonomy reports. | -| Regex builders use configured prefix | All regex builders (hasFileOptIn, hasDocDirectives, normalizeTag) must use the configured tag prefix, not a hardcoded one. | Regex patterns that ignore the configured prefix would miss annotations in projects using custom prefixes, silently skipping source files. | +| Custom prefix configuration works correctly | Custom tag prefix and file opt-in tag overrides must be applied to the configuration instance, replacing the preset defaults. | Consuming projects may use different annotation prefixes — custom prefixes enable the toolkit to work with any tag convention without forking presets. | +| Preset categories replace base categories entirely | When a preset defines its own category set, it must fully replace (not merge with) the base categories. | Category sets are curated per-preset — merging would include irrelevant categories (e.g., DDD categories in a generic project) that pollute taxonomy reports. | +| Regex builders use configured prefix | All regex builders (hasFileOptIn, hasDocDirectives, normalizeTag) must use the configured tag prefix, not a hardcoded one. | Regex patterns that ignore the configured prefix would miss annotations in projects using custom prefixes, silently skipping source files. | ### Define Config Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| defineConfig is an identity function | The defineConfig helper must return its input unchanged, serving only as a type annotation aid for IDE autocomplete. | defineConfig exists for TypeScript type inference in config files — any transformation would surprise users who expect their config object to pass through unmodified. | -| Schema validates correct configurations | Valid configuration objects (both minimal and fully-specified) must pass schema validation without errors. | The schema must accept all legitimate configuration shapes — rejecting valid configs would block users from using supported features. | -| Schema rejects invalid configurations | The configuration schema must reject invalid values including empty globs, directory traversal patterns, mutually exclusive options, invalid preset names, and unknown fields. | Schema validation is the first line of defense against misconfiguration — permissive validation lets invalid configs produce confusing downstream errors. | -| Type guards distinguish config formats | The isProjectConfig and isLegacyInstance type guards must correctly distinguish between new-style project configs and legacy configuration instances. | The codebase supports both config formats during migration — incorrect type detection would apply the wrong loading path and produce runtime errors. | +| Rule | Invariant | Rationale | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| defineConfig is an identity function | The defineConfig helper must return its input unchanged, serving only as a type annotation aid for IDE autocomplete. | defineConfig exists for TypeScript type inference in config files — any transformation would surprise users who expect their config object to pass through unmodified. | +| Schema validates correct configurations | Valid configuration objects (both minimal and fully-specified) must pass schema validation without errors. | The schema must accept all legitimate configuration shapes — rejecting valid configs would block users from using supported features. | +| Schema rejects invalid configurations | The configuration schema must reject invalid values including empty globs, directory traversal patterns, mutually exclusive options, invalid preset names, and unknown fields. | Schema validation is the first line of defense against misconfiguration — permissive validation lets invalid configs produce confusing downstream errors. | +| Type guards distinguish config formats | The isProjectConfig and isLegacyInstance type guards must correctly distinguish between new-style project configs and legacy configuration instances. | The codebase supports both config formats during migration — incorrect type detection would apply the wrong loading path and produce runtime errors. | + +### Monorepo Support + +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Config supports workspace-aware package definitions | When a packages field is present in the config, each entry maps a package name to its source globs. The top-level sources field becomes optional. Packages without their own feature or stub globs inherit those values from the corresponding top-level feature and stub settings. Repos without packages work exactly as before (backward compatible). | The consumer monorepo has no config file because the system only supports flat glob arrays. Adding packages enables a single config file to replace duplicated globs across 15+ scripts. | +| Extracted patterns carry package provenance from glob matching | When packages config is active, every ExtractedPattern has an optional package field set from the matching glob. If no packages config exists, the field is undefined. First match wins on overlapping globs. | Package provenance must be derived automatically from config, not from manual annotation. This ensures zero additional developer effort. | +| CLI commands accept a package filter that composes with existing filters | The --package flag filters patterns to those from a specific package. It composes with --status, --phase, --category via logical AND. | In a 600-file monorepo, unscoped queries return too many results. Package-scoped filtering lets developers focus on a single workspace member. | +| Cross-package dependencies are visible as a package-level graph | The cross-package subcommand aggregates pattern-level relationships into package-level edges, showing source package, target package, and the patterns forming the dependency. Intra-package dependencies are excluded. | Understanding cross-package dependencies is essential for release planning and impact analysis. The relationship data already exists in relationshipIndex -- this adds package-level aggregation. | +| Coverage analysis reports annotation completeness per package | When packages config is active, arch coverage reports per-package annotation counts alongside the aggregate total. | Different packages have different annotation maturity. Per-package breakdown lets teams track their own progress and identify which packages need the most work. | ### Preset System -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Generic preset provides minimal taxonomy | The generic preset must provide exactly 3 categories (core, api, infra) with @docs- prefix. | Simple projects need minimal configuration without DDD-specific categories cluttering the taxonomy. | -| Libar generic preset provides minimal taxonomy with libar prefix | The libar-generic preset must provide exactly 3 categories with @libar-docs- prefix. | This package uses @libar-docs- prefix to avoid collisions with consumer projects' annotations. | -| DDD-ES-CQRS preset provides full taxonomy | The DDD preset must provide all 21 categories spanning DDD, ES, CQRS, and infrastructure domains. | DDD architectures require fine-grained categorization to distinguish bounded contexts, aggregates, and projections. | -| Presets can be accessed by name | All preset instances must be accessible via the PRESETS map using their canonical string key. | Programmatic access enables config files to reference presets by name instead of importing instances. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| Generic preset provides minimal taxonomy | The generic preset must provide exactly 3 categories (core, api, infra) with @docs- prefix. | Simple projects need minimal configuration without DDD-specific categories cluttering the taxonomy. | +| Libar generic preset provides minimal taxonomy with libar prefix | The libar-generic preset must provide exactly 3 categories with @libar-docs- prefix. | This package uses @libar-docs- prefix to avoid collisions with consumer projects' annotations. | +| DDD-ES-CQRS preset provides full taxonomy | The DDD preset must provide all 21 categories spanning DDD, ES, CQRS, and infrastructure domains. | DDD architectures require fine-grained categorization to distinguish bounded contexts, aggregates, and projections. | +| Presets can be accessed by name | All preset instances must be accessible via the PRESETS map using their canonical string key. | Programmatic access enables config files to reference presets by name instead of importing instances. | ### Project Config Loader -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Missing config returns defaults | When no config file exists, loadProjectConfig must return a default resolved config with isDefault=true. | Graceful fallback enables zero-config usage — new projects work without requiring config file creation. | -| New-style config is loaded and resolved | A file exporting defineConfig must be loaded, validated, and resolved with correct preset categories. | defineConfig is the primary config format — correct loading is the critical path for all documentation generation. | -| Legacy config is loaded with backward compatibility | A file exporting createDeliveryProcess must be loaded and produce a valid resolved config. | Backward compatibility prevents breaking existing consumers during migration to the new config format. | -| Invalid configs produce clear errors | Config files without a default export or with invalid data must produce descriptive error messages. | Actionable error messages reduce debugging time — users need to know what to fix, not just that something failed. | +| Rule | Invariant | Rationale | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Missing config returns defaults | When no config file exists, loadProjectConfig must return a default resolved config with isDefault=true. | Graceful fallback enables zero-config usage — new projects work without requiring config file creation. | +| New-style config is loaded and resolved | A file exporting defineConfig must be loaded, validated, and resolved with correct preset categories. | defineConfig is the primary config format — correct loading is the critical path for all documentation generation. | +| Legacy config is loaded with backward compatibility | A file exporting createDeliveryProcess must be loaded and produce a valid resolved config. | Backward compatibility prevents breaking existing consumers during migration to the new config format. | +| Invalid configs produce clear errors | Config files without a default export or with invalid data must produce descriptive error messages. | Actionable error messages reduce debugging time — users need to know what to fix, not just that something failed. | + +### Setup Command + +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Init detects existing project context before making changes | The init command reads the target directory for package.json, tsconfig.json, delivery-process.config.ts (or .js), and monorepo markers before prompting or generating any files. Detection results determine which steps are skipped. | Blindly generating files overwrites user configuration and breaks working setups. Context detection enables safe adoption into existing projects by skipping steps that are already complete. | +| Interactive prompts configure preset and source paths with smart defaults | The init command prompts for preset selection from the three available presets (generic, libar-generic, ddd-es-cqrs) with descriptions, and for source glob paths with defaults inferred from project structure. The --yes flag skips non-destructive selection prompts and uses defaults. Destructive overwrites require an explicit --force flag; otherwise init exits without modifying existing files. | New users do not know which preset to choose or what glob patterns to use. Smart defaults reduce decisions to confirmations. The --yes flag enables scripted adoption in CI. | +| Generated config file uses defineConfig with correct imports | The generated delivery-process.config.ts (or .js) imports defineConfig from the correct path, uses the selected preset, and includes configured source globs. An existing config file is never overwritten without confirmation. | The config file is the most important artifact. An incorrect import path or malformed glob causes every subsequent command to fail. The overwrite guard prevents destroying custom configuration. | +| Npm scripts are injected using bin command names | Injected scripts reference bin names (process-api, generate-docs) resolved via node_modules/.bin, not dist paths. Existing scripts are preserved. The package.json "type" field is preserved. ESM migration is an explicit opt-in via --esm flag. | The tutorial uses long fragile dist paths. Bin commands are the stable public API. Setting type:module ensures ESM imports work for the config. | +| Directory structure and example annotation enable immediate first run | The init command creates directories for configured source globs and generates one example annotated TypeScript file with the minimum annotation set (opt-in marker, pattern tag, status, category, description). | Empty source globs produce a confusing "0 patterns" result. An example file proves the pipeline works and teaches annotation syntax by example. | +| Init validates the complete setup by running the pipeline | After all files are generated, init runs process-api overview and reports whether the pipeline detected the example pattern. Success prints a summary and next steps. Failure prints diagnostic information. | Generating files without verification produces false confidence. Running the pipeline as the final step proves config, globs, directories, and the example annotation all work together. | ### Source Merging -| Rule | Invariant | Rationale | -| --- | --- | --- | -| No override returns base unchanged | When no source overrides are provided, the merged result must be identical to the base source configuration. | The merge function must be safe to call unconditionally — returning modified results without overrides would corrupt default source paths. | -| Feature overrides control feature source selection | additionalFeatures must append to base feature sources while replaceFeatures must completely replace them, and these two options are mutually exclusive. | Projects need both additive and replacement strategies — additive for extending (monorepo packages), replacement for narrowing (focused generation runs). | -| TypeScript source overrides append additional input | additionalInput must append to (not replace) the base TypeScript source paths. | TypeScript sources are always additive — the base sources contain core patterns that must always be included alongside project-specific additions. | -| Combined overrides apply together | Feature overrides and TypeScript overrides must compose independently when both are provided simultaneously. | Real configs often specify both feature and TypeScript overrides — they must not interfere with each other or produce order-dependent results. | -| Exclude is always inherited from base | The exclude patterns must always come from the base configuration, never from overrides. | Exclude patterns are a safety mechanism — allowing overrides to modify excludes could accidentally include sensitive or generated files in the scan. | +| Rule | Invariant | Rationale | +| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| No override returns base unchanged | When no source overrides are provided, the merged result must be identical to the base source configuration. | The merge function must be safe to call unconditionally — returning modified results without overrides would corrupt default source paths. | +| Feature overrides control feature source selection | additionalFeatures must append to base feature sources while replaceFeatures must completely replace them, and these two options are mutually exclusive. | Projects need both additive and replacement strategies — additive for extending (monorepo packages), replacement for narrowing (focused generation runs). | +| TypeScript source overrides append additional input | additionalInput must append to (not replace) the base TypeScript source paths. | TypeScript sources are always additive — the base sources contain core patterns that must always be included alongside project-specific additions. | +| Combined overrides apply together | Feature overrides and TypeScript overrides must compose independently when both are provided simultaneously. | Real configs often specify both feature and TypeScript overrides — they must not interfere with each other or produce order-dependent results. | +| Exclude is always inherited from base | The exclude patterns must always come from the base configuration, never from overrides. | Exclude patterns are a safety mechanism — allowing overrides to modify excludes could accidentally include sensitive or generated files in the scan. | --- diff --git a/docs-live/product-areas/DATA-API.md b/docs-live/product-areas/DATA-API.md index 1daa2de2..85832611 100644 --- a/docs-live/product-areas/DATA-API.md +++ b/docs-live/product-areas/DATA-API.md @@ -179,10 +179,10 @@ interface PipelineOptions { } ``` -| Property | Description | -| --- | --- | -| includeValidation | DD-3: When false, skip validation pass (default true). | -| failOnScanErrors | DD-5: When true, return error on individual scan failures (default false). | +| Property | Description | +| ----------------- | -------------------------------------------------------------------------- | +| includeValidation | DD-3: When false, skip validation pass (default true). | +| failOnScanErrors | DD-5: When true, return error on individual scan failures (default false). | ### PipelineResult (interface) @@ -278,7 +278,14 @@ MasterDatasetSchema = z.object({ /** Optional architecture index for diagram generation */ archIndex: ArchIndexSchema.optional(), -}) + + // ───────────────────────────────────────────────────────────────────────── + // Sequence Data (optional) + // ───────────────────────────────────────────────────────────────────────── + + /** Optional sequence index for design review diagram generation */ + sequenceIndex: SequenceIndexSchema.optional(), +}); ``` ### StatusGroupsSchema (const) @@ -305,7 +312,7 @@ StatusGroupsSchema = z.object({ /** Patterns with status 'roadmap', 'planned', or undefined */ planned: z.array(ExtractedPatternSchema), -}) +}); ``` ### StatusCountsSchema (const) @@ -330,7 +337,7 @@ StatusCountsSchema = z.object({ /** Total number of patterns */ total: z.number().int().nonnegative(), -}) +}); ``` ### PhaseGroupSchema (const) @@ -358,7 +365,7 @@ PhaseGroupSchema = z.object({ /** Pre-computed status counts for this phase */ counts: StatusCountsSchema, -}) +}); ``` ### SourceViewsSchema (const) @@ -383,7 +390,7 @@ SourceViewsSchema = z.object({ /** Patterns with PRD metadata (productArea, userRole, businessValue) */ prd: z.array(ExtractedPatternSchema), -}) +}); ``` ### RelationshipEntrySchema (const) @@ -429,7 +436,7 @@ RelationshipEntrySchema = z.object({ /** File paths to implementation APIs (from @libar-docs-api-ref tag) */ apiRef: z.array(z.string()), -}) +}); ``` ### ArchIndexSchema (const) @@ -458,318 +465,328 @@ ArchIndexSchema = z.object({ /** Patterns with any architecture metadata (for diagram generation) */ all: z.array(ExtractedPatternSchema), -}) +}); ``` --- ## Business Rules -33 patterns, 140 rules with invariants (140 total) +34 patterns, 145 rules with invariants (145 total) ### Arch Queries Test -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Neighborhood and comparison views | The architecture query API must provide pattern neighborhood views (direct connections) and cross-context comparison views (shared/unique dependencies), returning undefined for nonexistent patterns. | Neighborhood and comparison views are the primary navigation tools for understanding architecture — without them, developers must manually trace relationship chains across files. | -| Taxonomy discovery via tags and sources | The API must aggregate tag values with counts across all patterns and categorize source files by type, returning empty reports when no patterns match. | Tag aggregation reveals annotation coverage gaps and source inventory helps teams understand their codebase composition — both are essential for project health monitoring. | -| Coverage analysis reports annotation completeness | Coverage analysis must detect unused taxonomy entries, cross-context integration points, and include all relationship types (implements, dependsOn, enables) in neighborhood views. | Unused taxonomy entries indicate dead configuration while missing relationship types produce incomplete architecture views — both degrade the reliability of generated documentation. | +| Rule | Invariant | Rationale | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Neighborhood and comparison views | The architecture query API must provide pattern neighborhood views (direct connections) and cross-context comparison views (shared/unique dependencies), returning undefined for nonexistent patterns. | Neighborhood and comparison views are the primary navigation tools for understanding architecture — without them, developers must manually trace relationship chains across files. | +| Taxonomy discovery via tags and sources | The API must aggregate tag values with counts across all patterns and categorize source files by type, returning empty reports when no patterns match. | Tag aggregation reveals annotation coverage gaps and source inventory helps teams understand their codebase composition — both are essential for project health monitoring. | +| Coverage analysis reports annotation completeness | Coverage analysis must detect unused taxonomy entries, cross-context integration points, and include all relationship types (implements, dependsOn, enables) in neighborhood views. | Unused taxonomy entries indicate dead configuration while missing relationship types produce incomplete architecture views — both degrade the reliability of generated documentation. | ### Context Assembler Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| assembleContext produces session-tailored context bundles | Each session type (design/planning/implement) must include exactly the context sections defined by its profile — no more, no less. | Over-fetching wastes AI context window tokens; under-fetching causes the agent to make uninformed decisions. | -| buildDepTree walks dependency chains with cycle detection | The dependency tree must walk the full chain up to the depth limit, mark the focal node, and terminate safely on circular references. | Dependency chains reveal implementation prerequisites — cycles and infinite recursion would crash the CLI. | -| buildOverview provides executive project summary | The overview must include progress counts (completed/active/planned), active phase listing, and blocking dependencies. | The overview is the first command in every session start recipe — it must provide a complete project health snapshot. | -| buildFileReadingList returns paths by relevance | Primary files (spec, implementation) must always be included; related files (dependency implementations) are included only when requested. | File reading lists power the "what to read" guidance — relevance sorting ensures the most important files are read first within token budgets. | +| Rule | Invariant | Rationale | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| assembleContext produces session-tailored context bundles | Each session type (design/planning/implement) must include exactly the context sections defined by its profile — no more, no less. | Over-fetching wastes AI context window tokens; under-fetching causes the agent to make uninformed decisions. | +| buildDepTree walks dependency chains with cycle detection | The dependency tree must walk the full chain up to the depth limit, mark the focal node, and terminate safely on circular references. | Dependency chains reveal implementation prerequisites — cycles and infinite recursion would crash the CLI. | +| buildOverview provides executive project summary | The overview must include progress counts (completed/active/planned), active phase listing, and blocking dependencies. | The overview is the first command in every session start recipe — it must provide a complete project health snapshot. | +| buildFileReadingList returns paths by relevance | Primary files (spec, implementation) must always be included; related files (dependency implementations) are included only when requested. | File reading lists power the "what to read" guidance — relevance sorting ensures the most important files are read first within token budgets. | ### Context Formatter Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| formatContextBundle renders section markers | The context formatter must render section markers for all populated sections in a context bundle, with design bundles rendering all sections and implement bundles focusing on deliverables and FSM. | Section markers enable structured parsing of context output — without them, AI consumers cannot reliably extract specific sections from the formatted bundle. | -| formatDepTree renders indented tree | The dependency tree formatter must render with indentation arrows and a focal pattern marker to visually distinguish the target pattern from its dependencies. | Visual hierarchy in the dependency tree makes dependency chains scannable at a glance — flat output would require mental parsing to understand depth and relationships. | -| formatOverview renders progress summary | The overview formatter must render a progress summary line showing completion metrics for the project. | The progress line is the first thing developers see when starting a session — it provides immediate project health awareness without requiring detailed exploration. | -| formatFileReadingList renders categorized file paths | The file reading list formatter must categorize paths into primary and dependency sections, producing minimal output when the list is empty. | Categorized file lists tell developers which files to read first (primary) versus reference (dependency) — uncategorized lists waste time on low-priority files. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| formatContextBundle renders section markers | The context formatter must render section markers for all populated sections in a context bundle, with design bundles rendering all sections and implement bundles focusing on deliverables and FSM. | Section markers enable structured parsing of context output — without them, AI consumers cannot reliably extract specific sections from the formatted bundle. | +| formatDepTree renders indented tree | The dependency tree formatter must render with indentation arrows and a focal pattern marker to visually distinguish the target pattern from its dependencies. | Visual hierarchy in the dependency tree makes dependency chains scannable at a glance — flat output would require mental parsing to understand depth and relationships. | +| formatOverview renders progress summary | The overview formatter must render a progress summary line showing completion metrics for the project. | The progress line is the first thing developers see when starting a session — it provides immediate project health awareness without requiring detailed exploration. | +| formatFileReadingList renders categorized file paths | The file reading list formatter must categorize paths into primary and dependency sections, producing minimal output when the list is empty. | Categorized file lists tell developers which files to read first (primary) versus reference (dependency) — uncategorized lists waste time on low-priority files. | ### Data API Architecture Queries -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Arch subcommand provides neighborhood and comparison views | Architecture queries resolve pattern names to concrete relationships and file paths, not just abstract names. | The current `arch graph ` returns dependency and relationship names but not the full picture of what surrounds a pattern. Design sessions need to understand: "If I'm working on X, what else is directly connected?" and "How do contexts A and B relate?" | -| Coverage analysis reports annotation completeness with gaps | Coverage reports identify unannotated files that should have the libar-docs opt-in marker based on their location and content. | Annotation completeness directly impacts the quality of all generated documentation and API queries. Files without the opt-in marker are invisible to the pipeline. Coverage gaps mean missing patterns in the registry, incomplete dependency graphs, and blind spots in architecture views. | -| Tags and sources commands provide taxonomy and inventory views | All tag values in use are discoverable without reading configuration files. Source file inventory shows the full scope of annotated and scanned content. | Agents frequently need to know "what categories exist?" or "how many feature files are there?" without reading taxonomy configuration. These are meta-queries about the annotation system itself, essential for writing new annotations correctly and understanding scope. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Arch subcommand provides neighborhood and comparison views | Architecture queries resolve pattern names to concrete relationships and file paths, not just abstract names. | The current `arch graph ` returns dependency and relationship names but not the full picture of what surrounds a pattern. Design sessions need to understand: "If I'm working on X, what else is directly connected?" and "How do contexts A and B relate?" | +| Coverage analysis reports annotation completeness with gaps | Coverage reports identify unannotated files that should have the libar-docs opt-in marker based on their location and content. | Annotation completeness directly impacts the quality of all generated documentation and API queries. Files without the opt-in marker are invisible to the pipeline. Coverage gaps mean missing patterns in the registry, incomplete dependency graphs, and blind spots in architecture views. | +| Tags and sources commands provide taxonomy and inventory views | All tag values in use are discoverable without reading configuration files. Source file inventory shows the full scope of annotated and scanned content. | Agents frequently need to know "what categories exist?" or "how many feature files are there?" without reading taxonomy configuration. These are meta-queries about the annotation system itself, essential for writing new annotations correctly and understanding scope. | ### Data API CLI Ergonomics -| Rule | Invariant | Rationale | -| --- | --- | --- | -| MasterDataset is cached between invocations with file-change invalidation | Cache is automatically invalidated when any source file (TypeScript or Gherkin) has a modification time newer than the cache. | The pipeline (scan -> extract -> transform) runs fresh on every invocation (~2-5 seconds). Most queries during a session don't need fresh data -- the source files haven't changed between queries. Caching the MasterDataset to a temp file with file-modification-time invalidation makes subsequent queries instant while ensuring staleness is impossible. | -| REPL mode keeps pipeline loaded for interactive multi-query sessions | REPL mode loads the pipeline once and accepts multiple queries on stdin, with optional tab completion for pattern names and subcommands. | Design sessions often involve 10-20 exploratory queries in sequence (check status, look up pattern, check deps, look up another pattern). REPL mode eliminates per-query pipeline overhead entirely. | -| Per-subcommand help and diagnostic modes aid discoverability | Every subcommand supports `--help` with usage, flags, and examples. Dry-run shows pipeline scope without executing. | AI agents read `--help` output to discover available commands and flags. Without per-subcommand help, agents must read external documentation. Dry-run mode helps diagnose "why no patterns found?" issues by showing what would be scanned. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| MasterDataset is cached between invocations with file-change invalidation | Cache is automatically invalidated when any source file (TypeScript or Gherkin) has a modification time newer than the cache. | The pipeline (scan -> extract -> transform) runs fresh on every invocation (~2-5 seconds). Most queries during a session don't need fresh data -- the source files haven't changed between queries. Caching the MasterDataset to a temp file with file-modification-time invalidation makes subsequent queries instant while ensuring staleness is impossible. | +| REPL mode keeps pipeline loaded for interactive multi-query sessions | REPL mode loads the pipeline once and accepts multiple queries on stdin, with optional tab completion for pattern names and subcommands. | Design sessions often involve 10-20 exploratory queries in sequence (check status, look up pattern, check deps, look up another pattern). REPL mode eliminates per-query pipeline overhead entirely. | +| Per-subcommand help and diagnostic modes aid discoverability | Every subcommand supports `--help` with usage, flags, and examples. Dry-run shows pipeline scope without executing. | AI agents read `--help` output to discover available commands and flags. Without per-subcommand help, agents must read external documentation. Dry-run mode helps diagnose "why no patterns found?" issues by showing what would be scanned. | ### Data API Context Assembly -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Context command assembles curated context for a single pattern | Given a pattern name, `context` returns everything needed to start working on that pattern: metadata, file locations, dependency status, and architecture position -- in ~1.5KB of structured text. | This is the core value proposition. The command crosses five gaps simultaneously: it assembles data from multiple MasterDataset indexes, shapes it compactly, resolves file paths from pattern names, discovers stubs by convention, and tailors output by session type. | -| Files command returns only file paths organized by relevance | `files` returns the most token-efficient output possible -- just file paths that Claude Code can read directly. | Most context tokens are spent reading actual files, not metadata. The `files` command tells Claude Code *which* files to read, organized by importance. Claude Code then reads what it needs. This is more efficient than `context` when the agent already knows the pattern and just needs the file list. | -| Dep-tree command shows recursive dependency chain with status | The dependency tree walks both `dependsOn`/`enables` (planning) and `uses`/`usedBy` (implementation) relationships with configurable depth. | Before starting work on a pattern, agents need to know the full dependency chain: what must be complete first, what this unblocks, and where the current pattern sits in the sequence. A tree visualization with status markers makes blocking relationships immediately visible. | -| Context command supports multiple patterns with merged output | Multi-pattern context deduplicates shared dependencies and highlights overlap between patterns. | Design sessions often span multiple related patterns (e.g., reviewing DS-2 through DS-5 together). Separate `context` calls would duplicate shared dependencies. Merged context shows the union of all dependencies with overlap analysis. | -| Overview provides executive project summary | `overview` returns project-wide health in one command. | Planning sessions start with "where are we?" This command answers that question without needing to run multiple queries and mentally aggregate results. Implementation readiness checks for specific patterns live in DataAPIDesignSessionSupport's `scope-validate` command. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Context command assembles curated context for a single pattern | Given a pattern name, `context` returns everything needed to start working on that pattern: metadata, file locations, dependency status, and architecture position -- in ~1.5KB of structured text. | This is the core value proposition. The command crosses five gaps simultaneously: it assembles data from multiple MasterDataset indexes, shapes it compactly, resolves file paths from pattern names, discovers stubs by convention, and tailors output by session type. | +| Files command returns only file paths organized by relevance | `files` returns the most token-efficient output possible -- just file paths that Claude Code can read directly. | Most context tokens are spent reading actual files, not metadata. The `files` command tells Claude Code _which_ files to read, organized by importance. Claude Code then reads what it needs. This is more efficient than `context` when the agent already knows the pattern and just needs the file list. | +| Dep-tree command shows recursive dependency chain with status | The dependency tree walks both `dependsOn`/`enables` (planning) and `uses`/`usedBy` (implementation) relationships with configurable depth. | Before starting work on a pattern, agents need to know the full dependency chain: what must be complete first, what this unblocks, and where the current pattern sits in the sequence. A tree visualization with status markers makes blocking relationships immediately visible. | +| Context command supports multiple patterns with merged output | Multi-pattern context deduplicates shared dependencies and highlights overlap between patterns. | Design sessions often span multiple related patterns (e.g., reviewing DS-2 through DS-5 together). Separate `context` calls would duplicate shared dependencies. Merged context shows the union of all dependencies with overlap analysis. | +| Overview provides executive project summary | `overview` returns project-wide health in one command. | Planning sessions start with "where are we?" This command answers that question without needing to run multiple queries and mentally aggregate results. Implementation readiness checks for specific patterns live in DataAPIDesignSessionSupport's `scope-validate` command. | ### Data API Design Session Support -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Scope-validate checks implementation prerequisites before session start | Scope validation surfaces all blocking conditions before committing to a session, preventing wasted effort on unready patterns. | Starting implementation on a pattern with incomplete dependencies wastes an entire session. Starting a design session without prior session deliverables means working with incomplete context. Pre-flight validation catches these issues in seconds rather than discovering them mid-session. | -| Handoff generates compact session state summary for multi-session work | Handoff documentation captures everything the next session needs to continue work without context loss. | Multi-session work (common for design phases spanning DS-1 through DS-7) requires state transfer between sessions. Without automated handoff, critical information is lost: what was completed, what's in progress, what blockers were discovered, and what should happen next. Manual handoff documentation is inconsistent and often forgotten. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Scope-validate checks implementation prerequisites before session start | Scope validation surfaces all blocking conditions before committing to a session, preventing wasted effort on unready patterns. | Starting implementation on a pattern with incomplete dependencies wastes an entire session. Starting a design session without prior session deliverables means working with incomplete context. Pre-flight validation catches these issues in seconds rather than discovering them mid-session. | +| Handoff generates compact session state summary for multi-session work | Handoff documentation captures everything the next session needs to continue work without context loss. | Multi-session work (common for design phases spanning DS-1 through DS-7) requires state transfer between sessions. Without automated handoff, critical information is lost: what was completed, what's in progress, what blockers were discovered, and what should happen next. Manual handoff documentation is inconsistent and often forgotten. | ### Data API Output Shaping -| Rule | Invariant | Rationale | -| --- | --- | --- | -| List queries return compact pattern summaries by default | List-returning API methods produce summaries, not full ExtractedPattern objects, unless `--full` is explicitly requested. | The single biggest usability problem. `getCurrentWork` returns 3 active patterns at ~3.5KB each = 10.5KB. Summarized: ~300 bytes total. The `directive` field (raw JSDoc AST) and `code` field (full source) are almost never needed for list queries. AI agents need name, status, category, phase, and file path -- nothing more. | -| Global output modifier flags apply to any list-returning command | Output modifiers are composable and apply uniformly across all list-returning subcommands and query methods. | AI agents frequently need just pattern names (for further queries), just counts (for progress checks), or specific fields (for focused analysis). These are post-processing transforms that should work with any data source. | -| Output format is configurable with typed response envelope | All CLI output uses the QueryResult envelope for success/error discrimination. The compact format strips empty and null fields. | The existing `QueryResult` types (`QuerySuccess`, `QueryError`) are defined in `src/api/types.ts` but not wired into the CLI output. Agents cannot distinguish success from error without try/catch on JSON parsing. Empty arrays, null values, and empty strings add noise to every response. | -| List subcommand provides composable filters and fuzzy search | The `list` subcommand replaces the need to call specific `getPatternsByX` methods. Filters are composable via AND logic. The `query` subcommand remains available for programmatic/raw access. | Currently, filtering by status AND category requires calling `getPatternsByCategory` then manually filtering by status. A single `list` command with composable filters eliminates multi-step queries. Fuzzy search reduces agent retry loops when pattern names are approximate. | -| CLI provides ergonomic defaults and helpful error messages | Common operations require minimal flags. Pattern name typos produce actionable suggestions. Empty results explain why. | Every extra flag and every retry loop costs AI agent context tokens. Config file defaults eliminate repetitive path arguments. Fuzzy matching with suggestions prevents the common "Pattern not found" → retry → still not found loop. Empty result hints guide agents toward productive queries. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| List queries return compact pattern summaries by default | List-returning API methods produce summaries, not full ExtractedPattern objects, unless `--full` is explicitly requested. | The single biggest usability problem. `getCurrentWork` returns 3 active patterns at ~3.5KB each = 10.5KB. Summarized: ~300 bytes total. The `directive` field (raw JSDoc AST) and `code` field (full source) are almost never needed for list queries. AI agents need name, status, category, phase, and file path -- nothing more. | +| Global output modifier flags apply to any list-returning command | Output modifiers are composable and apply uniformly across all list-returning subcommands and query methods. | AI agents frequently need just pattern names (for further queries), just counts (for progress checks), or specific fields (for focused analysis). These are post-processing transforms that should work with any data source. | +| Output format is configurable with typed response envelope | All CLI output uses the QueryResult envelope for success/error discrimination. The compact format strips empty and null fields. | The existing `QueryResult` types (`QuerySuccess`, `QueryError`) are defined in `src/api/types.ts` but not wired into the CLI output. Agents cannot distinguish success from error without try/catch on JSON parsing. Empty arrays, null values, and empty strings add noise to every response. | +| List subcommand provides composable filters and fuzzy search | The `list` subcommand replaces the need to call specific `getPatternsByX` methods. Filters are composable via AND logic. The `query` subcommand remains available for programmatic/raw access. | Currently, filtering by status AND category requires calling `getPatternsByCategory` then manually filtering by status. A single `list` command with composable filters eliminates multi-step queries. Fuzzy search reduces agent retry loops when pattern names are approximate. | +| CLI provides ergonomic defaults and helpful error messages | Common operations require minimal flags. Pattern name typos produce actionable suggestions. Empty results explain why. | Every extra flag and every retry loop costs AI agent context tokens. Config file defaults eliminate repetitive path arguments. Fuzzy matching with suggestions prevents the common "Pattern not found" → retry → still not found loop. Empty result hints guide agents toward productive queries. | ### Data API Platform Integration -| Rule | Invariant | Rationale | -| --- | --- | --- | -| ProcessStateAPI is accessible as an MCP server for Claude Code | The MCP server exposes all ProcessStateAPI methods as MCP tools with typed input/output schemas. The pipeline is loaded once on server start and refreshed on source file changes. | MCP is Claude Code's native tool integration protocol. An MCP server eliminates the CLI subprocess overhead (2-5s per query) and enables Claude Code to call process queries as naturally as it calls other tools. Stateful operation means the pipeline loads once and serves many queries. | -| Process state can be auto-generated as CLAUDE.md context sections | Generated CLAUDE.md sections are additive layers that provide pattern metadata, relationships, and reading lists for specific scopes. | CLAUDE.md is the primary mechanism for providing persistent context to Claude Code sessions. Auto-generating CLAUDE.md sections from process state ensures the context is always fresh and consistent with the source annotations. This applies the "code-first documentation" principle to AI context itself. | -| Cross-package views show dependencies spanning multiple packages | Cross-package queries aggregate patterns from multiple input sources and resolve cross-package relationships. | In the monorepo, patterns in `platform-core` are used by patterns in `platform-bc`, which are used by the example app. Understanding these cross-package dependencies is essential for release planning and impact analysis. Currently each package must be queried independently with separate input globs. | -| Process validation integrates with git hooks and file watching | Pre-commit hooks validate annotation consistency. Watch mode re-generates docs on source changes. | Git hooks catch annotation errors at commit time (e.g., new `uses` reference to non-existent pattern, invalid `arch-role` value, stub `@target` to non-existent directory). Watch mode enables live documentation regeneration during implementation sessions. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ProcessStateAPI is accessible as an MCP server for Claude Code | The MCP server exposes all ProcessStateAPI methods as MCP tools with typed input/output schemas. The pipeline is loaded once on server start and refreshed on source file changes. | MCP is Claude Code's native tool integration protocol. An MCP server eliminates the CLI subprocess overhead (2-5s per query) and enables Claude Code to call process queries as naturally as it calls other tools. Stateful operation means the pipeline loads once and serves many queries. | +| Process state can be auto-generated as CLAUDE.md context sections | Generated CLAUDE.md sections are additive layers that provide pattern metadata, relationships, and reading lists for specific scopes. | CLAUDE.md is the primary mechanism for providing persistent context to Claude Code sessions. Auto-generating CLAUDE.md sections from process state ensures the context is always fresh and consistent with the source annotations. This applies the "code-first documentation" principle to AI context itself. | +| Cross-package views show dependencies spanning multiple packages | Cross-package queries aggregate patterns from multiple input sources and resolve cross-package relationships. | In the monorepo, patterns in `platform-core` are used by patterns in `platform-bc`, which are used by the example app. Understanding these cross-package dependencies is essential for release planning and impact analysis. Currently each package must be queried independently with separate input globs. | +| Process validation integrates with git hooks and file watching | Pre-commit hooks validate annotation consistency. Watch mode re-generates docs on source changes. | Git hooks catch annotation errors at commit time (e.g., new `uses` reference to non-existent pattern, invalid `arch-role` value, stub `@target` to non-existent directory). Watch mode enables live documentation regeneration during implementation sessions. | ### Data API Relationship Graph -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Graph command traverses relationships recursively with configurable depth | Graph traversal walks both planning relationships (`dependsOn`, `enables`) and implementation relationships (`uses`, `usedBy`) with cycle detection to prevent infinite loops. | Flat lookups show direct connections. Recursive traversal shows the full picture: transitive dependencies, indirect consumers, and the complete chain from root to leaf. Depth limiting prevents overwhelming output on deeply connected graphs. | -| Impact analysis shows transitive dependents of a pattern | Impact analysis answers "if I change X, what else is affected?" by walking `usedBy` + `enables` recursively. | Before modifying a completed pattern (which requires unlock), understanding the blast radius prevents unintended breakage. Impact analysis is the reverse of dependency traversal -- it looks forward, not backward. | -| Path finding discovers relationship chains between two patterns | Path finding returns the shortest chain of relationships connecting two patterns, or indicates no path exists. Traversal considers all relationship types (uses, usedBy, dependsOn, enables). | Understanding how two seemingly unrelated patterns connect helps agents assess indirect dependencies before making changes. When pattern A and pattern D are connected through B and C, modifying A requires understanding that chain. | -| Graph health commands detect broken references and isolated patterns | Dangling references (pattern names in `uses`/`dependsOn` that don't match any pattern definition) are detectable. Orphan patterns (no relationships at all) are identifiable. | The MasterDataset transformer already computes dangling references during Pass 3 (relationship resolution) but does not expose them via the API. Orphan patterns indicate missing annotations. Both are data quality signals that improve over time with attention. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Graph command traverses relationships recursively with configurable depth | Graph traversal walks both planning relationships (`dependsOn`, `enables`) and implementation relationships (`uses`, `usedBy`) with cycle detection to prevent infinite loops. | Flat lookups show direct connections. Recursive traversal shows the full picture: transitive dependencies, indirect consumers, and the complete chain from root to leaf. Depth limiting prevents overwhelming output on deeply connected graphs. | +| Impact analysis shows transitive dependents of a pattern | Impact analysis answers "if I change X, what else is affected?" by walking `usedBy` + `enables` recursively. | Before modifying a completed pattern (which requires unlock), understanding the blast radius prevents unintended breakage. Impact analysis is the reverse of dependency traversal -- it looks forward, not backward. | +| Path finding discovers relationship chains between two patterns | Path finding returns the shortest chain of relationships connecting two patterns, or indicates no path exists. Traversal considers all relationship types (uses, usedBy, dependsOn, enables). | Understanding how two seemingly unrelated patterns connect helps agents assess indirect dependencies before making changes. When pattern A and pattern D are connected through B and C, modifying A requires understanding that chain. | +| Graph health commands detect broken references and isolated patterns | Dangling references (pattern names in `uses`/`dependsOn` that don't match any pattern definition) are detectable. Orphan patterns (no relationships at all) are identifiable. | The MasterDataset transformer already computes dangling references during Pass 3 (relationship resolution) but does not expose them via the API. Orphan patterns indicate missing annotations. Both are data quality signals that improve over time with attention. | ### Data API Stub Integration -| Rule | Invariant | Rationale | -| --- | --- | --- | -| All stubs are visible to the scanner pipeline | Every stub file in `delivery-process/stubs/` has `@libar-docs` opt-in and `@libar-docs-implements` linking it to its parent pattern. | The scanner requires `@libar-docs` opt-in marker to include a file. Without it, stubs are invisible regardless of other annotations. The `@libar-docs-implements` tag creates the bidirectional link: spec defines the pattern (via `@libar-docs-pattern`), stub implements it. Per PDR-009, stubs must NOT use `@libar-docs-pattern` -- that belongs to the feature file. | -| Stubs subcommand lists design stubs with implementation status | `stubs` returns stub files with their target paths, design session origins, and whether the target file already exists. | Before implementation, agents need to know: which stubs exist for a pattern, where they should be moved to, and which have already been implemented. The stub-to-implementation resolver compares `@libar-docs-target` paths against actual files to determine status. | -| Decisions and PDR commands surface design rationale | Design decisions (AD-N items) and PDR references from stub annotations are queryable by pattern name or PDR number. | Design sessions produce numbered decisions (AD-1, AD-2, etc.) and reference PDR decision records (see PDR-012). When reviewing designs or starting implementation, agents need to find these decisions without reading every stub file manually. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| All stubs are visible to the scanner pipeline | Every stub file in `delivery-process/stubs/` has `@libar-docs` opt-in and `@libar-docs-implements` linking it to its parent pattern. | The scanner requires `@libar-docs` opt-in marker to include a file. Without it, stubs are invisible regardless of other annotations. The `@libar-docs-implements` tag creates the bidirectional link: spec defines the pattern (via `@libar-docs-pattern`), stub implements it. Per PDR-009, stubs must NOT use `@libar-docs-pattern` -- that belongs to the feature file. | +| Stubs subcommand lists design stubs with implementation status | `stubs` returns stub files with their target paths, design session origins, and whether the target file already exists. | Before implementation, agents need to know: which stubs exist for a pattern, where they should be moved to, and which have already been implemented. The stub-to-implementation resolver compares `@libar-docs-target` paths against actual files to determine status. | +| Decisions and PDR commands surface design rationale | Design decisions (AD-N items) and PDR references from stub annotations are queryable by pattern name or PDR number. | Design sessions produce numbered decisions (AD-1, AD-2, etc.) and reference PDR decision records (see PDR-012). When reviewing designs or starting implementation, agents need to find these decisions without reading every stub file manually. | ### Fuzzy Match Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Fuzzy matching uses tiered scoring | Pattern matching must use a tiered scoring system: exact match (1.0) > prefix match (0.9) > substring match (0.7) > Levenshtein distance, with results sorted by score descending and case-insensitive matching. | Tiered scoring ensures the most intuitive match wins — an exact match should always rank above a substring match, preventing surprising suggestions for common pattern names. | -| findBestMatch returns single suggestion | findBestMatch must return the single highest-scoring match above the threshold, or undefined when no match exceeds the threshold. | A single best suggestion simplifies "did you mean?" prompts in the CLI — returning multiple matches would require additional UI to disambiguate. | -| Levenshtein distance computation | The Levenshtein distance function must correctly compute edit distance between strings, returning 0 for identical strings. | Levenshtein distance is the fallback matching tier — incorrect distance computation would produce wrong fuzzy match scores for typo correction. | +| Rule | Invariant | Rationale | +| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Fuzzy matching uses tiered scoring | Pattern matching must use a tiered scoring system: exact match (1.0) > prefix match (0.9) > substring match (0.7) > Levenshtein distance, with results sorted by score descending and case-insensitive matching. | Tiered scoring ensures the most intuitive match wins — an exact match should always rank above a substring match, preventing surprising suggestions for common pattern names. | +| findBestMatch returns single suggestion | findBestMatch must return the single highest-scoring match above the threshold, or undefined when no match exceeds the threshold. | A single best suggestion simplifies "did you mean?" prompts in the CLI — returning multiple matches would require additional UI to disambiguate. | +| Levenshtein distance computation | The Levenshtein distance function must correctly compute edit distance between strings, returning 0 for identical strings. | Levenshtein distance is the fallback matching tier — incorrect distance computation would produce wrong fuzzy match scores for typo correction. | ### Generate Docs Cli -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The --help and -v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — they must work standalone so users can discover usage without reading external documentation. | -| CLI requires input patterns | The generate-docs CLI must fail with a clear error when the --input flag is not provided. | Without input source paths, the generator has nothing to scan — failing early with a clear message prevents confusing "no patterns found" errors downstream. | -| CLI lists available generators | The --list-generators flag must display all registered generator names without performing any generation. | Users need to discover available generators before specifying --generator — listing them avoids trial-and-error with invalid generator names. | -| CLI generates documentation from source files | Given valid input patterns and a generator name, the CLI must scan sources, extract patterns, and produce markdown output files. | This is the core pipeline — the CLI is the primary entry point for transforming annotated source code into generated documentation. | -| CLI rejects unknown options | Unrecognized CLI flags must cause an error with a descriptive message rather than being silently ignored. | Silent flag ignoring hides typos and misconfigurations — users typing --ouput instead of --output would get unexpected default behavior without realizing their flag was ignored. | +| Rule | Invariant | Rationale | +| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The --help and -v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — they must work standalone so users can discover usage without reading external documentation. | +| CLI requires input patterns | The generate-docs CLI must fail with a clear error when the --input flag is not provided. | Without input source paths, the generator has nothing to scan — failing early with a clear message prevents confusing "no patterns found" errors downstream. | +| CLI lists available generators | The --list-generators flag must display all registered generator names without performing any generation. | Users need to discover available generators before specifying --generator — listing them avoids trial-and-error with invalid generator names. | +| CLI generates documentation from source files | Given valid input patterns and a generator name, the CLI must scan sources, extract patterns, and produce markdown output files. | This is the core pipeline — the CLI is the primary entry point for transforming annotated source code into generated documentation. | +| CLI rejects unknown options | Unrecognized CLI flags must cause an error with a descriptive message rather than being silently ignored. | Silent flag ignoring hides typos and misconfigurations — users typing --ouput instead of --output would get unexpected default behavior without realizing their flag was ignored. | ### Generate Tag Taxonomy Cli -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | -| CLI generates taxonomy at specified output path | The taxonomy generator must write output to the specified path, creating parent directories if they do not exist, and defaulting to a standard path when no output is specified. | Flexible output paths support both default conventions and custom layouts — auto-creating directories prevents "ENOENT" errors on first run. | -| CLI respects overwrite flag for existing files | The CLI must refuse to overwrite existing output files unless the --overwrite or -f flag is explicitly provided. | Overwrite protection prevents accidental destruction of hand-edited taxonomy files — requiring an explicit flag makes destructive operations intentional. | -| Generated taxonomy contains expected sections | The generated taxonomy file must include category documentation and statistics sections reflecting the configured tag registry. | The taxonomy is a reference document — incomplete output missing categories or statistics would leave developers without the information they need to annotate correctly. | -| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Taxonomy generation is non-destructive — warning without failing is more user-friendly than hard errors for minor flag typos, while still surfacing the issue. | +| Rule | Invariant | Rationale | +| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | +| CLI generates taxonomy at specified output path | The taxonomy generator must write output to the specified path, creating parent directories if they do not exist, and defaulting to a standard path when no output is specified. | Flexible output paths support both default conventions and custom layouts — auto-creating directories prevents "ENOENT" errors on first run. | +| CLI respects overwrite flag for existing files | The CLI must refuse to overwrite existing output files unless the --overwrite or -f flag is explicitly provided. | Overwrite protection prevents accidental destruction of hand-edited taxonomy files — requiring an explicit flag makes destructive operations intentional. | +| Generated taxonomy contains expected sections | The generated taxonomy file must include category documentation and statistics sections reflecting the configured tag registry. | The taxonomy is a reference document — incomplete output missing categories or statistics would leave developers without the information they need to annotate correctly. | +| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Taxonomy generation is non-destructive — warning without failing is more user-friendly than hard errors for minor flag typos, while still surfacing the issue. | ### Handoff Generator Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Handoff generates compact session state summary | The handoff generator must produce a compact session state summary including pattern status, discovered items, inferred session type, modified files, and dependency blockers, throwing an error for unknown patterns. | Handoff documents are the bridge between multi-session work — without compact state capture, the next session starts from scratch instead of resuming where the previous one left off. | -| Formatter produces structured text output | The handoff formatter must produce structured text output with ADR-008 section markers for machine-parseable session state. | ADR-008 markers enable the context assembler to parse handoff output programmatically — unstructured text would require fragile regex parsing. | +| Formatter produces structured text output | The handoff formatter must produce structured text output with ADR-008 section markers for machine-parseable session state. | ADR-008 markers enable the context assembler to parse handoff output programmatically — unstructured text would require fragile regex parsing. | ### Lint Patterns Cli -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The --help and -v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — they must work standalone so users can discover usage without reading external documentation. | -| CLI requires input patterns | The lint-patterns CLI must fail with a clear error when the --input flag is not provided. | Without input paths, the linter has nothing to validate — failing early prevents confusing "no violations" output that falsely implies clean annotations. | -| Lint passes for valid patterns | Fully annotated patterns with all required tags must pass linting with zero violations. | False positives erode developer trust in the linter — valid annotations must always pass to maintain the tool's credibility. | -| Lint detects violations in incomplete patterns | Patterns with missing or incomplete annotations must produce specific violation reports identifying what is missing. | Actionable violation messages guide developers to fix annotations — generic "lint failed" messages without specifics waste debugging time. | -| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive use while JSON format enables CI/CD pipeline integration and programmatic consumption of lint results. | -| Strict mode treats warnings as errors | When --strict is enabled, warnings must be promoted to errors causing a non-zero exit code; without --strict, warnings must not cause failure. | CI pipelines need strict enforcement while local development benefits from lenient mode — the flag lets teams choose their enforcement level. | +| Rule | Invariant | Rationale | +| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The --help and -v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — they must work standalone so users can discover usage without reading external documentation. | +| CLI requires input patterns | The lint-patterns CLI must fail with a clear error when the --input flag is not provided. | Without input paths, the linter has nothing to validate — failing early prevents confusing "no violations" output that falsely implies clean annotations. | +| Lint passes for valid patterns | Fully annotated patterns with all required tags must pass linting with zero violations. | False positives erode developer trust in the linter — valid annotations must always pass to maintain the tool's credibility. | +| Lint detects violations in incomplete patterns | Patterns with missing or incomplete annotations must produce specific violation reports identifying what is missing. | Actionable violation messages guide developers to fix annotations — generic "lint failed" messages without specifics waste debugging time. | +| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive use while JSON format enables CI/CD pipeline integration and programmatic consumption of lint results. | +| Strict mode treats warnings as errors | When --strict is enabled, warnings must be promoted to errors causing a non-zero exit code; without --strict, warnings must not cause failure. | CI pipelines need strict enforcement while local development benefits from lenient mode — the flag lets teams choose their enforcement level. | ### Lint Process Cli -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | -| CLI requires git repository for validation | The lint-process CLI must fail with a clear error when run outside a git repository in both staged and all modes. | Process guard validation depends on git diff for change detection — running without git produces undefined behavior rather than useful validation results. | -| CLI validates file mode input | In file mode, the CLI must require at least one file path via positional argument or --file flag, and fail with a clear error when none is provided. | File mode is for targeted validation of specific files — accepting zero files would silently produce a "no violations" result that falsely implies the files are valid. | -| CLI handles no changes gracefully | When no relevant changes are detected (empty diff), the CLI must exit successfully with a zero exit code. | No changes means no violations are possible — failing on empty diffs would break CI pipelines on commits that only modify non-spec files. | -| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive pre-commit use while JSON format enables CI/CD pipeline integration and automated violation processing. | -| CLI supports debug options | The --show-state flag must display the derived process state (FSM states, protection levels, deliverables) without affecting validation behavior. | Process guard decisions are derived from complex state — exposing the intermediate state helps developers understand why a specific validation passed or failed. | -| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Process validation is critical-path at commit time — hard-failing on a typo in an optional flag would block commits unnecessarily when the core validation would succeed. | +| Rule | Invariant | Rationale | +| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | +| CLI requires git repository for validation | The lint-process CLI must fail with a clear error when run outside a git repository in both staged and all modes. | Process guard validation depends on git diff for change detection — running without git produces undefined behavior rather than useful validation results. | +| CLI validates file mode input | In file mode, the CLI must require at least one file path via positional argument or --file flag, and fail with a clear error when none is provided. | File mode is for targeted validation of specific files — accepting zero files would silently produce a "no violations" result that falsely implies the files are valid. | +| CLI handles no changes gracefully | When no relevant changes are detected (empty diff), the CLI must exit successfully with a zero exit code. | No changes means no violations are possible — failing on empty diffs would break CI pipelines on commits that only modify non-spec files. | +| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive pre-commit use while JSON format enables CI/CD pipeline integration and automated violation processing. | +| CLI supports debug options | The --show-state flag must display the derived process state (FSM states, protection levels, deliverables) without affecting validation behavior. | Process guard decisions are derived from complex state — exposing the intermediate state helps developers understand why a specific validation passed or failed. | +| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Process validation is critical-path at commit time — hard-failing on a typo in an optional flag would block commits unnecessarily when the core validation would succeed. | + +### MCP Server Integration + +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| MCP server starts via stdio transport and manages its own lifecycle | The MCP server communicates over stdio using JSON-RPC. It builds the pipeline once during initialization, then enters a request-response loop. No non-MCP output is written to stdout (no console.log, no pnpm banners). | MCP defines stdio as the standard transport for local tool servers. Claude Code spawns the process and communicates over stdin/stdout pipes. Any extraneous stdout output corrupts the JSON-RPC stream. Loading the pipeline during initialization ensures the first tool call is fast. | +| ProcessStateAPI methods and CLI subcommands are registered as MCP tools | Every CLI subcommand is registered as an MCP tool with a JSON Schema describing its input parameters. Tool names use snake*case with a "dp*" prefix to avoid collisions with other MCP servers. | MCP tools are the unit of interaction. Each tool needs a name, description (for LLM tool selection), and JSON Schema for input validation. The "dp\_" prefix prevents collisions in multi-server setups. | +| MasterDataset is loaded once and reused across all tool invocations | The pipeline runs exactly once during server initialization. All subsequent tool calls read from in-memory MasterDataset. A manual rebuild can be triggered via a "dp_rebuild" tool. | The pipeline costs 2-5 seconds. Running it per tool call negates MCP benefits. Pre-computed views provide O(1) access ideal for a query server. | +| Source file changes trigger automatic dataset rebuild with debouncing | When --watch is enabled, changes to source files trigger an automatic pipeline rebuild. Multiple rapid changes are debounced into a single rebuild (default 500ms window). | During implementation sessions, source files change frequently. Without auto-rebuild, agents must manually call dp_rebuild. Debouncing prevents redundant rebuilds during rapid-fire saves. | +| MCP server is configurable via standard client configuration | The server works with .mcp.json (Claude Code), claude_desktop_config.json (Claude Desktop), and any MCP client. It accepts --input, --features, --base-dir args and auto-detects delivery-process.config.ts. | MCP clients discover servers through configuration files. The server must work with sensible defaults (config auto-detection) while supporting explicit overrides for monorepo setups. | ### Output Pipeline Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Output modifiers apply with correct precedence | Output modifiers (count, names-only, fields, full) must apply to pattern arrays with correct precedence, passing scalar inputs through unchanged, with summaries as the default mode. | Predictable modifier behavior enables composable CLI queries — unexpected precedence or scalar handling would produce confusing output for piped commands. | -| Modifier conflicts are rejected | Mutually exclusive modifier combinations (full+names-only, full+count, full+fields) and invalid field names must be rejected with clear error messages. | Conflicting modifiers produce ambiguous intent — rejecting early with a clear message is better than silently picking one modifier and ignoring the other. | -| List filters compose via AND logic | Multiple list filters (status, category) must compose via AND logic, with pagination (limit/offset) applied after filtering and empty results for out-of-range offsets. | AND composition is the intuitive default for filters — "status=active AND category=core" should narrow results, not widen them via OR logic. | -| Empty stripping removes noise | Null and empty values must be stripped from output objects to reduce noise in API responses. | Empty fields in pattern summaries create visual clutter and waste tokens in AI context windows — stripping them keeps output focused on meaningful data. | +| Modifier conflicts are rejected | Mutually exclusive modifier combinations (full+names-only, full+count, full+fields) and invalid field names must be rejected with clear error messages. | Conflicting modifiers produce ambiguous intent — rejecting early with a clear message is better than silently picking one modifier and ignoring the other. | +| List filters compose via AND logic | Multiple list filters (status, category) must compose via AND logic, with pagination (limit/offset) applied after filtering and empty results for out-of-range offsets. | AND composition is the intuitive default for filters — "status=active AND category=core" should narrow results, not widen them via OR logic. | +| Empty stripping removes noise | Null and empty values must be stripped from output objects to reduce noise in API responses. | Empty fields in pattern summaries create visual clutter and waste tokens in AI context windows — stripping them keeps output focused on meaningful data. | ### Pattern Helpers Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| getPatternName uses patternName tag when available | getPatternName must return the patternName tag value when set, falling back to the pattern's name field when the tag is absent. | The patternName tag allows human-friendly display names — without the fallback, patterns missing the tag would display as undefined. | -| findPatternByName performs case-insensitive matching | findPatternByName must match pattern names case-insensitively, returning undefined when no match exists. | Case-insensitive matching prevents frustrating "not found" errors when developers type "processguard" instead of "ProcessGuard" — both clearly refer to the same pattern. | -| getRelationships looks up with case-insensitive fallback | getRelationships must first try exact key lookup in the relationship index, then fall back to case-insensitive matching, returning undefined when no match exists. | Exact-first with case-insensitive fallback balances performance (O(1) exact lookup) with usability (tolerates case mismatches in cross-references). | -| suggestPattern provides fuzzy suggestions | suggestPattern must return fuzzy match suggestions for close pattern names, returning empty results when no close match exists. | Fuzzy suggestions power "did you mean?" UX in the CLI — without them, typos produce unhelpful "pattern not found" messages. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| getPatternName uses patternName tag when available | getPatternName must return the patternName tag value when set, falling back to the pattern's name field when the tag is absent. | The patternName tag allows human-friendly display names — without the fallback, patterns missing the tag would display as undefined. | +| findPatternByName performs case-insensitive matching | findPatternByName must match pattern names case-insensitively, returning undefined when no match exists. | Case-insensitive matching prevents frustrating "not found" errors when developers type "processguard" instead of "ProcessGuard" — both clearly refer to the same pattern. | +| getRelationships looks up with case-insensitive fallback | getRelationships must first try exact key lookup in the relationship index, then fall back to case-insensitive matching, returning undefined when no match exists. | Exact-first with case-insensitive fallback balances performance (O(1) exact lookup) with usability (tolerates case mismatches in cross-references). | +| suggestPattern provides fuzzy suggestions | suggestPattern must return fuzzy match suggestions for close pattern names, returning empty results when no close match exists. | Fuzzy suggestions power "did you mean?" UX in the CLI — without them, typos produce unhelpful "pattern not found" messages. | ### Pattern Summarize Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | summarizePattern projects to compact summary | summarizePattern must project a full pattern object to a compact summary containing exactly 6 fields, using the patternName tag over the name field when available and omitting undefined optional fields. | Compact summaries reduce token usage by 80-90% compared to full patterns — they provide enough context for navigation without overwhelming AI context windows. | -| summarizePatterns batch processes arrays | summarizePatterns must batch-process an array of patterns, returning a correctly-sized array of compact summaries. | Batch processing avoids N individual function calls — the API frequently needs to summarize all patterns matching a query in a single operation. | +| summarizePatterns batch processes arrays | summarizePatterns must batch-process an array of patterns, returning a correctly-sized array of compact summaries. | Batch processing avoids N individual function calls — the API frequently needs to summarize all patterns matching a query in a single operation. | ### PDR 001 Session Workflow Commands -| Rule | Invariant | Rationale | -| --- | --- | --- | -| DD-1 - Text output with section markers | scope-validate and handoff must return plain text with === SECTION === markers, never JSON. | Inconsistent output formats force consumers to detect and branch on format type, breaking the dual output path contract. | -| DD-2 - Git integration is opt-in via --git flag | Domain logic must never invoke shell commands or depend on git directly. | Shell dependencies in domain logic make functions untestable without git fixtures and break deterministic behavior. | -| DD-3 - Session type inferred from FSM status | Every FSM status must map to exactly one default session type, overridable by an explicit --session flag. | Ambiguous or missing inference forces users to always specify --session manually, defeating the ergonomic benefit of status-based defaults. | -| DD-4 - Severity levels match Process Guard model | Scope validation must use exactly three severity levels (PASS, BLOCKED, WARN) consistent with Process Guard. | Divergent severity models cause confusion when the same violation appears in both systems with different severity classifications. | -| DD-5 - Current date only for handoff | Handoff must always use the current system date with no override mechanism. | A --date flag enables backdating handoff timestamps, which breaks audit trail integrity for multi-session work. | -| DD-6 - Both positional and flag forms for scope type | scope-validate must accept scope type as both a positional argument and a --type flag. | Supporting only one form creates inconsistency with CLI conventions and forces users to remember which form each subcommand uses. | -| DD-7 - Co-located formatter functions | Each module must export both its data builder and text formatter as co-located functions. | Splitting builder and formatter across files increases coupling surface and makes it harder to trace data flow through the module. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | +| DD-1 - Text output with section markers | scope-validate and handoff must return plain text with === SECTION === markers, never JSON. | Inconsistent output formats force consumers to detect and branch on format type, breaking the dual output path contract. | +| DD-2 - Git integration is opt-in via --git flag | Domain logic must never invoke shell commands or depend on git directly. | Shell dependencies in domain logic make functions untestable without git fixtures and break deterministic behavior. | +| DD-3 - Session type inferred from FSM status | Every FSM status must map to exactly one default session type, overridable by an explicit --session flag. | Ambiguous or missing inference forces users to always specify --session manually, defeating the ergonomic benefit of status-based defaults. | +| DD-4 - Severity levels match Process Guard model | Scope validation must use exactly three severity levels (PASS, BLOCKED, WARN) consistent with Process Guard. | Divergent severity models cause confusion when the same violation appears in both systems with different severity classifications. | +| DD-5 - Current date only for handoff | Handoff must always use the current system date with no override mechanism. | A --date flag enables backdating handoff timestamps, which breaks audit trail integrity for multi-session work. | +| DD-6 - Both positional and flag forms for scope type | scope-validate must accept scope type as both a positional argument and a --type flag. | Supporting only one form creates inconsistency with CLI conventions and forces users to remember which form each subcommand uses. | +| DD-7 - Co-located formatter functions | Each module must export both its data builder and text formatter as co-located functions. | Splitting builder and formatter across files increases coupling surface and makes it harder to trace data flow through the module. | ### Process Api Cli Core -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The CLI must always provide discoverable usage and version information via standard flags. | Without accessible help and version output, users cannot self-serve CLI usage or report issues with a specific version. | -| CLI requires input flag for subcommands | Every data-querying subcommand must receive an explicit `--input` glob specifying the source files to scan. | Without an input source, the pipeline has no files to scan and would produce empty or misleading results instead of a clear error. | -| CLI status subcommand shows delivery state | The status subcommand must return structured JSON containing delivery progress derived from the MasterDataset. | Consumers depend on machine-readable status output for scripting and CI integration; unstructured output breaks downstream automation. | -| CLI query subcommand executes API methods | The query subcommand must dispatch to any public Data API method by name and pass positional arguments through. | The CLI is the primary interface for ad-hoc queries; failing to resolve a valid method name or its arguments silently drops the user's request. | -| CLI pattern subcommand shows pattern detail | The pattern subcommand must return the full JSON detail for an exact pattern name match, or a clear error if not found. | Pattern lookup is the primary debugging tool for annotation issues; ambiguous or silent failures waste investigation time. | -| CLI arch subcommand queries architecture | The arch subcommand must expose role, bounded context, and layer queries over the MasterDataset's architecture metadata. | Architecture queries replace manual exploration of annotated sources; missing or incorrect results lead to wrong structural assumptions during design sessions. | -| CLI shows errors for missing subcommand arguments | Subcommands that require arguments must reject invocations with missing arguments and display usage guidance. | Silent acceptance of incomplete input would produce confusing pipeline errors instead of actionable feedback at the CLI boundary. | -| CLI handles argument edge cases | The CLI must gracefully handle non-standard argument forms including numeric coercion and the `--` pnpm separator. | Real-world invocations via pnpm pass `--` separators and numeric strings; mishandling these causes silent data loss or crashes in automated workflows. | +| Rule | Invariant | Rationale | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The CLI must always provide discoverable usage and version information via standard flags. | Without accessible help and version output, users cannot self-serve CLI usage or report issues with a specific version. | +| CLI requires input flag for subcommands | Every data-querying subcommand must receive an explicit `--input` glob specifying the source files to scan. | Without an input source, the pipeline has no files to scan and would produce empty or misleading results instead of a clear error. | +| CLI status subcommand shows delivery state | The status subcommand must return structured JSON containing delivery progress derived from the MasterDataset. | Consumers depend on machine-readable status output for scripting and CI integration; unstructured output breaks downstream automation. | +| CLI query subcommand executes API methods | The query subcommand must dispatch to any public Data API method by name and pass positional arguments through. | The CLI is the primary interface for ad-hoc queries; failing to resolve a valid method name or its arguments silently drops the user's request. | +| CLI pattern subcommand shows pattern detail | The pattern subcommand must return the full JSON detail for an exact pattern name match, or a clear error if not found. | Pattern lookup is the primary debugging tool for annotation issues; ambiguous or silent failures waste investigation time. | +| CLI arch subcommand queries architecture | The arch subcommand must expose role, bounded context, and layer queries over the MasterDataset's architecture metadata. | Architecture queries replace manual exploration of annotated sources; missing or incorrect results lead to wrong structural assumptions during design sessions. | +| CLI shows errors for missing subcommand arguments | Subcommands that require arguments must reject invocations with missing arguments and display usage guidance. | Silent acceptance of incomplete input would produce confusing pipeline errors instead of actionable feedback at the CLI boundary. | +| CLI handles argument edge cases | The CLI must gracefully handle non-standard argument forms including numeric coercion and the `--` pnpm separator. | Real-world invocations via pnpm pass `--` separators and numeric strings; mishandling these causes silent data loss or crashes in automated workflows. | ### Process Api Cli Modifiers And Rules -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Output modifiers work when placed after the subcommand | Output modifiers (--count, --names-only, --fields) produce identical results regardless of position relative to the subcommand and its filters. | Users should not need to memorize argument ordering rules; the CLI should be forgiving. | -| CLI arch health subcommands detect graph quality issues | Health subcommands (dangling, orphans, blocking) operate on the relationship index, not the architecture index, and return results without requiring arch annotations. | Graph quality issues (broken references, isolated patterns, blocked dependencies) are relationship-level concerns that should be queryable even when no architecture metadata exists. | -| CLI rules subcommand queries business rules and invariants | The rules subcommand returns structured business rules extracted from Gherkin Rule: blocks, grouped by product area and phase, with parsed invariant and rationale annotations. | Live business rule queries replace static generated markdown, enabling on-demand filtering by product area, pattern, and invariant presence. | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Output modifiers work when placed after the subcommand | Output modifiers (--count, --names-only, --fields) produce identical results regardless of position relative to the subcommand and its filters. | Users should not need to memorize argument ordering rules; the CLI should be forgiving. | +| CLI arch health subcommands detect graph quality issues | Health subcommands (dangling, orphans, blocking) operate on the relationship index, not the architecture index, and return results without requiring arch annotations. | Graph quality issues (broken references, isolated patterns, blocked dependencies) are relationship-level concerns that should be queryable even when no architecture metadata exists. | +| CLI rules subcommand queries business rules and invariants | The rules subcommand returns structured business rules extracted from Gherkin Rule: blocks, grouped by product area and phase, with parsed invariant and rationale annotations. | Live business rule queries replace static generated markdown, enabling on-demand filtering by product area, pattern, and invariant presence. | ### Process Api Cli Subcommands -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI list subcommand filters patterns | The list subcommand must return a valid JSON result for valid filters and a non-zero exit code with a descriptive error for invalid filters. | Consumers parse list output programmatically; malformed JSON or silent failures cause downstream tooling to break without diagnosis. | -| CLI search subcommand finds patterns by fuzzy match | The search subcommand must require a query argument and return only patterns whose names match the query. | Missing query validation would produce unfiltered result sets, defeating the purpose of search and wasting context budget in AI sessions. | -| CLI context assembly subcommands return text output | Context assembly subcommands (context, overview, dep-tree) must produce non-empty human-readable text containing the requested pattern or summary, and require a pattern argument where applicable. | These subcommands replace manual file reads in AI sessions; empty or off-target output forces expensive explore-agent fallbacks that consume 5-10x more context. | -| CLI tags and sources subcommands return JSON | The tags and sources subcommands must return valid JSON with the expected top-level structure (data key for tags, array for sources). | Annotation exploration depends on machine-parseable output; invalid JSON prevents automated enrichment workflows from detecting unannotated files and tag gaps. | -| CLI extended arch subcommands query architecture relationships | Extended arch subcommands (neighborhood, compare, coverage) must return valid JSON reflecting the actual architecture relationships present in the scanned sources. | Architecture queries drive design-session decisions; stale or structurally invalid output leads to incorrect dependency analysis and missed coupling between bounded contexts. | -| CLI unannotated subcommand finds files without annotations | The unannotated subcommand must return valid JSON listing every TypeScript file that lacks the `@libar-docs` opt-in marker. | Files missing the opt-in marker are invisible to the scanner; without this subcommand, unannotated files silently drop out of generated documentation and validation. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| CLI list subcommand filters patterns | The list subcommand must return a valid JSON result for valid filters and a non-zero exit code with a descriptive error for invalid filters. | Consumers parse list output programmatically; malformed JSON or silent failures cause downstream tooling to break without diagnosis. | +| CLI search subcommand finds patterns by fuzzy match | The search subcommand must require a query argument and return only patterns whose names match the query. | Missing query validation would produce unfiltered result sets, defeating the purpose of search and wasting context budget in AI sessions. | +| CLI context assembly subcommands return text output | Context assembly subcommands (context, overview, dep-tree) must produce non-empty human-readable text containing the requested pattern or summary, and require a pattern argument where applicable. | These subcommands replace manual file reads in AI sessions; empty or off-target output forces expensive explore-agent fallbacks that consume 5-10x more context. | +| CLI tags and sources subcommands return JSON | The tags and sources subcommands must return valid JSON with the expected top-level structure (data key for tags, array for sources). | Annotation exploration depends on machine-parseable output; invalid JSON prevents automated enrichment workflows from detecting unannotated files and tag gaps. | +| CLI extended arch subcommands query architecture relationships | Extended arch subcommands (neighborhood, compare, coverage) must return valid JSON reflecting the actual architecture relationships present in the scanned sources. | Architecture queries drive design-session decisions; stale or structurally invalid output leads to incorrect dependency analysis and missed coupling between bounded contexts. | +| CLI unannotated subcommand finds files without annotations | The unannotated subcommand must return valid JSON listing every TypeScript file that lacks the `@libar-docs` opt-in marker. | Files missing the opt-in marker are invisible to the scanner; without this subcommand, unannotated files silently drop out of generated documentation and validation. | ### Process API Layered Extraction -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI file contains only routing, no domain logic | `process-api.ts` parses arguments, calls the pipeline factory for the MasterDataset, routes subcommands to API modules, and formats output. It does not build Maps, filter patterns, group data, or resolve relationships. Thin view projections (3-5 line `.map()` calls over pre-computed archIndex views) are acceptable as formatting. | Domain logic in the CLI file is only accessible via the command line. Extracting it to `src/api/` makes it programmatically testable, reusable by future consumers (MCP server, watch mode), and aligned with the feature-consumption layer defined in ADR-006. | -| Pipeline factory is shared across CLI consumers | The scan-extract-transform sequence is defined once in `src/generators/pipeline/build-pipeline.ts`. CLI consumers that need a MasterDataset call the factory rather than wiring the pipeline independently. The factory accepts `mergeConflictStrategy` to handle behavioral differences between consumers. | Three consumers (process-api, validate-patterns, orchestrator) independently wire the same 8-step sequence: loadConfig, scanPatterns, extractPatterns, scanGherkinFiles, extractPatternsFromGherkin, mergePatterns, computeHierarchyChildren, transformToMasterDataset. The only semantic difference is merge-conflict handling (fatal vs concatenate). This is a Parallel Pipeline anti-pattern per ADR-006. | -| Domain logic lives in API modules | Query logic that operates on MasterDataset lives in `src/api/` modules. The `rules-query.ts` module provides business rules querying with the same grouping logic that was inline in handleRules: filter by product area and pattern, group by area -> phase -> feature -> rules, parse annotations, compute totals. | `handleRules` is 184 lines with 5 Map/Set constructions, codec-layer imports (`parseBusinessRuleAnnotations`, `deduplicateScenarioNames`), and a complex 3-level grouping algorithm. This is the last significant inline domain logic in process-api.ts. Moving it to `src/api/` follows the same pattern as the 12 existing API modules (context-assembler, arch-queries, scope-validator, etc.). | -| Pipeline factory returns Result for consumer-owned error handling | The factory returns `Result` rather than throwing or calling `process.exit()`. Each consumer maps the error to its own strategy: process-api.ts calls `process.exit(1)`, validate-patterns.ts throws, and orchestrator.ts (future) returns `Result.err()`. | The current `buildPipeline()` in process-api.ts calls `process.exit(1)` on errors, making it non-reusable. The factory must work across consumers with different error handling models. The Result monad is the project's established pattern for this (see `src/types/result.ts`). | -| End-to-end verification confirms behavioral equivalence | After extraction, all CLI commands produce identical output to pre-refactor behavior with zero build, test, lint, and validation errors. | The refactor must not change observable behavior. Full CLI verification confirms the extraction is a pure refactor. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI file contains only routing, no domain logic | `process-api.ts` parses arguments, calls the pipeline factory for the MasterDataset, routes subcommands to API modules, and formats output. It does not build Maps, filter patterns, group data, or resolve relationships. Thin view projections (3-5 line `.map()` calls over pre-computed archIndex views) are acceptable as formatting. | Domain logic in the CLI file is only accessible via the command line. Extracting it to `src/api/` makes it programmatically testable, reusable by future consumers (MCP server, watch mode), and aligned with the feature-consumption layer defined in ADR-006. | +| Pipeline factory is shared across CLI consumers | The scan-extract-transform sequence is defined once in `src/generators/pipeline/build-pipeline.ts`. CLI consumers that need a MasterDataset call the factory rather than wiring the pipeline independently. The factory accepts `mergeConflictStrategy` to handle behavioral differences between consumers. | Three consumers (process-api, validate-patterns, orchestrator) independently wire the same 8-step sequence: loadConfig, scanPatterns, extractPatterns, scanGherkinFiles, extractPatternsFromGherkin, mergePatterns, computeHierarchyChildren, transformToMasterDataset. The only semantic difference is merge-conflict handling (fatal vs concatenate). This is a Parallel Pipeline anti-pattern per ADR-006. | +| Domain logic lives in API modules | Query logic that operates on MasterDataset lives in `src/api/` modules. The `rules-query.ts` module provides business rules querying with the same grouping logic that was inline in handleRules: filter by product area and pattern, group by area -> phase -> feature -> rules, parse annotations, compute totals. | `handleRules` is 184 lines with 5 Map/Set constructions, codec-layer imports (`parseBusinessRuleAnnotations`, `deduplicateScenarioNames`), and a complex 3-level grouping algorithm. This is the last significant inline domain logic in process-api.ts. Moving it to `src/api/` follows the same pattern as the 12 existing API modules (context-assembler, arch-queries, scope-validator, etc.). | +| Pipeline factory returns Result for consumer-owned error handling | The factory returns `Result` rather than throwing or calling `process.exit()`. Each consumer maps the error to its own strategy: process-api.ts calls `process.exit(1)`, validate-patterns.ts throws, and orchestrator.ts (future) returns `Result.err()`. | The current `buildPipeline()` in process-api.ts calls `process.exit(1)` on errors, making it non-reusable. The factory must work across consumers with different error handling models. The Result monad is the project's established pattern for this (see `src/types/result.ts`). | +| End-to-end verification confirms behavioral equivalence | After extraction, all CLI commands produce identical output to pre-refactor behavior with zero build, test, lint, and validation errors. | The refactor must not change observable behavior. Full CLI verification confirms the extraction is a pure refactor. | ### Process Api Reference Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Generated reference file contains all three table sections | PROCESS-API-REFERENCE.md contains Global Options, Output Modifiers, and List Filters tables generated from the CLI schema. | | -| CLI schema stays in sync with parser | Every flag recognized by parseArgs() has a corresponding entry in the CLI schema. A missing schema entry means the sync test fails. | | -| showHelp output reflects CLI schema | The help text rendered by showHelp() includes all options from the CLI schema, formatted for terminal display. | | +| Rule | Invariant | Rationale | +| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------- | +| Generated reference file contains all three table sections | PROCESS-API-REFERENCE.md contains Global Options, Output Modifiers, and List Filters tables generated from the CLI schema. | | +| CLI schema stays in sync with parser | Every flag recognized by parseArgs() has a corresponding entry in the CLI schema. A missing schema entry means the sync test fails. | | +| showHelp output reflects CLI schema | The help text rendered by showHelp() includes all options from the CLI schema, formatted for terminal display. | | ### Process State API CLI -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| ----------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | CLI supports status-based pattern queries | Every ProcessStateAPI status query method is accessible via CLI. | The most common planning question is "what's the current state?" Status queries (active, roadmap, completed) answer this directly without reading docs. Without CLI access, Claude Code must regenerate markdown and parse unstructured text. | -| CLI supports phase-based queries | Patterns can be filtered by phase number. | Phase 18 (Event Durability) is the current focus per roadmap priorities. Quick phase queries help assess progress and remaining work within a phase. Phase-based planning is the primary organization method for roadmap work. | -| CLI provides progress summary queries | Overall and per-phase progress is queryable in a single command. | Planning sessions need quick answers to "where are we?" without reading the full PATTERNS.md generated file. Progress metrics drive prioritization and help identify where to focus effort. | -| CLI supports multiple output formats | JSON output is parseable by AI agents without transformation. | Claude Code can parse JSON directly. Text format is for human reading. JSON format enables scripting and integration with other tools. The primary use case is AI agent parsing where structured output reduces context and errors. | -| CLI supports individual pattern lookup | Any pattern can be queried by name with full details. | During implementation, Claude Code needs to check specific pattern status, deliverables, and dependencies without reading the full spec file. Pattern lookup is essential for focused implementation work. | -| CLI provides discoverable help | All flags are documented via --help with examples. | Claude Code can read --help output to understand available queries without needing external documentation. Self-documenting CLIs reduce the need for Claude Code to read additional context files. | +| CLI supports phase-based queries | Patterns can be filtered by phase number. | Phase 18 (Event Durability) is the current focus per roadmap priorities. Quick phase queries help assess progress and remaining work within a phase. Phase-based planning is the primary organization method for roadmap work. | +| CLI provides progress summary queries | Overall and per-phase progress is queryable in a single command. | Planning sessions need quick answers to "where are we?" without reading the full PATTERNS.md generated file. Progress metrics drive prioritization and help identify where to focus effort. | +| CLI supports multiple output formats | JSON output is parseable by AI agents without transformation. | Claude Code can parse JSON directly. Text format is for human reading. JSON format enables scripting and integration with other tools. The primary use case is AI agent parsing where structured output reduces context and errors. | +| CLI supports individual pattern lookup | Any pattern can be queried by name with full details. | During implementation, Claude Code needs to check specific pattern status, deliverables, and dependencies without reading the full spec file. Pattern lookup is essential for focused implementation work. | +| CLI provides discoverable help | All flags are documented via --help with examples. | Claude Code can read --help output to understand available queries without needing external documentation. Self-documenting CLIs reduce the need for Claude Code to read additional context files. | ### Process State API Relationship Queries -| Rule | Invariant | Rationale | -| --- | --- | --- | -| API provides implementation relationship queries | Every pattern with `implementedBy` entries is discoverable via the API. | Claude Code needs to navigate from abstract patterns to concrete code. Without this, exploration requires manual grep + file reading, wasting context tokens. | -| API provides inheritance hierarchy queries | Pattern inheritance chains are fully navigable in both directions. | Patterns form specialization hierarchies (e.g., ReactiveProjections extends ProjectionCategories). Claude Code needs to understand what specializes a base pattern and what a specialized pattern inherits from. | -| API provides combined relationship views | All relationship types are accessible through a unified interface. | Claude Code often needs the complete picture: dependencies AND implementations AND inheritance. A single call reduces round-trips and context switching. | -| API supports bidirectional traceability queries | Navigation from spec to code and code to spec is symmetric. | Traceability is bidirectional by definition. If a spec links to code, the code should link back to the spec. The API should surface broken links. | +| Rule | Invariant | Rationale | +| ------------------------------------------------ | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| API provides implementation relationship queries | Every pattern with `implementedBy` entries is discoverable via the API. | Claude Code needs to navigate from abstract patterns to concrete code. Without this, exploration requires manual grep + file reading, wasting context tokens. | +| API provides inheritance hierarchy queries | Pattern inheritance chains are fully navigable in both directions. | Patterns form specialization hierarchies (e.g., ReactiveProjections extends ProjectionCategories). Claude Code needs to understand what specializes a base pattern and what a specialized pattern inherits from. | +| API provides combined relationship views | All relationship types are accessible through a unified interface. | Claude Code often needs the complete picture: dependencies AND implementations AND inheritance. A single call reduces round-trips and context switching. | +| API supports bidirectional traceability queries | Navigation from spec to code and code to spec is symmetric. | Traceability is bidirectional by definition. If a spec links to code, the code should link back to the spec. The API should surface broken links. | ### Process State API Testing -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Status queries return correct patterns | Status queries must correctly filter by both normalized status (planned = roadmap + deferred) and FSM status (exact match). | The two-domain status convention requires separate query methods — mixing them produces incorrect filtered results. | -| Phase queries return correct phase data | Phase queries must return only patterns in the requested phase, with accurate progress counts and completion percentage. | Phase-level queries power the roadmap and session planning views — incorrect counts cascade into wrong progress percentages. | -| FSM queries expose transition validation | FSM queries must validate transitions against the PDR-005 state machine and expose protection levels per status. | Programmatic FSM access enables tooling to enforce delivery process rules without reimplementing the state machine. | -| Pattern queries find and retrieve pattern data | Pattern lookup must be case-insensitive by name, and category queries must return only patterns with the requested category. | Case-insensitive search reduces friction in CLI and AI agent usage where exact casing is often unknown. | -| Timeline queries group patterns by time | Quarter queries must correctly filter by quarter string, and recently completed must be sorted by date descending with limit. | Timeline grouping enables quarterly reporting and session context — recent completions show delivery momentum. | +| Rule | Invariant | Rationale | +| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| Status queries return correct patterns | Status queries must correctly filter by both normalized status (planned = roadmap + deferred) and FSM status (exact match). | The two-domain status convention requires separate query methods — mixing them produces incorrect filtered results. | +| Phase queries return correct phase data | Phase queries must return only patterns in the requested phase, with accurate progress counts and completion percentage. | Phase-level queries power the roadmap and session planning views — incorrect counts cascade into wrong progress percentages. | +| FSM queries expose transition validation | FSM queries must validate transitions against the PDR-005 state machine and expose protection levels per status. | Programmatic FSM access enables tooling to enforce delivery process rules without reimplementing the state machine. | +| Pattern queries find and retrieve pattern data | Pattern lookup must be case-insensitive by name, and category queries must return only patterns with the requested category. | Case-insensitive search reduces friction in CLI and AI agent usage where exact casing is often unknown. | +| Timeline queries group patterns by time | Quarter queries must correctly filter by quarter string, and recently completed must be sorted by date descending with limit. | Timeline grouping enables quarterly reporting and session context — recent completions show delivery momentum. | ### Scope Validator Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Implementation scope validation checks all prerequisites | Implementation scope validation must check FSM transition validity, dependency completeness, PDR references, and deliverable presence, with strict mode promoting warnings to blockers. | Starting implementation without passing scope validation wastes an entire session — the validator catches all known blockers before any code is written. | -| Design scope validation checks dependency stubs | Design scope validation must verify that dependencies have corresponding code stubs, producing warnings when stubs are missing. | Design sessions that reference unstubbed dependencies cannot produce actionable interfaces — stub presence indicates the dependency's API surface is at least sketched. | -| Formatter produces structured text output | The scope validator formatter must produce structured text with ADR-008 markers, showing verdict text for warnings and blocker details for blocked verdicts. | Structured formatter output enables the CLI to display verdicts consistently — unstructured output would vary by validation type and be hard to parse. | +| Rule | Invariant | Rationale | +| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Implementation scope validation checks all prerequisites | Implementation scope validation must check FSM transition validity, dependency completeness, PDR references, and deliverable presence, with strict mode promoting warnings to blockers. | Starting implementation without passing scope validation wastes an entire session — the validator catches all known blockers before any code is written. | +| Design scope validation checks dependency stubs | Design scope validation must verify that dependencies have corresponding code stubs, producing warnings when stubs are missing. | Design sessions that reference unstubbed dependencies cannot produce actionable interfaces — stub presence indicates the dependency's API surface is at least sketched. | +| Formatter produces structured text output | The scope validator formatter must produce structured text with ADR-008 markers, showing verdict text for warnings and blocker details for blocked verdicts. | Structured formatter output enables the CLI to display verdicts consistently — unstructured output would vary by validation type and be hard to parse. | ### Stub Resolver Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | -| Stubs are identified by path or target metadata | A pattern must be identified as a stub if it resides in the stubs directory OR has a targetPath metadata field. | Dual identification supports both convention-based (directory) and metadata-based (targetPath) stub detection — relying on only one would miss stubs organized differently. | -| Stubs are resolved against the filesystem | Resolved stubs must show whether their target file exists on the filesystem and must be grouped by the pattern they implement. | Target existence status tells developers whether a stub has been implemented — grouping by pattern enables the "stubs --unresolved" command to show per-pattern implementation gaps. | -| Decision items are extracted from descriptions | AD-N formatted items must be extracted from pattern description text, with empty descriptions returning no items and malformed items being skipped. | Decision items (AD-1, AD-2, etc.) link stubs to architectural decisions — extracting them enables traceability from code stubs back to the design rationale. | -| PDR references are found across patterns | The resolver must find all patterns that reference a given PDR identifier, returning empty results when no references exist. | PDR cross-referencing enables impact analysis — knowing which patterns reference a decision helps assess the blast radius of changing that decision. | +| Rule | Invariant | Rationale | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Stubs are identified by path or target metadata | A pattern must be identified as a stub if it resides in the stubs directory OR has a targetPath metadata field. | Dual identification supports both convention-based (directory) and metadata-based (targetPath) stub detection — relying on only one would miss stubs organized differently. | +| Stubs are resolved against the filesystem | Resolved stubs must show whether their target file exists on the filesystem and must be grouped by the pattern they implement. | Target existence status tells developers whether a stub has been implemented — grouping by pattern enables the "stubs --unresolved" command to show per-pattern implementation gaps. | +| Decision items are extracted from descriptions | AD-N formatted items must be extracted from pattern description text, with empty descriptions returning no items and malformed items being skipped. | Decision items (AD-1, AD-2, etc.) link stubs to architectural decisions — extracting them enables traceability from code stubs back to the design rationale. | +| PDR references are found across patterns | The resolver must find all patterns that reference a given PDR identifier, returning empty results when no references exist. | PDR cross-referencing enables impact analysis — knowing which patterns reference a decision helps assess the blast radius of changing that decision. | ### Stub Taxonomy Tag Tests -| Rule | Invariant | Rationale | -| --- | --- | --- | +| Rule | Invariant | Rationale | +| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | Taxonomy tags are registered in the registry | The target and since stub metadata tags must be registered in the tag registry as recognized taxonomy entries. | Unregistered tags would be flagged as unknown by the linter — registration ensures stub metadata tags pass validation alongside standard annotation tags. | -| Tags are part of the stub metadata group | The target and since tags must be grouped under the stub metadata domain in the built registry. | Domain grouping enables the taxonomy codec to render stub metadata tags in their own section — ungrouped tags would be lost in the "Other" category. | +| Tags are part of the stub metadata group | The target and since tags must be grouped under the stub metadata domain in the built registry. | Domain grouping enables the taxonomy codec to render stub metadata tags in their own section — ungrouped tags would be lost in the "Other" category. | ### Validate Patterns Cli -| Rule | Invariant | Rationale | -| --- | --- | --- | -| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | -| CLI requires input and feature patterns | The validate-patterns CLI must fail with clear errors when either --input or --features flags are missing. | Cross-source validation requires both TypeScript and Gherkin inputs — running with only one source would produce incomplete validation that misses cross-source mismatches. | -| CLI validates patterns across TypeScript and Gherkin sources | The validator must detect mismatches between TypeScript and Gherkin sources including phase and status discrepancies. | Dual-source architecture requires consistency — a pattern with status "active" in TypeScript but "roadmap" in Gherkin creates conflicting truth and broken reports. | -| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive use while JSON format enables CI/CD pipeline integration and programmatic consumption of validation results. | -| Strict mode treats warnings as errors | When --strict is enabled, warnings must be promoted to errors causing a non-zero exit code (exit 2); without --strict, warnings must not cause failure. | CI pipelines need strict enforcement while local development benefits from lenient mode — the flag lets teams choose their enforcement level. | -| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Pattern validation is non-destructive — warning without failing is more user-friendly than hard errors for minor flag typos, while still surfacing the issue. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CLI displays help and version information | The --help/-h and --version/-v flags must produce usage/version output and exit successfully without requiring other arguments. | Help and version are universal CLI conventions — both short and long flag forms must work for discoverability and scripting compatibility. | +| CLI requires input and feature patterns | The validate-patterns CLI must fail with clear errors when either --input or --features flags are missing. | Cross-source validation requires both TypeScript and Gherkin inputs — running with only one source would produce incomplete validation that misses cross-source mismatches. | +| CLI validates patterns across TypeScript and Gherkin sources | The validator must detect mismatches between TypeScript and Gherkin sources including phase and status discrepancies. | Dual-source architecture requires consistency — a pattern with status "active" in TypeScript but "roadmap" in Gherkin creates conflicting truth and broken reports. | +| CLI supports multiple output formats | The CLI must support JSON and pretty (human-readable) output formats, with pretty as the default. | Pretty format serves interactive use while JSON format enables CI/CD pipeline integration and programmatic consumption of validation results. | +| Strict mode treats warnings as errors | When --strict is enabled, warnings must be promoted to errors causing a non-zero exit code (exit 2); without --strict, warnings must not cause failure. | CI pipelines need strict enforcement while local development benefits from lenient mode — the flag lets teams choose their enforcement level. | +| CLI warns about unknown flags | Unrecognized CLI flags must produce a warning message but allow execution to continue. | Pattern validation is non-destructive — warning without failing is more user-friendly than hard errors for minor flag typos, while still surfacing the issue. | --- diff --git a/docs-live/product-areas/GENERATION.md b/docs-live/product-areas/GENERATION.md index 0fb73532..031b2172 100644 --- a/docs-live/product-areas/GENERATION.md +++ b/docs-live/product-areas/GENERATION.md @@ -62,26 +62,31 @@ graph TB SourceMapper[/"SourceMapper"/] Documentation_Generation_Orchestrator("Documentation Generation Orchestrator") TransformDataset("TransformDataset") + SequenceTransformUtils("SequenceTransformUtils") ProcessApiReferenceGenerator["ProcessApiReferenceGenerator"] + DesignReviewGenerator("DesignReviewGenerator") DecisionDocGenerator("DecisionDocGenerator") CliRecipeGenerator["CliRecipeGenerator"] end subgraph renderer["Renderer"] loadPreambleFromMarkdown___Shared_Markdown_to_SectionBlock_Parser["loadPreambleFromMarkdown — Shared Markdown-to-SectionBlock Parser"] PatternsCodec[("PatternsCodec")] + MermaidDiagramUtils["MermaidDiagramUtils"] + DesignReviewCodec[("DesignReviewCodec")] DecisionDocCodec[("DecisionDocCodec")] CompositeCodec[("CompositeCodec")] ArchitectureCodec[("ArchitectureCodec")] end subgraph related["Related"] - MasterDataset["MasterDataset"]:::neighbor Pattern_Scanner["Pattern Scanner"]:::neighbor GherkinASTParser["GherkinASTParser"]:::neighbor + MasterDataset["MasterDataset"]:::neighbor ShapeExtractor["ShapeExtractor"]:::neighbor ReferenceDocShowcase["ReferenceDocShowcase"]:::neighbor ProcessApiHybridGeneration["ProcessApiHybridGeneration"]:::neighbor ProceduralGuideCodec["ProceduralGuideCodec"]:::neighbor PatternRelationshipModel["PatternRelationshipModel"]:::neighbor + DesignReviewGeneration["DesignReviewGeneration"]:::neighbor CliRecipeCodec["CliRecipeCodec"]:::neighbor end loadPreambleFromMarkdown___Shared_Markdown_to_SectionBlock_Parser ..->|implements| ProceduralGuideCodec @@ -90,14 +95,23 @@ graph TB SourceMapper -.->|depends on| GherkinASTParser Documentation_Generation_Orchestrator -->|uses| Pattern_Scanner PatternsCodec ..->|implements| PatternRelationshipModel + DesignReviewCodec -->|uses| MasterDataset + DesignReviewCodec -->|uses| MermaidDiagramUtils + DesignReviewCodec ..->|implements| DesignReviewGeneration CompositeCodec ..->|implements| ReferenceDocShowcase ArchitectureCodec -->|uses| MasterDataset TransformDataset -->|uses| MasterDataset TransformDataset ..->|implements| PatternRelationshipModel + SequenceTransformUtils -->|uses| MasterDataset + SequenceTransformUtils ..->|implements| DesignReviewGeneration ProcessApiReferenceGenerator ..->|implements| ProcessApiHybridGeneration + DesignReviewGenerator -->|uses| DesignReviewCodec + DesignReviewGenerator -->|uses| MasterDataset + DesignReviewGenerator ..->|implements| DesignReviewGeneration DecisionDocGenerator -.->|depends on| DecisionDocCodec DecisionDocGenerator -.->|depends on| SourceMapper CliRecipeGenerator ..->|implements| CliRecipeCodec + DesignReviewGeneration -.->|depends on| MermaidDiagramUtils CliRecipeCodec -.->|depends on| ProcessApiHybridGeneration classDef neighbor stroke-dasharray: 5 5 ``` @@ -280,7 +294,7 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; ## Business Rules -88 patterns, 423 rules with invariants (424 total) +91 patterns, 439 rules with invariants (440 total) ### ADR 005 Codec Based Markdown Rendering @@ -324,7 +338,7 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; | Rule | Invariant | Rationale | | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| Layered diagrams group patterns by architectural layer | Layered diagrams must render patterns grouped by architectural layer (domain, application, infrastructure) with top-to-bottom flow. | Layered architecture visualization shows dependency direction - infrastructure at top, domain at bottom - following conventional layer ordering. | +| Layered diagrams group patterns by architectural layer | Layered diagrams must render patterns grouped by architectural layer (domain, application, infrastructure) with top-to-bottom flow. | Layered architecture visualization shows dependency direction - domain at top, infrastructure at bottom - following conventional layer ordering. | | Architecture generator is registered with generator registry | An "architecture" generator must be registered with the generator registry to enable `pnpm docs:architecture` via the existing `generate-docs.js` CLI. | The delivery-process uses a generator registry pattern. New generators register with the orchestrator rather than creating separate CLI commands. | | Sequence diagrams render interaction flows | Sequence diagrams must render interaction flows (command flow, saga flow) showing step-by-step message passing between components. | Component diagrams show structure but not behavior. Sequence diagrams show runtime flow - essential for understanding command/saga execution. | @@ -417,7 +431,7 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; | Rule | Invariant | Rationale | | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | CLI recipes are a separate generator from reference tables | The `CliRecipeGenerator` is a standalone sibling to `ProcessApiReferenceGenerator`, not an extension of it. Both implement `DocumentGenerator`, both consume `CLI_SCHEMA` directly, and both produce independent `OutputFile[]` via the standard orchestrator write path. The recipe generator produces `docs-live/reference/PROCESS-API-RECIPES.md` while the reference generator produces `docs-live/reference/PROCESS-API-REFERENCE.md`. | Reference tables and recipe guides serve different audiences and change at different cadences. Reference tables change when CLI flags are added or removed. Recipes change when workflow recommendations evolve. Coupling them in one generator would force both to change together and make the generator responsible for two distinct content types. ProcessApiReferenceGenerator is already completed and tested (Phase 43) -- extending it risks regressions. Two small standalone generators are easier to test and maintain than one large one. | -| Recipe content uses a structured schema extension | `CLI_SCHEMA` is extended with a `recipes` field containing `RecipeGroup[]`. Each `RecipeGroup` has a title, optional description, and an array of `RecipeExample` objects. Each `RecipeExample` has a title, a purpose description, an array of command strings, and an optional expected output block. The schema extension is additive -- existing `CLIOptionGroup` types are unchanged. | Recipes are multi-command sequences ("run these 3 commands in order") with explanatory context. They do not fit into `CLIOptionGroup` which models individual flags. A separate `RecipeGroup[]` keeps the schema type-safe and makes recipes independently testable. Static expected output strings in the schema are deterministic -- no build-time CLI execution needed. | +| Recipe content uses a structured schema extension | `CLI_SCHEMA` is extended with a `recipes` field containing `RecipeGroup[]`. Each `RecipeGroup` has a title, optional description, and an array of `RecipeExample` objects. Each `RecipeExample` has a title, a purpose description, an array of RecipeStep entries (each with a command string and optional comment), and an optional expected output block. The schema extension is additive -- existing `CLIOptionGroup` types are unchanged. | Recipes are multi-command sequences ("run these 3 commands in order") with explanatory context. They do not fit into `CLIOptionGroup` which models individual flags. A separate `RecipeGroup[]` keeps the schema type-safe and makes recipes independently testable. Static expected output strings in the schema are deterministic -- no build-time CLI execution needed. | | Narrative prose uses preamble mechanism | Editorial content that cannot be derived from the CLI schema -- specifically "Why Use This" motivational prose, the Quick Start example with output, and the session type decision tree -- uses a preamble mechanism in the generator configuration. Preamble content is manually authored in `delivery-process.config.ts` as structured section data and appears before all generated recipe content in the output file. | The "Why Use This" section explains the value proposition of the Data API CLI with a comparison table (CLI vs reading markdown). This is editorial judgment, not derivable from command metadata. The Quick Start shows a specific 3-command workflow with example terminal output. The session decision tree maps cognitive states ("Starting to code?") to session types. None of these have a source annotation -- they are instructional content authored for human understanding. The preamble mechanism exists precisely for this (proven by DocsConsolidationStrategy Phase 2 preamble implementation). | | Generated recipe file complements manual PROCESS-API.md | After this pattern completes, `docs/PROCESS-API.md` is trimmed to a slim editorial introduction (~30 lines) containing the document title, a one-paragraph purpose statement, and links to both generated files: `docs-live/reference/PROCESS-API-REFERENCE.md` (option tables from Phase 43) and `docs-live/reference/PROCESS-API-RECIPES.md` (recipes and narratives from this pattern). The manual file retains the JSON Envelope, Exit Codes, and JSON Piping sections (~40 lines) which are operational reference unlikely to drift. All other prose sections are replaced by the generated recipe file. | Phase 43 established the hybrid pattern: keep editorial prose in the manual file, extract derivable content to generated files. This pattern extends the hybrid by recognizing that recipe content IS derivable from a structured schema. The ~460 lines of command descriptions, example output, and recipe blocks can be maintained as schema data rather than freeform markdown. What remains in the manual file (~70 lines total) is true operational reference (JSON envelope format, exit codes, piping tips) that changes rarely and has no schema source. | | Command narrative descriptions are sourced from schema metadata | Each command group in the generated recipe file includes a narrative description sourced from the CLI schema, not hardcoded in the generator. The existing `CLIOptionGroup.description` and `CLIOptionGroup.postNote` fields carry per-group narrative text. For command groups not currently in CLI_SCHEMA (Session Workflow Commands, Pattern Discovery, Architecture Queries, Metadata and Inventory), new `CommandGroup` entries are added to the schema with title, description, and per-command narrative metadata. | The manual PROCESS-API.md contains narrative descriptions for each command ("Highest-impact command. Pre-flight readiness check that prevents wasted sessions.") that are valuable developer context. Hardcoding these in the generator would create a second maintenance location. Placing them in CLI_SCHEMA co-locates command metadata (what the command does) with command definition (what flags it accepts), following the same single-source-of-truth principle that drove Phase 43. | @@ -549,6 +563,38 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; | PRD acceptance criteria are formatted with numbering and bold keywords | PRD output must number acceptance criteria and bold Given/When/Then keywords when steps are enabled. | Unnumbered criteria are difficult to reference in reviews; unformatted step keywords blend into prose, making scenarios harder to parse visually. | | Business values are formatted for human readability | Hyphenated business value tags must be converted to space-separated readable text in all output contexts. | Raw hyphenated tags like "enable-rich-prd" are annotation artifacts; displaying them verbatim in generated docs confuses readers expecting natural language. | +### Design Review Generation + +| Rule | Invariant | Rationale | +| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| SequenceIndex pre-computes ordered steps from rule-level tags | The MasterDataset sequenceIndex contains one entry per pattern that has libar-docs-sequence-orchestrator and at least one rule with libar-docs-sequence-step. Steps are sorted by stepNumber. Participants are deduplicated and ordered with orchestrator first. | Pre-computing in the transform pass avoids repeated parsing in the codec. ADR-006 mandates the MasterDataset as the sole read model. Downstream consumers (codec, process API) read structured data, not raw tags. | +| DesignReviewCodec generates sequence diagrams from ordered steps | The sequence diagram contains participants derived from sequence-module tags, Note blocks from Rule names, call arrows from Input/Output markers, and alt blocks from sequence-error scenarios. Participant order follows step order with User and orchestrator first. | Sequence diagrams verify interaction ordering and error handling completeness. Auto-generation ensures diagrams stay synchronized with spec annotations. Manual diagrams drift within days of a spec edit. | +| DesignReviewCodec generates component diagrams from data flow types | The component diagram groups modules into subgraphs by shared Input type, renders distinct Output types as hexagon nodes with field lists, and draws directed edges showing data flow through the orchestrator. No circular edges exist in the generated diagram. | Component diagrams verify unidirectional data flow and interface completeness. Type hexagon nodes make the central contracts visible, informing stub creation with exact field lists. | +| Process API exposes sequence data via subcommand | The sequence subcommand with no args lists all patterns with sequence annotations. With a pattern name, it returns the full SequenceIndexEntry including steps, participants, and data flow types. | The Process API is the primary query interface for AI sessions. Exposing sequence data enables design review analysis without regenerating the full document or reading generated markdown. | + +### Design Review Generation Tests + +| Rule | Invariant | Rationale | +| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SequenceIndex pre-computes ordered steps from annotated rules | buildSequenceIndexEntry produces a SequenceIndexEntry with steps sorted by stepNumber, participants deduplicated with orchestrator first, and data flow types collected from Input/Output annotations. | Pre-computing in the transform pass avoids repeated parsing in the codec. ADR-006 mandates the MasterDataset as the sole read model. | +| Participants are deduplicated with orchestrator first | The participants array starts with the orchestrator, followed by module names in first-appearance step order, with no duplicates. | Sequence diagram participant declarations must be ordered and unique. The orchestrator is always the first participant as the entry point. | +| Data flow types are extracted from Input and Output annotations | The dataFlowTypes array contains distinct type names parsed from Input and Output annotation strings using the "TypeName -- fields" format. | Data flow types are used by the component diagram to render hexagon nodes and by the type definitions table to show producers and consumers. | +| DesignReviewCodec produces sequence diagram with correct participant count | The rendered sequence diagram participant list includes User plus all participants from the SequenceIndexEntry. The count equals 1 (User) plus the number of unique participants. | Correct participant count proves the codec reads SequenceIndex data correctly and maps it to Mermaid syntax. | +| Error scenarios produce alt blocks in sequence diagrams | Each error scenario name from a step's errorScenarios array produces an alt block in the Mermaid sequence diagram with the scenario name as the condition text. | Alt blocks make error handling visible in the sequence diagram, enabling design review verification of error path completeness. | +| Component diagram groups modules by shared input type | Contiguous steps sharing the same Input type annotation are grouped into a single subgraph in the component diagram. Non-contiguous steps with the same input become separate subgraphs. | Grouping by input type reveals natural phase boundaries in the orchestration flow, making data flow architecture visible. | +| Component diagram module nodes are scoped per phase | Repeated modules in non-contiguous phases render with distinct Mermaid node IDs, while repeated use of the same module inside one phase reuses a single declaration. | Mermaid node IDs are global across the diagram. Reusing raw module IDs causes later phases to collapse into earlier declarations and misrepresent the orchestration flow. | +| Type hexagons show field definitions from Output annotations | Output annotations with the "TypeName -- field1, field2" format produce hexagon nodes in the component diagram containing the type name and field names separated by newlines. | Type hexagons make central data contracts visible, enabling design reviewers to verify interface completeness. | +| Mermaid-sensitive text is escaped across rendered labels | Participant aliases, subgraph labels, type hexagon text, and edge labels escape Mermaid-sensitive characters such as quotes, pipes, and comment markers before rendering. | Design review diagrams are generated directly from annotations. Valid annotation text must not break Mermaid parsing when rendered into different label positions. | +| Design questions table includes auto-computed metrics | The Design Questions section contains a table with auto-computed step count, type count, and error path count drawn from the SequenceIndexEntry data. | Auto-computed metrics reduce manual counting during design reviews and highlight coverage gaps (e.g., 0 error paths). | +| Invalid sequence annotations are skipped with validation warnings | Patterns with ambiguous sequence-step numbering or empty sequence-module tags are excluded from sequenceIndex and reported through malformedPatterns. | Design reviews should never render misleading diagrams from malformed annotations. The transform pass is the correct place to validate and suppress bad sequence entries. | +| Process API sequence lookup resolves pattern names case-insensitively | The sequence subcommand resolves pattern names with the same case-insensitive matching behavior as other pattern-oriented process-api queries. | Design review consumers should not need exact display-name casing when querying sequence data from the CLI. | + +### Design Review Generator Lifecycle Tests + +| Rule | Invariant | Rationale | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Orphaned design review files are scheduled for deletion | Existing markdown files in design-reviews/ that no longer map to the current sequenceIndex must be returned in filesToDelete, while current patterns remain preserved. | Renaming or removing sequence-annotated patterns otherwise leaves stale design review documents behind, which misleads readers and downstream tooling. | + ### Doc Generation Proof Of Concept | Rule | Invariant | Rationale | @@ -563,20 +609,20 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; ### Docs Consolidation Strategy -| Rule | Invariant | Rationale | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Convention tags are the primary consolidation mechanism | Each consolidation phase follows the same pattern: register a convention tag value in `src/taxonomy/conventions.ts`, annotate source files with `@libar-docs-convention` tags using structured JSDoc, add a `ReferenceDocConfig` entry in `delivery-process.config.ts`, and replace the manual doc section with a pointer to the generated reference document. | Convention-tagged annotations are the only sustainable way to keep docs in sync with implementation. The reference codec (`createReferenceCodec`) already handles the 4-layer composition so each phase only needs annotation work and config — no new codec infrastructure required. | -| Preamble preserves editorial context in generated docs | `ReferenceDocConfig.preamble` accepts `readonly SectionBlock[]` that are prepended before all generated content. Preamble sections appear in both detailed and summary (claude-md) outputs, followed by a separator. A config without preamble produces no extra separator or empty sections. | Not all documentation content can be extracted from code annotations. Introductory prose, cross-cutting context, and reading guides require human authorship. The preamble provides a designated place for this content within the generated document structure, avoiding a separate hand-maintained file. | -| Each consolidation phase is independently deliverable | Each phase can be implemented and validated independently. A phase is complete when: the manual doc section has been replaced with a pointer to the generated equivalent, `pnpm docs:all` produces the generated output without errors, and the generated content covers the replaced manual content. No phase requires another uncompleted phase to function. | Independent phases allow incremental consolidation without blocking on the full initiative. Each merged PR reduces manual maintenance immediately. Phase ordering in the plan is a suggested sequence (simplest first), not a dependency chain. | -| Manual docs retain editorial and tutorial content | Documents containing philosophy (METHODOLOGY.md), workflow guides (SESSION-GUIDES.md), tutorials (GHERKIN-PATTERNS.md), CLI reference (PROCESS-API.md), and operational procedures (PUBLISHING.md) remain fully manual. These docs are ~2,300 lines total and contain instructional content that cannot be expressed as source annotations. | The consolidation targets sections most likely to drift when code changes: reference tables, codec listings, validation rules, API types. Editorial content changes at a different cadence and requires human judgment to update. Forcing this into annotations would produce worse documentation. | -| Audience alignment determines document location | Each document lives in the location matching its primary audience: `docs/` (deployed to libar.dev) for content that serves package users and developers; repo root for GitHub-visible metadata (CONTRIBUTING.md, SECURITY.md, MAINTAINERS.md); CLAUDE.md for AI session context. A document appearing in docs/ must be useful to a developer or user visiting the website — maintainer-only operational procedures (npm publishing workflow, GitHub Actions setup) belong at the repo root. | The audit found PUBLISHING.md (maintainer-only) in docs/ alongside user-facing guides. SESSION-GUIDES.md (AI session procedures) duplicates CLAUDE.md with 95% overlap. Audience mismatches increase website noise for users and create drift risk when the same content lives in two locations. | +| Rule | Invariant | Rationale | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Convention tags are the primary consolidation mechanism | Each consolidation phase follows the same pattern: register a convention tag value in `src/taxonomy/conventions.ts`, annotate source files with `@libar-docs-convention` tags using structured JSDoc, add a `ReferenceDocConfig` entry in `delivery-process.config.ts`, and replace the manual doc section with a pointer to the generated reference document. | Convention-tagged annotations are the only sustainable way to keep docs in sync with implementation. The reference codec (`createReferenceCodec`) already handles the 4-layer composition so each phase only needs annotation work and config — no new codec infrastructure required. | +| Preamble preserves editorial context in generated docs | `ReferenceDocConfig.preamble` accepts `readonly SectionBlock[]` that are prepended before all generated content. Preamble sections appear in both detailed and summary (claude-md) outputs, followed by a separator. A config without preamble produces no extra separator or empty sections. | Not all documentation content can be extracted from code annotations. Introductory prose, cross-cutting context, and reading guides require human authorship. The preamble provides a designated place for this content within the generated document structure, avoiding a separate hand-maintained file. | +| Each consolidation phase is independently deliverable | Each phase can be implemented and validated independently. A phase is complete when: the manual doc section has been replaced with a pointer to the generated equivalent, `pnpm docs:all` produces the generated output without errors, and the generated content covers the replaced manual content. No phase requires another uncompleted phase to function. | Independent phases allow incremental consolidation without blocking on the full initiative. Each merged PR reduces manual maintenance immediately. Phase ordering in the plan is a suggested sequence (simplest first), not a dependency chain. | +| Manual docs retain editorial and tutorial content | Documents containing philosophy (METHODOLOGY.md) remain fully manual with no generated equivalent (~238 lines). Documents that were originally manual but now have generated equivalents or have been restructured (SESSION-GUIDES.md, GHERKIN-PATTERNS.md, PROCESS-API.md) retain their editorial content as preamble within generated outputs. PUBLISHING.md was relocated to MAINTAINERS.md at the repository root. | The consolidation targets sections most likely to drift when code changes: reference tables, codec listings, validation rules, API types. Editorial content changes at a different cadence and requires human judgment to update. Forcing this into annotations would produce worse documentation. Documents that transitioned to hybrid generation preserve their editorial voice via preamble while keeping reference content in sync with source annotations. | +| Audience alignment determines document location | Each document lives in the location matching its primary audience: `docs/` (deployed to libar.dev) for content that serves package users and developers; repo root for GitHub-visible metadata (CONTRIBUTING.md, SECURITY.md, MAINTAINERS.md); CLAUDE.md for AI session context. A document appearing in docs/ must be useful to a developer or user visiting the website — maintainer-only operational procedures (npm publishing workflow, GitHub Actions setup) belong at the repo root. | The audit found PUBLISHING.md (maintainer-only) in docs/ alongside user-facing guides. SESSION-GUIDES.md (AI session procedures) duplicates CLAUDE.md with 95% overlap. Audience mismatches increase website noise for users and create drift risk when the same content lives in two locations. | ### Docs Live Consolidation -| Rule | Invariant | Rationale | -| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| docs-live/ is the single directory for website-published content | Every file appearing on `libar.dev` or referenced by CLAUDE.md comes from `docs-live/`. No production reference document is published from `docs-generated/`. The `docs-generated/` directory contains no production reference content after `docs:all` runs. Business-rules, taxonomy, and validation-rules output to `docs-live/` alongside other reference docs. | DD-1: Splitting production output across two directories creates ambiguity about where authoritative content lives. Website configuration, CLAUDE.md path references, and team navigation all benefit from a single source directory. `docs-generated/` name signals "build cache", not "publishable output". | -| All \_claude-md/ compact files consolidate under docs-live/ | All `_claude-md/` compact context files live under `docs-live/_claude-md/`. Architecture-scoped compacts (architecture-codecs, architecture-types) move from `docs-generated/_claude-md/architecture/` to `docs-live/_claude-md/architecture/`. Product-area compacts remain at `docs-live/_claude-md/` unchanged. | DD-2: `_claude-md/` compact versions are the Claude consumption contract — agents read compacts, not full product area docs. Having compacts split across two directories (docs-generated/ and docs-live/) means Claude sessions following "read from docs-live/" miss the architecture compacts entirely. | +| Rule | Invariant | Rationale | +| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| docs-live/ is the single directory for website-published content | Every file appearing on `libar.dev` or referenced by CLAUDE.md comes from `docs-live/`. No production reference document is published from `docs-generated/`. The `docs-generated/` directory contains no production reference content after `docs:all` runs. Business-rules, taxonomy, and validation-rules output to `docs-live/` alongside other reference docs. | DD-1: Splitting production output across two directories creates ambiguity about where authoritative content lives. Website configuration, CLAUDE.md path references, and team navigation all benefit from a single source directory. `docs-generated/` name signals "build cache", not "publishable output". | +| Architecture reference compacts generate under docs-live/\_claude-md/ | Architecture reference summary files live under `docs-live/_claude-md/architecture/`. Architecture-scoped compacts (architecture-codecs, architecture-types) move from `docs-generated/_claude-md/architecture/` to `docs-live/_claude-md/architecture/`. This consolidation does not affect the separate claude-modules output at the repository root `_claude-md/`. | DD-2: `_claude-md/` compact versions are the Claude consumption contract — agents read compacts, not full product area docs. Having compacts split across two directories (docs-generated/ and docs-live/) means Claude sessions following "read from docs-live/" miss the architecture compacts entirely. | ### Documentation Orchestrator @@ -1081,12 +1127,11 @@ function transformToMasterDataset(raw: RawDataset): RuntimeMasterDataset; ### Traceability Generator -| Rule | Invariant | Rationale | -| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | -| Parses Verified by annotations to extract scenario references | Scenario names in `**Verified by:**` are matched against actual scenarios in feature files. Unmatched references are reported as warnings. | Verified by annotations create explicit traceability. Validating references ensures the traceability matrix reflects actual test coverage. | -| Generates Rule-to-Scenario traceability matrix | Every Rule appears in the matrix with its verification status. Scenarios are linked by name and file location. | A matrix format enables quick scanning of coverage status and supports audit requirements for bidirectional traceability. | -| Detects and reports coverage gaps | Orphan scenarios (not referenced by any Rule) and unverified rules are listed in dedicated sections. | Coverage gaps indicate either missing traceability annotations or actual missing test coverage. Surfacing them enables remediation. | -| Supports filtering by phase and domain | CLI flags allow filtering the matrix by phase number or domain category to generate focused traceability reports. | Large codebases have many rules. Filtering enables relevant subset extraction for specific audits or reviews. | +| Rule | Invariant | Rationale | +| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Cross-references Verified by annotations against actual scenarios | Every `verifiedBy` string extracted from a Rule description is matched against scenario names in the MasterDataset. The traceability matrix shows each Rule with its verification status: verified (all references resolve), partially verified (some resolve), or unverified (none resolve or no annotation). | `parseBusinessRuleAnnotations()` already extracts `verifiedBy` arrays from Rule descriptions. Without cross-referencing against actual scenario names, the traceability report cannot distinguish between claimed and actual test coverage. A dangling reference (scenario name that does not exist) is worse than no annotation because it creates false confidence. | +| Detects orphan scenarios and unverified rules | Orphan scenarios (acceptance-criteria scenarios not referenced by any Rule's Verified by annotation) and unverified rules (Rules without a Verified by annotation or with zero matched scenarios) are listed in dedicated sections of the traceability output. | Coverage gaps indicate either missing traceability annotations or actual missing test coverage. Orphan scenarios may be valuable tests that lack traceability links, or dead tests that should be removed. Unverified rules are business constraints with no demonstrated test coverage. | +| Traceability output is wired into the docs pipeline | The TraceabilityCodec output is generated as part of `pnpm docs:all` via a `docs:traceability` npm script backed by a ReferenceDocConfig entry in `delivery-process.config.ts`. The output file lands in `docs-live/TRACEABILITY.md`. | The TraceabilityCodec is registered in the CodecRegistry but not wired into `delivery-process.config.ts` or `package.json`. Without config wiring, the codec is only usable programmatically or via tests. Adding it to the docs pipeline makes traceability output a first-class generated artifact alongside CHANGELOG.md, OVERVIEW.md, and other reporting codecs. | ### Transform Dataset Testing diff --git a/docs-live/reference/ARCHITECTURE-CODECS.md b/docs-live/reference/ARCHITECTURE-CODECS.md index bf17cad6..6fec55f5 100644 --- a/docs-live/reference/ARCHITECTURE-CODECS.md +++ b/docs-live/reference/ARCHITECTURE-CODECS.md @@ -454,6 +454,26 @@ const doc = PatternsDocumentCodec.decode(dataset); --- +## DesignReviewCodec + +Transforms MasterDataset into a RenderableDocument containing design review +artifacts: sequence diagrams, component diagrams, type definition tables, +and design question templates. + +**Purpose:** Auto-generate design review documents from sequence annotations +on Gherkin specs. Diagrams stay synchronized with spec changes. + +**Output Files:** `delivery-process/design-reviews/{pattern-name}.md` + +### Factory Pattern + +```typescript +const codec = createDesignReviewCodec({ patternName: 'SetupCommand' }); +const doc = codec.decode(dataset); +``` + +--- + ## CompositeCodec Assembles reference documents from multiple codec outputs by concatenating diff --git a/docs-live/reference/ARCHITECTURE-TYPES.md b/docs-live/reference/ARCHITECTURE-TYPES.md index 238ce0a8..9dcb3217 100644 --- a/docs-live/reference/ARCHITECTURE-TYPES.md +++ b/docs-live/reference/ARCHITECTURE-TYPES.md @@ -83,7 +83,14 @@ MasterDatasetSchema = z.object({ /** Optional architecture index for diagram generation */ archIndex: ArchIndexSchema.optional(), -}) + + // ───────────────────────────────────────────────────────────────────────── + // Sequence Data (optional) + // ───────────────────────────────────────────────────────────────────────── + + /** Optional sequence index for design review diagram generation */ + sequenceIndex: SequenceIndexSchema.optional(), +}); ``` ### StatusGroupsSchema (const) @@ -110,7 +117,7 @@ StatusGroupsSchema = z.object({ /** Patterns with status 'roadmap', 'planned', or undefined */ planned: z.array(ExtractedPatternSchema), -}) +}); ``` ### StatusCountsSchema (const) @@ -135,7 +142,7 @@ StatusCountsSchema = z.object({ /** Total number of patterns */ total: z.number().int().nonnegative(), -}) +}); ``` ### PhaseGroupSchema (const) @@ -163,7 +170,7 @@ PhaseGroupSchema = z.object({ /** Pre-computed status counts for this phase */ counts: StatusCountsSchema, -}) +}); ``` ### SourceViewsSchema (const) @@ -188,7 +195,7 @@ SourceViewsSchema = z.object({ /** Patterns with PRD metadata (productArea, userRole, businessValue) */ prd: z.array(ExtractedPatternSchema), -}) +}); ``` ### RelationshipEntrySchema (const) @@ -234,7 +241,7 @@ RelationshipEntrySchema = z.object({ /** File paths to implementation APIs (from @libar-docs-api-ref tag) */ apiRef: z.array(z.string()), -}) +}); ``` ### RuntimeMasterDataset (interface) @@ -257,8 +264,8 @@ interface RuntimeMasterDataset extends MasterDataset { } ``` -| Property | Description | -| --- | --- | +| Property | Description | +| -------- | -------------------------------------------------- | | workflow | Optional workflow configuration (not serializable) | ### RawDataset (interface) @@ -286,12 +293,12 @@ interface RawDataset { } ``` -| Property | Description | -| --- | --- | -| patterns | Extracted patterns from TypeScript and/or Gherkin sources | -| tagRegistry | Tag registry for category lookups | -| workflow | Optional workflow configuration for phase names (can be undefined) | -| contextInferenceRules | Optional rules for inferring bounded context from file paths | +| Property | Description | +| --------------------- | ------------------------------------------------------------------ | +| patterns | Extracted patterns from TypeScript and/or Gherkin sources | +| tagRegistry | Tag registry for category lookups | +| workflow | Optional workflow configuration for phase names (can be undefined) | +| contextInferenceRules | Optional rules for inferring bounded context from file paths | ### PipelineOptions (interface) @@ -323,10 +330,10 @@ interface PipelineOptions { } ``` -| Property | Description | -| --- | --- | -| includeValidation | DD-3: When false, skip validation pass (default true). | -| failOnScanErrors | DD-5: When true, return error on individual scan failures (default false). | +| Property | Description | +| ----------------- | -------------------------------------------------------------------------- | +| includeValidation | DD-3: When false, skip validation pass (default true). | +| failOnScanErrors | DD-5: When true, return error on individual scan failures (default false). | ### PipelineResult (interface) diff --git a/docs-live/reference/PROCESS-API-REFERENCE.md b/docs-live/reference/PROCESS-API-REFERENCE.md index 3b78e3a4..7e60573a 100644 --- a/docs-live/reference/PROCESS-API-REFERENCE.md +++ b/docs-live/reference/PROCESS-API-REFERENCE.md @@ -4,14 +4,14 @@ ## Global Options -| Flag | Short | Description | Default | -| --- | --- | --- | --- | -| `--input ` | `-i` | TypeScript glob pattern (repeatable) | from config or auto-detected | -| `--features ` | `-f` | Gherkin glob pattern (repeatable) | from config or auto-detected | -| `--base-dir ` | `-b` | Base directory | cwd | -| `--workflow ` | `-w` | Workflow config JSON | default | -| `--help` | `-h` | Show help | --- | -| `--version` | `-v` | Show version | --- | +| Flag | Short | Description | Default | +| ---------------------- | ----- | ------------------------------------ | ---------------------------- | +| `--input ` | `-i` | TypeScript glob pattern (repeatable) | from config or auto-detected | +| `--features ` | `-f` | Gherkin glob pattern (repeatable) | from config or auto-detected | +| `--base-dir ` | `-b` | Base directory | cwd | +| `--workflow ` | `-w` | Workflow config JSON | default | +| `--help` | `-h` | Show help | --- | +| `--version` | `-v` | Show version | --- | **Config auto-detection:** If `--input` and `--features` are not provided, the CLI loads defaults from `delivery-process.config.ts` in the current directory. If no config file exists, it falls back to filesystem-based detection. If neither works, `--input` is required. @@ -21,13 +21,13 @@ Composable with `list`, `arch context/layer`, and pattern-array `query` methods. -| Output Modifier | Description | -| --- | --- | -| `--names-only` | Return array of pattern name strings | -| `--count` | Return integer count | -| `--fields ` | Return only specified fields per pattern | -| `--full` | Bypass summarization, return raw patterns | -| `--format ` | `json` (default, pretty-printed) or `compact` | +| Output Modifier | Description | +| ---------------------- | --------------------------------------------- | +| `--names-only` | Return array of pattern name strings | +| `--count` | Return integer count | +| `--fields ` | Return only specified fields per pattern | +| `--full` | Bypass summarization, return raw patterns | +| `--format ` | `json` (default, pretty-printed) or `compact` | Valid fields for `--fields`: `patternName`, `status`, `category`, `phase`, `file`, `source`. @@ -41,16 +41,16 @@ Precedence: `--count` > `--names-only` > `--fields` > default summarize. For the `list` subcommand. All filters are composable. -| List Filter | Description | -| --- | --- | -| `--status ` | Filter by FSM status (roadmap, active, completed, deferred) | -| `--phase ` | Filter by roadmap phase number | -| `--category ` | Filter by category | -| `--source ` | Filter by source type | -| `--arch-context ` | Filter by architecture context | -| `--product-area ` | Filter by product area | -| `--limit ` | Maximum results | -| `--offset ` | Skip first n results | +| List Filter | Description | +| ------------------------ | ----------------------------------------------------------- | +| `--status ` | Filter by FSM status (roadmap, active, completed, deferred) | +| `--phase ` | Filter by roadmap phase number | +| `--category ` | Filter by category | +| `--source ` | Filter by source type | +| `--arch-context ` | Filter by architecture context | +| `--product-area ` | Filter by product area | +| `--limit ` | Maximum results | +| `--offset ` | Skip first n results | --- @@ -58,6 +58,6 @@ For the `list` subcommand. All filters are composable. For the `--session` flag used with `context` and `scope-validate`. -| Session Types | Description | -| --- | --- | +| Session Types | Description | +| ------------------ | -------------------------------------------------- | | `--session ` | Session type: `planning`, `design`, or `implement` | diff --git a/docs-live/reference/REFERENCE-SAMPLE.md b/docs-live/reference/REFERENCE-SAMPLE.md index cad683c4..9a477e7b 100644 --- a/docs-live/reference/REFERENCE-SAMPLE.md +++ b/docs-live/reference/REFERENCE-SAMPLE.md @@ -251,8 +251,14 @@ classDiagram class TransformDataset { <> } + class SequenceTransformUtils { + <> + } class ProcessApiReferenceGenerator { } + class DesignReviewGenerator { + <> + } class DecisionDocGenerator { <> } @@ -262,9 +268,11 @@ classDiagram class Pattern_Scanner class GherkinASTParser class ShapeExtractor + class DesignReviewCodec class DecisionDocCodec class ProcessApiHybridGeneration class PatternRelationshipModel + class DesignReviewGeneration class CliRecipeCodec SourceMapper ..> DecisionDocCodec : depends on SourceMapper ..> ShapeExtractor : depends on @@ -272,10 +280,17 @@ classDiagram Documentation_Generation_Orchestrator ..> Pattern_Scanner : uses TransformDataset ..> MasterDataset : uses TransformDataset ..|> PatternRelationshipModel : implements + SequenceTransformUtils ..> MasterDataset : uses + SequenceTransformUtils ..|> DesignReviewGeneration : implements ProcessApiReferenceGenerator ..|> ProcessApiHybridGeneration : implements + DesignReviewGenerator ..> DesignReviewCodec : uses + DesignReviewGenerator ..> MasterDataset : uses + DesignReviewGenerator ..|> DesignReviewGeneration : implements DecisionDocGenerator ..> DecisionDocCodec : depends on DecisionDocGenerator ..> SourceMapper : depends on CliRecipeGenerator ..|> CliRecipeCodec : implements + DesignReviewCodec ..> MasterDataset : uses + DesignReviewCodec ..|> DesignReviewGeneration : implements CliRecipeCodec ..> ProcessApiHybridGeneration : depends on ``` @@ -384,10 +399,10 @@ graph LR end TagRegistryBuilder ..->|implements| TypeScriptTaxonomyImplementation loadPreambleFromMarkdown___Shared_Markdown_to_SectionBlock_Parser ..->|implements| ProceduralGuideCodec - CLISchema ..->|implements| ProcessApiHybridGeneration ProjectConfigTypes -->|uses| ConfigurationTypes ProjectConfigTypes -->|uses| ConfigurationPresets ConfigurationPresets -->|uses| ConfigurationTypes + CLISchema ..->|implements| ProcessApiHybridGeneration PatternHelpers ..->|implements| DataAPIOutputShaping ArchQueriesImpl -->|uses| ProcessStateAPI ArchQueriesImpl -->|uses| MasterDataset @@ -547,122 +562,6 @@ Validation happens later at load time via Zod schema in `loadProjectConfig()`. - In `delivery-process.config.ts` at project root to get type-safe configuration with autocompletion. -### ConfigBasedWorkflowDefinition - -[View ConfigBasedWorkflowDefinition source](delivery-process/specs/config-based-workflow-definition.feature) - -**Problem:** -Every `pnpm process:query` and `pnpm docs:*` invocation prints: -`Failed to load default workflow (6-phase-standard): Workflow file not found` - -The `loadDefaultWorkflow()` function resolves to `catalogue/workflows/` -which does not exist. The directory was deleted during monorepo extraction. -The system already degrades gracefully (workflow = undefined), but the -warning is noise for both human CLI use and future hook consumers (HUD). - -The old `6-phase-standard.json` conflated three concerns: - -- Taxonomy vocabulary (status names) — already in `src/taxonomy/` -- FSM behavior (transitions) — already in `src/validation/fsm/` -- Workflow structure (phases) — orphaned, no proper home - -**Solution:** -Inline the default workflow as a constant in `workflow-loader.ts`, built -from canonical taxonomy values. Make `loadDefaultWorkflow()` synchronous. -Preserve `loadWorkflowFromPath()` for custom `--workflow ` overrides. - -The workflow definition uses only the 4 canonical statuses from ADR-001 -(roadmap, active, completed, deferred) — not the stale 5-status set from -the deleted JSON (which included non-canonical `implemented` and `partial`). - -Phase definitions (Inception, Elaboration, Session, Construction, -Validation, Retrospective) move from a missing JSON file to an inline -constant, making the default workflow always available without file I/O. - -Design Decisions (DS-1, 2026-02-15): - -| ID | Decision | Rationale | -| DD-1 | Inline constant in workflow-loader.ts, not preset integration | Minimal correct fix, zero type regression risk. Preset integration deferred. | -| DD-2 | Constant satisfies existing WorkflowConfig type | Reuse createLoadedWorkflow() from workflow-config.ts. No new types needed. | -| DD-3 | Remove dead code: getCatalogueWorkflowsPath, loadWorkflowConfig, DEFAULT_WORKFLOW_NAME | Dead since monorepo extraction. Public API break is safe (function always threw). | -| DD-4 | loadDefaultWorkflow() returns LoadedWorkflow synchronously | Infallible constant needs no async or error handling. | -| DD-5 | Amend ADR-001 with canonical phase definitions | Phase names are canonical values; fits existing governance in ADR-001. | - -
-Default workflow is built from an inline constant (2 scenarios) - -#### Default workflow is built from an inline constant - -**Invariant:** `loadDefaultWorkflow()` returns a `LoadedWorkflow` without file system access. It cannot fail. The default workflow constant uses only canonical status values from `src/taxonomy/status-values.ts`. - -**Rationale:** The file-based loading path (`catalogue/workflows/`) has been dead code since monorepo extraction. Both callers (orchestrator, process-api) already handle the failure gracefully, proving the system works without it. Making the function synchronous and infallible removes the try-catch ceremony and the warning noise. - -**Verified by:** - -- Default workflow loads without warning -- Workflow constant uses canonical statuses only -- Workflow constant uses canonical statuses only - - Implementation approach: - -
- -
-Custom workflow files still work via --workflow flag (1 scenarios) - -#### Custom workflow files still work via --workflow flag - -**Invariant:** `loadWorkflowFromPath()` remains available for projects that need custom workflow definitions. The `--workflow ` CLI flag and `workflowPath` config field continue to work. - -**Rationale:** The inline default replaces file-based _default_ loading, not file-based _custom_ loading. Projects may define custom phases or additional statuses via JSON files. - -**Verified by:** - -- Custom workflow file overrides default - -
- -
-FSM validation and Process Guard are not affected - -#### FSM validation and Process Guard are not affected - -**Invariant:** The FSM transition matrix, protection levels, and Process Guard rules remain hardcoded in `src/validation/fsm/` and `src/lint/process-guard/`. They do not read from `LoadedWorkflow`. - -**Rationale:** FSM and workflow are separate concerns. FSM enforces status transitions (4-state model from PDR-005). Workflow defines phase structure (6-phase USDP). The workflow JSON declared `transitionsTo` on its statuses, but no code ever read those values — the FSM uses its own `VALID_TRANSITIONS` constant. This separation is correct and intentional. Blast radius analysis confirmed zero workflow imports in: - src/validation/fsm/ (4 files) - src/lint/process-guard/ (5 files) - src/taxonomy/ (all files) - -
- -
-Workflow as a configurable preset field is deferred - -#### Workflow as a configurable preset field is deferred - -**Invariant:** The inline default workflow constant is the only workflow source until preset integration is implemented. No preset or project config field exposes workflow customization. - -**Rationale:** Coupling workflow into the preset/config system before the inline fix ships would widen the blast radius and risk type regressions across all config consumers. - -**Verified by:** - -- N/A - deferred until preset integration - - Adding `workflow` as a field on `DeliveryProcessConfig` (presets) and - `DeliveryProcessProjectConfig` (project config) is a natural next step - but NOT required for the MVP fix. - - The inline constant in `workflow-loader.ts` resolves the warning. Moving - workflow into the preset/config system enables: - - Different presets with different default phases (e.g. - -- 3-phase generic) - - Per-project phase customization in delivery-process.config.ts - - Phase definitions appearing in generated documentation - - See ideation artifact for design options: - delivery-process/ideations/2026-02-15-workflow-config-and-fsm-extensibility.feature - -
- ### ADR005CodecBasedMarkdownRendering [View ADR005CodecBasedMarkdownRendering source](delivery-process/decisions/adr-005-codec-based-markdown-rendering.feature) @@ -965,6 +864,122 @@ These are the durable constants of the delivery process. +### ConfigBasedWorkflowDefinition + +[View ConfigBasedWorkflowDefinition source](delivery-process/specs/config-based-workflow-definition.feature) + +**Problem:** +Every `pnpm process:query` and `pnpm docs:*` invocation prints: +`Failed to load default workflow (6-phase-standard): Workflow file not found` + +The `loadDefaultWorkflow()` function resolves to `catalogue/workflows/` +which does not exist. The directory was deleted during monorepo extraction. +The system already degrades gracefully (workflow = undefined), but the +warning is noise for both human CLI use and future hook consumers (HUD). + +The old `6-phase-standard.json` conflated three concerns: + +- Taxonomy vocabulary (status names) — already in `src/taxonomy/` +- FSM behavior (transitions) — already in `src/validation/fsm/` +- Workflow structure (phases) — orphaned, no proper home + +**Solution:** +Inline the default workflow as a constant in `workflow-loader.ts`, built +from canonical taxonomy values. Make `loadDefaultWorkflow()` synchronous. +Preserve `loadWorkflowFromPath()` for custom `--workflow ` overrides. + +The workflow definition uses only the 4 canonical statuses from ADR-001 +(roadmap, active, completed, deferred) — not the stale 5-status set from +the deleted JSON (which included non-canonical `implemented` and `partial`). + +Phase definitions (Inception, Elaboration, Session, Construction, +Validation, Retrospective) move from a missing JSON file to an inline +constant, making the default workflow always available without file I/O. + +Design Decisions (DS-1, 2026-02-15): + +| ID | Decision | Rationale | +| DD-1 | Inline constant in workflow-loader.ts, not preset integration | Minimal correct fix, zero type regression risk. Preset integration deferred. | +| DD-2 | Constant satisfies existing WorkflowConfig type | Reuse createLoadedWorkflow() from workflow-config.ts. No new types needed. | +| DD-3 | Remove dead code: getCatalogueWorkflowsPath, loadWorkflowConfig, DEFAULT_WORKFLOW_NAME | Dead since monorepo extraction. Public API break is safe (function always threw). | +| DD-4 | loadDefaultWorkflow() returns LoadedWorkflow synchronously | Infallible constant needs no async or error handling. | +| DD-5 | Amend ADR-001 with canonical phase definitions | Phase names are canonical values; fits existing governance in ADR-001. | + +
+Default workflow is built from an inline constant (2 scenarios) + +#### Default workflow is built from an inline constant + +**Invariant:** `loadDefaultWorkflow()` returns a `LoadedWorkflow` without file system access. It cannot fail. The default workflow constant uses only canonical status values from `src/taxonomy/status-values.ts`. + +**Rationale:** The file-based loading path (`catalogue/workflows/`) has been dead code since monorepo extraction. Both callers (orchestrator, process-api) already handle the failure gracefully, proving the system works without it. Making the function synchronous and infallible removes the try-catch ceremony and the warning noise. + +**Verified by:** + +- Default workflow loads without warning +- Workflow constant uses canonical statuses only +- Workflow constant uses canonical statuses only + + Implementation approach: + +
+ +
+Custom workflow files still work via --workflow flag (1 scenarios) + +#### Custom workflow files still work via --workflow flag + +**Invariant:** `loadWorkflowFromPath()` remains available for projects that need custom workflow definitions. The `--workflow ` CLI flag and `workflowPath` config field continue to work. + +**Rationale:** The inline default replaces file-based _default_ loading, not file-based _custom_ loading. Projects may define custom phases or additional statuses via JSON files. + +**Verified by:** + +- Custom workflow file overrides default + +
+ +
+FSM validation and Process Guard are not affected + +#### FSM validation and Process Guard are not affected + +**Invariant:** The FSM transition matrix, protection levels, and Process Guard rules remain hardcoded in `src/validation/fsm/` and `src/lint/process-guard/`. They do not read from `LoadedWorkflow`. + +**Rationale:** FSM and workflow are separate concerns. FSM enforces status transitions (4-state model from PDR-005). Workflow defines phase structure (6-phase USDP). The workflow JSON declared `transitionsTo` on its statuses, but no code ever read those values — the FSM uses its own `VALID_TRANSITIONS` constant. This separation is correct and intentional. Blast radius analysis confirmed zero workflow imports in: - src/validation/fsm/ (4 files) - src/lint/process-guard/ (5 files) - src/taxonomy/ (all files) + +
+ +
+Workflow as a configurable preset field is deferred + +#### Workflow as a configurable preset field is deferred + +**Invariant:** The inline default workflow constant is the only workflow source until preset integration is implemented. No preset or project config field exposes workflow customization. + +**Rationale:** Coupling workflow into the preset/config system before the inline fix ships would widen the blast radius and risk type regressions across all config consumers. + +**Verified by:** + +- N/A - deferred until preset integration + + Adding `workflow` as a field on `DeliveryProcessConfig` (presets) and + `DeliveryProcessProjectConfig` (project config) is a natural next step + but NOT required for the MVP fix. + + The inline constant in `workflow-loader.ts` resolves the warning. Moving + workflow into the preset/config system enables: + - Different presets with different default phases (e.g. + +- 3-phase generic) + - Per-project phase customization in delivery-process.config.ts + - Phase definitions appearing in generated documentation + + See ideation artifact for design options: + delivery-process/ideations/2026-02-15-workflow-config-and-fsm-extensibility.feature + +
+ ### ProcessGuardTesting [View ProcessGuardTesting source](tests/features/validation/process-guard.feature) @@ -1006,7 +1021,7 @@ All validation follows the Decider pattern: (state, changes, options) => result.
-Status transitions must follow PDR-005 FSM (2 scenarios) +Status transitions must follow PDR-005 FSM (3 scenarios) #### Status transitions must follow PDR-005 FSM @@ -1018,6 +1033,7 @@ All validation follows the Decider pattern: (state, changes, options) => result. - Valid transitions pass validation - Invalid transitions fail validation +- Existing file with unlock-reason bypasses FSM check
diff --git a/docs-live/taxonomy/metadata-tags.md b/docs-live/taxonomy/metadata-tags.md index 14a70eff..0a6c58fd 100644 --- a/docs-live/taxonomy/metadata-tags.md +++ b/docs-live/taxonomy/metadata-tags.md @@ -6,7 +6,7 @@ ## Metadata Tag Definitions -56 metadata tags with full details. +60 metadata tags with full details. | Tag | Format | Purpose | Required | Repeatable | Values | Default | | ------------------------ | ------------ | -------------------------------------------------------------------------- | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | @@ -66,6 +66,10 @@ | `claude-module` | value | Module identifier for CLAUDE.md module generation (becomes filename) | No | No | - | - | | `claude-section` | enum | Target section directory in \_claude-md/ for module output | No | No | core, delivery-process, testing, infrastructure, workflow | - | | `claude-tags` | csv | Variation filtering tags for modular-claude-md inclusion | No | No | - | - | +| `sequence-orchestrator` | value | Identifies the coordinator module for sequence diagram generation | No | No | - | - | +| `sequence-step` | number | Explicit execution ordering number for sequence diagram steps | No | No | - | - | +| `sequence-module` | csv | Maps Rule to deliverable module(s) for sequence diagram participants | No | No | - | - | +| `sequence-error` | flag | Marks scenario as error/alternative path in sequence diagram | No | No | - | - | ## Tag Details @@ -644,6 +648,46 @@ | Repeatable | No | | Example | `@libar-docs-claude-tags core-mandatory, delivery-process` | +### `sequence-orchestrator` + +| Property | Value | +| ---------- | ----------------------------------------------------------------- | +| Format | value | +| Purpose | Identifies the coordinator module for sequence diagram generation | +| Required | No | +| Repeatable | No | +| Example | `@libar-docs-sequence-orchestrator:init-cli` | + +### `sequence-step` + +| Property | Value | +| ---------- | ------------------------------------------------------------- | +| Format | number | +| Purpose | Explicit execution ordering number for sequence diagram steps | +| Required | No | +| Repeatable | No | +| Example | `@libar-docs-sequence-step:1` | + +### `sequence-module` + +| Property | Value | +| ---------- | -------------------------------------------------------------------- | +| Format | csv | +| Purpose | Maps Rule to deliverable module(s) for sequence diagram participants | +| Required | No | +| Repeatable | No | +| Example | `@libar-docs-sequence-module:detect-context` | + +### `sequence-error` + +| Property | Value | +| ---------- | ------------------------------------------------------------ | +| Format | flag | +| Purpose | Marks scenario as error/alternative path in sequence diagram | +| Required | No | +| Repeatable | No | +| Example | `@libar-docs-sequence-error` | + --- [Back to Taxonomy Reference](../TAXONOMY.md) diff --git a/package.json b/package.json index a1b3a7cf..b2a3fa20 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,8 @@ "docs:process-api-reference": "tsx src/cli/generate-docs.ts -g process-api-reference", "docs:cli-recipe": "tsx src/cli/generate-docs.ts -g cli-recipe", "docs:index": "tsx src/cli/generate-docs.ts -g index", - "docs:all": "pnpm docs:decisions && pnpm docs:product-areas && pnpm docs:taxonomy && pnpm docs:business-rules && pnpm docs:validation && pnpm docs:reference && pnpm docs:claude-modules && pnpm docs:process-api-reference && pnpm docs:cli-recipe && pnpm docs:architecture && pnpm docs:changelog && pnpm docs:index", + "docs:design-review": "tsx src/cli/generate-docs.ts -g design-review", + "docs:all": "pnpm docs:decisions && pnpm docs:product-areas && pnpm docs:taxonomy && pnpm docs:business-rules && pnpm docs:validation && pnpm docs:reference && pnpm docs:claude-modules && pnpm docs:process-api-reference && pnpm docs:cli-recipe && pnpm docs:architecture && pnpm docs:changelog && pnpm docs:design-review && pnpm docs:index", "docs:all-preview": "pnpm docs:patterns && pnpm docs:roadmap && pnpm docs:remaining && pnpm docs:changelog && pnpm docs:architecture && pnpm docs:decisions && pnpm docs:reference && pnpm docs:product-areas && pnpm docs:taxonomy && pnpm docs:validation && pnpm docs:requirements && pnpm docs:current && pnpm docs:milestones && pnpm docs:business-rules", "process:query": "tsx src/cli/process-api.ts", "process:q": "tsx src/cli/process-api.ts", diff --git a/src/api/pattern-helpers.ts b/src/api/pattern-helpers.ts index 1d8e112f..7c86110d 100644 --- a/src/api/pattern-helpers.ts +++ b/src/api/pattern-helpers.ts @@ -15,7 +15,11 @@ */ import type { ExtractedPattern } from '../validation-schemas/extracted-pattern.js'; -import type { MasterDataset, RelationshipEntry } from '../validation-schemas/master-dataset.js'; +import type { + MasterDataset, + RelationshipEntry, + SequenceIndexEntry, +} from '../validation-schemas/master-dataset.js'; import { findBestMatch } from './fuzzy-match.js'; /** @@ -57,6 +61,23 @@ export function getRelationships( return undefined; } +/** + * Look up a sequenceIndex entry by pattern name with case-insensitive fallback. + */ +export function getSequenceEntry( + dataset: MasterDataset, + name: string +): SequenceIndexEntry | undefined { + if (dataset.sequenceIndex === undefined) return undefined; + const exact = dataset.sequenceIndex[name]; + if (exact !== undefined) return exact; + + const pattern = findPatternByName(dataset.patterns, name); + if (pattern === undefined) return undefined; + + return dataset.sequenceIndex[getPatternName(pattern)]; +} + /** * Get all pattern display names from the dataset. */ diff --git a/src/cli/process-api.ts b/src/cli/process-api.ts index 3eb312d0..c10e25e7 100644 --- a/src/cli/process-api.ts +++ b/src/cli/process-api.ts @@ -65,6 +65,7 @@ import type { ProcessStatusValue } from '../taxonomy/index.js'; import { fuzzyMatchPatterns } from '../api/fuzzy-match.js'; import { allPatternNames, + getSequenceEntry, suggestPattern, firstImplements, getPatternName, @@ -354,6 +355,12 @@ Architecture Queries (JSON output): arch layer [name] Patterns in architecture layer (list all if no name) arch compare Cross-context shared deps and integration points +Design Review: + + sequence [name] Sequence diagram data for design reviews + No args: list patterns with sequence annotations + With name: steps, participants, data flow types + Metadata & Inventory: tags Tag usage report (counts per tag and value) @@ -994,6 +1001,40 @@ async function handleArch(ctx: RouteContext): Promise { // Stub Integration Handlers // ============================================================================= +function handleSequence( + dataset: RuntimeMasterDataset, + subArgs: string[], + modifiers: OutputModifiers +): unknown { + const index = dataset.sequenceIndex; + + if (!index || Object.keys(index).length === 0) { + return { message: 'No patterns with sequence annotations found', patterns: [], count: 0 }; + } + + if (subArgs.length === 0) { + const patterns = Object.keys(index); + if (modifiers.count) return { count: patterns.length }; + if (modifiers.namesOnly) return patterns; + return { patterns, count: patterns.length }; + } + + const patternName = subArgs[0] ?? ''; + const entry = getSequenceEntry(dataset, patternName); + if (!entry) { + const available = Object.keys(index); + const hint = suggestPattern(patternName, available); + throw new QueryApiError( + 'PATTERN_NOT_FOUND', + `No sequence data for "${patternName}".${hint} Available: ${available.join(', ')}` + ); + } + + return entry; +} + +// ============================================================================= + function handleStubs(dataset: RuntimeMasterDataset, subArgs: string[], baseDir: string): unknown { const stubs = findStubPatterns(dataset); const resolutions = resolveStubs(stubs, baseDir); @@ -1479,6 +1520,9 @@ async function routeSubcommand(ctx: RouteContext): Promise { case 'sources': return buildSourceInventory(ctx.dataset); + case 'sequence': + return handleSequence(ctx.dataset, ctx.subArgs, ctx.modifiers); + case 'unannotated': { let pathFilter: string | undefined; for (let i = 0; i < ctx.subArgs.length; i++) { @@ -1505,7 +1549,7 @@ async function routeSubcommand(ctx: RouteContext): Promise { default: throw new QueryApiError( 'UNKNOWN_METHOD', - `Unknown subcommand: ${ctx.subcommand}\nAvailable: context, files, dep-tree, overview, scope-validate, handoff, status, query, pattern, list, search, arch, stubs, decisions, pdr, rules, tags, sources, unannotated` + `Unknown subcommand: ${ctx.subcommand}\nAvailable: context, files, dep-tree, overview, scope-validate, handoff, status, query, pattern, list, search, arch, stubs, decisions, pdr, rules, tags, sources, sequence, unannotated` ); } } diff --git a/src/extractor/gherkin-extractor.ts b/src/extractor/gherkin-extractor.ts index fb2a5ecb..70a37a15 100644 --- a/src/extractor/gherkin-extractor.ts +++ b/src/extractor/gherkin-extractor.ts @@ -338,6 +338,8 @@ export function extractPatternsFromGherkin( assignIfDefined(rawPattern, 'claudeModule', metadata.claudeModule); assignIfDefined(rawPattern, 'claudeSection', metadata.claudeSection); assignIfNonEmpty(rawPattern, 'claudeTags', metadata.claudeTags); + // Sequence diagram annotation fields + assignIfDefined(rawPattern, 'sequenceOrchestrator', metadata.sequenceOrchestrator); // NOTE: ADR content is now derived from Gherkin Rule: keywords // (Context, Decision, Consequences) instead of parsed markdown. // The rules array is populated below and rendered by the ADR codec. @@ -387,12 +389,19 @@ export function extractPatternsFromGherkin( // Add rules if present (Gherkin v6+ business rule groupings) if (rules && rules.length > 0) { - rawPattern['rules'] = rules.map((rule) => ({ - name: rule.name, - description: rule.description, - scenarioCount: rule.scenarios.length, - scenarioNames: rule.scenarios.map((s) => s.name), - })); + rawPattern['rules'] = rules.map((rule) => { + const errorNames = rule.scenarios + .filter((s) => s.tags.some((t) => t === 'sequence-error')) + .map((s) => s.name); + return { + name: rule.name, + description: rule.description, + scenarioCount: rule.scenarios.length, + scenarioNames: rule.scenarios.map((s) => s.name), + ...(rule.tags.length > 0 && { tags: rule.tags }), + ...(errorNames.length > 0 && { errorScenarioNames: errorNames }), + }; + }); } // Validate against schema (schema-first enforcement) @@ -630,6 +639,8 @@ export async function extractPatternsFromGherkinAsync( assignIfNonEmpty(rawPattern, 'convention', metadata.convention); // Cross-cutting document inclusion tags assignIfNonEmpty(rawPattern, 'include', metadata.include); + // Sequence diagram annotation fields + assignIfDefined(rawPattern, 'sequenceOrchestrator', metadata.sequenceOrchestrator); // NOTE: ADR content derived from Gherkin Rule: keywords, not parsed markdown assignIfNonEmpty(rawPattern, 'whenToUse', whenToUse); @@ -663,12 +674,19 @@ export async function extractPatternsFromGherkinAsync( assignIfDefined(rawPattern, 'behaviorFile', behaviorFile); if (rules && rules.length > 0) { - rawPattern['rules'] = rules.map((rule) => ({ - name: rule.name, - description: rule.description, - scenarioCount: rule.scenarios.length, - scenarioNames: rule.scenarios.map((s) => s.name), - })); + rawPattern['rules'] = rules.map((rule) => { + const errorNames = rule.scenarios + .filter((s) => s.tags.some((t) => t === 'sequence-error')) + .map((s) => s.name); + return { + name: rule.name, + description: rule.description, + scenarioCount: rule.scenarios.length, + scenarioNames: rule.scenarios.map((s) => s.name), + ...(rule.tags.length > 0 && { tags: rule.tags }), + ...(errorNames.length > 0 && { errorScenarioNames: errorNames }), + }; + }); } const validation = ExtractedPatternSchema.safeParse(rawPattern); diff --git a/src/generators/built-in/codec-generators.ts b/src/generators/built-in/codec-generators.ts index 1e9d5f87..e7a88ad2 100644 --- a/src/generators/built-in/codec-generators.ts +++ b/src/generators/built-in/codec-generators.ts @@ -3,6 +3,7 @@ * @libar-docs-core * @libar-docs-pattern CodecGeneratorRegistration * @libar-docs-status completed + * @libar-docs-uses DesignReviewGenerator, DecisionDocGenerator, ProcessApiReferenceGenerator, CliRecipeGenerator * * ## Codec-Based Generator Registration * @@ -28,6 +29,7 @@ import { generatorRegistry } from '../registry.js'; import { createCodecGenerator } from '../codec-based.js'; import { createDecisionDocGenerator } from './decision-doc-generator.js'; +import { createDesignReviewGenerator } from './design-review-generator.js'; import { createProcessApiReferenceGenerator } from './process-api-reference-generator.js'; import { createCliRecipeGenerator } from './cli-recipe-generator.js'; import { loadPreambleFromMarkdown } from '../../renderable/load-preamble.js'; @@ -175,6 +177,17 @@ generatorRegistry.register(createCodecGenerator('index', 'index')); */ generatorRegistry.register(createDecisionDocGenerator()); +// ═══════════════════════════════════════════════════════════════════════════ +// Design Review Generator (Sequence + Component Diagrams) +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Design Review Generator + * Generates design review documents with sequence and component diagrams + * from @libar-docs-sequence-* annotations on feature specs. + */ +generatorRegistry.register(createDesignReviewGenerator()); + // ═══════════════════════════════════════════════════════════════════════════ // Process API Reference Generator (Schema-Based, not Codec-Based) // ═══════════════════════════════════════════════════════════════════════════ diff --git a/src/generators/built-in/design-review-generator.ts b/src/generators/built-in/design-review-generator.ts new file mode 100644 index 00000000..a1d49156 --- /dev/null +++ b/src/generators/built-in/design-review-generator.ts @@ -0,0 +1,118 @@ +/** + * @libar-docs + * @libar-docs-core + * @libar-docs-pattern DesignReviewGenerator + * @libar-docs-status active + * @libar-docs-implements DesignReviewGeneration + * @libar-docs-arch-role service + * @libar-docs-arch-context generator + * @libar-docs-arch-layer application + * @libar-docs-uses DesignReviewCodec, MasterDataset, SequenceIndex + * @libar-docs-product-area:Generation + * + * ## DesignReviewGenerator + * + * Generates design review documents for patterns with sequence annotations. + * Auto-discovers annotated patterns from MasterDataset.sequenceIndex and + * produces one design review per entry. + * + * Output: `delivery-process/design-reviews/{pattern-name}.md` + */ + +import * as fs from 'node:fs/promises'; +import * as path from 'node:path'; +import type { DocumentGenerator, GeneratorContext, GeneratorOutput, OutputFile } from '../types.js'; +import type { ExtractedPattern } from '../../validation-schemas/index.js'; +import type { RenderableDocument } from '../../renderable/schema.js'; +import { createDesignReviewCodec } from '../../renderable/codecs/design-review.js'; +import { renderToMarkdown } from '../../renderable/render.js'; +import { toKebabCase } from '../../utils/string-utils.js'; + +// ============================================================================= +// Design Review Generator +// ============================================================================= + +async function listExistingDesignReviewFiles( + baseDir: string, + outputDir: string +): Promise { + const outputRoot = path.isAbsolute(outputDir) ? outputDir : path.join(baseDir, outputDir); + const reviewsDir = path.join(outputRoot, 'design-reviews'); + + try { + const entries = await fs.readdir(reviewsDir, { withFileTypes: true }); + return entries + .filter((entry) => entry.isFile() && entry.name.endsWith('.md')) + .map((entry) => `design-reviews/${entry.name}`) + .sort(); + } catch (error: unknown) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + return []; + } + + const message = error instanceof Error ? error.message : String(error); + console.warn(`[design-review] Skipping orphan cleanup scan: ${message}`); + return []; + } +} + +class DesignReviewGeneratorImpl implements DocumentGenerator { + readonly name = 'design-review'; + readonly description = 'Design review diagrams from sequence annotations'; + + async generate( + _patterns: readonly ExtractedPattern[], + context: GeneratorContext + ): Promise { + const files: OutputFile[] = []; + const dataset = context.masterDataset; + + if (!dataset) { + return { files }; + } + + const sequenceIndex = dataset.sequenceIndex; + if (!sequenceIndex || Object.keys(sequenceIndex).length === 0) { + return { files }; + } + + const expectedFiles = new Set( + Object.keys(sequenceIndex).map( + (patternName) => `design-reviews/${toKebabCase(patternName)}.md` + ) + ); + const existingFiles = await listExistingDesignReviewFiles(context.baseDir, context.outputDir); + const filesToDelete = existingFiles.filter((filePath) => !expectedFiles.has(filePath)); + + for (const patternName of Object.keys(sequenceIndex)) { + try { + const codec = createDesignReviewCodec({ patternName }); + // Cast needed: RenderableDocumentOutputSchema uses z.string().optional() for purpose/detailLevel + // which under exactOptionalPropertyTypes produces `string | undefined`, not compatible with + // RenderableDocument's `purpose?: string` (absent-or-string, not present-as-undefined). + // The codec always provides these values, so the cast is safe at runtime. + const doc = codec.decode(dataset) as RenderableDocument; + const markdown = renderToMarkdown(doc); + + const filename = `design-reviews/${toKebabCase(patternName)}.md`; + files.push({ path: filename, content: markdown }); + } catch (error: unknown) { + // Keep generating remaining design reviews even if one annotated pattern is invalid. + const message = error instanceof Error ? error.message : String(error); + console.warn(`[design-review] Skipping ${patternName}: ${message}`); + } + } + + return { + files, + ...(filesToDelete.length > 0 && { filesToDelete }), + }; + } +} + +/** + * Create design review generator instance + */ +export function createDesignReviewGenerator(): DocumentGenerator { + return new DesignReviewGeneratorImpl(); +} diff --git a/src/generators/pipeline/sequence-utils.ts b/src/generators/pipeline/sequence-utils.ts new file mode 100644 index 00000000..7f130cee --- /dev/null +++ b/src/generators/pipeline/sequence-utils.ts @@ -0,0 +1,306 @@ +/** + * @libar-docs + * @libar-docs-core + * @libar-docs-pattern SequenceTransformUtils + * @libar-docs-status active + * @libar-docs-implements DesignReviewGeneration + * @libar-docs-arch-role service + * @libar-docs-arch-context generator + * @libar-docs-arch-layer application + * @libar-docs-include pipeline-stages + * @libar-docs-uses MasterDataset, ExtractedPattern + * @libar-docs-product-area:Generation + * + * ## SequenceTransformUtils - Sequence Index Builder + * + * Builds pre-computed SequenceIndexEntry objects from patterns that have + * sequence diagram annotations. Used during the single-pass transform to + * populate MasterDataset.sequenceIndex. + * + * ### Layer Boundary + * + * This module intentionally duplicates the Input/Output/Invariant regex + * patterns from parseBusinessRuleAnnotations (renderable/codecs/helpers.ts) + * to avoid a cross-layer dependency from pipeline → codec. The regexes are + * simple (3 patterns) and stable. + */ + +import type { BusinessRule } from '../../validation-schemas/extracted-pattern.js'; +import type { SequenceStep, SequenceIndexEntry } from '../../validation-schemas/master-dataset.js'; + +// ============================================================================= +// Tag Parsing (simple string split — no dependency on extractPatternTags) +// ============================================================================= + +interface RuleSequenceTags { + readonly step: number | undefined; + readonly modules: readonly string[]; +} + +/** + * Parse sequence-step and sequence-module from a rule's tag array. + * + * Tags arrive normalized (without @libar-docs- prefix) from the scanner. + */ +function parseRuleSequenceTags(tags: readonly string[]): RuleSequenceTags { + let step: number | undefined; + const modules: string[] = []; + for (const tag of tags) { + if (tag.startsWith('sequence-step:')) { + const parsed = parseInt(tag.split(':')[1] ?? '', 10); + if (!Number.isNaN(parsed)) { + step = parsed; + } + } else if (tag.startsWith('sequence-module:')) { + const value = tag.split(':')[1] ?? ''; + modules.push( + ...value + .split(',') + .map((m) => m.trim()) + .filter(Boolean) + ); + } + } + return { step, modules }; +} + +// ============================================================================= +// Annotation Parsing (lightweight — 3 fields only) +// ============================================================================= + +interface SequenceAnnotations { + readonly input: string | undefined; + readonly output: string | undefined; + readonly invariant: string | undefined; +} + +interface ParsedSequenceRule { + readonly stepNumber: number; + readonly ruleName: string; + readonly modules: readonly string[]; + readonly annotations: SequenceAnnotations; + readonly errorScenarios: readonly string[]; +} + +export interface SequenceIndexBuildResult { + readonly entry: SequenceIndexEntry | undefined; + readonly issues: readonly string[]; +} + +/** + * Extract Input, Output, and Invariant from a rule description. + * + * Duplicates the regex patterns from parseBusinessRuleAnnotations in + * renderable/codecs/helpers.ts to avoid a cross-layer import. + */ +function parseSequenceAnnotations(description: string): SequenceAnnotations { + let input: string | undefined; + let output: string | undefined; + let invariant: string | undefined; + + const invariantPattern = /\*\*Invariant:\*\*\s*([\s\S]*?)(?=\*\*[A-Z]|\*\*$|$)/i; + const invariantMatch = invariantPattern.exec(description); + if (invariantMatch?.[1]) { + invariant = invariantMatch[1] + .replace(/\n\s*\n/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + } + + const inputPattern = /\*\*Input:\*\*\s*([\s\S]*?)(?=\*\*[A-Z]|\*\*$|$)/i; + const inputMatch = inputPattern.exec(description); + if (inputMatch?.[1]) { + input = inputMatch[1] + .replace(/\n\s*\n/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + } + + const outputPattern = /\*\*Output:\*\*\s*([\s\S]*?)(?=\*\*[A-Z]|\*\*$|$)/i; + const outputMatch = outputPattern.exec(description); + if (outputMatch?.[1]) { + output = outputMatch[1] + .replace(/\n\s*\n/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + } + + return { input, output, invariant }; +} + +// ============================================================================= +// Data Flow Type Extraction +// ============================================================================= + +/** + * Extract the type name from an Input or Output annotation string. + * + * Format: "TypeName -- field1, field2, field3" → "TypeName" + * Or just: "TypeName" → "TypeName" + */ +function extractTypeName(annotation: string): string { + const dashIndex = annotation.indexOf('--'); + if (dashIndex >= 0) { + return annotation.slice(0, dashIndex).trim(); + } + return annotation.trim(); +} + +/** + * Decide whether an annotation should contribute to dataFlowTypes. + * + * Keeps structured type declarations (`TypeName -- fields`), bare identifiers, + * and type-ish parameter signatures such as `targetDir: string`, while excluding + * prose outputs like "package.json updated with process and docs scripts". + */ +function extractDataFlowTypeName(annotation: string): string | undefined { + const trimmed = annotation.trim(); + if (trimmed.length === 0) return undefined; + + if (trimmed.includes('--')) { + return extractTypeName(trimmed); + } + + if (!/\s/.test(trimmed)) { + return trimmed; + } + + return /[:<>{}\[\]|&()]/.test(trimmed) ? trimmed : undefined; +} + +// ============================================================================= +// Index Builder +// ============================================================================= + +/** + * Build a SequenceIndexEntry from a pattern's orchestrator and business rules. + * + * Returns undefined if no rules have sequence-step tags (the pattern has an + * orchestrator annotation but no step-annotated rules). + */ +export function buildSequenceIndexEntry( + orchestrator: string, + rules: readonly BusinessRule[] +): SequenceIndexEntry | undefined { + return buildSequenceIndexEntryWithValidation(orchestrator, rules).entry; +} + +/** + * Build a SequenceIndexEntry and collect validation issues for malformed annotations. + * + * Returns: + * - `entry` when the sequence annotations are valid + * - `issues` when step-tagged rules are ambiguous or incomplete + * - no entry and no issues when no rules have sequence-step tags + */ +export function buildSequenceIndexEntryWithValidation( + orchestrator: string, + rules: readonly BusinessRule[] +): SequenceIndexBuildResult { + const steps: SequenceStep[] = []; + const parsedRules: ParsedSequenceRule[] = []; + const stepNumbers = new Map(); + const issues: string[] = []; + + for (const rule of rules) { + if (!rule.tags || rule.tags.length === 0) continue; + + const { step, modules } = parseRuleSequenceTags(rule.tags); + if (step === undefined) continue; + + if (modules.length === 0) { + issues.push( + `Rule "${rule.name}" has @libar-docs-sequence-step:${String(step)} but no @libar-docs-sequence-module values` + ); + continue; + } + + const annotations = parseSequenceAnnotations(rule.description); + const rulesForStep = stepNumbers.get(step) ?? []; + rulesForStep.push(rule.name); + stepNumbers.set(step, rulesForStep); + + parsedRules.push({ + stepNumber: step, + ruleName: rule.name, + modules, + annotations, + errorScenarios: rule.errorScenarioNames ?? [], + }); + } + + for (const [stepNumber, ruleNames] of stepNumbers.entries()) { + if (ruleNames.length > 1) { + issues.push( + `Duplicate @libar-docs-sequence-step:${String(stepNumber)} used by rules: ${ruleNames.join(', ')}` + ); + } + } + + if (issues.length > 0) { + return { entry: undefined, issues }; + } + + if (parsedRules.length === 0) { + return { entry: undefined, issues: [] }; + } + + for (const parsedRule of parsedRules) { + steps.push({ + stepNumber: parsedRule.stepNumber, + ruleName: parsedRule.ruleName, + modules: [...parsedRule.modules], + input: parsedRule.annotations.input, + output: parsedRule.annotations.output, + invariant: parsedRule.annotations.invariant, + errorScenarios: [...parsedRule.errorScenarios], + }); + } + + // Sort by step number + steps.sort((a, b) => a.stepNumber - b.stepNumber); + + // Deduplicate participants: orchestrator first, then modules in step order + const seen = new Set([orchestrator]); + const participants: string[] = [orchestrator]; + for (const step of steps) { + for (const mod of step.modules) { + if (!seen.has(mod)) { + seen.add(mod); + participants.push(mod); + } + } + } + + // Collect all error scenario names + const errorPaths = steps.flatMap((s) => s.errorScenarios); + + // Collect distinct data flow type names from Input/Output + const typeNames = new Set(); + for (const step of steps) { + if (step.input) { + const inputType = extractDataFlowTypeName(step.input); + if (inputType) { + typeNames.add(inputType); + } + } + if (step.output) { + const outputType = extractDataFlowTypeName(step.output); + if (outputType) { + typeNames.add(outputType); + } + } + } + const dataFlowTypes = [...typeNames]; + + return { + entry: { + orchestrator, + steps, + participants, + errorPaths, + dataFlowTypes, + }, + issues: [], + }; +} diff --git a/src/generators/pipeline/transform-dataset.ts b/src/generators/pipeline/transform-dataset.ts index e47b8c21..20a9098b 100644 --- a/src/generators/pipeline/transform-dataset.ts +++ b/src/generators/pipeline/transform-dataset.ts @@ -45,10 +45,12 @@ import type { RelationshipEntry, ImplementationRef, ArchIndex, + SequenceIndexEntry, } from '../../validation-schemas/master-dataset.js'; import type { MasterDataset } from '../../validation-schemas/master-dataset.js'; import { normalizeStatus, ACCEPTED_STATUS_VALUES } from '../../taxonomy/index.js'; +import { buildSequenceIndexEntryWithValidation } from './sequence-utils.js'; // ============================================================================= // Validation Summary Types @@ -372,6 +374,9 @@ export function transformToMasterDatasetWithValidation(raw: RawDataset): Transfo const relationshipIndex: Record = {}; + // Sequence index for design review diagram generation + const sequenceIndex: Record = {}; + // Architecture index for diagram generation const archIndex: ArchIndex = { byRole: {}, @@ -492,6 +497,38 @@ export function transformToMasterDatasetWithValidation(raw: RawDataset): Transfo } } } + + // ─── Sequence index (for design review diagram generation) ──────────── + if (pattern.sequenceOrchestrator) { + if (pattern.rules && pattern.rules.length > 0) { + const result = buildSequenceIndexEntryWithValidation( + pattern.sequenceOrchestrator, + pattern.rules + ); + if (result.entry !== undefined) { + sequenceIndex[patternKey] = result.entry; + } else if (result.issues.length > 0) { + malformedPatterns.push({ + patternId: patternKey, + issues: [...result.issues], + }); + } else { + malformedPatterns.push({ + patternId: patternKey, + issues: [ + 'Has @libar-docs-sequence-orchestrator but no rules with @libar-docs-sequence-step tags', + ], + }); + } + } else { + malformedPatterns.push({ + patternId: patternKey, + issues: [ + 'Has @libar-docs-sequence-orchestrator but no Rule: blocks to extract sequence steps from', + ], + }); + } + } } // ───────────────────────────────────────────────────────────────────────── @@ -679,6 +716,8 @@ export function transformToMasterDatasetWithValidation(raw: RawDataset): Transfo relationshipIndex, // Only include archIndex if it has content ...(archIndex.all.length > 0 && { archIndex }), + // Only include sequenceIndex if it has content + ...(Object.keys(sequenceIndex).length > 0 && { sequenceIndex }), }; // Only include workflow if defined (exactOptionalPropertyTypes compliance) diff --git a/src/renderable/codecs/design-review.ts b/src/renderable/codecs/design-review.ts new file mode 100644 index 00000000..ceaa5563 --- /dev/null +++ b/src/renderable/codecs/design-review.ts @@ -0,0 +1,688 @@ +/** + * @libar-docs + * @libar-docs-core + * @libar-docs-pattern DesignReviewCodec + * @libar-docs-status active + * @libar-docs-implements DesignReviewGeneration + * @libar-docs-arch-role projection + * @libar-docs-arch-context renderer + * @libar-docs-arch-layer application + * @libar-docs-include codec-transformation + * @libar-docs-uses MasterDataset, SequenceIndex, MermaidDiagramUtils + * @libar-docs-convention codec-registry + * @libar-docs-product-area:Generation + * + * ## DesignReviewCodec + * + * Transforms MasterDataset into a RenderableDocument containing design review + * artifacts: sequence diagrams, component diagrams, type definition tables, + * and design question templates. + * + * **Purpose:** Auto-generate design review documents from sequence annotations + * on Gherkin specs. Diagrams stay synchronized with spec changes. + * + * **Output Files:** `delivery-process/design-reviews/{pattern-name}.md` + * + * ### Factory Pattern + * + * ```typescript + * const codec = createDesignReviewCodec({ patternName: 'SetupCommand' }); + * const doc = codec.decode(dataset); + * ``` + */ + +import { z } from 'zod'; +import { + MasterDatasetSchema, + type MasterDataset, + type SequenceIndexEntry, + type SequenceStep, +} from '../../validation-schemas/master-dataset.js'; +import type { ExtractedPattern } from '../../validation-schemas/index.js'; +import { + type RenderableDocument, + type SectionBlock, + heading, + paragraph, + separator, + table, + mermaid, + document, +} from '../schema.js'; +import { getPatternName, findPatternByName } from '../../api/pattern-helpers.js'; +import { type BaseCodecOptions, DEFAULT_BASE_OPTIONS, mergeOptions } from './types/base.js'; +import { RenderableDocumentOutputSchema } from './shared-schema.js'; +import { sanitizeNodeId } from './diagram-utils.js'; + +// ═══════════════════════════════════════════════════════════════════════════ +// Design Review Codec Options +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Options for DesignReviewCodec + */ +export interface DesignReviewCodecOptions extends BaseCodecOptions { + /** Pattern name to generate the design review for (required) */ + patternName: string; + + /** Include the annotation convention section (default: true) */ + includeAnnotationConvention?: boolean; + + /** Include the component diagram (default: true) */ + includeComponentDiagram?: boolean; +} + +/** + * Default options for DesignReviewCodec + */ +const DEFAULT_DESIGN_REVIEW_OPTIONS: Required = { + ...DEFAULT_BASE_OPTIONS, + patternName: '', + includeAnnotationConvention: true, + includeComponentDiagram: true, +}; + +// ═══════════════════════════════════════════════════════════════════════════ +// Design Review Document Codec +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Create a DesignReviewCodec with the given options. + * + * @param options - Codec configuration (patternName is required) + * @returns Configured Zod codec that transforms MasterDataset → RenderableDocument + */ +export function createDesignReviewCodec( + options: DesignReviewCodecOptions +): z.ZodCodec { + const opts = mergeOptions(DEFAULT_DESIGN_REVIEW_OPTIONS, options); + + return z.codec(MasterDatasetSchema, RenderableDocumentOutputSchema, { + decode: (dataset: MasterDataset): RenderableDocument => { + return buildDesignReviewDocument(dataset, opts); + }, + /** @throws Always - this codec is decode-only. See zod-codecs.md */ + encode: (): never => { + throw new Error('DesignReviewCodec is decode-only. See zod-codecs.md'); + }, + }); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Document Builder +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Build the design review document from dataset + */ +function buildDesignReviewDocument( + dataset: MasterDataset, + options: Required +): RenderableDocument { + const { patternName } = options; + + // Find the pattern and its sequence index entry + const pattern = findPatternByName(dataset.patterns, patternName); + if (!pattern) { + return document(`Design Review: ${patternName}`, [ + heading(2, 'Pattern Not Found'), + paragraph(`Pattern "${patternName}" not found in dataset.`), + ]); + } + + const entry = dataset.sequenceIndex?.[getPatternName(pattern)]; + if (!entry) { + return document(`Design Review: ${patternName}`, [ + heading(2, 'No Sequence Data'), + paragraph( + `Pattern "${patternName}" has no sequence annotations. ` + + 'Add `@libar-docs-sequence-orchestrator` and `@libar-docs-sequence-step` ' + + 'tags to generate design review diagrams.' + ), + ]); + } + + const sections: SectionBlock[] = []; + const displayName = getPatternName(pattern); + + // Header metadata + sections.push(...buildHeaderSection(pattern, entry)); + + // Annotation Convention (optional) + if (options.includeAnnotationConvention) { + sections.push(separator()); + sections.push(...buildAnnotationConventionSection()); + } + + // Sequence Diagram + sections.push(separator()); + sections.push(...buildSequenceDiagramSection(entry, pattern)); + + // Component Diagram (optional) + if (options.includeComponentDiagram) { + sections.push(separator()); + sections.push(...buildComponentDiagramSection(entry, pattern)); + } + + // Key Type Definitions + sections.push(separator()); + sections.push(...buildTypeDefinitionsSection(entry)); + + // Design Questions (template with auto metrics) + sections.push(separator()); + sections.push(...buildDesignQuestionsSection(entry)); + + // Findings (placeholder) + sections.push(separator()); + sections.push(...buildFindingsSection()); + + // Summary + sections.push(separator()); + sections.push(...buildSummarySection(entry, displayName)); + + return document(`Design Review: ${displayName}`, sections, { + purpose: 'Auto-generated design review with sequence and component diagrams', + detailLevel: 'Design review artifact from sequence annotations', + }); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Section Builders +// ═══════════════════════════════════════════════════════════════════════════ + +function buildHeaderSection(pattern: ExtractedPattern, entry: SequenceIndexEntry): SectionBlock[] { + const name = getPatternName(pattern); + const phase = pattern.phase !== undefined ? `Phase ${String(pattern.phase)}` : 'N/A'; + const status = pattern.status ?? 'unknown'; + + return [ + paragraph( + `**Pattern:** ${name} | **Phase:** ${phase} | **Status:** ${status} | ` + + `**Orchestrator:** ${entry.orchestrator} | **Steps:** ${String(entry.steps.length)} | ` + + `**Participants:** ${String(entry.participants.length)}` + ), + paragraph(`**Source:** \`${pattern.source.file}\``), + ]; +} + +function buildAnnotationConventionSection(): SectionBlock[] { + return [ + heading(2, 'Annotation Convention'), + paragraph('This design review is generated from the following annotations:'), + table( + ['Tag', 'Level', 'Format', 'Purpose'], + [ + ['sequence-orchestrator', 'Feature', 'value', 'Identifies the coordinator module'], + ['sequence-step', 'Rule', 'number', 'Explicit execution ordering'], + ['sequence-module', 'Rule', 'csv', 'Maps Rule to deliverable module(s)'], + ['sequence-error', 'Scenario', 'flag', 'Marks scenario as error/alt path'], + ] + ), + paragraph( + 'Description markers: `**Input:**` and `**Output:**` in Rule descriptions ' + + 'define data flow types for sequence diagram call arrows and component diagram edges.' + ), + ]; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Sequence Diagram Builder +// ═══════════════════════════════════════════════════════════════════════════ + +function buildSequenceDiagramSection( + entry: SequenceIndexEntry, + pattern: ExtractedPattern +): SectionBlock[] { + const lines = generateSequenceDiagram(entry, pattern); + return [ + heading(2, 'Sequence Diagram — Runtime Interaction Flow'), + paragraph( + 'Generated from: `@libar-docs-sequence-step`, `@libar-docs-sequence-module`, ' + + '`@libar-docs-sequence-error`, `**Input:**`/`**Output:**` markers, and ' + + '`@libar-docs-sequence-orchestrator` on the Feature.' + ), + mermaid(lines.join('\n')), + ]; +} + +function generateSequenceDiagram(entry: SequenceIndexEntry, pattern: ExtractedPattern): string[] { + const lines: string[] = ['sequenceDiagram']; + + // Participant declarations + lines.push(' participant User'); + const orchId = sanitizeNodeId(entry.orchestrator); + const orchLabel = resolveModuleLabel(entry.orchestrator, pattern); + lines.push(` participant ${orchId} as ${quoteMermaidText(orchLabel)}`); + + for (const mod of entry.participants) { + if (mod === entry.orchestrator) continue; + const modId = sanitizeNodeId(mod); + const modLabel = resolveModuleLabel(mod, pattern); + lines.push(` participant ${modId} as ${quoteMermaidText(modLabel)}`); + } + + lines.push(''); + + // User → orchestrator initial call + lines.push(` User->>${orchId}: invoke`); + lines.push(''); + + // Steps + for (const step of entry.steps) { + // Note block for rule + lines.push( + ` Note over ${orchId}: Rule ${String(step.stepNumber)} — ${sanitizeMermaidRawText(step.invariant ?? step.ruleName)}` + ); + lines.push(''); + + // Call from orchestrator to each module + for (const mod of step.modules) { + const modId = sanitizeNodeId(mod); + const inputLabel = sanitizeMermaidRawText(extractTypeName(step.input) || step.ruleName); + const outputLabel = sanitizeMermaidRawText(extractTypeName(step.output) || 'result'); + + lines.push(` ${orchId}->>+${modId}: ${inputLabel}`); + lines.push(` ${modId}-->>-${orchId}: ${outputLabel}`); + } + + // Alt blocks for error scenarios + if (step.errorScenarios.length > 0) { + for (const errScenario of step.errorScenarios) { + lines.push(''); + lines.push(` alt ${sanitizeMermaidRawText(errScenario)}`); + lines.push(` ${orchId}-->>User: error`); + lines.push(` ${orchId}->>${orchId}: exit(1)`); + lines.push(' end'); + } + } + + lines.push(''); + } + + return lines; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Component Diagram Builder +// ═══════════════════════════════════════════════════════════════════════════ + +function buildComponentDiagramSection( + entry: SequenceIndexEntry, + pattern: ExtractedPattern +): SectionBlock[] { + const lines = generateComponentDiagram(entry, pattern); + return [ + heading(2, 'Component Diagram — Types and Data Flow'), + paragraph( + 'Generated from: `@libar-docs-sequence-module` (nodes), `**Input:**`/`**Output:**` ' + + '(edges and type shapes), deliverables table (locations), and `sequence-step` (grouping).' + ), + mermaid(lines.join('\n')), + ]; +} + +function generateComponentDiagram(entry: SequenceIndexEntry, pattern: ExtractedPattern): string[] { + const lines: string[] = ['graph LR']; + + // Group steps into phases by shared input type + const phases = groupStepsByInputType(entry.steps); + + // Render phase subgraphs (use phase_N prefix for unique IDs) + for (let i = 0; i < phases.length; i++) { + const phase = phases[i]; + if (!phase) continue; + const phaseId = `phase_${String(i + 1)}`; + lines.push( + ` subgraph ${phaseId}[${quoteMermaidText(`Phase ${String(i + 1)}: ${phase.label}`)}]` + ); + const declaredModules = new Set(); + for (const mod of phase.modules) { + const modId = getPhaseModuleNodeId(i, mod); + if (declaredModules.has(modId)) continue; + declaredModules.add(modId); + const modLabel = resolveModuleLabel(mod, pattern); + lines.push(` ${modId}[${quoteMermaidText(modLabel)}]`); + } + lines.push(' end'); + lines.push(''); + } + + // Orchestrator subgraph + const orchId = sanitizeNodeId(entry.orchestrator); + const orchLabel = resolveModuleLabel(entry.orchestrator, pattern); + lines.push(` subgraph orchestrator[${quoteMermaidText('Orchestrator')}]`); + lines.push(` ${orchId}[${quoteMermaidText(orchLabel)}]`); + lines.push(' end'); + lines.push(''); + + // Type nodes (hexagon shape) for types with field definitions + const typeDefs = collectTypeDefs(entry.steps); + if (typeDefs.length > 0) { + lines.push(` subgraph types[${quoteMermaidText('Key Types')}]`); + for (const typeDef of typeDefs) { + const typeId = sanitizeNodeId(typeDef.name); + if (typeDef.fields) { + // Convert comma-separated fields to newline-separated for hexagon node display + const hexFields = typeDef.fields + .split(',') + .map((f) => f.trim()) + .filter(Boolean) + .join('\n'); + lines.push( + ` ${typeId}{{${quoteMermaidText(`${typeDef.name}\n-----------\n${hexFields}`, { + preserveLineBreaks: true, + })}}}` + ); + } else { + lines.push(` ${typeId}{{${quoteMermaidText(typeDef.name)}}}`); + } + } + lines.push(' end'); + lines.push(''); + } + + // Edges: module outputs → orchestrator (only for proper types with -- field separator) + for (let i = 0; i < phases.length; i++) { + const phase = phases[i]; + if (!phase) continue; + for (const step of phase.steps) { + if (step.output?.includes('--') !== true) continue; + const outputType = extractTypeName(step.output); + for (const mod of step.modules) { + const modId = getPhaseModuleNodeId(i, mod); + lines.push(` ${modId} -->|${quoteMermaidText(outputType)}| ${orchId}`); + } + } + } + + // Orchestrator dispatches to steps that take an input + for (let i = 0; i < phases.length; i++) { + const phase = phases[i]; + if (!phase) continue; + for (const step of phase.steps) { + const inputType = extractTypeName(step.input); + if (!inputType) continue; + for (const mod of step.modules) { + const modId = getPhaseModuleNodeId(i, mod); + lines.push(` ${orchId} -->|${quoteMermaidText(inputType)}| ${modId}`); + } + } + } + + return lines; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Type Definitions Section +// ═══════════════════════════════════════════════════════════════════════════ + +function buildTypeDefinitionsSection(entry: SequenceIndexEntry): SectionBlock[] { + const typeDefs = collectTypeDefs(entry.steps); + + if (typeDefs.length === 0) { + return [ + heading(2, 'Key Type Definitions'), + paragraph('No structured types found in Output annotations.'), + ]; + } + + const rows: string[][] = []; + for (const typeDef of typeDefs) { + const producedBy = findProducers(entry.steps, typeDef.name); + const consumedBy = findConsumers(entry.steps, typeDef.name); + rows.push([ + `\`${typeDef.name}\``, + typeDef.fields ?? '—', + producedBy.join(', '), + consumedBy.join(', '), + ]); + } + + return [ + heading(2, 'Key Type Definitions'), + table(['Type', 'Fields', 'Produced By', 'Consumed By'], rows), + ]; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Design Questions (template with auto metrics) +// ═══════════════════════════════════════════════════════════════════════════ + +function buildDesignQuestionsSection(entry: SequenceIndexEntry): SectionBlock[] { + const stepCount = entry.steps.length; + const typeCount = collectTypeDefs(entry.steps).length; + const errorCount = entry.errorPaths.length; + + return [ + heading(2, 'Design Questions'), + paragraph('Verify these design properties against the diagrams above:'), + table( + ['#', 'Question', 'Auto-Check', 'Diagram'], + [ + [ + 'DQ-1', + 'Is the execution ordering correct?', + `${String(stepCount)} steps in monotonic order`, + 'Sequence', + ], + [ + 'DQ-2', + 'Are all interfaces well-defined?', + `${String(typeCount)} distinct types across ${String(stepCount)} steps`, + 'Component', + ], + [ + 'DQ-3', + 'Is error handling complete?', + `${String(errorCount)} error paths identified`, + 'Sequence', + ], + ['DQ-4', 'Is data flow unidirectional?', 'Review component diagram edges', 'Component'], + ['DQ-5', 'Does validation prove the full path?', 'Review final step', 'Both'], + ] + ), + ]; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Findings (placeholder for human review) +// ═══════════════════════════════════════════════════════════════════════════ + +function buildFindingsSection(): SectionBlock[] { + return [ + heading(2, 'Findings'), + paragraph( + 'Record design observations from reviewing the diagrams above. ' + + 'Each finding should reference which diagram revealed it and its impact on the spec.' + ), + table( + ['#', 'Finding', 'Diagram Source', 'Impact on Spec'], + [['F-1', '(Review the diagrams and add findings here)', '—', '—']] + ), + ]; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Summary +// ═══════════════════════════════════════════════════════════════════════════ + +function buildSummarySection(entry: SequenceIndexEntry, displayName: string): SectionBlock[] { + const stepCount = entry.steps.length; + const participantCount = entry.participants.length; + const typeCount = collectTypeDefs(entry.steps).length; + const errorCount = entry.errorPaths.length; + + return [ + heading(2, 'Summary'), + paragraph( + `The ${displayName} design review covers ${String(stepCount)} sequential steps across ` + + `${String(participantCount)} participants with ${String(typeCount)} key data types ` + + `and ${String(errorCount)} error paths.` + ), + ]; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Shared Helpers +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Sanitize Mermaid text in raw positions such as notes, alt conditions, and + * sequence message labels where the text is not wrapped in quotes. + */ +function sanitizeMermaidRawText(text: string): string { + return ( + text + .replace(/\r?\n/g, ' ') + .replace(/%%/g, '% %') + .replace(/\|/g, '|') + .replace(/"/g, '"') + .replace(/>>/g, '> >') + // In raw (unquoted) Mermaid positions, `--` is edge syntax; em-dash renders visually equivalent + .replace(/--/g, '\u2014') + .trim() + ); +} + +interface MermaidTextOptions { + readonly preserveLineBreaks?: boolean; +} + +/** + * Quote Mermaid text for label positions that already use Mermaid string syntax. + */ +function quoteMermaidText(text: string, options: MermaidTextOptions = {}): string { + const { preserveLineBreaks = false } = options; + const escapedBackslashes = text.replace(/\\/g, '\\\\'); + const normalized = preserveLineBreaks + ? escapedBackslashes.replace(/\r?\n/g, '\\n') + : escapedBackslashes.replace(/\r?\n/g, ' '); + + return `"${normalized + .replace(/%%/g, '% %') + .replace(/\|/g, '|') + .replace(/"/g, '"') + .trim()}"`; +} + +/** + * Extract the type name from an Input or Output annotation string. + * Format: "TypeName -- field1, field2" → "TypeName" + * Or just: "some description" → "some description" + * Returns empty string for undefined. + */ +function extractTypeName(annotation: string | undefined): string { + if (!annotation) return ''; + const dashIndex = annotation.indexOf('--'); + if (dashIndex >= 0) { + return annotation.slice(0, dashIndex).trim(); + } + return annotation.trim(); +} + +/** + * Resolve a module ID to a display label. + * Checks deliverables table for Location basenames, falls back to module ID + .ts. + */ +function resolveModuleLabel(moduleId: string, pattern: ExtractedPattern): string { + if (pattern.deliverables) { + for (const d of pattern.deliverables) { + const basename = + d.location + .split('/') + .pop() + ?.replace(/\.[^.]+$/, '') ?? ''; + if (basename === moduleId) { + return d.location.split('/').pop() ?? `${moduleId}.ts`; + } + } + } + return `${moduleId}.ts`; +} + +/** TypeDef extracted from Output annotations */ +interface TypeDef { + readonly name: string; + readonly fields: string | undefined; +} + +/** + * Collect distinct type definitions from Output annotations. + * Only includes annotations with the "TypeName -- fields" format. + */ +function collectTypeDefs(steps: readonly SequenceStep[]): TypeDef[] { + const seen = new Set(); + const defs: TypeDef[] = []; + + for (const step of steps) { + if (!step.output) continue; + const dashIndex = step.output.indexOf('--'); + if (dashIndex < 0) continue; // Skip description-style outputs + + const name = step.output.slice(0, dashIndex).trim(); + if (seen.has(name)) continue; + seen.add(name); + + const rawFields = step.output.slice(dashIndex + 2).trim(); + defs.push({ name, fields: rawFields.length > 0 ? rawFields : undefined }); + } + + return defs; +} + +/** Find which modules produce a given type (by Output annotation) */ +function findProducers(steps: readonly SequenceStep[], typeName: string): string[] { + const producers: string[] = []; + for (const step of steps) { + if (step.output && extractTypeName(step.output) === typeName) { + producers.push(...step.modules); + } + } + return producers; +} + +/** Find which modules consume a given type (by Input annotation) */ +function findConsumers(steps: readonly SequenceStep[], typeName: string): string[] { + const consumers: string[] = []; + for (const step of steps) { + if (step.input && extractTypeName(step.input) === typeName) { + consumers.push(...step.modules); + } + } + return consumers; +} + +/** Phase grouping for component diagram */ +interface PhaseGroup { + readonly label: string; + readonly modules: string[]; + readonly steps: SequenceStep[]; +} + +/** + * Group contiguous steps by shared Input type into phases. + * Only adjacent steps with the same Input type are grouped together. + * Non-contiguous steps with the same input type become separate phases. + */ +function groupStepsByInputType(steps: readonly SequenceStep[]): PhaseGroup[] { + const phases: PhaseGroup[] = []; + + for (const step of steps) { + const inputType = extractTypeName(step.input) || `Step ${String(step.stepNumber)}`; + const lastPhase = phases[phases.length - 1]; + + // Only merge into the previous phase if it has the same input type (contiguous grouping) + if (lastPhase?.label === inputType) { + lastPhase.modules.push(...step.modules); + lastPhase.steps.push(step); + } else { + phases.push({ label: inputType, modules: [...step.modules], steps: [step] }); + } + } + + return phases; +} + +function getPhaseModuleNodeId(phaseIndex: number, moduleId: string): string { + return `phase_${String(phaseIndex + 1)}_${sanitizeNodeId(moduleId)}`; +} diff --git a/src/renderable/codecs/helpers.ts b/src/renderable/codecs/helpers.ts index 856993a5..013282f0 100644 --- a/src/renderable/codecs/helpers.ts +++ b/src/renderable/codecs/helpers.ts @@ -610,6 +610,10 @@ export interface BusinessRuleAnnotations { codeExamples?: SectionBlock[]; /** API implementation references extracted from **API:** See `path` patterns */ apiRefs?: readonly string[]; + /** Input type annotation for sequence diagram steps (from **Input:** marker) */ + input?: string; + /** Output type annotation for sequence diagram steps (from **Output:** marker) */ + output?: string; /** Remaining description content after annotation extraction */ remainingContent?: string; } @@ -807,6 +811,32 @@ export function parseBusinessRuleAnnotations(description: string): BusinessRuleA result.apiRefs = apiRefs; } + // Extract **Input:** - sequence step input type annotation + const inputPattern = /\*\*Input:\*\*\s*([\s\S]*?)(?=\*\*[A-Z]|\*\*$|$)/i; + const inputMatch = inputPattern.exec(protectedText); + if (inputMatch?.[1]) { + const inputText = inputMatch[1].trim(); + result.input = restore( + inputText + .replace(/\n\s*\n/g, ' ') + .replace(/\s+/g, ' ') + .trim() + ); + } + + // Extract **Output:** - sequence step output type annotation + const outputPattern = /\*\*Output:\*\*\s*([\s\S]*?)(?=\*\*[A-Z]|\*\*$|$)/i; + const outputMatch = outputPattern.exec(protectedText); + if (outputMatch?.[1]) { + const outputText = outputMatch[1].trim(); + result.output = restore( + outputText + .replace(/\n\s*\n/g, ' ') + .replace(/\s+/g, ' ') + .trim() + ); + } + // Step 4: Calculate remaining content (after removing annotations from protected text) // Use protectedText to ensure regexes don't stop at `**X` inside backticks let remaining = protectedText; @@ -820,6 +850,12 @@ export function parseBusinessRuleAnnotations(description: string): BusinessRuleA // Remove **Verified by:** block remaining = remaining.replace(/\*\*Verified by:\*\*\s*[\s\S]*?(?=\*\*[A-Z]|\*\*$|$)/i, ''); + // Remove **Input:** block + remaining = remaining.replace(/\*\*Input:\*\*\s*[\s\S]*?(?=\*\*[A-Z]|\*\*$|$)/i, ''); + + // Remove **Output:** block + remaining = remaining.replace(/\*\*Output:\*\*\s*[\s\S]*?(?=\*\*[A-Z]|\*\*$|$)/i, ''); + // Remove **API:** See `path` references (use original pattern for protected text) // Pattern matches both bold and non-bold, case-insensitive remaining = remaining.replace(/(?:\*\*)?API:(?:\*\*)?\s*See\s*__LIBAR_BT_\d+__/gi, ''); diff --git a/src/renderable/render.ts b/src/renderable/render.ts index b9725559..d2cf1a67 100644 --- a/src/renderable/render.ts +++ b/src/renderable/render.ts @@ -354,20 +354,25 @@ function renderCollapsibleClaudeMdModule(block: CollapsibleBlock): string[] { // ═══════════════════════════════════════════════════════════════════════════ /** - * Render a table block to markdown. + * Render a table block to markdown with column-width padding. + * Produces prettier-compatible aligned tables. */ function renderTable(block: TableBlock): string[] { if (block.columns.length === 0) { return []; } - const lines: string[] = []; - - // Header row - escape pipes and newlines in column names + // Escape all cells first const escapedColumns = block.columns.map(escapeTableCell); - lines.push(`| ${escapedColumns.join(' | ')} |`); + const escapedRows = block.rows.map((row) => { + const paddedRow = [...row]; + while (paddedRow.length < block.columns.length) { + paddedRow.push(''); + } + return paddedRow.map(escapeTableCell); + }); - // Separator row with alignment + // Build separators const separators = block.columns.map((_, i) => { const align = block.alignment?.[i] ?? 'left'; switch (align) { @@ -379,18 +384,45 @@ function renderTable(block: TableBlock): string[] { return '---'; } }); - lines.push(`| ${separators.join(' | ')} |`); - // Data rows - for (const row of block.rows) { - // Pad row to match columns if needed - const paddedRow = [...row]; - while (paddedRow.length < block.columns.length) { - paddedRow.push(''); + // Compute max width per column (header, separator, and all data rows) + const colWidths = block.columns.map((_, i) => { + const headerWidth = escapedColumns[i]?.length ?? 0; + const sepWidth = separators[i]?.length ?? 3; + const dataWidth = Math.max(0, ...escapedRows.map((row) => row[i]?.length ?? 0)); + return Math.max(headerWidth, sepWidth, dataWidth); + }); + + // Pad and join + const padCell = (cell: string, width: number): string => cell.padEnd(width); + const padSep = (sep: string, width: number, colIndex: number): string => { + const align = block.alignment?.[colIndex] ?? 'left'; + const fill = width - sep.length; + if (fill <= 0) return sep; + switch (align) { + case 'center': + return `:${'-'.repeat(width - 2)}:`; + case 'right': + return `${'-'.repeat(width - 1)}:`; + default: + return sep + '-'.repeat(fill); } - // Escape pipe characters and newlines in cell content - const escapedRow = paddedRow.map(escapeTableCell); - lines.push(`| ${escapedRow.join(' | ')} |`); + }; + + const lines: string[] = []; + + // Header row + const headerCells = escapedColumns.map((col, i) => padCell(col, colWidths[i] ?? 0)); + lines.push(`| ${headerCells.join(' | ')} |`); + + // Separator row + const sepCells = separators.map((sep, i) => padSep(sep, colWidths[i] ?? 0, i)); + lines.push(`| ${sepCells.join(' | ')} |`); + + // Data rows + for (const row of escapedRows) { + const dataCells = row.map((cell, i) => padCell(cell, colWidths[i] ?? 0)); + lines.push(`| ${dataCells.join(' | ')} |`); } lines.push(''); diff --git a/src/scanner/gherkin-ast-parser.ts b/src/scanner/gherkin-ast-parser.ts index c437e77c..36c1b4e3 100644 --- a/src/scanner/gherkin-ast-parser.ts +++ b/src/scanner/gherkin-ast-parser.ts @@ -570,6 +570,10 @@ export function extractPatternTags(tags: readonly string[]): { readonly claudeModule?: string; readonly claudeSection?: string; readonly claudeTags?: readonly string[]; + readonly sequenceOrchestrator?: string; + readonly sequenceStep?: number; + readonly sequenceModule?: readonly string[]; + readonly sequenceError?: boolean; // Index signature enables registry-driven extensibility: new tags in // buildRegistry() work without updating this return type. readonly [key: string]: unknown; diff --git a/src/taxonomy/registry-builder.ts b/src/taxonomy/registry-builder.ts index 993ed6f9..77a698e2 100644 --- a/src/taxonomy/registry-builder.ts +++ b/src/taxonomy/registry-builder.ts @@ -169,6 +169,12 @@ export const METADATA_TAGS_BY_GROUP = { stub: ['target', 'since'] as const, convention: ['convention'] as const, claude: ['claude-module', 'claude-section', 'claude-tags'] as const, + sequence: [ + 'sequence-orchestrator', + 'sequence-step', + 'sequence-module', + 'sequence-error', + ] as const, } as const; // Transform helpers for data-driven Gherkin tag extraction @@ -585,6 +591,32 @@ export function buildRegistry(): TagRegistry { purpose: 'Variation filtering tags for modular-claude-md inclusion', example: '@libar-docs-claude-tags core-mandatory, delivery-process', }, + + // ── Sequence diagram annotation tags (DesignReviewCodec) ────────── + { + tag: 'sequence-orchestrator', + format: 'value', + purpose: 'Identifies the coordinator module for sequence diagram generation', + example: '@libar-docs-sequence-orchestrator:init-cli', + }, + { + tag: 'sequence-step', + format: 'number', + purpose: 'Explicit execution ordering number for sequence diagram steps', + example: '@libar-docs-sequence-step:1', + }, + { + tag: 'sequence-module', + format: 'csv', + purpose: 'Maps Rule to deliverable module(s) for sequence diagram participants', + example: '@libar-docs-sequence-module:detect-context', + }, + { + tag: 'sequence-error', + format: 'flag', + purpose: 'Marks scenario as error/alternative path in sequence diagram', + example: '@libar-docs-sequence-error', + }, ], aggregationTags: [ diff --git a/src/validation-schemas/extracted-pattern.ts b/src/validation-schemas/extracted-pattern.ts index e57cef25..5a03e3b6 100644 --- a/src/validation-schemas/extracted-pattern.ts +++ b/src/validation-schemas/extracted-pattern.ts @@ -52,6 +52,10 @@ export const BusinessRuleSchema = z.object({ scenarioCount: z.number().int().nonnegative(), /** Scenario names under this rule */ scenarioNames: z.array(z.string()).readonly(), + /** Tags applied to this rule (from @libar-docs-* tags on Rule: keyword) */ + tags: z.array(z.string()).readonly().optional(), + /** Scenario names tagged with @libar-docs-sequence-error (for design review diagrams) */ + errorScenarioNames: z.array(z.string()).readonly().optional(), }); /** @@ -519,6 +523,11 @@ export const ExtractedPatternSchema = z /** Variation filtering tags for modular-claude-md (from @libar-docs-claude-tags CSV tag) */ claudeTags: z.array(z.string()).readonly().optional(), + + // Sequence diagram annotation fields (from @libar-docs-sequence-* tags) + + /** Sequence diagram orchestrator module identifier (from @libar-docs-sequence-orchestrator tag) */ + sequenceOrchestrator: z.string().optional(), }) .strict(); diff --git a/src/validation-schemas/master-dataset.ts b/src/validation-schemas/master-dataset.ts index 3e40a99c..fc7f10e7 100644 --- a/src/validation-schemas/master-dataset.ts +++ b/src/validation-schemas/master-dataset.ts @@ -203,6 +203,53 @@ export const ArchIndexSchema = z.object({ all: z.array(ExtractedPatternSchema), }); +// ═══════════════════════════════════════════════════════════════════════════ +// Sequence Index Schema (for design review diagram generation) +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * A single step in a sequence diagram, derived from a Rule with sequence annotations + */ +export const SequenceStepSchema = z.object({ + /** Step execution order (from @libar-docs-sequence-step tag) */ + stepNumber: z.number().int().positive(), + /** Business rule name (the Rule: keyword text) */ + ruleName: z.string().trim().min(1), + /** Module identifiers for this step (from @libar-docs-sequence-module CSV tag) */ + modules: z.array(z.string().trim().min(1)).min(1).readonly(), + /** Input type annotation (from **Input:** marker in rule description) */ + input: z.string().optional(), + /** Output type annotation (from **Output:** marker in rule description) */ + output: z.string().optional(), + /** Invariant text (for Note blocks in sequence diagram) */ + invariant: z.string().optional(), + /** Scenario names tagged with @libar-docs-sequence-error */ + errorScenarios: z.array(z.string().trim().min(1)).readonly(), +}); + +/** + * Pre-computed sequence data for a single pattern, keyed by pattern name + */ +export const SequenceIndexEntrySchema = z.object({ + /** Orchestrator module identifier (from @libar-docs-sequence-orchestrator tag) */ + orchestrator: z.string().trim().min(1), + /** Ordered sequence steps (sorted by stepNumber) */ + steps: z.array(SequenceStepSchema).min(1).readonly(), + /** Deduplicated participant module names (in step order, orchestrator first) */ + participants: z.array(z.string().trim().min(1)).min(1).readonly(), + /** All error scenario names across all steps */ + errorPaths: z.array(z.string().trim().min(1)).readonly(), + /** Distinct data flow type names from Input/Output annotations */ + dataFlowTypes: z.array(z.string().trim().min(1)).readonly(), +}); + +/** + * Sequence index: pattern name → pre-computed sequence data + * + * Supports multiple annotated patterns simultaneously. + */ +export const SequenceIndexSchema = z.record(z.string().trim().min(1), SequenceIndexEntrySchema); + // ═══════════════════════════════════════════════════════════════════════════ // Master Dataset Schema // ═══════════════════════════════════════════════════════════════════════════ @@ -278,6 +325,13 @@ export const MasterDatasetSchema = z.object({ /** Optional architecture index for diagram generation */ archIndex: ArchIndexSchema.optional(), + + // ───────────────────────────────────────────────────────────────────────── + // Sequence Data (optional) + // ───────────────────────────────────────────────────────────────────────── + + /** Optional sequence index for design review diagram generation */ + sequenceIndex: SequenceIndexSchema.optional(), }); // ═══════════════════════════════════════════════════════════════════════════ @@ -292,3 +346,6 @@ export type SourceViews = z.infer; export type ImplementationRef = z.infer; export type RelationshipEntry = z.infer; export type ArchIndex = z.infer; +export type SequenceStep = z.infer; +export type SequenceIndexEntry = z.infer; +export type SequenceIndex = z.infer; diff --git a/tests/features/behavior/render-blocks.feature b/tests/features/behavior/render-blocks.feature index ecc21440..ceeb73e4 100644 --- a/tests/features/behavior/render-blocks.feature +++ b/tests/features/behavior/render-blocks.feature @@ -119,9 +119,9 @@ Feature: Universal Markdown Renderer - Block Types Then the output contains the table: """ | Column1 | Column2 | - | --- | --- | - | a | b | - | c | d | + | ------- | ------- | + | a | b | + | c | d | """ Scenario: Render table with alignment @@ -131,7 +131,7 @@ Feature: Universal Markdown Renderer - Block Types | Center | center | | Right | right | When rendering to markdown - Then the output contains "| --- | :---: | ---: |" + Then the output contains "| ---- | :----: | ----: |" Scenario: Render empty table (no columns) Given a document with a table with no columns diff --git a/tests/features/generation/design-review-generator.feature b/tests/features/generation/design-review-generator.feature new file mode 100644 index 00000000..875e9fe3 --- /dev/null +++ b/tests/features/generation/design-review-generator.feature @@ -0,0 +1,28 @@ +@libar-docs +@libar-docs-pattern:DesignReviewGeneratorLifecycleTests +@libar-docs-status:active +@libar-docs-product-area:Generation +@behavior @design-review +Feature: Design Review Generator Lifecycle + + The design review generator cleans up stale markdown files when annotated + patterns are renamed or removed from the current dataset. + + Background: + Given a temporary design review output directory + + Rule: Orphaned design review files are scheduled for deletion + + **Invariant:** Existing markdown files in design-reviews/ that no longer map to the current sequenceIndex must be returned in filesToDelete, while current patterns remain preserved. + **Rationale:** Renaming or removing sequence-annotated patterns otherwise leaves stale design review documents behind, which misleads readers and downstream tooling. + **Verified by:** Renamed pattern schedules stale design review for deletion + + @acceptance-criteria @cleanup + Scenario: Renamed pattern schedules stale design review for deletion + Given an existing design review file "design-reviews/setup-command.md" + And a dataset with sequence data for pattern "RenamedPattern" + When generating design review files + Then the generator output should include files to delete + And the files to delete should include "design-reviews/setup-command.md" + And the files to delete should not include "design-reviews/renamed-pattern.md" + And the generated files should include "design-reviews/renamed-pattern.md" diff --git a/tests/features/generation/design-review.feature b/tests/features/generation/design-review.feature new file mode 100644 index 00000000..fedf444a --- /dev/null +++ b/tests/features/generation/design-review.feature @@ -0,0 +1,206 @@ +@libar-docs +@libar-docs-pattern:DesignReviewGenerationTests +@libar-docs-status:active +@libar-docs-product-area:Generation +@behavior @design-review +Feature: Design Review Generation Pipeline + + Tests the full design review generation pipeline: sequence annotations are + extracted from patterns with business rules, pre-computed into a SequenceIndex + on MasterDataset, then rendered through DesignReviewCodec into markdown with + Mermaid sequence diagrams, component diagrams, type definition tables, and + design question templates. + + Background: + Given a design review test context + + Rule: SequenceIndex pre-computes ordered steps from annotated rules + + **Invariant:** buildSequenceIndexEntry produces a SequenceIndexEntry with steps sorted by stepNumber, participants deduplicated with orchestrator first, and data flow types collected from Input/Output annotations. + **Rationale:** Pre-computing in the transform pass avoids repeated parsing in the codec. ADR-006 mandates the MasterDataset as the sole read model. + **Verified by:** SequenceIndex populated for annotated pattern, Steps sorted by step number, Patterns without sequence annotations have no entry + + @acceptance-criteria @happy-path + Scenario: SequenceIndex populated for annotated pattern + Given a pattern with orchestrator "init-cli" and 3 sequence-step rules + When building the sequence index entry + Then the entry has orchestrator "init-cli" + And the entry has 3 steps + + @acceptance-criteria @happy-path + Scenario: Steps sorted by step number + Given rules with step numbers 3 and 1 and 2 + When building the sequence index entry + Then step 1 has stepNumber 1 + And step 2 has stepNumber 2 + And step 3 has stepNumber 3 + + @acceptance-criteria @validation + Scenario: Patterns without sequence annotations have no entry + Given rules with no sequence-step tags + When building the sequence index entry + Then the entry is undefined + + Rule: Participants are deduplicated with orchestrator first + + **Invariant:** The participants array starts with the orchestrator, followed by module names in first-appearance step order, with no duplicates. + **Rationale:** Sequence diagram participant declarations must be ordered and unique. The orchestrator is always the first participant as the entry point. + **Verified by:** Participants ordered with orchestrator first + + @acceptance-criteria @happy-path + Scenario: Participants ordered with orchestrator first + Given a pattern with orchestrator "main" and modules "alpha" then "beta" then "alpha" + When building the sequence index entry + Then participants are "main" then "alpha" then "beta" + + Rule: Data flow types are extracted from Input and Output annotations + + **Invariant:** The dataFlowTypes array contains distinct type names parsed from Input and Output annotation strings using the "TypeName -- fields" format. + **Rationale:** Data flow types are used by the component diagram to render hexagon nodes and by the type definitions table to show producers and consumers. + **Verified by:** Data flow types collected from annotations, Prose outputs are excluded from data flow types + + @acceptance-criteria @happy-path + Scenario: Data flow types collected from annotations + Given a rule with Input "ProjectContext -- packageJson, tsconfigExists" and Output "InitConfig -- preset, sources" + When building the sequence index entry + Then data flow types include "ProjectContext" and "InitConfig" + + @acceptance-criteria @validation + Scenario: Prose outputs are excluded from data flow types + Given a rule with Input "ProjectContext" and Output "package.json updated with process and docs scripts" + When building the sequence index entry + Then data flow types include "ProjectContext" + And data flow types do not include "package.json updated with process and docs scripts" + + Rule: DesignReviewCodec produces sequence diagram with correct participant count + + **Invariant:** The rendered sequence diagram participant list includes User plus all participants from the SequenceIndexEntry. The count equals 1 (User) plus the number of unique participants. + **Rationale:** Correct participant count proves the codec reads SequenceIndex data correctly and maps it to Mermaid syntax. + **Verified by:** Sequence diagram has correct participant count + + @acceptance-criteria @happy-path + Scenario: Sequence diagram has correct participant count + Given a dataset with a pattern having orchestrator and 2 distinct modules + When generating the design review document + Then the sequence diagram declares 4 participants + + Rule: Error scenarios produce alt blocks in sequence diagrams + + **Invariant:** Each error scenario name from a step's errorScenarios array produces an alt block in the Mermaid sequence diagram with the scenario name as the condition text. + **Rationale:** Alt blocks make error handling visible in the sequence diagram, enabling design review verification of error path completeness. + **Verified by:** Error scenarios produce alt blocks in output + + @acceptance-criteria @happy-path + Scenario: Error scenarios produce alt blocks in output + Given a step with error scenarios "Config not found" and "Invalid preset" + When generating the design review document + Then the rendered markdown contains: + """ + alt Config not found + alt Invalid preset + """ + + Rule: Component diagram groups modules by shared input type + + **Invariant:** Contiguous steps sharing the same Input type annotation are grouped into a single subgraph in the component diagram. Non-contiguous steps with the same input become separate subgraphs. + **Rationale:** Grouping by input type reveals natural phase boundaries in the orchestration flow, making data flow architecture visible. + **Verified by:** Modules with same input grouped together + + @acceptance-criteria @happy-path + Scenario: Modules with same input grouped together + Given 2 steps both with Input "InitConfig" + When generating the design review document + Then the component diagram contains a subgraph labeled "InitConfig" + + Rule: Component diagram module nodes are scoped per phase + + **Invariant:** Repeated modules in non-contiguous phases render with distinct Mermaid node IDs, while repeated use of the same module inside one phase reuses a single declaration. + **Rationale:** Mermaid node IDs are global across the diagram. Reusing raw module IDs causes later phases to collapse into earlier declarations and misrepresent the orchestration flow. + **Verified by:** Repeated module in non-contiguous phases gets distinct node ids, Repeated module in one phase is declared once + + @acceptance-criteria @happy-path + Scenario: Repeated module in non-contiguous phases gets distinct node ids + Given a pattern with the same module in non-contiguous phases + When generating the design review document + Then the rendered markdown contains phase-scoped node id "phase_1_shared_mod" + And the rendered markdown contains phase-scoped node id "phase_3_shared_mod" + And the rendered markdown routes first phase input "InputA" to phase node "phase_1_shared_mod" + And the rendered markdown routes later phase input "InputC" to phase node "phase_3_shared_mod" + + @acceptance-criteria @validation + Scenario: Repeated module in one phase is declared once + Given 2 contiguous steps in the same phase using the same module + When generating the design review document + Then phase node "phase_1_shared_mod" is declared 1 time in the rendered markdown + + Rule: Type hexagons show field definitions from Output annotations + + **Invariant:** Output annotations with the "TypeName -- field1, field2" format produce hexagon nodes in the component diagram containing the type name and field names separated by newlines. + **Rationale:** Type hexagons make central data contracts visible, enabling design reviewers to verify interface completeness. + **Verified by:** Type hexagon rendered with fields + + @acceptance-criteria @happy-path + Scenario: Type hexagon rendered with fields + Given a step with Output "SetupResult -- success, patternCount, diagnostics" + When generating the design review document + Then the component diagram contains a hexagon for "SetupResult" with fields + + Rule: Mermaid-sensitive text is escaped across rendered labels + + **Invariant:** Participant aliases, subgraph labels, type hexagon text, and edge labels escape Mermaid-sensitive characters such as quotes, pipes, and comment markers before rendering. + **Rationale:** Design review diagrams are generated directly from annotations. Valid annotation text must not break Mermaid parsing when rendered into different label positions. + **Verified by:** Mermaid-sensitive text is escaped in rendered markdown + + @acceptance-criteria @validation + Scenario: Mermaid-sensitive text is escaped in rendered markdown + Given a rule with Mermaid-sensitive annotations + When generating the design review document + Then the rendered markdown contains "module|"alpha.ts" + And the rendered markdown contains "Config "Draft" | Preview % % comment" + And the rendered markdown also contains "SetupResult|"Quoted"" + And the rendered markdown does not contain "%% comment" + + Rule: Design questions table includes auto-computed metrics + + **Invariant:** The Design Questions section contains a table with auto-computed step count, type count, and error path count drawn from the SequenceIndexEntry data. + **Rationale:** Auto-computed metrics reduce manual counting during design reviews and highlight coverage gaps (e.g., 0 error paths). + **Verified by:** Design questions table has correct metrics + + @acceptance-criteria @happy-path + Scenario: Design questions table has correct metrics + Given a dataset with 3 steps and 2 types and 1 error path + When generating the design review document + Then the design questions mention "3 steps" and "2 distinct types" and "1 error paths" + + Rule: Invalid sequence annotations are skipped with validation warnings + + **Invariant:** Patterns with ambiguous sequence-step numbering or empty sequence-module tags are excluded from sequenceIndex and reported through malformedPatterns. + **Rationale:** Design reviews should never render misleading diagrams from malformed annotations. The transform pass is the correct place to validate and suppress bad sequence entries. + **Verified by:** Duplicate step numbers are reported as malformed, Sequence step without modules is reported as malformed + + @acceptance-criteria @validation + Scenario: Duplicate step numbers are reported as malformed + Given a pattern with duplicate sequence-step values + When transforming the pattern with validation + Then validation issues contain "Duplicate @libar-docs-sequence-step:1" + And sequenceIndex does not contain the pattern + + @acceptance-criteria @validation + Scenario: Sequence step without modules is reported as malformed + Given a pattern with a sequence step but no sequence modules + When transforming the pattern with validation + Then validation issues contain "has @libar-docs-sequence-step:1 but no @libar-docs-sequence-module values" + And sequenceIndex does not contain the pattern + + Rule: Process API sequence lookup resolves pattern names case-insensitively + + **Invariant:** The sequence subcommand resolves pattern names with the same case-insensitive matching behavior as other pattern-oriented process-api queries. + **Rationale:** Design review consumers should not need exact display-name casing when querying sequence data from the CLI. + **Verified by:** Sequence lookup accepts lowercase pattern name + + @acceptance-criteria @happy-path + Scenario: Sequence lookup accepts lowercase pattern name + Given a dataset with sequence data for pattern "SetupCommand" + When resolving sequence data for pattern name "setupcommand" + Then the resolved sequence entry exists + And the resolved sequence entry has orchestrator "init-cli" diff --git a/tests/fixtures/pattern-factories.ts b/tests/fixtures/pattern-factories.ts index b8bd8ebb..aafa9996 100644 --- a/tests/fixtures/pattern-factories.ts +++ b/tests/fixtures/pattern-factories.ts @@ -133,6 +133,8 @@ export interface TestPatternOptions { rules?: BusinessRule[]; /** Extracted TypeScript shapes (default: none) */ extractedShapes?: ExtractedShape[]; + /** Sequence diagram orchestrator identifier (default: none) */ + sequenceOrchestrator?: string; } /** @@ -231,6 +233,8 @@ export function createTestPattern(options: TestPatternOptions = {}): ExtractedPa convention, rules, extractedShapes, + // Sequence diagram fields + sequenceOrchestrator, } = options; const directive: DocDirective = { @@ -311,6 +315,8 @@ export function createTestPattern(options: TestPatternOptions = {}): ExtractedPa ...(convention && convention.length > 0 ? { convention } : {}), ...(rules && rules.length > 0 ? { rules } : {}), ...(extractedShapes && extractedShapes.length > 0 ? { extractedShapes } : {}), + // Sequence diagram fields + ...(sequenceOrchestrator ? { sequenceOrchestrator } : {}), }; } diff --git a/tests/steps/behavior/cli/process-api-reference.steps.ts b/tests/steps/behavior/cli/process-api-reference.steps.ts index 220514df..ef8e8d8e 100644 --- a/tests/steps/behavior/cli/process-api-reference.steps.ts +++ b/tests/steps/behavior/cli/process-api-reference.steps.ts @@ -4,7 +4,7 @@ import { loadFeature, describeFeature } from '@amiceli/vitest-cucumber'; import { expect } from 'vitest'; -import { CLI_SCHEMA } from '../../../../src/cli/cli-schema.js'; +import { CLI_SCHEMA, type CLIOptionGroup, type CLISchema } from '../../../../src/cli/cli-schema.js'; import { createProcessApiReferenceGenerator } from '../../../../src/generators/built-in/process-api-reference-generator.js'; import type { GeneratorContext } from '../../../../src/generators/types.js'; @@ -102,10 +102,11 @@ describeFeature(feature, ({ AfterEachScenario, Rule }) => { 'the output contains a table with columns {string}, {string}, {string}, {string}', (_ctx: unknown, c1: string, c2: string, c3: string, c4: string) => { const content = getContent(); - expect(content).toContain(`| ${c1} |`); - expect(content).toContain(`| ${c2} |`); - expect(content).toContain(`| ${c3} |`); - expect(content).toContain(`| ${c4} |`); + // Check column names exist in header (padding-tolerant — no trailing ` |`) + expect(content).toContain(`| ${c1}`); + expect(content).toContain(`| ${c2}`); + expect(content).toContain(`| ${c3}`); + expect(content).toContain(`| ${c4}`); } ); @@ -139,8 +140,8 @@ describeFeature(feature, ({ AfterEachScenario, Rule }) => { 'the output contains a table with columns {string}, {string}', (_ctx: unknown, c1: string, c2: string) => { const content = getContent(); - expect(content).toContain(`| ${c1} |`); - expect(content).toContain(`| ${c2} |`); + expect(content).toContain(`| ${c1}`); + expect(content).toContain(`| ${c2}`); } ); @@ -177,8 +178,8 @@ describeFeature(feature, ({ AfterEachScenario, Rule }) => { 'the output contains a table with columns {string}, {string}', (_ctx: unknown, c1: string, c2: string) => { const content = getContent(); - expect(content).toContain(`| ${c1} |`); - expect(content).toContain(`| ${c2} |`); + expect(content).toContain(`| ${c1}`); + expect(content).toContain(`| ${c2}`); } ); @@ -300,8 +301,9 @@ describeFeature(feature, ({ AfterEachScenario, Rule }) => { 'all schema groups contain at least one option:', (_ctx: unknown, table: Array<{ group: string }>) => { for (const row of table) { - const group = row.group as keyof typeof CLI_SCHEMA; - expect(CLI_SCHEMA[group].options.length).toBeGreaterThan(0); + const group = row.group as keyof CLISchema; + const section = CLI_SCHEMA[group] as CLIOptionGroup; + expect(section.options.length).toBeGreaterThan(0); } } ); diff --git a/tests/steps/behavior/render-output.steps.ts b/tests/steps/behavior/render-output.steps.ts index 914c30d0..37284cc3 100644 --- a/tests/steps/behavior/render-output.steps.ts +++ b/tests/steps/behavior/render-output.steps.ts @@ -174,7 +174,7 @@ describeFeature(feature, ({ Rule, Background, AfterEachScenario }) => { And('the output contains a table between details tags', () => { const detailsStart = state!.markdown.indexOf('
'); const detailsEnd = state!.markdown.indexOf('
'); - const tableMarker = state!.markdown.indexOf('| A | B |'); + const tableMarker = state!.markdown.indexOf('| A | B |'); expect(tableMarker).toBeGreaterThan(detailsStart); expect(tableMarker).toBeLessThan(detailsEnd); diff --git a/tests/steps/generation/design-review-generator.steps.ts b/tests/steps/generation/design-review-generator.steps.ts new file mode 100644 index 00000000..06c8681e --- /dev/null +++ b/tests/steps/generation/design-review-generator.steps.ts @@ -0,0 +1,137 @@ +/** + * Design Review Generator lifecycle step definitions. + * + * Covers orphan cleanup for stale design-review markdown files. + * + * @libar-docs + * @libar-docs-uses DesignReviewGenerator + */ + +import { loadFeature, describeFeature } from '@amiceli/vitest-cucumber'; +import { expect } from 'vitest'; +import type { GeneratorOutput } from '../../../src/generators/types.js'; +import { createDesignReviewGenerator } from '../../../src/generators/built-in/design-review-generator.js'; +import { createDefaultTagRegistry } from '../../../src/validation-schemas/tag-registry.js'; +import type { RuntimeMasterDataset } from '../../../src/generators/pipeline/transform-dataset.js'; +import { + createTempDir, + writeTempFile, + type TempDirContext, +} from '../../support/helpers/file-system.js'; +import { createTestMasterDataset, createTestPattern } from '../../fixtures/dataset-factories.js'; +import { createSequenceRule } from '../../support/helpers/design-review-state.js'; + +interface DesignReviewGeneratorState { + tempContext: TempDirContext | null; + dataset: RuntimeMasterDataset | null; + output: GeneratorOutput | null; +} + +let state: DesignReviewGeneratorState | null = null; + +function requireState(): DesignReviewGeneratorState { + if (!state) throw new Error('Design review generator state not initialized'); + return state; +} + +const feature = await loadFeature('tests/features/generation/design-review-generator.feature'); + +describeFeature(feature, ({ Background, AfterEachScenario, Rule }) => { + AfterEachScenario(async () => { + if (state?.tempContext) { + await state.tempContext.cleanup(); + } + state = null; + }); + + Background(({ Given }) => { + Given('a temporary design review output directory', async () => { + state = { + tempContext: await createTempDir({ prefix: 'design-review-generator-' }), + dataset: null, + output: null, + }; + }); + }); + + Rule('Orphaned design review files are scheduled for deletion', ({ RuleScenario }) => { + RuleScenario( + 'Renamed pattern schedules stale design review for deletion', + ({ Given, And, When, Then }) => { + Given( + 'an existing design review file {string}', + async (_ctx: unknown, relativePath: string) => { + await writeTempFile( + requireState().tempContext!.tempDir, + relativePath, + '# stale review\n' + ); + } + ); + + And( + 'a dataset with sequence data for pattern {string}', + (_ctx: unknown, patternName: string) => { + const pattern = createTestPattern({ + name: patternName, + status: 'active', + filePath: 'delivery-process/specs/test-pattern.feature', + sequenceOrchestrator: 'orch', + rules: [ + createSequenceRule({ + name: 'Generate review', + step: 1, + modules: ['writer'], + input: 'InputConfig', + output: 'OutputModel -- id', + }), + ], + }); + + requireState().dataset = createTestMasterDataset({ patterns: [pattern] }); + } + ); + + When('generating design review files', async () => { + const current = requireState(); + const generator = createDesignReviewGenerator(); + if (!current.dataset) { + throw new Error('Dataset not initialized'); + } + current.output = await generator.generate(current.dataset.patterns, { + baseDir: current.tempContext!.tempDir, + outputDir: '.', + registry: createDefaultTagRegistry(), + masterDataset: current.dataset, + }); + }); + + Then('the generator output should include files to delete', () => { + expect(requireState().output?.filesToDelete?.length ?? 0).toBeGreaterThan(0); + }); + + And( + 'the files to delete should include {string}', + (_ctx: unknown, relativePath: string) => { + expect(requireState().output?.filesToDelete).toContain(relativePath); + } + ); + + And( + 'the files to delete should not include {string}', + (_ctx: unknown, relativePath: string) => { + expect(requireState().output?.filesToDelete ?? []).not.toContain(relativePath); + } + ); + + And( + 'the generated files should include {string}', + (_ctx: unknown, relativePath: string) => { + const generatedPaths = requireState().output?.files.map((file) => file.path) ?? []; + expect(generatedPaths).toContain(relativePath); + } + ); + } + ); + }); +}); diff --git a/tests/steps/generation/design-review.steps.ts b/tests/steps/generation/design-review.steps.ts new file mode 100644 index 00000000..32f147cf --- /dev/null +++ b/tests/steps/generation/design-review.steps.ts @@ -0,0 +1,793 @@ +/** + * Design Review Generation Step Definitions + * + * BDD step definitions for testing the design review generation pipeline: + * - SequenceIndex building from annotated business rules + * - Step sorting by stepNumber + * - Participant deduplication with orchestrator first + * - Data flow type extraction from Input/Output annotations + * - DesignReviewCodec sequence diagram generation + * - Error scenario alt block rendering + * - Component diagram module grouping + * - Type hexagon rendering with fields + * - Design questions auto-computed metrics + * - Case-insensitive process-api sequence lookup + * - Mermaid-safe escaping across rendered label positions + * + * @libar-docs + * @libar-docs-uses DesignReviewCodec, SequenceIndex, renderToMarkdown + */ +import { loadFeature, describeFeature } from '@amiceli/vitest-cucumber'; +import { expect } from 'vitest'; + +import { + type DesignReviewState, + initState, + requireState, + createSequenceRule, + createPlainRule, + buildEntry, + generateDesignReview, + resolveSequenceEntry, + transformWithValidation, +} from '../../support/helpers/design-review-state.js'; + +// ============================================================================= +// Module-level state (reset per scenario) +// ============================================================================= + +let state: DesignReviewState | null = null; + +// ============================================================================= +// Feature Definition +// ============================================================================= + +const feature = await loadFeature('tests/features/generation/design-review.feature'); + +describeFeature(feature, ({ Background, Rule, AfterEachScenario }) => { + // --------------------------------------------------------------------------- + // Lifecycle Hooks + // --------------------------------------------------------------------------- + + AfterEachScenario(() => { + state = null; + }); + + // --------------------------------------------------------------------------- + // Background + // --------------------------------------------------------------------------- + + Background(({ Given }) => { + Given('a design review test context', () => { + state = initState(); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: SequenceIndex pre-computes ordered steps from annotated rules + // --------------------------------------------------------------------------- + + Rule('SequenceIndex pre-computes ordered steps from annotated rules', ({ RuleScenario }) => { + RuleScenario('SequenceIndex populated for annotated pattern', ({ Given, When, Then, And }) => { + Given( + 'a pattern with orchestrator {string} and 3 sequence-step rules', + (_ctx: unknown, orchestrator: string) => { + const s = requireState(state); + s.orchestrator = orchestrator; + s.rules = [ + createSequenceRule({ + name: 'Detect context', + step: 1, + modules: ['detect-context'], + input: 'targetDir: string', + output: 'ProjectContext -- packageJson, tsconfigExists', + }), + createSequenceRule({ + name: 'Run prompts', + step: 2, + modules: ['prompts'], + input: 'ProjectContext', + output: 'InitConfig -- preset, sources', + }), + createSequenceRule({ + name: 'Generate config', + step: 3, + modules: ['generate-config'], + input: 'InitConfig', + output: 'config file written', + }), + ]; + } + ); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then('the entry has orchestrator {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.entry).toBeDefined(); + expect(s.entry?.orchestrator).toBe(expected); + }); + + And('the entry has 3 steps', () => { + const s = requireState(state); + expect(s.entry?.steps).toHaveLength(3); + }); + }); + + RuleScenario('Steps sorted by step number', ({ Given, When, Then, And }) => { + Given('rules with step numbers 3 and 1 and 2', () => { + const s = requireState(state); + s.orchestrator = 'orchestrator'; + s.rules = [ + createSequenceRule({ name: 'Step C', step: 3, modules: ['mod-c'] }), + createSequenceRule({ name: 'Step A', step: 1, modules: ['mod-a'] }), + createSequenceRule({ name: 'Step B', step: 2, modules: ['mod-b'] }), + ]; + }); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then('step 1 has stepNumber 1', () => { + const s = requireState(state); + expect(s.entry?.steps[0]?.stepNumber).toBe(1); + }); + + And('step 2 has stepNumber 2', () => { + const s = requireState(state); + expect(s.entry?.steps[1]?.stepNumber).toBe(2); + }); + + And('step 3 has stepNumber 3', () => { + const s = requireState(state); + expect(s.entry?.steps[2]?.stepNumber).toBe(3); + }); + }); + + RuleScenario('Patterns without sequence annotations have no entry', ({ Given, When, Then }) => { + Given('rules with no sequence-step tags', () => { + const s = requireState(state); + s.orchestrator = 'orchestrator'; + s.rules = [createPlainRule('Plain rule A'), createPlainRule('Plain rule B')]; + }); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then('the entry is undefined', () => { + const s = requireState(state); + expect(s.entry).toBeUndefined(); + }); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Participants are deduplicated with orchestrator first + // --------------------------------------------------------------------------- + + Rule('Participants are deduplicated with orchestrator first', ({ RuleScenario }) => { + RuleScenario('Participants ordered with orchestrator first', ({ Given, When, Then }) => { + Given( + 'a pattern with orchestrator {string} and modules {string} then {string} then {string}', + (_ctx: unknown, orchestrator: string, mod1: string, mod2: string, mod3: string) => { + const s = requireState(state); + s.orchestrator = orchestrator; + s.rules = [ + createSequenceRule({ name: 'Step 1', step: 1, modules: [mod1] }), + createSequenceRule({ name: 'Step 2', step: 2, modules: [mod2] }), + createSequenceRule({ name: 'Step 3', step: 3, modules: [mod3] }), + ]; + } + ); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then( + 'participants are {string} then {string} then {string}', + (_ctx: unknown, p1: string, p2: string, p3: string) => { + const s = requireState(state); + expect(s.entry?.participants).toEqual([p1, p2, p3]); + } + ); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Data flow types are extracted from Input and Output annotations + // --------------------------------------------------------------------------- + + Rule('Data flow types are extracted from Input and Output annotations', ({ RuleScenario }) => { + RuleScenario('Data flow types collected from annotations', ({ Given, When, Then }) => { + Given( + 'a rule with Input {string} and Output {string}', + (_ctx: unknown, input: string, output: string) => { + const s = requireState(state); + s.orchestrator = 'orchestrator'; + s.rules = [ + createSequenceRule({ + name: 'Data flow step', + step: 1, + modules: ['mod-a'], + input, + output, + }), + ]; + } + ); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then( + 'data flow types include {string} and {string}', + (_ctx: unknown, type1: string, type2: string) => { + const s = requireState(state); + expect(s.entry?.dataFlowTypes).toContain(type1); + expect(s.entry?.dataFlowTypes).toContain(type2); + } + ); + }); + + RuleScenario( + 'Prose outputs are excluded from data flow types', + ({ Given, When, Then, And }) => { + Given( + 'a rule with Input {string} and Output {string}', + (_ctx: unknown, input: string, output: string) => { + const s = requireState(state); + s.orchestrator = 'orchestrator'; + s.rules = [ + createSequenceRule({ + name: 'Filtered data flow step', + step: 1, + modules: ['mod-a'], + input, + output, + }), + ]; + } + ); + + When('building the sequence index entry', () => { + buildEntry(requireState(state)); + }); + + Then('data flow types include {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.entry?.dataFlowTypes).toContain(expected); + }); + + And('data flow types do not include {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.entry?.dataFlowTypes).not.toContain(expected); + }); + } + ); + }); + + // --------------------------------------------------------------------------- + // Rule: DesignReviewCodec produces sequence diagram with correct participant count + // --------------------------------------------------------------------------- + + Rule( + 'DesignReviewCodec produces sequence diagram with correct participant count', + ({ RuleScenario }) => { + RuleScenario('Sequence diagram has correct participant count', ({ Given, When, Then }) => { + Given('a dataset with a pattern having orchestrator and 2 distinct modules', () => { + const s = requireState(state); + s.orchestrator = 'main-orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Step A', + step: 1, + modules: ['module-alpha'], + input: 'InputA', + output: 'OutputA', + }), + createSequenceRule({ + name: 'Step B', + step: 2, + modules: ['module-beta'], + input: 'InputB', + output: 'OutputB', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then('the sequence diagram declares 4 participants', () => { + const s = requireState(state); + // 4 participants: User + main-orch + module-alpha + module-beta + const participantMatches = s.markdown.match(/participant /g); + expect(participantMatches).toHaveLength(4); + }); + }); + } + ); + + // --------------------------------------------------------------------------- + // Rule: Error scenarios produce alt blocks in sequence diagrams + // --------------------------------------------------------------------------- + + Rule('Error scenarios produce alt blocks in sequence diagrams', ({ RuleScenario }) => { + RuleScenario('Error scenarios produce alt blocks in output', ({ Given, When, Then }) => { + Given( + 'a step with error scenarios {string} and {string}', + (_ctx: unknown, err1: string, err2: string) => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Error step', + step: 1, + modules: ['error-mod'], + input: 'SomeInput', + output: 'SomeOutput', + errorScenarios: [err1, err2], + }), + ]; + } + ); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then('the rendered markdown contains:', (_ctx: unknown, expectedBlock: string) => { + const s = requireState(state); + for (const expected of expectedBlock + .split('\n') + .map((line) => line.trim()) + .filter(Boolean)) { + expect(s.markdown).toContain(expected); + } + }); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Component diagram groups modules by shared input type + // --------------------------------------------------------------------------- + + Rule('Component diagram groups modules by shared input type', ({ RuleScenario }) => { + RuleScenario('Modules with same input grouped together', ({ Given, When, Then }) => { + Given('2 steps both with Input {string}', (_ctx: unknown, input: string) => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Config step A', + step: 1, + modules: ['config-reader'], + input, + output: 'OutputA -- fieldA', + }), + createSequenceRule({ + name: 'Config step B', + step: 2, + modules: ['config-writer'], + input, + output: 'OutputB -- fieldB', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then( + 'the component diagram contains a subgraph labeled {string}', + (_ctx: unknown, label: string) => { + const s = requireState(state); + expect(s.markdown).toContain(`"Phase 1: ${label}"`); + } + ); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Component diagram module nodes are scoped per phase + // --------------------------------------------------------------------------- + + Rule('Component diagram module nodes are scoped per phase', ({ RuleScenario }) => { + RuleScenario( + 'Repeated module in non-contiguous phases gets distinct node ids', + ({ Given, When, Then, And }) => { + Given('a pattern with the same module in non-contiguous phases', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Phase A', + step: 1, + modules: ['shared-mod'], + input: 'InputA', + output: 'OutputA -- fieldA', + }), + createSequenceRule({ + name: 'Phase B', + step: 2, + modules: ['other-mod'], + input: 'InputB', + output: 'OutputB -- fieldB', + }), + createSequenceRule({ + name: 'Phase C', + step: 3, + modules: ['shared-mod'], + input: 'InputC', + output: 'OutputC -- fieldC', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then( + 'the rendered markdown contains phase-scoped node id {string}', + (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.markdown).toContain(expected); + } + ); + + And( + 'the rendered markdown contains phase-scoped node id {string}', + (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.markdown).toContain(expected); + } + ); + + And( + 'the rendered markdown routes first phase input {string} to phase node {string}', + (_ctx: unknown, input: string, phaseNode: string) => { + const s = requireState(state); + expect(s.markdown).toContain(`orch -->|"${input}"| ${phaseNode}`); + } + ); + + And( + 'the rendered markdown routes later phase input {string} to phase node {string}', + (_ctx: unknown, input: string, phaseNode: string) => { + const s = requireState(state); + expect(s.markdown).toContain(`orch -->|"${input}"| ${phaseNode}`); + } + ); + } + ); + + RuleScenario('Repeated module in one phase is declared once', ({ Given, When, Then }) => { + Given('2 contiguous steps in the same phase using the same module', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Shared step A', + step: 1, + modules: ['shared-mod'], + input: 'SharedInput', + output: 'OutputA -- fieldA', + }), + createSequenceRule({ + name: 'Shared step B', + step: 2, + modules: ['shared-mod'], + input: 'SharedInput', + output: 'OutputB -- fieldB', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then( + 'phase node {string} is declared {int} time in the rendered markdown', + (_ctx: unknown, nodeId: string, count: number) => { + const s = requireState(state); + const matches = s.markdown.match(new RegExp(`${nodeId}\\[`, 'g')); + expect(matches ?? []).toHaveLength(count); + } + ); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Type hexagons show field definitions from Output annotations + // --------------------------------------------------------------------------- + + Rule('Type hexagons show field definitions from Output annotations', ({ RuleScenario }) => { + RuleScenario('Type hexagon rendered with fields', ({ Given, When, Then }) => { + Given('a step with Output {string}', (_ctx: unknown, output: string) => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Validate step', + step: 1, + modules: ['validator'], + input: 'SomeInput', + output, + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then( + 'the component diagram contains a hexagon for {string} with fields', + (_ctx: unknown, typeName: string) => { + const s = requireState(state); + // Hexagons use {{ }} syntax in Mermaid + expect(s.markdown).toContain(typeName); + expect(s.markdown).toContain('{{'); + } + ); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Mermaid-sensitive text is escaped across rendered labels + // --------------------------------------------------------------------------- + + Rule('Mermaid-sensitive text is escaped across rendered labels', ({ RuleScenario }) => { + RuleScenario( + 'Mermaid-sensitive text is escaped in rendered markdown', + ({ Given, When, Then, And }) => { + Given('a rule with Mermaid-sensitive annotations', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Config "Draft" | Preview', + step: 1, + modules: ['module|"alpha'], + input: 'Config "Draft" | Preview %% comment', + output: 'SetupResult|"Quoted" -- field|"one", "two"', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then('the rendered markdown contains {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.markdown).toContain(expected); + }); + + And('the rendered markdown contains {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.markdown).toContain(expected); + }); + + And('the rendered markdown also contains {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.markdown).toContain(expected); + }); + + And( + 'the rendered markdown does not contain {string}', + (_ctx: unknown, unexpected: string) => { + const s = requireState(state); + expect(s.markdown).not.toContain(unexpected); + } + ); + } + ); + }); + + // --------------------------------------------------------------------------- + // Rule: Design questions table includes auto-computed metrics + // --------------------------------------------------------------------------- + + Rule('Design questions table includes auto-computed metrics', ({ RuleScenario }) => { + RuleScenario('Design questions table has correct metrics', ({ Given, When, Then }) => { + Given('a dataset with 3 steps and 2 types and 1 error path', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'TestPattern'; + s.rules = [ + createSequenceRule({ + name: 'Step 1', + step: 1, + modules: ['mod-a'], + input: 'InputA', + output: 'TypeAlpha -- fieldX, fieldY', + errorScenarios: ['Error case'], + }), + createSequenceRule({ + name: 'Step 2', + step: 2, + modules: ['mod-b'], + input: 'InputB', + output: 'TypeBeta -- fieldZ', + }), + createSequenceRule({ + name: 'Step 3', + step: 3, + modules: ['mod-c'], + input: 'InputC', + output: 'plain output no dash', + }), + ]; + }); + + When('generating the design review document', () => { + generateDesignReview(requireState(state)); + }); + + Then( + 'the design questions mention {string} and {string} and {string}', + (_ctx: unknown, steps: string, types: string, errors: string) => { + const s = requireState(state); + expect(s.markdown).toContain(steps); + expect(s.markdown).toContain(types); + expect(s.markdown).toContain(errors); + } + ); + }); + }); + + // --------------------------------------------------------------------------- + // Rule: Invalid sequence annotations are skipped with validation warnings + // --------------------------------------------------------------------------- + + Rule('Invalid sequence annotations are skipped with validation warnings', ({ RuleScenario }) => { + RuleScenario( + 'Duplicate step numbers are reported as malformed', + ({ Given, When, Then, And }) => { + Given('a pattern with duplicate sequence-step values', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'InvalidSequencePattern'; + s.rules = [ + createSequenceRule({ + name: 'Duplicate Step A', + step: 1, + modules: ['mod-a'], + input: 'InputA', + output: 'OutputA -- fieldA', + }), + createSequenceRule({ + name: 'Duplicate Step B', + step: 1, + modules: ['mod-b'], + input: 'InputB', + output: 'OutputB -- fieldB', + }), + ]; + }); + + When('transforming the pattern with validation', () => { + transformWithValidation(requireState(state)); + }); + + Then('validation issues contain {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + const issues = s.validation?.malformedPatterns.flatMap((pattern) => pattern.issues) ?? []; + expect(issues.some((issue) => issue.includes(expected))).toBe(true); + }); + + And('sequenceIndex does not contain the pattern', () => { + const s = requireState(state); + expect(s.entry).toBeUndefined(); + expect(s.dataset?.sequenceIndex?.[s.patternName]).toBeUndefined(); + }); + } + ); + + RuleScenario( + 'Sequence step without modules is reported as malformed', + ({ Given, When, Then, And }) => { + Given('a pattern with a sequence step but no sequence modules', () => { + const s = requireState(state); + s.orchestrator = 'orch'; + s.patternName = 'InvalidSequencePattern'; + s.rules = [ + createSequenceRule({ + name: 'Module-less Step', + step: 1, + modules: [], + input: 'InputA', + output: 'OutputA -- fieldA', + }), + ]; + }); + + When('transforming the pattern with validation', () => { + transformWithValidation(requireState(state)); + }); + + Then('validation issues contain {string}', (_ctx: unknown, expected: string) => { + const s = requireState(state); + const issues = s.validation?.malformedPatterns.flatMap((pattern) => pattern.issues) ?? []; + expect(issues.some((issue) => issue.includes(expected))).toBe(true); + }); + + And('sequenceIndex does not contain the pattern', () => { + const s = requireState(state); + expect(s.entry).toBeUndefined(); + expect(s.dataset?.sequenceIndex?.[s.patternName]).toBeUndefined(); + }); + } + ); + }); + + // --------------------------------------------------------------------------- + // Rule: Process API sequence lookup resolves pattern names case-insensitively + // --------------------------------------------------------------------------- + + Rule( + 'Process API sequence lookup resolves pattern names case-insensitively', + ({ RuleScenario }) => { + RuleScenario( + 'Sequence lookup accepts lowercase pattern name', + ({ Given, When, Then, And }) => { + Given( + 'a dataset with sequence data for pattern {string}', + (_ctx: unknown, patternName: string) => { + const s = requireState(state); + s.orchestrator = 'init-cli'; + s.patternName = patternName; + s.rules = [ + createSequenceRule({ + name: 'Lookup step', + step: 1, + modules: ['lookup-module'], + input: 'LookupInput', + output: 'LookupOutput', + }), + ]; + } + ); + + When( + 'resolving sequence data for pattern name {string}', + (_ctx: unknown, patternName: string) => { + resolveSequenceEntry(requireState(state), patternName); + } + ); + + Then('the resolved sequence entry exists', () => { + const s = requireState(state); + expect(s.entry).toBeDefined(); + }); + + And( + 'the resolved sequence entry has orchestrator {string}', + (_ctx: unknown, expected: string) => { + const s = requireState(state); + expect(s.entry?.orchestrator).toBe(expected); + } + ); + } + ); + } + ); +}); diff --git a/tests/support/helpers/design-review-state.ts b/tests/support/helpers/design-review-state.ts new file mode 100644 index 00000000..21fd6207 --- /dev/null +++ b/tests/support/helpers/design-review-state.ts @@ -0,0 +1,223 @@ +/** + * Shared state and helpers for design review generation tests. + * + * Provides test state management and fixture builders for testing + * the SequenceIndex builder and DesignReviewCodec pipeline. + * + * @libar-docs + * @libar-docs-uses SequenceIndex, DesignReviewCodec, MasterDataset + */ + +import type { BusinessRule } from '../../../src/validation-schemas/extracted-pattern.js'; +import type { + SequenceIndexEntry, + MasterDataset, +} from '../../../src/validation-schemas/master-dataset.js'; +import type { RenderableDocument } from '../../../src/renderable/schema.js'; +import { getSequenceEntry } from '../../../src/api/pattern-helpers.js'; +import { buildSequenceIndexEntry } from '../../../src/generators/pipeline/sequence-utils.js'; +import { + transformToMasterDatasetWithValidation, + type ValidationSummary, +} from '../../../src/generators/pipeline/transform-dataset.js'; +import { createDesignReviewCodec } from '../../../src/renderable/codecs/design-review.js'; +import { renderToMarkdown } from '../../../src/renderable/render.js'; +import { createDefaultTagRegistry } from '../../../src/validation-schemas/tag-registry.js'; +import { createTestMasterDataset, createTestPattern } from '../../fixtures/dataset-factories.js'; + +// ============================================================================= +// State Types +// ============================================================================= + +export interface DesignReviewState { + /** Orchestrator name for buildSequenceIndexEntry */ + orchestrator: string; + + /** Business rules to pass to buildSequenceIndexEntry */ + rules: BusinessRule[]; + + /** Result of buildSequenceIndexEntry */ + entry: SequenceIndexEntry | undefined; + + /** MasterDataset for codec tests */ + dataset: MasterDataset | null; + + /** Validation summary from transformToMasterDatasetWithValidation */ + validation: ValidationSummary | null; + + /** Pattern name for codec lookup */ + patternName: string; + + /** RenderableDocument output from codec */ + doc: RenderableDocument | null; + + /** Rendered markdown string */ + markdown: string; +} + +// ============================================================================= +// State Management +// ============================================================================= + +export function initState(): DesignReviewState { + return { + orchestrator: '', + rules: [], + entry: undefined, + dataset: null, + validation: null, + patternName: '', + doc: null, + markdown: '', + }; +} + +export function requireState(state: DesignReviewState | null): DesignReviewState { + if (!state) throw new Error('Design review state not initialized'); + return state; +} + +// ============================================================================= +// Fixture Builders +// ============================================================================= + +/** + * Create a business rule with sequence tags and optional annotations. + */ +export function createSequenceRule(options: { + name: string; + step: number; + modules: string[]; + input?: string; + output?: string; + invariant?: string; + errorScenarios?: string[]; +}): BusinessRule { + const descriptionParts: string[] = []; + if (options.invariant) { + descriptionParts.push(`**Invariant:** ${options.invariant}`); + } + if (options.input) { + descriptionParts.push(`**Input:** ${options.input}`); + } + if (options.output) { + descriptionParts.push(`**Output:** ${options.output}`); + } + + const tags = [ + `sequence-step:${String(options.step)}`, + `sequence-module:${options.modules.join(',')}`, + ]; + + return { + name: options.name, + description: descriptionParts.join('\n\n'), + scenarioCount: 1 + (options.errorScenarios?.length ?? 0), + scenarioNames: [`Happy path for ${options.name}`, ...(options.errorScenarios ?? [])], + tags, + errorScenarioNames: options.errorScenarios ?? [], + }; +} + +/** + * Create a business rule with no sequence tags. + */ +export function createPlainRule(name: string): BusinessRule { + return { + name, + description: `Description for ${name}`, + scenarioCount: 1, + scenarioNames: [`Test for ${name}`], + tags: [], + errorScenarioNames: [], + }; +} + +/** + * Build the sequence index entry from current state. + */ +export function buildEntry(state: DesignReviewState): void { + state.entry = buildSequenceIndexEntry(state.orchestrator, state.rules); +} + +/** + * Build a MasterDataset with a single pattern that has sequence data, + * and generate the design review document from it. + */ +export function generateDesignReview(state: DesignReviewState): void { + // First build the entry if not already done + if (state.entry === undefined && state.rules.length > 0) { + buildEntry(state); + } + + // Create a pattern with sequence orchestrator and rules + const pattern = createTestPattern({ + name: state.patternName || 'TestPattern', + status: 'active', + filePath: 'delivery-process/specs/test-pattern.feature', + rules: state.rules, + sequenceOrchestrator: state.orchestrator, + }); + + // Build the dataset with this pattern + const dataset = createTestMasterDataset({ patterns: [pattern] }); + + // The transform pipeline should have built the sequenceIndex + state.dataset = dataset; + + // Create and run the codec + const codec = createDesignReviewCodec({ patternName: state.patternName || 'TestPattern' }); + state.doc = codec.decode(dataset); + state.markdown = renderToMarkdown(state.doc); +} + +/** + * Transform a pattern through the dataset pipeline and keep validation output. + */ +export function transformWithValidation(state: DesignReviewState): void { + const pattern = createTestPattern({ + name: state.patternName || 'TestPattern', + status: 'active', + filePath: 'delivery-process/specs/test-pattern.feature', + rules: state.rules, + sequenceOrchestrator: state.orchestrator, + }); + + const result = transformToMasterDatasetWithValidation({ + patterns: [pattern], + tagRegistry: createDefaultTagRegistry(), + workflow: undefined, + }); + + state.dataset = result.dataset; + state.validation = result.validation; + state.entry = getSequenceEntry(result.dataset, state.patternName || 'TestPattern'); +} + +/** + * Resolve a sequence entry using the same case-insensitive lookup helper as process-api. + */ +export function resolveSequenceEntry( + state: DesignReviewState, + queryName: string +): SequenceIndexEntry | undefined { + const pattern = createTestPattern({ + name: state.patternName || 'TestPattern', + status: 'active', + filePath: 'delivery-process/specs/test-pattern.feature', + rules: state.rules, + sequenceOrchestrator: state.orchestrator, + }); + + state.dataset = createTestMasterDataset({ patterns: [pattern] }); + state.entry = getSequenceEntry(state.dataset, queryName); + return state.entry; +} + +// ============================================================================= +// Re-exports +// ============================================================================= + +export { buildSequenceIndexEntry } from '../../../src/generators/pipeline/sequence-utils.js'; +export { createDesignReviewCodec } from '../../../src/renderable/codecs/design-review.js'; +export { renderToMarkdown } from '../../../src/renderable/render.js';