Skip to content

feat: issue 337 code extraction writes through - #360

Closed
NTCoding wants to merge 11 commits into
mainfrom
issue-337-code-extraction-writes-through
Closed

NTCoding wants to merge 11 commits into
mainfrom
issue-337-code-extraction-writes-through

Conversation

@NTCoding

@NTCoding NTCoding commented Apr 25, 2026 •

Copy link
Copy Markdown
Owner

closes: https://github.com/NTCoding/living-architecture/issues/337

Summary by CodeRabbit

  • New Features

    • Enhanced extraction configuration schema with optional sources and domains properties for standalone graph building.
    • Added extraction orchestration functionality supporting both extraction and enrichment modes with connection detection.
  • Documentation

    • Updated schema documentation with refined property types and new object structures for extraction rules.
    • Expanded extraction configuration documentation with examples for new top-level configuration options.
  • Bug Fixes

    • Improved error handling and output validation with stricter type checking.
  • Refactor

    • Reorganized test infrastructure for better maintainability.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 25, 2026 •

Copy link
Copy Markdown

Deploying living-architecture with  Cloudflare Pages  Cloudflare Pages

Latest commit: bbb438e
Status: ✅  Deploy successful!
Preview URL: https://894d0eeb.living-architecture.pages.dev
Branch Preview URL: https://issue-337-code-extraction-wr.living-architecture.pages.dev

View logs

@coderabbitai

coderabbitai Bot commented Apr 25, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@NTCoding has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 40 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 40 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 478739a9-1ac3-4399-97d4-fa8085ef3765

📥 Commits

Reviewing files that changed from the base of the PR and between fb8ea4e and bbb438e.

⛔ Files ignored due to path filters (1)
  • packages/riviere-extract-config/docs/generated/schema.md is excluded by !**/generated/**
📒 Files selected for processing (13)
  • apps/docs/reference/extraction-config/schema.md
  • packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts
  • packages/riviere-cli/src/features/extract/domain/detect-connections-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts
  • packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts
  • packages/riviere-extract-config/scripts/generate-docs.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into-fixtures.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.branch-coverage.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts
📝 Walkthrough

Walkthrough

This PR implements a pure extraction core in riviere-extract-ts via extractInto, refactors test fixtures to a centralized location, adds write-port abstractions for CLI and workflow integration, updates extraction configuration schemas, and provides comprehensive test coverage for the new extraction pipeline.

Changes

Cohort / File(s) Summary
Documentation schema updates
apps/docs/reference/extraction-config/schema.md
Adds optional root-level sources and domains configuration properties with new sourceInfo and domainMetadata schemas; refines extractionRule property types from generic object/any forms into explicit structured shapes with specific parameters and optional transform fields.
Test fixture centralization
packages/riviere-cli/src/fixtures/command-test-fixtures.ts, packages/riviere-cli/src/features/builder/commands/*.spec.ts, packages/riviere-cli/src/features/builder/entrypoint/*.spec.ts, packages/riviere-cli/src/features/extract/entrypoint/*.spec.ts, packages/riviere-cli/src/features/query/entrypoint/*.spec.ts, packages/riviere-cli/src/features/extract/__fixtures__/extraction-test-fixtures.ts, packages/riviere-cli/src/features/query/infra/persistence/*.spec.ts, packages/riviere-cli/src/features/query/queries/*.spec.ts
Moves command test fixtures from platform/__fixtures__ to a new shared fixtures directory and creates a barrel re-export module; updates ~30 test files to reference the new location.
Core extraction orchestration
packages/riviere-extract-ts/src/features/extraction/domain/extract-into.ts, packages/riviere-extract-ts/src/features/extraction/domain/extract-into*.spec.ts, packages/riviere-extract-ts/src/features/extraction/domain/extract-into-fixtures.ts
Implements extractInto(writePort, config, options) function for unified extraction/enrichment/connection-detection flow; adds comprehensive test suites covering branch/edge cases, full extraction scenarios, and orphaned component error handling.
Write-port abstractions
packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts, packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port*.spec.ts
Defines ExtractionWritePort interface and diagnostic event types; implements strictWritePort adapter for CLI (routes to builder, buffers diagnostics) and mergeWritePort adapter for workflows (deduping, same-step duplicate enforcement); includes comprehensive test coverage.
Component write-input conversion
packages/riviere-extract-ts/src/features/extraction/domain/extract-into-component-write-input.ts, packages/riviere-extract-ts/src/features/extraction/domain/extract-into-component-write-input.spec.ts
Converts enriched components to write-port inputs with strict metadata validation; maps component types to discriminated union variants; verifies required metadata presence and content constraints.
Source file and module context resolution
packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts, packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.spec.ts
New module for resolving TypeScript source files per module, handling git-based change detection, explicit file filtering, and creating ts-morph ModuleContext[]; throws validation errors for missing files/patterns.
Extraction project refactoring
packages/riviere-cli/src/features/extract/domain/extraction-project.ts, packages/riviere-cli/src/features/extract/domain/extraction-project.spec.ts, packages/riviere-cli/src/features/extract/domain/extraction-project-builder-backed.spec.ts
Delegates extraction/enrichment to extractInto core; reworks draft-component module resolution with normalizeDraftComponents() and AmbiguousDraftComponentModuleError; adds extractIntoWorkflowBuilder method for workflow integration.
Presentation layer
packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.ts, packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.spec.ts
New domain service for converting extracted components/links to presented form; adds _missing/_uncertain annotations from diagnostic data; generates canonical component IDs and location defaults.
Extraction repository updates
packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts, packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts
Delegates source-file and module-context creation to new helpers; updates PackageResolveError to support optional cause chain; standardizes filesystem/path handling.
Extract-per-module domain tests
packages/riviere-cli/src/features/extract/domain/detect-connections-per-module.spec.ts, packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts
Replaces granular mocking with extractInto mocking; tests delegation flow, draft-module resolution error handling, and workflow-style enrichment integration.
Extract command tests
packages/riviere-cli/src/features/extract/entrypoint/extract.spec.ts, packages/riviere-cli/src/features/extract/entrypoint/extract.dry-run.spec.ts, packages/riviere-cli/src/features/extract/entrypoint/extract.enrichment.spec.ts, packages/riviere-cli/src/features/extract/entrypoint/extract.pr-extraction.spec.ts, packages/riviere-cli/src/features/extract/entrypoint/extract.connections.spec.ts
Updates to reflect fixture relocation and adjusted test expectations (e.g., dry-run now returns draft-only component count); refactored error assertion for non-writable output paths.
CLI fixture and error handler updates
packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts, packages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.spec.ts, packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts, packages/riviere-cli/src/platform/infra/cli/presentation/file-existence.spec.ts
Hardens fixture cleanup with guard conditions; refactors error handler specs to use inline setup instead of shared fixtures; adds parseErrorOutput/hasSuccessOutputStructure Zod schema validation.
Output writer and error handling
packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts, packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts
Changes outputResult to accept structured payload { success, data, warnings }; removes try/catch error handling for file writes; adds test spec with writeFileSync mocking.
Extraction config schema and generation
packages/riviere-extract-config/extraction-config.schema.json, packages/riviere-extract-config/src/extraction-config-schema.ts, packages/riviere-extract-config/src/validation-whitespace-required-fields.spec.ts, packages/riviere-extract-config/scripts/generate-docs.ts
Adds top-level sources and domains configuration; adds whitespace validation pattern to string properties; enhances doc-generation script to render inline object types and composite schemas; updates tsconfig to reference riviere-schema.
Package dependency updates
packages/riviere-extract-config/package.json, packages/riviere-extract-ts/package.json, packages/riviere-extract-ts/tsconfig.lib.json
Adds @living-architecture/riviere-schema to extract-config dependencies; adds @living-architecture/riviere-builder to extract-ts dependencies; extends build-graph references.
Export surface expansion
packages/riviere-extract-ts/src/index.ts, packages/shell/cli.ts
Re-exports new extraction orchestration (extractInto, options/summary types), write-port utilities, and diagnostic types; updates module specifier from 'module' to 'node:module' for createRequire.
Documentation
apps/docs/reference/extraction-config/predicates.md
Minor formatting update to hasDecorator.name parameter type display.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as Extract CLI
    participant ExproctInto as extractInto
    participant Extract as Component Extraction
    participant Enrich as Component Enrichment
    participant ConnDetect as Connection Detection
    participant WritePort as ExtractionWritePort
    participant Builder as RiviereBuilder

    CLI->>ExproctInto: extractInto(writePort, config, options)
    alt mode: 'extract'
        ExproctInto->>Extract: Extract draft components from module contexts
    else mode: 'enrich'
        ExproctInto->>ExproctInto: Load draft components from options
    end
    
    ExproctInto->>Enrich: Enrich draft components per module
    alt enrichment succeeds
        Enrich-->>ExproctInto: Enriched components + failures
        ExproctInto->>ConnDetect: Detect per-module connections
        ExproctInto->>ConnDetect: Detect cross-module connections
        ExproctInto->>WritePort: addComponent(...) for each enriched component
        ExproctInto->>WritePort: addLink(...) for each detected link
        ExproctInto->>WritePort: reportMissingField(...) for failed fields
        WritePort->>Builder: Routes to builder.add*/link*
        ExproctInto-->>CLI: { kind: 'full', components, links, ... }
    else enrichment fails and allowIncomplete=false
        ExproctInto->>WritePort: reportMissingField(...) for failures
        ExproctInto-->>CLI: { kind: 'fieldFailure', failedFields }
    else no connections
        ExproctInto-->>CLI: { kind: 'draftOnly', components }
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

  • Implements extractInto pure core, ExtractionWritePort, and strictWritePort adapter as specified in issue #337, enabling decoupled extraction for CLI and workflow use cases.
  • Addresses issue #316 by providing mergeWritePort workflow adapter and WorkflowDiagnostics/WorkflowBuilder contracts for workflow-integrated extraction with same-step duplicate enforcement.

