Skip to content

fix(riviere-cli): enforce entrypoint layering - #372

Merged
NTCoding merged 2 commits into
mainfrom
fix/entrypoint-layering
May 4, 2026
Merged

NTCoding merged 2 commits into
mainfrom
fix/entrypoint-layering

Conversation

@NTCoding

@NTCoding NTCoding commented May 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Move CLI entrypoints into entrypoint/{entrypoint}/entrypoint.ts folders so opening entrypoint/ shows available entrypoints.
  • Split CLI presentation roles into output formatting, response formatting, response writing, and boundary error handling.
  • Replace query graph-load exception handling with typed query results and enforce entrypoint domain/persistence import boundaries.

Verification

  • pnpm nx run @living-architecture/source:role-check
  • pnpm nx test riviere-cli
  • pnpm nx test dev-workflow-v2
  • pnpm nx typecheck dev-workflow-v2
  • pnpm exec nx affected -t lint test build typecheck check-generated-docs smoke-test --verbose
  • pre-commit hook: lint-md, role-check, build, depcruise, lint, typecheck, test, check-generated-docs, knip

Closes #371

Summary by CodeRabbit

  • Documentation

    • Added CLI role definitions (error handler, response formatter, response writer) and updated ADRs on folder structure and responsibilities.
  • New Features

    • New/updated query CLI commands: entry-points, domains, orphans, search, and trace with consistent JSON error/result formatting.
  • Bug Fixes & Refactors

    • Improved handling and typing of query-graph load failures for clearer, safer CLI error responses.
    • Reorganized CLI entrypoint wiring and enforcing role boundaries for more predictable command behavior.

Move CLI entrypoints into per-entrypoint folders.

Enforce domain and persistence import boundaries at the entrypoint layer.
@coderabbitai

coderabbitai Bot commented May 4, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Restructures CLI entrypoints into per-entrypoint folders, adds three CLI roles (cli-error-handler, cli-response-formatter, cli-response-writer) and role-enforcement rules, replaces thrown query-graph errors with discriminated union load-failure results and helper/formatters, and updates import paths across many entrypoint/fixture files and configs (.dependency-cruiser, ESLint, ADR-002).

Changes

Role & Entrypoint Enforcement

Layer / File(s) Summary
Role Type System
.riviere/roles.ts
Added cli-error-handler, cli-response-formatter, cli-response-writer to RoleName and allRoles (targeting functions).
Role Definitions
.riviere/role-definitions/*.md
Added docs for cli-error-handler, cli-response-formatter, cli-response-writer; updated cli-output-formatter to clarify delegation and boundaries.
Enforcement Config
.riviere/role-enforcement.config.ts
Added entrypointRoles, expanded cliPresentationRoles, introduced /entrypoint/{entrypoint} sublocation and adjusted infra/presentation sublocations.
Dependency Rules
.dependency-cruiser.mjs, .dependency-cruiser.frontend.mjs
Tightened entrypoint-restricted-deps (now forbids entrypoint alongside other same-feature dirs) and narrowed domain-no-infra from.path matcher.
Project Wiring / ESLint / ADR
eslint.config.mjs, docs/architecture/adr/ADR-002-allowed-folder-structures.md
ESLint override narrowed to **/entrypoint/**/entrypoint.ts; ADR updated to describe per-entrypoint DTO/mappers and platform CLI ownership.
Entrypoint & CLI import rewires
packages/*/src/.../entrypoint/**/entrypoint.ts, packages/*/src/shell/cli.ts, tools/dev-workflow-v2/src/**
Moved/created per-entrypoint entrypoint/entrypoint.ts modules, updated many relative import paths and CLI bootstrap imports to new entrypoint locations; adjusted workflow CLI exports/imports and bumped plugin version.

Query failure model, use-case refactor & presentation

Layer / File(s) Summary
Failure Model & Loader
packages/riviere-cli/src/features/query/queries/query-graph-load-failure.ts
Added QueryGraphLoadFailure and LoadedQueryGraph discriminated types, toQueryGraphLoadFailure() and loadQueryGraph() to convert known repository errors into failure objects (or rethrow unexpected).
Result Type Changes
packages/riviere-cli/src/features/query/queries/*-result.ts
Updated query result types (detect-orphans, list-components, list-domains, list-entry-points, search-components, trace-flow) to be unions that include QueryGraphLoadFailure.
Use-case Changes
packages/riviere-cli/src/features/query/queries/*.ts
Refactored use cases to call loadQueryGraph() and return early with failure objects on load failure; success paths use loaded.query.*.
Presentation Formatter
packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.ts
Added formatQueryGraphLoadFailure() to map discriminated failures to CLI error envelopes.
Removed error-handler
packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.ts, .../query-graph-load-error-handler.spec.ts
Deleted prior handleQueryGraphLoadError and its tests—error handling moved into result shapes and the new formatter.
Entrypoints updated to handle results
packages/riviere-cli/src/features/query/entrypoint/**/entrypoint.ts, packages/riviere-cli/src/features/query/entrypoint/components/entrypoint.ts
Query commands updated/recreated under per-entrypoint folders to branch on 'kind' in result and use formatQueryGraphLoadFailure() instead of catching thrown graph errors. Tests updated to match new module locations and behavior.
Tests
packages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.ts, .../query-graph-load-failure-output.spec.ts
Added unit tests for mapping errors to QueryGraphLoadFailure and for formatting those failures to CLI output.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as Entrypoint (CLI)
    participant UC as Use Case
    participant Repo as Repository
    participant Fmt as CLI Formatter

    CLI->>UC: execute(input)
    UC->>Repo: repository.load(graphPathOption) via loadQueryGraph()
    alt Repository returns query
        Repo-->>UC: { kind: 'loaded', query }
        UC-->>CLI: success result (e.g., { components: [...] })
    else Repository throws GraphNotFound/GraphCorrupted
        Repo-->>UC: { kind: 'graphNotFound'|'graphCorrupted', message }
        UC-->>CLI: QueryGraphLoadFailure ({ kind, message })
    else Repository throws unexpected error
        Repo-->>UC: throw unexpected error (rethrow)
        UC-->>CLI: error bubbles up (uncaught)
    end

    alt Success case at CLI
        CLI->>Fmt: formatSuccess(result)
        Fmt-->>CLI: success envelope (printed or returned)
    else Failure case at CLI
        CLI->>Fmt: formatQueryGraphLoadFailure(failure)
        Fmt-->>CLI: error envelope (printed)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