Possibly related PRs

  • PR #84: Adds draft-component extraction to riviere-extract-ts alongside existing extractor APIs; both modify core extraction types/functions.
  • PR #259: Updates CLI extraction flow to support tsconfig control via loadExtractionProject and --no-ts-config flag; shares extraction command refactoring scope.
  • PR #88: Implements full extraction pipeline (schema, extractInto, CLI command, tests); main PR builds directly on this foundation with write-port abstractions.

Suggested labels

Review effort 4/5

Poem

🐰 Behold! A core born pure and free,
Write-ports dance in harmony,
Extraction flows both strict and merged,
Where CLI and workflows converge!
The fixtures find their shared home,
No more scattered, lost, alone.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: issue 337 code extraction writes through' clearly summarizes the main change: implementing extraction writes through a write port as described in issue #337.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #337: extractInto function, ExtractionWritePort interface, strictWritePort and mergeWritePort adapters, reworked CLI extract command, preserved CLI parity, added comprehensive tests, and exported public surface with proper role enforcement.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #337 objectives: extraction core implementation, write port infrastructure, CLI rework, documentation updates, schema enhancements, and fixture reorganization all support the core requirement of extraction writing through a pure shared interface.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-337-code-extraction-writes-through

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@NTCoding NTCoding changed the title Issue 337 code extraction writes through feat: issue 337 code extraction writes through Apr 25, 2026
coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 22

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts (1)

21-31: ⚠️ Potential issue | 🟠 Major

Error code/exit code mismatch and catches a system-level failure.

Two concerns in this catch block:

  1. The reported code is CliErrorCode.ValidationError ('VALIDATION_ERROR') while the exit code is ExitCode.RuntimeError (3). A filesystem write failure is not a validation error, and consumers parsing the JSON will see a misleading category that disagrees with the process exit code.
  2. Wrapping writeFileSync errors into structured JSON conflicts with the team's documented fail-fast policy for system-level exceptions (disk I/O failures should surface with stack traces, not be reformatted as user-facing errors). With the current behavior, the caller cannot distinguish a permission/disk error from a validation problem, and the original stack is discarded.

Consider either letting writeFileSync errors propagate (preferred), or — if a structured failure is intentional here — introducing a dedicated RUNTIME_ERROR / IO_ERROR CliErrorCode so the JSON code matches the ExitCode.RuntimeError semantics.