architecture, refactoring, role-enforcement, cli

Poem

🐰 I hopped through folders, straight and neat,
Entrypoints found their tidy seat.
Roles I burrowed, small and spry,
Errors caught — or left to fly.
Thump! A clean CLI — carrot pie.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/entrypoint-layering

@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: 10

Caution

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

⚠️ Outside diff range comments (9)
packages/riviere-cli/src/features/extract/entrypoint/extract/extract.pr-extraction.spec.ts (1)

311-331: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

errorSpy.mockRestore() is not guarded — spy leaks if the await rejects.

If parseCommandWithErrorHandling rejects unexpectedly (e.g., a misconfiguration in CI), mockRestore() on line 328 is never reached and the console.error spy persists into the next test.

🛡️ Proposed fix: wrap in `try/finally`
-  await parseCommandWithErrorHandling([
-    'node',
-    'riviere',
-    'extract',
-    '--config',
-    configPath,
-    '--pr',
-    '--base',
-    'main',
-    '--components-only',
-  ])
-
-  errorSpy.mockRestore()
-
-  expect(stderrOutput.some((msg) => msg.includes('untracked'))).toBe(true)
+  try {
+    await parseCommandWithErrorHandling([
+      'node',
+      'riviere',
+      'extract',
+      '--config',
+      configPath,
+      '--pr',
+      '--base',
+      'main',
+      '--components-only',
+    ])
+  } finally {
+    errorSpy.mockRestore()
+  }
+
+  expect(stderrOutput.some((msg) => msg.includes('untracked'))).toBe(true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/extract/entrypoint/extract/extract.pr-extraction.spec.ts`
around lines 311 - 331, The test currently sets up errorSpy (vi.spyOn(console,
'error')) then awaits parseCommandWithErrorHandling but calls
errorSpy.mockRestore() unguarded so the spy can leak if the await rejects; wrap
the invocation of parseCommandWithErrorHandling and the subsequent mockRestore
in a try/finally block so that errorSpy.mockRestore() always runs, referencing
the existing errorSpy and parseCommandWithErrorHandling identifiers to locate
where to apply the try/finally.
packages/riviere-cli/src/features/builder/entrypoint/validate/validate.spec.ts (1)

256-289: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing test case for the GRAPH_CORRUPTED error path — violates mandatory 100% coverage.

The entrypoint.ts else-branch maps any !result.success code that is not 'GRAPH_NOT_FOUND' to CliErrorCode.GraphCorrupted. This branch is never exercised in this spec; only the GRAPH_NOT_FOUND path is covered. Given mandatory 100% coverage, add a case that writes a malformed graph file and asserts CliErrorCode.GraphCorrupted appears in the output.

✅ Proposed additional test
+    it('returns GRAPH_CORRUPTED when graph file is malformed', async () => {
+      const graphPath = join(ctx.testDir, '.riviere/graph.json')
+      await mkdir(dirname(graphPath), { recursive: true })
+      await writeFile(graphPath, 'not valid json')
+      await createProgram().parseAsync(['node', 'riviere', 'builder', 'validate'])
+      expect(ctx.consoleOutput.join('\n')).toContain(CliErrorCode.GraphCorrupted)
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/builder/entrypoint/validate/validate.spec.ts`
around lines 256 - 289, Add a test in validate.spec.ts under the "error
handling" describe that exercises the entrypoint.ts branch mapping non-success
results to CliErrorCode.GraphCorrupted: use createGraph or directly write a
malformed graph JSON file into ctx.testDir (e.g., invalid JSON or missing
required fields), then run
createProgram().parseAsync(['node','riviere','builder','validate','--graph',
malformedPath]) and assert that ctx.consoleOutput contains
CliErrorCode.GraphCorrupted; reference the existing tests and utilities
(createGraph, createProgram, parseOutput, CliErrorCode) to mirror style and
ensure the corrupted-graph branch is executed.
packages/riviere-cli/src/features/builder/entrypoint/component-checklist/entrypoint.ts (1)

32-50: 🧹 Nitpick | 🔵 Trivial

Remove the async keyword on line 32 — componentChecklist.execute() is synchronous and does not require await.

The ComponentChecklist.execute() method returns ComponentChecklistResult directly, not a Promise. The method is synchronous, so the async keyword on the action handler is unnecessary. The code works correctly as-is, but the async declaration should be removed for clarity and to avoid false implications about async operations.

Proposed fix
-    .action(async (options: ComponentChecklistOptions) => {
+    .action((options: ComponentChecklistOptions) => {
       if (options.type !== undefined && !isValidComponentType(options.type)) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/builder/entrypoint/component-checklist/entrypoint.ts`
around lines 32 - 50, Remove the unnecessary async on the action handler since
ComponentChecklist.execute is synchronous: change the handler signature from
.action(async (options: ComponentChecklistOptions) => { ... }) to
.action((options: ComponentChecklistOptions) => { ... }); ensure there are no
await expressions inside the handler (referencing componentChecklist.execute and
ComponentChecklistResult) and leave the logic and error handling unchanged.
packages/riviere-cli/src/features/query/entrypoint/entry-points/entry-points.spec.ts (1)

155-163: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a test for error output without --json.

The graph-load failure path in entrypoint.ts outputs unconditionally (not gated on --json), but the only error test (line 159) passes --json. This means the test passes whether or not the error output is gated on --json — a future accidental regression where --json gating is applied to the error path would go undetected.

✅ Suggested additional test
  it('returns GRAPH_NOT_FOUND when no graph exists', async () => {
    await createProgram().parseAsync(['node', 'riviere', 'query', 'entry-points', '--json'])
    expect(ctx.consoleOutput.join('\n')).toContain(CliErrorCode.GraphNotFound)
  })
+
+  it('returns GRAPH_NOT_FOUND without --json flag (errors are always surfaced)', async () => {
+    await createProgram().parseAsync(['node', 'riviere', 'query', 'entry-points'])
+    expect(ctx.consoleOutput.join('\n')).toContain(CliErrorCode.GraphNotFound)
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/query/entrypoint/entry-points/entry-points.spec.ts`
around lines 155 - 163, Add a second test that verifies the non-JSON error path:
duplicate the existing spec that calls createProgram().parseAsync(['node',
'riviere', 'query', 'entry-points']) without the '--json' flag and assert that
ctx.consoleOutput.join('\n') contains CliErrorCode.GraphNotFound; this ensures
the graph-load failure message printed by the entry-points command (see
entry-points.spec.ts using createProgram, parseAsync, ctx.consoleOutput, and
CliErrorCode.GraphNotFound) is covered for the non-JSON output case.
packages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.ts (2)

171-183: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Error output without --json is untested.

The entrypoint outputs graph-load failures unconditionally (no --json check), but both error tests pass --json. If error output were accidentally gated on --json in the future, these tests would still pass.

Add a companion test without --json to lock in the unconditional-output contract, mirroring the same gap in entry-points.spec.ts.

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

In `@packages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.ts`
around lines 171 - 183, Add a companion non-JSON test in trace.spec.ts that
asserts the same unconditional error output when no graph exists: duplicate the
existing "returns GRAPH_NOT_FOUND when no graph exists" test but remove the
'--json' flag, call createProgram().parseAsync([...
'query','trace','any:mod:type:component']), capture console output via
parseOutput(ctx.consoleOutput) and assert via expectErrorOutput(...) that
output.error.code equals CliErrorCode.GraphNotFound (mirroring the gap fixed in
entry-points.spec.ts) to lock in the unconditional error-emission contract.

217-238: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Prefer vi.spyOn over direct prototype mutation.

Directly assigning to queryClass.prototype.traceFlow bypasses TypeScript's type-checking on the replacement function and requires manual try/finally cleanup. vi.spyOn is type-safe, self-documenting, and lets vi.restoreAllMocks() handle teardown automatically — which setupCommandTest likely already calls in afterEach.

♻️ Proposed refactor
-      const originalTraceFlow = queryClass.prototype.traceFlow
-
-      queryClass.prototype.traceFlow = () => {
-        throw new TestAssertionError('Unexpected internal error')
-      }
-
-      try {
-        await expect(
-          createProgram().parseAsync([
-            'node',
-            'riviere',
-            'query',
-            'trace',
-            'orders:checkout:api:place-order',
-            '--json',
-          ]),
-        ).rejects.toThrow('Unexpected internal error')
-      } finally {
-        queryClass.prototype.traceFlow = originalTraceFlow
-      }
+      vi.spyOn(queryClass.prototype, 'traceFlow').mockImplementation(() => {
+        throw new TestAssertionError('Unexpected internal error')
+      })
+
+      await expect(
+        createProgram().parseAsync([
+          'node',
+          'riviere',
+          'query',
+          'trace',
+          'orders:checkout:api:place-order',
+          '--json',
+        ]),
+      ).rejects.toThrow('Unexpected internal error')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.ts`
around lines 217 - 238, Replace the direct prototype mutation of
queryClass.prototype.traceFlow with a vi.spyOn on the RiviereQuery class method:
import the module as queryModule, get queryModule.RiviereQuery, and call
vi.spyOn(queryClass.prototype, 'traceFlow').mockImplementation(() => { throw new
TestAssertionError('Unexpected internal error') }); remove the manual
try/finally that restores originalTraceFlow and rely on the test harness (e.g.
setupCommandTest/vi.restoreAllMocks) to teardown mocks; keep the
createProgram().parseAsync(...) expect(...).rejects.toThrow assertion unchanged.
packages/riviere-cli/src/platform/infra/cli/presentation/output.ts (1)

3-18: ⚠️ Potential issue | 🔴 Critical

Add missing export keywords and @riviere-role annotations to SuccessOutput and ErrorOutput interfaces.

output-writer.ts line 3 imports SuccessOutput (import { type SuccessOutput }), but the interfaces in output.ts are not exported. This is a TypeScript compilation error. Both interfaces must be exported and must include the required @riviere-role annotation per the coding guidelines.

Proposed fix
+/** `@riviere-role` cli-response-formatter */
+export interface SuccessOutput<T> {
   success: true
   data: T
   warnings: string[]
 }
 
+/** `@riviere-role` cli-response-formatter */
+export interface ErrorOutput {
   success: false
   error: {
     code: string
     message: string
     suggestions: string[]
   }
 }
🤖 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.ts` around
lines 3 - 18, The interfaces SuccessOutput and ErrorOutput are not exported and
lack the required `@riviere-role` annotation; update the declarations for
SuccessOutput<T> and ErrorOutput to include the export keyword and add the
`@riviere-role` annotation above each interface declaration so they are publicly
available and comply with coding guidelines (locate the interface declarations
SuccessOutput and ErrorOutput in this file and add the export modifier and the
annotation).
packages/riviere-cli/src/features/query/entrypoint/components/components.errors.spec.ts (1)

67-82: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

console.error spy not restored on assertion failure.

If either assertion at lines 80–81 fails, errorSpy.mockRestore() is skipped and the console.error mock leaks into subsequent tests, potentially suppressing real errors or causing false results. Explicitly scope teardown outside the assertion path using a beforeEach/afterEach pair or check whether restoreMocks: true is set in the Vitest config.

🛡️ Proposed fix using a scoped afterEach
-  it('outputs error to stderr when --type is invalid and --json not provided', async () => {
-    await createGraph(ctx.testDir, {
+  describe('outputs error to stderr when --type is invalid and --json not provided', () => {
+    const errorOutput: string[] = []
+    let errorSpy: ReturnType<typeof vi.spyOn>
+
+    beforeEach(() => {
+      errorSpy = vi
+        .spyOn(console, 'error')
+        .mockImplementation((msg: string) => errorOutput.push(msg))
+    })
+
+    afterEach(() => {
+      errorSpy.mockRestore()
+      errorOutput.length = 0
+    })
+
+    it('outputs error message', async () => {
+      await createGraph(ctx.testDir, {
         // ... graph setup unchanged ...
-    })
-
-    const errorOutput: string[] = []
-    const errorSpy = vi
-      .spyOn(console, 'error')
-      .mockImplementation((msg: string) => errorOutput.push(msg))
-
-    await createProgram().parseAsync([
-      // ...
-    ])
-
-    expect(errorOutput).toHaveLength(1)
-    expect(errorOutput[0]).toContain('Invalid component type: InvalidType')
-    errorSpy.mockRestore()
-  })
+      })
+
+      await createProgram().parseAsync([
+        // ...
+      ])
+
+      expect(errorOutput).toHaveLength(1)
+      expect(errorOutput[0]).toContain('Invalid component type: InvalidType')
+    })
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/riviere-cli/src/features/query/entrypoint/components/components.errors.spec.ts`
around lines 67 - 82, The test creates a console.error spy (errorSpy) but calls
errorSpy.mockRestore() only after assertions, so if an expectation throws the
mock is not restored and leaks; fix by restoring the spy in a teardown path
(e.g., move the mocking into beforeEach and call errorSpy.mockRestore() in
afterEach, or wrap the parse/assert block in a try/finally and restore errorSpy
in finally) — update the test that calls createProgram().parseAsync([...]) and
the errorSpy setup/teardown so console.error is always restored regardless of
test failures.
.riviere/role-definitions/cli-output-formatter.md (1)

15-22: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider updating the canonical example to reflect delegation guidance.

The behavioral contract (lines 9-10) states that output formatters should delegate writing to cli-response-writer, but the canonical example directly calls console.log. This inconsistency may confuse implementers about whether direct output is acceptable within this role.

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

In @.riviere/role-definitions/cli-output-formatter.md around lines 15 - 22, The
canonical example violates the role contract by calling console.log directly;
update the presentExtractionResult example to delegate all output to the
cli-response-writer interface instead of printing directly (i.e., replace
console.log usage with a call to the CLI response writer provided by the
cli-response-writer role so both the formatted table
(formatTable(result.components)) and the extraction outcome are written via that
writer). Ensure the example references presentExtractionResult and formatTable
and shows using the cli-response-writer API to emit both lines.
🤖 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/features/builder/entrypoint/init/init.spec.ts`:
- Around line 10-14: Consolidate the duplicate imports from
'../../../../platform/__fixtures__/command-test-fixtures' by replacing the
separate `import type { TestContext }` and the value import for
`createTestContext, setupCommandTest` with a single import that declares
`TestContext` as an inline type (e.g., import { createTestContext,
setupCommandTest } and include TestContext as a type in the same statement), so
only one import line references that module and satisfies
`import/no-duplicates`.

In `@packages/riviere-cli/src/features/query/entrypoint/components/entrypoint.ts`:
- Around line 52-67: The current entrypoint is forcing JSON-only output: when
listComponents.execute returns an error it directly prints JSON via
formatQueryGraphLoadFailure and on success it only prints when options.json is
true; instead route both success and failure through the CLI response
writer/human formatter. Modify the code around listComponents.execute (the
result handling block that uses formatQueryGraphLoadFailure, formatSuccess,
toComponentOutput and options.json) so that: 1) errors detected by 'kind' in
result are converted into the platform's generic error envelope and passed to
the CLI response writer/human formatter (not raw JSON); 2) successful results
(components = result.components.map(toComponentOutput)) are wrapped with
formatSuccess and sent to the same response writer regardless of options.json,
letting the writer decide JSON vs human formatting; and 3) remove direct
console.log(JSON.stringify(...)) usage here so all output side effects are owned
by the CLI response writer.

In `@packages/riviere-cli/src/features/query/entrypoint/domains/entrypoint.ts`:
- Around line 13-38: The new CLI command created by createDomainsCommand
(ListDomains) lacks a corresponding workflow prompt under the docs/workflow
directory; add a new workflow prompt file for the "domains" command that follows
the existing prompt file pattern, naming the command "domains", including the
short description "List domains with component counts", the examples shown in
the command help ($ riviere query domains, $ riviere query domains --json), and
document the options (--graph <path> and --json) plus expected successful JSON
output shape (formatSuccess) and the graph load failure case
(formatQueryGraphLoadFailure) so the docs cover normal and error flows. Ensure
the new prompt file mirrors the structure and metadata used by other CLI command
prompts so it is discovered by the workflow tooling.

In `@packages/riviere-cli/src/features/query/entrypoint/orphans/entrypoint.ts`:
- Around line 13-38: The PR adds the createOrphansCommand CLI but forgot
documentation in docs/workflow/; add a new workflow doc for the "orphans"
command describing its purpose, usage examples (match those in
createOrphansCommand help text), options (--graph, --json), expected outputs
(JSON success via formatSuccess and error via formatQueryGraphLoadFailure), and
any preconditions for detectOrphans.execute (graphPathOption). Name the file to
clearly reference "orphans" and ensure the examples and option descriptions
mirror the text in createOrphansCommand and OrphansOptions so docs stay in sync.
- Around line 29-32: The current code in entrypoint.ts logs graph-load failures
with formatQueryGraphLoadFailure and then returns, causing a zero exit; change
the error path so that when ('kind' in result) you call the CLI error writer
(exitWithCliError) with the formatted failure (or use
outputResult/cli-response-writer API if available) instead of just returning;
update the same pattern in domains/entrypoint.ts and any other query entrypoints
introduced in this PR to ensure graph-load failures propagate a non-zero exit
via exitWithCliError (or the appropriate outputResult wrapper).

In `@packages/riviere-cli/src/features/query/entrypoint/search/entrypoint.ts`:
- Around line 39-44: The search entrypoint currently only prints JSON when
options.json is true and is silent otherwise; update the handler that maps
result.components via toComponentOutput and uses formatSuccess so that when
options.json is false it emits a human-readable representation to stdout (e.g.,
via console.log) while preserving the existing JSON path; specifically, in the
block using result.components, check options.json and
JSON.stringify(formatSuccess({ components })) for true, else call a new or
existing pretty-print function (e.g., formatHumanReadable or reuse formatSuccess
but rendered as text) to render components in a readable form for the user;
ensure the change is applied in the same scope where toComponentOutput,
formatSuccess, and options.json are referenced so other query entrypoints can be
updated similarly.

In
`@packages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.ts`:
- Around line 68-88: Add a test in the loadQueryGraph suite that exercises the
repository throwing GraphCorruptedError: call loadQueryGraph with new
FailingRepository(new GraphCorruptedError('graph.json')) (same pattern as the
existing GraphNotFoundError test) and assert it returns the corresponding
failure object (kind 'graphCorrupted' with an appropriate message), referencing
loadQueryGraph, GraphCorruptedError, and FailingRepository so the
GraphCorruptedError branch is covered.

In `@packages/riviere-cli/src/features/query/queries/query-graph-load-failure.ts`:
- Around line 7-22: The QueryGraphLoadFailure type is missing the
'graphNotFound' variant that toQueryGraphLoadFailure returns and
LoadedQueryGraph redundantly repeats that variant; update QueryGraphLoadFailure
to be a union covering both { kind: 'graphCorrupted'; message: string } and {
kind: 'graphNotFound'; message?: string } (or include a message field if you
prefer), then simplify LoadedQueryGraph to reuse QueryGraphLoadFailure instead
of duplicating the graphNotFound branch; adjust any usages of
toQueryGraphLoadFailure, QueryGraphLoadFailure, and LoadedQueryGraph accordingly
(refer to the type names QueryGraphLoadFailure, LoadedQueryGraph and the
function toQueryGraphLoadFailure and the GraphNotFoundError case).

In
`@packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.ts`:
- Around line 4-7: The interface QueryGraphLoadFailureOutput is not exported but
is used as the parameter type of a public API; change its declaration to export
interface QueryGraphLoadFailureOutput { ... } so callers can import and type
values explicitly, and if this file is consumed via a barrel index, also ensure
the module re-exports QueryGraphLoadFailureOutput from that barrel so consumers
can import it from the same public entrypoint.

In
`@tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-test-fixtures.ts`:
- Around line 15-22: The import of Workflow is only used as a type for the alias
WorkflowDeps (type WorkflowDeps = Parameters<typeof Workflow.rehydrate>[1]) so
change the value import to a type-only import (use "import type { Workflow }
from '../../../domain/workflow'") so the module is not treated as a runtime
dependency; update the import line that currently reads "import { Workflow }
..." to "import type { Workflow } ..." while leaving references to
Workflow.rehydrate and the WorkflowDeps type alias unchanged.

---

Outside diff comments:
In @.riviere/role-definitions/cli-output-formatter.md:
- Around line 15-22: The canonical example violates the role contract by calling
console.log directly; update the presentExtractionResult example to delegate all
output to the cli-response-writer interface instead of printing directly (i.e.,
replace console.log usage with a call to the CLI response writer provided by the
cli-response-writer role so both the formatted table
(formatTable(result.components)) and the extraction outcome are written via that
writer). Ensure the example references presentExtractionResult and formatTable
and shows using the cli-response-writer API to emit both lines.

In
`@packages/riviere-cli/src/features/builder/entrypoint/component-checklist/entrypoint.ts`:
- Around line 32-50: Remove the unnecessary async on the action handler since
ComponentChecklist.execute is synchronous: change the handler signature from
.action(async (options: ComponentChecklistOptions) => { ... }) to
.action((options: ComponentChecklistOptions) => { ... }); ensure there are no
await expressions inside the handler (referencing componentChecklist.execute and
ComponentChecklistResult) and leave the logic and error handling unchanged.

In
`@packages/riviere-cli/src/features/builder/entrypoint/validate/validate.spec.ts`:
- Around line 256-289: Add a test in validate.spec.ts under the "error handling"
describe that exercises the entrypoint.ts branch mapping non-success results to
CliErrorCode.GraphCorrupted: use createGraph or directly write a malformed graph
JSON file into ctx.testDir (e.g., invalid JSON or missing required fields), then
run createProgram().parseAsync(['node','riviere','builder','validate','--graph',
malformedPath]) and assert that ctx.consoleOutput contains
CliErrorCode.GraphCorrupted; reference the existing tests and utilities
(createGraph, createProgram, parseOutput, CliErrorCode) to mirror style and
ensure the corrupted-graph branch is executed.

In
`@packages/riviere-cli/src/features/extract/entrypoint/extract/extract.pr-extraction.spec.ts`:
- Around line 311-331: The test currently sets up errorSpy (vi.spyOn(console,
'error')) then awaits parseCommandWithErrorHandling but calls
errorSpy.mockRestore() unguarded so the spy can leak if the await rejects; wrap
the invocation of parseCommandWithErrorHandling and the subsequent mockRestore
in a try/finally block so that errorSpy.mockRestore() always runs, referencing
the existing errorSpy and parseCommandWithErrorHandling identifiers to locate
where to apply the try/finally.

In
`@packages/riviere-cli/src/features/query/entrypoint/components/components.errors.spec.ts`:
- Around line 67-82: The test creates a console.error spy (errorSpy) but calls
errorSpy.mockRestore() only after assertions, so if an expectation throws the
mock is not restored and leaks; fix by restoring the spy in a teardown path
(e.g., move the mocking into beforeEach and call errorSpy.mockRestore() in
afterEach, or wrap the parse/assert block in a try/finally and restore errorSpy
in finally) — update the test that calls createProgram().parseAsync([...]) and
the errorSpy setup/teardown so console.error is always restored regardless of
test failures.

In
`@packages/riviere-cli/src/features/query/entrypoint/entry-points/entry-points.spec.ts`:
- Around line 155-163: Add a second test that verifies the non-JSON error path:
duplicate the existing spec that calls createProgram().parseAsync(['node',
'riviere', 'query', 'entry-points']) without the '--json' flag and assert that
ctx.consoleOutput.join('\n') contains CliErrorCode.GraphNotFound; this ensures
the graph-load failure message printed by the entry-points command (see
entry-points.spec.ts using createProgram, parseAsync, ctx.consoleOutput, and
CliErrorCode.GraphNotFound) is covered for the non-JSON output case.

In `@packages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.ts`:
- Around line 171-183: Add a companion non-JSON test in trace.spec.ts that
asserts the same unconditional error output when no graph exists: duplicate the
existing "returns GRAPH_NOT_FOUND when no graph exists" test but remove the
'--json' flag, call createProgram().parseAsync([...
'query','trace','any:mod:type:component']), capture console output via
parseOutput(ctx.consoleOutput) and assert via expectErrorOutput(...) that
output.error.code equals CliErrorCode.GraphNotFound (mirroring the gap fixed in
entry-points.spec.ts) to lock in the unconditional error-emission contract.
- Around line 217-238: Replace the direct prototype mutation of
queryClass.prototype.traceFlow with a vi.spyOn on the RiviereQuery class method:
import the module as queryModule, get queryModule.RiviereQuery, and call
vi.spyOn(queryClass.prototype, 'traceFlow').mockImplementation(() => { throw new
TestAssertionError('Unexpected internal error') }); remove the manual
try/finally that restores originalTraceFlow and rely on the test harness (e.g.
setupCommandTest/vi.restoreAllMocks) to teardown mocks; keep the
createProgram().parseAsync(...) expect(...).rejects.toThrow assertion unchanged.

In `@packages/riviere-cli/src/platform/infra/cli/presentation/output.ts`:
- Around line 3-18: The interfaces SuccessOutput and ErrorOutput are not
exported and lack the required `@riviere-role` annotation; update the declarations
for SuccessOutput<T> and ErrorOutput to include the export keyword and add the
`@riviere-role` annotation above each interface declaration so they are publicly
available and comply with coding guidelines (locate the interface declarations
SuccessOutput and ErrorOutput in this file and add the export modifier and the
annotation).
🪄 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: 0adb9de8-30d5-4835-bb67-1b0b8541abf0

📥 Commits

Reviewing files that changed from the base of the PR and between 9050ddd and 978420f.

📒 Files selected for processing (108)
  • .dependency-cruiser.frontend.mjs
  • .dependency-cruiser.mjs
  • .riviere/role-definitions/cli-error-handler.md
  • .riviere/role-definitions/cli-output-formatter.md
  • .riviere/role-definitions/cli-response-formatter.md
  • .riviere/role-definitions/cli-response-writer.md
  • .riviere/role-enforcement.config.ts
  • .riviere/roles.ts
  • docs/architecture/adr/ADR-002-allowed-folder-structures.md
  • eslint.config.mjs
  • packages/riviere-cli/src/features/builder/entrypoint/add-component/add-component-custom.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-component/add-component.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-component/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-domain/add-domain.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-domain/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-source/add-source.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-source/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/add-source/graph-error-coverage.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/check-consistency/check-consistency.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/check-consistency/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-checklist/component-checklist.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-checklist/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-summary/component-summary.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/component-summary/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/define-custom-type/define-custom-type.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/define-custom-type/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/enrich/enrich.signature.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/enrich/enrich.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/enrich/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/finalize/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/finalize/finalize.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/init/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/init/init-options.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/init/init.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-external/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-external/link-external.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-http/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link-http/link-http.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/link/link.spec.ts
  • packages/riviere-cli/src/features/builder/entrypoint/validate/entrypoint.ts
  • packages/riviere-cli/src/features/builder/entrypoint/validate/validate.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/create-command-inputs.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/create-enrich-draft-components-input.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/create-extract-draft-components-input.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/entrypoint.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/extract.connections.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/extract.dry-run.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/extract.enrichment.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/extract.pr-extraction.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/extract.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/present-extraction-result.spec.ts
  • packages/riviere-cli/src/features/extract/entrypoint/extract/present-extraction-result.ts
  • packages/riviere-cli/src/features/query/entrypoint/components/components.errors.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/components/components.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/components/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/components/query-entrypoint-error-rethrow.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/domains.ts
  • packages/riviere-cli/src/features/query/entrypoint/domains/domains.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/domains/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/entry-points.ts
  • packages/riviere-cli/src/features/query/entrypoint/entry-points/entry-points.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/entry-points/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/orphans.ts
  • packages/riviere-cli/src/features/query/entrypoint/orphans/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/orphans/orphans.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/search.ts
  • packages/riviere-cli/src/features/query/entrypoint/search/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/search/search.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/trace.ts
  • packages/riviere-cli/src/features/query/entrypoint/trace/entrypoint.ts
  • packages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.ts
  • packages/riviere-cli/src/features/query/queries/detect-orphans-result.ts
  • packages/riviere-cli/src/features/query/queries/detect-orphans.ts
  • packages/riviere-cli/src/features/query/queries/list-components-result.ts
  • packages/riviere-cli/src/features/query/queries/list-components.ts
  • packages/riviere-cli/src/features/query/queries/list-domains-result.ts
  • packages/riviere-cli/src/features/query/queries/list-domains.ts
  • packages/riviere-cli/src/features/query/queries/list-entry-points-result.ts
  • packages/riviere-cli/src/features/query/queries/list-entry-points.ts
  • packages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.ts
  • packages/riviere-cli/src/features/query/queries/query-graph-load-failure.ts
  • packages/riviere-cli/src/features/query/queries/search-components-result.ts
  • packages/riviere-cli/src/features/query/queries/search-components.ts
  • packages/riviere-cli/src/features/query/queries/trace-flow-result.ts
  • packages/riviere-cli/src/features/query/queries/trace-flow.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/exit-with-cli-error.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output-writer.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/output.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.spec.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.ts
  • packages/riviere-cli/src/shell/cli.ts
  • packages/riviere-role-enforcement/src/features/enforcement/entrypoint/cli/entrypoint.ts
  • packages/riviere-role-enforcement/src/shell/bin.ts
  • tools/dev-workflow-v2/.claude-plugin/plugin.json
  • tools/dev-workflow-v2/src/features/workflow/commands/workflow-cli-definition.ts
  • tools/dev-workflow-v2/src/features/workflow/domain/fixtures/review-command-test-fixtures.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/entrypoint.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-state-steps-test-fixtures.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-test-fixtures.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli-hooks.spec.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli-transitions.spec.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli.spec.ts
  • tools/dev-workflow-v2/src/shell/cli.ts
  • tools/dev-workflow-v2/src/shell/opencode-plugin.ts
💤 Files with no reviewable changes (8)
  • packages/riviere-cli/src/features/query/entrypoint/trace.ts
  • packages/riviere-cli/src/features/query/entrypoint/search.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.ts
  • tools/dev-workflow-v2/src/features/workflow/commands/workflow-cli-definition.ts
  • packages/riviere-cli/src/features/query/entrypoint/orphans.ts
  • packages/riviere-cli/src/features/query/entrypoint/entry-points.ts
  • packages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.ts
  • packages/riviere-cli/src/features/query/entrypoint/domains.ts

Comment thread packages/riviere-cli/src/features/builder/entrypoint/init/init.spec.ts Outdated
Comment thread packages/riviere-cli/src/features/query/queries/query-graph-load-failure.ts Outdated
@NTCoding NTCoding changed the title Enforce per-entrypoint CLI layering fix(riviere-cli): enforce entrypoint layering May 4, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 4, 2026 •

Copy link
Copy Markdown

Deploying living-architecture with  Cloudflare Pages  Cloudflare Pages

Latest commit: 32f8913
Status: ✅  Deploy successful!
Preview URL: https://c36fe664.living-architecture.pages.dev
Branch Preview URL: https://fix-entrypoint-layering.living-architecture.pages.dev

View logs

@sonarqubecloud

sonarqubecloud Bot commented May 4, 2026

Copy link
Copy Markdown

@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: 1

🤖 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/features/query/queries/query-graph-load-failure.ts`:
- Around line 34-38: The GraphCorruptedError branch in buildResponse (when
checking instanceof GraphCorruptedError) currently returns a generic message;
change it to include the error.graphPath (similar to GraphNotFoundError using
error.message) so the returned object contains the path (e.g., "Graph file
contains invalid JSON at {graphPath}" or include the path in the message field),
by updating the branch handling GraphCorruptedError in
query-graph-load-failure.ts (reference GraphCorruptedError and
GraphNotFoundError) and update the corresponding assertion in
query-graph-load-failure.spec.ts to expect the path-aware message.
🪄 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: f046da4e-5a85-4214-b8a1-00c716569470

📥 Commits

Reviewing files that changed from the base of the PR and between 978420f and 32f8913.

📒 Files selected for processing (4)
  • packages/riviere-cli/src/features/builder/entrypoint/init/init.spec.ts
  • packages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.ts
  • packages/riviere-cli/src/features/query/queries/query-graph-load-failure.ts
  • tools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-test-fixtures.ts

@NTCoding
NTCoding merged commit 2b16ba8 into main May 4, 2026
17 checks passed
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.

Enforce per-entrypoint folders and forbid entrypoint domain imports

1 participant