Based on learnings: "CLI extraction commands should only catch and format expected validation errors as structured JSON. System-level exceptions (disk I/O failures, OOM, AST parse errors) should fail loudly with stack traces for debugging rather than being wrapped in try-catch and converted to JSON errors."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts`
around lines 21 - 31, The catch block around the file write is misclassifying
system-level I/O failures as CliErrorCode.ValidationError while exiting with
ExitCode.RuntimeError and also discards the original stack; fix by either
removing the try/catch so writeFileSync errors propagate (preferred) or, if you
must return structured JSON, map the error to a new runtime/io code (e.g.,
CliErrorCode.RuntimeError or CliErrorCode.IOError) and use that code in
formatError while keeping ExitCode.RuntimeError so JSON code and process exit
match; update the block that calls formatError/buildWriteFailureMessage (and any
references to options.output) accordingly so callers can distinguish validation
vs system errors.
packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts (1)

308-321: ⚠️ Potential issue | 🟡 Minor

Add assertions to validate the domain and name values in the top-level-rules conversion test.

The current test loadFromFullProject loads config with top-level extends using all defaults (lines 210–220) only asserts .toBeDefined(), which does not validate that domain: 'extended' and name: 'extended' are correctly set. Since domain is a required field in the Module schema and is used downstream for domain derivation and component identity, add an assertion to verify the converted module has the expected values:

const result = new ExtractionProjectRepository().loadFromFullProject({
  configPath: join(dir, 'extract.yml'),
  useTsConfig: false,
})
expect(result).toBeDefined()
expect(result.domain).toBe('extended')
expect(result.name).toBe('extended')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts`
around lines 308 - 321, Update the test "loadFromFullProject loads config with
top-level extends using all defaults" to assert the converted module's domain
and name are correct: after calling
ExtractionProjectRepository().loadFromFullProject(...) assert result is defined
and that result.domain === 'extended' and result.name === 'extended'; this
verifies topLevelRulesToModule produces a Module with domain and name set to
'extended' rather than only checking for presence.
packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts (1)

1-9: ⚠️ Potential issue | 🔴 Critical

Missing vi import — this test will throw ReferenceError: vi is not defined.

Line 317 calls vi.spyOn(...) but vi is not imported on lines 1–3. The test won't run; it will fail at evaluation with a ReferenceError, masking whatever behaviour you intended to assert (and the existing .toThrow(/bad-config/) matcher will never be reached).

Even after adding the import, vi.spyOn(ExtractConfig, 'parseExtractionConfig') patches the property on the namespace alias imported on line 9, but extraction-project-repository.ts imports parseExtractionConfig as a bare named binding (confirmed at line 9 of the SUT). In ESM, these are separate live bindings, so the spy won't intercept the SUT's call. The standard pattern in this repo is a top-level vi.mock(...) with an inline factory. Recommend converting to vi.mock so the SUT's binding is reliably replaced.

A secondary nit: the test name claims the failure is "stringified", but .toThrow(/bad-config/) would also pass if the SUT simply rethrew the original non-Error string. Consider asserting on a wrapped Error shape (e.g. instanceof ConfigValidationError and .message contains 'bad-config') to actually exercise the stringification behaviour the title promises.

🐛 Proposed fix — import `vi`, prefer `vi.mock`, and tighten the assertion
 import {
-  describe, expect, it 
+  describe, expect, it, vi
 } from 'vitest'
 import {
   mkdirSync, mkdtempSync, rmSync, writeFileSync 
 } from 'node:fs'
 import { join } from 'node:path'
 import { tmpdir } from 'node:os'
-import * as ExtractConfig from '@living-architecture/riviere-extract-config'
+import { parseExtractionConfig } from '@living-architecture/riviere-extract-config'
 import { ConfigValidationError } from '../../../../../platform/infra/cli/presentation/error-codes'
 import { ExtractionProjectRepository } from './extraction-project-repository'
+
+vi.mock('@living-architecture/riviere-extract-config', async (importOriginal) => {
+  const actual = await importOriginal<typeof import('@living-architecture/riviere-extract-config')>()
+  return { ...actual, parseExtractionConfig: vi.fn(actual.parseExtractionConfig) }
+})
-      vi.spyOn(ExtractConfig, 'parseExtractionConfig').mockImplementationOnce(() => {
+      vi.mocked(parseExtractionConfig).mockImplementationOnce(() => {
         throw 'bad-config'
       })

       expect(() =>
         new ExtractionProjectRepository().loadFromFullProject({
           configPath: join(dir, 'extract.yml'),
           useTsConfig: false,
         }),
-      ).toThrow(/bad-config/)
+      ).toThrow(ConfigValidationError)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts`
around lines 1 - 9, Add the missing vitest runtime import and convert the spy to
a module mock so the SUT's named binding is replaced: import vi from 'vitest'
(or add vi to the existing import list) and replace the vi.spyOn(ExtractConfig,
'parseExtractionConfig') usage with a top-level
vi.mock('@living-architecture/riviere-extract-config', () => ({
parseExtractionConfig: () => { throw 'bad-config' } })) so
extraction-project-repository.ts receives the mocked parseExtractionConfig;
finally tighten the assertion to check for a wrapped Error type (e.g. instance
of ConfigValidationError and message contains 'bad-config') instead of relying
on a regex-only .toThrow('/bad-config/') to prove stringification behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts`:
- Around line 127-178: Move the standalone fixture constants into a single
contiguous block so all component/link fixtures are grouped together and helper
functions/interfaces live separately; specifically, relocate domainOpComponent
and simpleUseCaseComponent to be adjacent with useCaseComponent, apiComponent,
eventHandlerComponent, validLink, and baseMetadata (and keep createGraphWith*
helpers and CustomTypeDefinition in a separate helpers section), ensuring the
file groups: 1) all fixture constants (baseMetadata, sourceLocation,
useCaseComponent, simpleUseCaseComponent, domainOpComponent, apiComponent,
eventHandlerComponent, validLink) followed by 2) all helper functions/interfaces
(createGraphWith*, CustomTypeDefinition) for improved readability.
- Around line 96-113: The afterEach teardown in setupCommandTest can mask
beforeEach failures because ctx.testDir and ctx.originalCwd may be empty; update
afterEach to only call process.chdir(ctx.originalCwd) and await rm(ctx.testDir,
{ recursive: true, force: true }) if those values are non-empty strings (e.g.
check ctx.testDir && ctx.originalCwd are truthy strings) and keep
vi.restoreAllMocks() as-is; this prevents chdir/rm from throwing on partial
setup and adds force: true to rm to silence "no such file" errors while
preserving the original setup error (references: setupCommandTest, beforeEach,
afterEach, ctx.testDir, ctx.originalCwd, ProcessExitError).

In
`@packages/riviere-cli/src/features/extract/domain/detect-connections-per-module.spec.ts`:
- Around line 53-90: The test title claims the "shared-core full result
unchanged" but only asserts result.kind and doesn't verify the transformed shape
produced by ExtractionProject.extractDraftComponents which calls
this.toBuilderBackedOutcome(builder.build(), outcome, writePort); update the
test (in detect-connections-per-module.spec.ts) to either rename it to reflect
it only preserves the kind and forwards options (e.g., "preserves the
shared-core full outcome kind and forwards options to extractInto") or add
assertions that check the full structure returned by toBuilderBackedOutcome for
the empty full outcome (use mockExtractInto return value and assert the
resulting object equals the expected builder-backed shape), and keep the
existing expectation that mockExtractInto was called with resolvedConfig and the
options object so you still verify forwarding.

In `@packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts`:
- Line 65: The top-level describe currently labeled
'ExtractionProject.enrichDraftComponents' is misleading because the spec also
tests extractDraftComponents and extractIntoWorkflowBuilder; rename or
restructure the suite so the describe accurately reflects the contained tests
(e.g., change the top-level describe name to 'ExtractionProject - extraction
helpers' or split into separate describes for
'ExtractionProject.extractDraftComponents' and
'ExtractionProject.extractIntoWorkflowBuilder'), and move the tests that
exercise extractDraftComponents and extractIntoWorkflowBuilder into their
corresponding describe blocks to ensure correct test discovery and clearer scope
for the functions extractDraftComponents, extractIntoWorkflowBuilder, and
enrichDraftComponents.
- Line 226: Export the DraftComponentModuleResolutionError class from
ExtractionProject (so it can be imported in tests) and update the test in
enrich-per-module.spec.ts to expect that specific error class instead of
matching the message string; specifically, export
DraftComponentModuleResolutionError from wherever
ExtractionProject/ExtractionProjectModule is defined and change the assertion in
the spec from toThrow("Unable to resolve module for draft component
'PlaceOrder'") to using expect(() =>
...).toThrowError(DraftComponentModuleResolutionError) (or the appropriate
matcher for that class).

In
`@packages/riviere-cli/src/features/extract/domain/extraction-project-builder-backed.spec.ts`:
- Around line 289-297: createModuleContext() is being called multiple times to
build duplicateModuleContext, which creates throwaway ts-morph Project
instances; instead call createModuleContext() once into a base variable, then
build duplicateModuleContext by spreading that single base (e.g., const base =
createModuleContext(); duplicateModuleContext = { ...base, module: {
...base.module, name: 'orders-duplicate' }, files: [...] }). Apply the same
single-call refactor for the second occurrence that similarly calls
createModuleContext() multiple times so you never instantiate more than one
Project per intended context.

In
`@packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.spec.ts`:
- Around line 1-9: Add explicit Vitest globals by importing describe, it, and
expect from 'vitest' at the top of the spec file to match the project's test
patterns; update the imports near toOutcomeComponentId, toPresentedComponent,
and toPresentedLink so the file starts with "import { describe, it, expect }
from 'vitest'" alongside the existing imports, keeping existing named imports
(toOutcomeComponentId, toPresentedComponent, toPresentedLink) and type imports
unchanged.

In `@packages/riviere-cli/src/features/extract/domain/extraction-project.ts`:
- Around line 199-211: Hoist the defensive-copy-producing calls out of the
per-element maps: call writePort.missingFields() and writePort.uncertainLinks()
once into local constants (e.g., missingFieldsCopy and uncertainLinksCopy)
before using graph.components.map(...) and graph.links.map(...), then pass those
copies into toPresentedComponent and toPresentedLink instead of invoking
writePort.missingFields() / writePort.uncertainLinks() inside each map to avoid
repeated cloning and reduce complexity from O(N·D) to O(N).

In
`@packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts`:
- Around line 72-93: The code is re-globbing module file lists inside
createModuleContexts instead of reusing the file resolution done by
resolveSourceFilePaths; change resolveSourceFilePaths to return a per-module
mapping (e.g. Map or Record keyed by module identifier/path) of resolved
absolute file paths, then update createModuleContexts to accept that per-module
map and use it to intersect with sourceFileSet (or use the map's array directly)
rather than calling globSync again; keep existing uses of findModuleTsConfigDir
and createConfiguredProject, and also adjust the globSync options object
formatting in this file to match the multiline shape used elsewhere (replace
single-line {cwd: configDir,} with the multiline form).
- Around line 55-69: The function resolveSelectedSourceFilePaths should accept
the config directory and resolve requestedFiles against it so comparisons match
allSourceFiles; change the signature to
resolveSelectedSourceFilePaths(allSourceFiles: string[], requestedFiles:
string[], configDir: string): string[] and update the existence check and the
requestedAbsolute set to use path.resolve(configDir, filePath) (replace
path.resolve(filePath) with path.resolve(configDir, filePath)); keep throwing
ConfigValidationError for missingFiles and ensure callers that invoke
resolveSelectedSourceFilePaths are updated to pass parsedConfigState.configDir.

In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts`:
- Around line 53-64: The PackageResolveError currently mutates this.message
after calling super; instead build the full error message string up front
(including the packageName and cause.message if present) and pass it into super
along with the cause via ErrorOptions (i.e., super(fullMessage, { cause })) so
you don't reassign this.message or this.cause afterward; update the
PackageResolveError constructor to compute fullMessage first and call
super(fullMessage, { cause }) and set this.name = 'PackageResolveError'.

In `@packages/riviere-cli/src/platform/__fixtures__/command-test-fixtures.ts`:
- Line 1: Remove the dead transitional shim file that only re-exports fixtures:
delete the file whose sole content is "export * from
'../../fixtures/command-test-fixtures'"; ensure there are no remaining imports
referencing platform/__fixtures__/command-test-fixtures and run tests/build to
verify nothing breaks after removal.

In
`@packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts`:
- Line 78: The test hard-codes code: 'VALIDATION_ERROR' for filesystem failures;
update the assertions in output-writer.spec.ts (around the expectation at the
line referencing code: 'VALIDATION_ERROR' and the similar assertion at line 111)
so they follow the production decision: either import and assert against the
error code constant exported by output-writer (or the IO/runtime error constant)
or compute expectedErrorCode from the behavior of OutputWriter (or allow either
VALIDATION_ERROR or IO_ERROR via a flexible matcher), and ensure the tests
reference writeFileSync-related behavior in output-writer.ts rather than a
literal string.

In `@packages/riviere-extract-config/extraction-config.schema.json`:
- Around line 38-59: Add human-readable top-level "description" strings to the
$defs.sourceInfo and $defs.domainMetadata schema objects and add per-property
"description" fields for each property (sourceInfo: repository, commit,
extractedAt; domainMetadata: description, systemType) so the generated docs no
longer show "(no description)". Keep the existing constraints (type, minLength,
pattern, enum, additionalProperties) but populate each schema and property with
brief descriptive text that explains the field purpose (e.g., repository: source
repo URL or identifier; commit: commit SHA or tag; extractedAt: ISO timestamp;
domainMetadata.description: short summary of domain; systemType: one of
"domain"|"bff"|"ui"|"other").

In `@packages/riviere-extract-config/scripts/generate-docs.ts`:
- Around line 93-104: The function readInlinePropertyType currently only handles
$ref, enum, and primitive types; update it to recurse into nested schemas so
arrays, inline objects, and oneOf/anyOf/allOf render correctly: for type ===
'array' use the items schema and return a recursive
readInlinePropertyType(items) plus [] (handle items.$ref/inline), for type ===
'object' with properties build an inline shape by iterating prop.properties and
recursively calling readInlinePropertyType for each property (honor required to
include ?), and for oneOf/anyOf/allOf join the recursive results with ' | ' (or
' & ' for allOf as appropriate). Reuse getRefTypeName for $ref cases and ensure
SchemaProperty/Schema types passed into recursive calls are the same shape as
used by readInlinePropertyType.

In
`@packages/riviere-extract-config/src/validation-whitespace-required-fields.spec.ts`:
- Around line 4-17: The test currently mutates all module string fields to
whitespace at once, which hides per-field regressions; change the spec to an
it.each over the four field names ('name', 'domain', 'path', 'glob') and for
each case clone the minimal config from createMinimalConfig(), set only that
single field on config.modules[0] to a whitespace-only string, and assert
isValidExtractionConfig(config) returns false — this preserves use of
createMinimalConfig and isValidExtractionConfig while giving one failing
assertion per property.

In
`@packages/riviere-extract-ts/src/features/extraction/domain/extract-into.edge-coverage.spec.ts`:
- Around line 56-132: Extract the duplicated test helpers (createModule,
createProjectWithDispose, createWritePortRecorder, createDraftComponent,
createEnrichedComponent) into a single shared test-fixtures module (e.g., export
them from extract-into.test-fixtures.ts) and replace the duplicated definitions
in each spec (extract-into.edge-coverage.spec.ts,
extract-into.branch-coverage.spec.ts, extract-into.spec.ts) with imports from
that module; keep any vi.mock(...) calls in each spec file as-is (do not move
them) to respect Vitest hoisting rules, and update imports/usages of the helper
functions (e.g., createModule, createProjectWithDispose,
createWritePortRecorder, createDraftComponent, createEnrichedComponent) so tests
continue to compile.

In `@packages/riviere-extract-ts/src/features/extraction/domain/extract-into.ts`:
- Line 343: The inline object in the spread expression using
externalLink.sourceLocation is missing the preferred spacing and trailing-comma
style; update the snippet that builds the spread (the object literal containing
sourceLocation from externalLink) to use spaced braces and no extraneous comma —
i.e. change {sourceLocation: externalLink.sourceLocation,} to { sourceLocation:
externalLink.sourceLocation } and run the repo formatter so the rest of
extract-into.ts matches project style.
- Around line 117-120: The field-failure branch in extractInto returns early
when enrichDraftComponents yields enrichment.kind === 'fieldFailure', which
prevents per-field reasons from being reported via writePort.reportMissingField
in writeCompleteExtraction; update extractInto (around the
enrichDraftComponents/enrichment handling) to, before returning the fieldFailure
summary, iterate enrichment.failures and call the writePort.reportMissingField
(or invoke the same reporting logic used by writeCompleteExtraction) for each
failure so mergeWritePort callers and strictWritePort.missingFields() receive
the per-field diagnostics; ensure you reference enrichDraftComponents,
enrichment.failures, writePort.reportMissingField, and toBuilderBackedOutcome
(or reuse writeCompleteExtraction) so the failure branch emits the same
missing-field events as the success path.
- Around line 107-136: The field-failure branch in extractInto returns early
when enrichment.kind === 'fieldFailure', skipping writeCompleteExtraction and
thus never calling reportMissingFields/writePort.reportMissingField; modify
extractInto so that before returning the 'fieldFailure' result it invokes
reportMissingFields(writePort, enrichment.failedFields, /* any needed context
from enrichment or options */) (or call writeCompleteExtraction with the
enrichment and an empty connectionResult) so diagnostics from
enrichDraftComponents are forwarded; additionally make disposal of module
contexts opt-in by changing the finally disposeProjects(options.moduleContexts)
to respect a new boolean option (e.g., options.disposeProjects defaulting to
true) or by moving disposeProjects out of finally and only calling it when that
flag is true, referencing disposeProjects and options.moduleContexts to locate
the change.

In
`@packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts`:
- Around line 166-179: In addComponent, move the duplicate identity check before
any side effects: compute componentId with toCanonicalComponentId(input) and if
emittedComponentIds.has(componentId) throw SameStepDuplicateComponentError
(using stepContext.step and stepContext.stepType) before calling
defineCustomTypeWhenNeeded; only after emitting add the id to
emittedComponentIds and call dispatchMergeComponentWrite(builder, input); keep
references to definedCustomTypes and builder for defineCustomTypeWhenNeeded but
ensure it runs after the duplicate guard.
- Around line 71-79: Change WorkflowDiagnosticEvent from an open shape with
optional properties into a discriminated union keyed by the kind field: define
one variant for kind 'missing-field' that requires componentId and field (and
optionally source/target/linkType if relevant), another for kind
'uncertain-link' that requires source, target, and linkType (and optionally
componentId/field if relevant), and keep reason required on each variant; update
the exported WorkflowDiagnosticEvent type to be the union of these specific
interfaces so consumers can narrow by event.kind and get non-optional fields
(refer to the WorkflowDiagnosticEvent type and the 'missing-field' and
'uncertain-link' kind strings when making the change).

---

Outside diff comments:
In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts`:
- Around line 1-9: Add the missing vitest runtime import and convert the spy to
a module mock so the SUT's named binding is replaced: import vi from 'vitest'
(or add vi to the existing import list) and replace the vi.spyOn(ExtractConfig,
'parseExtractionConfig') usage with a top-level
vi.mock('@living-architecture/riviere-extract-config', () => ({
parseExtractionConfig: () => { throw 'bad-config' } })) so
extraction-project-repository.ts receives the mocked parseExtractionConfig;
finally tighten the assertion to check for a wrapped Error type (e.g. instance
of ConfigValidationError and message contains 'bad-config') instead of relying
on a regex-only .toThrow('/bad-config/') to prove stringification behavior.

In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts`:
- Around line 308-321: Update the test "loadFromFullProject loads config with
top-level extends using all defaults" to assert the converted module's domain
and name are correct: after calling
ExtractionProjectRepository().loadFromFullProject(...) assert result is defined
and that result.domain === 'extended' and result.name === 'extended'; this
verifies topLevelRulesToModule produces a Module with domain and name set to
'extended' rather than only checking for presence.

In `@packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts`:
- Around line 21-31: The catch block around the file write is misclassifying
system-level I/O failures as CliErrorCode.ValidationError while exiting with
ExitCode.RuntimeError and also discards the original stack; fix by either
removing the try/catch so writeFileSync errors propagate (preferred) or, if you
must return structured JSON, map the error to a new runtime/io code (e.g.,
CliErrorCode.RuntimeError or CliErrorCode.IOError) and use that code in
formatError while keeping ExitCode.RuntimeError so JSON code and process exit
match; update the block that calls formatError/buildWriteFailureMessage (and any
references to options.output) accordingly so callers can distinguish validation
vs system errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 71a11a4f-cbe5-40e6-93a1-5e2d6d9983b7

📥 Commits

Reviewing files that changed from the base of the PR and between 1dfddb0 and 5a64576.

⛔ Files ignored due to path filters (26)
  • apps/docs/reference/api/generated/riviere-builder/README.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/BuildValidationError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/ComponentNotFoundError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/ComponentTypeMismatchError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/CustomTypeAlreadyDefinedError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/CustomTypeNotFoundError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/DomainNotFoundError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/DuplicateComponentError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/InvalidGraphError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/MissingDomainsError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/MissingRequiredPropertiesError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/MissingSourcesError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/RiviereBuilder.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/classes/SourceConflictError.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/APIInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/BuilderWarning.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/CustomInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/CustomTypeInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/DomainOpInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/EventHandlerInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/EventInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/UIInput.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/UpsertOptions.md is excluded by !**/generated/**
  • apps/docs/reference/api/generated/riviere-builder/interfaces/UseCaseInput.md is excluded by !**/generated/**
  • packages/riviere-extract-config/docs/generated/schema.md is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (70)
  • apps/docs/reference/extraction-config/schema.md
  • packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts
  • packages/riviere-cli/src/features/builder/commands/add-component.spec.ts
  • packages/riviere-cli/src/features/builder/commands/error-coverage.spec.ts
  • packages/riviere-cli/src/features/builder/commands/more-command-coverage.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-component-custom.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-component.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-domain.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-source.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/check-consistency.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-checklist.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-summary.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/define-custom-type.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/enrich.signature.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/enrich.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/finalize.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/graph-error-coverage.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/init.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-external.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-http.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/validate.spec.ts
  • packages/riviere-cli/src/features/extract/__fixtures__/extraction-test-fixtures.ts
  • packages/riviere-cli/src/features/extract/domain/detect-connections-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project-builder-backed.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.connections.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.dry-run.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.enrichment.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.pr-extraction.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.spec.ts
  • packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts
  • packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts
  • packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts
  • packages/riviere-cli/src/features/query/entrypoint/components.errors.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/components.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/domains.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/entry-points.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/orphans.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/search.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/trace.spec.ts
  • packages/riviere-cli/src/features/query/infra/persistence/riviere-query-repository.spec.ts
  • packages/riviere-cli/src/features/query/queries/trace-flow.spec.ts
  • packages/riviere-cli/src/fixtures/command-test-fixtures.ts
  • packages/riviere-cli/src/platform/__fixtures__/command-test-fixtures.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts
  • packages/riviere-cli/src/shell/cli.ts
  • packages/riviere-extract-config/extraction-config.schema.json
  • packages/riviere-extract-config/package.json
  • packages/riviere-extract-config/scripts/generate-docs.ts
  • packages/riviere-extract-config/src/extraction-config-schema.ts
  • packages/riviere-extract-config/src/validation-whitespace-required-fields.spec.ts
  • packages/riviere-extract-config/tsconfig.lib.json
  • packages/riviere-extract-ts/package.json
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into-component-write-input.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into-component-write-input.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.branch-coverage.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.edge-coverage.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port-helpers.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.merge.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts
  • packages/riviere-extract-ts/src/index.ts
  • packages/riviere-extract-ts/tsconfig.lib.json

Comment thread packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts
Comment thread packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts
Comment thread packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts Outdated
Comment thread packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts`:
- Around line 376-383: Remove the helper function parseErrorOutputSchema and
inline the Zod parse call where it was used: delete parseErrorOutputSchema and
replace its usages (e.g., in parseErrorOutput) with a one-liner
errorOutputSchema.parse(value) (or errorOutputSchema.safeParse(value) + throw if
you need TestAssertionError); reference symbols: parseErrorOutputSchema,
errorOutputSchema, TestAssertionError, and parseErrorOutput to locate and update
the code.

In `@packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts`:
- Around line 229-257: The test title claims it verifies a fallback "extracted"
domain when module domains are blank, but because mockExtractInto returns {
kind: 'draftOnly' } and the builder pathway (toBuilderBackedOutcome ->
builder.build().domains) is never exercised, the assertion only checks that
mockExtractInto was called with mode: 'extract' and doesn't prove the fallback
domain; fix by either removing or adjusting the mock so the real builder path
runs and then assert on builder.build().domains (referencing
extractDraftComponents, mockExtractInto, toBuilderBackedOutcome and
builder.build().domains) to validate the fallback extracted domain, or rename
the test to reflect the current assertion (e.g., "passes mode 'extract' for
draft-only extraction") and update the test name accordingly.

In
`@packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts`:
- Line 110: The exported declaration extractionProjectGlobMatcher is missing the
required JSDoc `@riviere-role` tag; add a JSDoc line immediately above the export
like /** `@riviere-role` <role-name> */ using the same role used by the other
exported functions in this module (the four above) so the role-enforcement lint
passes, leaving the export as extractionProjectGlobMatcher =
ExtractTs.matchesGlob.
- Around line 42-53: resolveChangedSourceFilePaths currently calls
detectChangedTypeScriptFiles(process.cwd(), …) and resolves returned paths
against process.cwd(), which mismatches allSourceFiles (which are resolved
against parsedConfigState.configDir); change resolveChangedSourceFilePaths
signature to accept configDir: string, call
detectChangedTypeScriptFiles(configDir, gitOptions), and normalize the returned
file paths with path.resolve(configDir, filePath) before building
changedAbsolute; also update the caller in extraction-project-repository (where
resolveChangedSourceFilePaths is invoked) to pass parsedConfigState.configDir so
both git detection and path normalization use the same configDir (and keep
baseBranch handling as-is).

In
`@packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts`:
- Around line 110-123: In loadFromChangedProject update the call to
resolveChangedSourceFilePaths to pass the project configDir so path resolution
uses the parsed config directory: retrieve parsedConfigState.configDir and
forward it as the additional argument to resolveChangedSourceFilePaths (which
will have been updated to accept configDir) when computing sourceFilePaths,
keeping the rest of the call and subsequent
createExtractionProject(parsedConfigState, sourceFilesByModule, sourceFilePaths,
params.useTsConfig) unchanged.
- Around line 336-342: The createExtractionProject signature uses the namespaced
type ExtractConfig.Module for sourceFilesByModule; change that to the imported
Module type (i.e. use Module instead of ExtractConfig.Module) in the
createExtractionProject parameter list to match the direct import from
'@living-architecture/riviere-extract-config' and avoid the undefined namespace.

In
`@packages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.spec.ts`:
- Around line 17-78: The file duplicates test harness code (TestAssertionError,
TestContext, createTestContext, setupCommandTest and ProcessExitSignal) that
already exists in the shared fixture; remove the local duplicates and import the
shared utilities from the common fixture (e.g., fixtures/command-test-fixtures)
instead, keeping only the local UnexpectedGlobalError if it is truly unique; if
you need a different name for the process-exit error, import the shared
ProcessExitError/fixture symbol and either alias it or re-export it locally
rather than reimplementing ProcessExitSignal; update uses of setupCommandTest,
createTestContext, TestContext, and TestAssertionError in this spec to use the
imported symbols.

In
`@packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts`:
- Around line 22-25: Replace the afterEach cleanup that calls vi.clearAllMocks()
and vi.restoreAllMocks() with a single vi.resetAllMocks() so hoisted mocks'
implementations are cleared between tests; specifically update the afterEach
block surrounding mockWriteFileSync (the vi.hoisted(vi.fn()) mock) to call
vi.resetAllMocks() to ensure any mockImplementation() (including the throwing
implementations set in tests 2 and 3) does not leak into subsequent tests.

In
`@packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts`:
- Around line 9-14: Replace the locally defined TestAssertionError class with
the shared exported one: remove the local class declaration in
query-graph-load-error-handler.spec.ts and add an import for TestAssertionError
from the shared fixture module (e.g., fixtures/command-test-fixtures) so tests
use the same TestAssertionError identity across specs and avoid duplicated code.

In `@packages/riviere-extract-config/scripts/generate-docs.ts`:
- Around line 133-138: The array item parenthesis guard in formatArrayType
incorrectly checks for unescaped ' | ' (and ' & ') while readInlinePropertyType
emits escaped pipes (' \\| ') for markdown, so union item types don't get
parenthesized; update formatArrayType to detect the escaped pipe produced by
readInlinePropertyType (and still accept unescaped forms) when deciding to wrap
the itemType in parentheses — i.e., adjust the conditional that builds
arrayItemType in formatArrayType to check for ' \\| ' (and keep checks for ' | '
and ' & ' as needed) so `(module \| moduleRef)[]` renders correctly.

In
`@packages/riviere-extract-ts/src/features/extraction/domain/extract-into-fixtures.ts`:
- Around line 36-74: The recorder currently types its arrays as unknown[],
losing type information; update createWritePortRecorder to type each array using
the concrete parameter/event types from ExtractionWritePort (e.g., use
Parameters<ExtractionWritePort['addComponent']>[0][] for components,
Parameters<ExtractionWritePort['addLink']>[0][] for links,
Parameters<ExtractionWritePort['addExternalLink']>[0][] for externalLinks,
Parameters<ExtractionWritePort['reportMissingField']>[0][] for missingFields,
and Parameters<ExtractionWritePort['reportUncertainLink']>[0][] for
uncertainLinks) and adjust the returned signature accordingly so consumers get
full compile-time types when asserting recorded entries.

In
`@packages/riviere-extract-ts/src/features/extraction/domain/extract-into.branch-coverage.spec.ts`:
- Around line 213-323: The test currently ignores the return value of
extractIntoModule.extractInto; capture its return into a variable (e.g., result)
and assert that result.kind === 'full' and that result.links and
result.externalLinks (and optionally result.timings) match the aggregated output
produced by mockDetectPerModuleConnections and mockDetectCrossModuleConnections;
update the test to reference extractIntoModule.extractInto and verify
result.links/result.externalLinks (and timings) alongside the existing
recorder.* and mockDetectCrossModuleConnections invocation assertions.
- Around line 1-58: Add an explicit Vitest import at the top of the file: import
{ describe, it, expect, beforeEach, vi } from 'vitest' so the test file follows
the package's import convention; keep the existing vi.hoisted() and vi.mock(...)
usages but reference the named vi from that import. Optionally, for conventional
grouping, move the import * as extractIntoModule from './extract-into' so it
appears before the vi.mock(...) blocks (or ensure mocks run before the module
import as required by the tests).

In
`@packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts`:
- Around line 196-203: The toCanonicalComponentId function currently uses
toComponentIdTypeSegment(input.type) which yields the literal "custom" for
input.type === 'custom' and causes mismatched IDs; update toCanonicalComponentId
so that when input.type === 'custom' it uses input.customTypeName (falling back
to toComponentIdTypeSegment(input.type) only if customTypeName is missing) to
build the type segment, ensuring the canonical ID matches builder-generated IDs
(e.g., generateComponentId) and toExtractionComponentId used elsewhere (affects
mergeWritePort.addComponent and enrichment/connection detection).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fea4c98f-87e7-425d-9bbd-262630611dd9

📥 Commits

Reviewing files that changed from the base of the PR and between 5a64576 and fb8ea4e.

⛔ Files ignored due to path filters (2)
  • packages/riviere-extract-config/docs/generated/predicates.md is excluded by !**/generated/**
  • packages/riviere-extract-config/docs/generated/schema.md is excluded by !**/generated/**
📒 Files selected for processing (27)
  • apps/docs/reference/extraction-config/predicates.md
  • apps/docs/reference/extraction-config/schema.md
  • packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts
  • packages/riviere-cli/src/features/extract/domain/detect-connections-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project-builder-backed.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project-presentation.spec.ts
  • packages/riviere-cli/src/features/extract/domain/extraction-project.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract.spec.ts
  • packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.spec.ts
  • packages/riviere-cli/src/features/extract/infra/external-clients/source-files/extraction-project-source-files.ts
  • packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.spec.ts
  • packages/riviere-cli/src/features/extract/infra/persistence/extraction-project/extraction-project-repository.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts
  • packages/riviere-cli/src/platform/infra/external-clients/filesystem/file-existence.spec.ts
  • packages/riviere-extract-config/extraction-config.schema.json
  • packages/riviere-extract-config/scripts/generate-docs.ts
  • packages/riviere-extract-config/src/validation-whitespace-required-fields.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into-component-write-input.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into-fixtures.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.branch-coverage.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.edge-coverage.spec.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extract-into.ts
  • packages/riviere-extract-ts/src/features/extraction/domain/extraction-write-port.ts

Comment thread packages/riviere-cli/src/__fixtures__/command-test-fixtures.ts Outdated
Comment thread packages/riviere-cli/src/features/extract/domain/enrich-per-module.spec.ts Outdated
Comment thread packages/riviere-extract-config/scripts/generate-docs.ts
@NTCoding
NTCoding dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] April 25, 2026 17:47

Addressed in follow-up commits.

@sonarqubecloud

Copy link
Copy Markdown

@@ -0,0 +1,112 @@
import * as fs from 'node:fs'

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

"extraction-project-source-files" is clearly NOT an external client. It uses terminology from our domain. A clear violation.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is valid. This module is coordinating extraction-project concepts rather than acting as a thin wrapper around glob, git, or ts-morph, so infra/external-clients is the wrong home. A follow-up split around source-file resolution and module-context creation would fit the boundaries more cleanly.

return groupedDrafts
}

function assertAllDraftsMatchModules(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

This exceeds the 400-line file length limit. How did this get past the checks?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size concern is valid. The checks did not fail because this repository enforces max-lines with blank lines and comments excluded, and this file still passes ESLint under that rule. The orchestration is still too large and needs to be split across smaller domain modules.

].includes(key)
}

function isSupportedMetadataValue(value: unknown): value is PresentedMetadataValue {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

Ugly hacky type guard. Why are we not using zod schema here as per the conventions?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That may not be correct as stated. This predicate is filtering in-process Custom component fields that already come from typed schema objects, not parsing external input, and this feature does not currently use production Zod schemas for this boundary. The stronger issue here is the mapper location and naming rather than the specific predicate mechanism.


type PresentedMetadataValue = string | number | boolean | string[]

/** @riviere-role domain-service */

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

is this really a domain service? I don't see any business rules here just mapping.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is valid. This file is doing outcome and presentation mapping, not enforcing business rules, so the domain-service role is misleading. It belongs in a mapper or presentation-oriented module rather than features/extract/domain.

}
}

function detectConnections(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

We already have methods called detectConnections in the codebase. How is this one different and why do we need multiple?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is valid. This function is not the same as connection-detection/detect-connections; it is an orchestration layer that collects per-module results, adds cross-module detection, and deduplicates. A name that reflects orchestration rather than raw detection would remove the overlap.

return 'dispose' in project && typeof project.dispose === 'function'
}

function enrichDraftComponents(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this duplicate logic already in the codebase?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplication concern is partly valid. This function owns module grouping, module membership validation, and per-project orchestration, but it also repeats accumulation flow that already appears elsewhere. The extraction-specific orchestration is distinct; the repeated accumulation pattern is the part worth extracting.

}

function hasDisposeMethod(project: Project): project is Project & { dispose: () => void } {
return 'dispose' in project && typeof project.dispose === 'function'

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FACTORY]

Ugly code. Use the type system properly.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stylistic concern is fair. hasDisposeMethod is a defensive runtime guard around project.dispose() rather than an explicit capability in the context type. The cleaner shape is to model disposable project capability in the relevant type and remove the ad hoc guard.

}
}

function loadDraftComponents(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is called load suggesting persistence logic is moving into the domain. This sounds like more of a repository method. but then the implementation doesn't load anything, it extracts components?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is valid. loadDraftComponents suggests retrieval, but in extract mode it performs fresh extraction and in enrich mode it returns supplied drafts. A name centered on resolving drafts for the selected mode would match the actual behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant