Repository navigation
fix(riviere-cli): enforce entrypoint layering - #372
Conversation
Move CLI entrypoints into per-entrypoint folders. Enforce domain and persistence import boundaries at the entrypoint layer.
📝 WalkthroughWalkthroughRestructures CLI entrypoints into per-entrypoint folders, adds three CLI roles ( ChangesRole & Entrypoint Enforcement
Query failure model, use-case refactor & presentation
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
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 theawaitrejects.If
parseCommandWithErrorHandlingrejects unexpectedly (e.g., a misconfiguration in CI),mockRestore()on line 328 is never reached and theconsole.errorspy 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 winMissing test case for the
GRAPH_CORRUPTEDerror path — violates mandatory 100% coverage.The
entrypoint.tselse-branch maps any!result.successcode that is not'GRAPH_NOT_FOUND'toCliErrorCode.GraphCorrupted. This branch is never exercised in this spec; only theGRAPH_NOT_FOUNDpath is covered. Given mandatory 100% coverage, add a case that writes a malformed graph file and assertsCliErrorCode.GraphCorruptedappears 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 | 🔵 TrivialRemove the
asynckeyword on line 32 —componentChecklist.execute()is synchronous and does not requireawait.The
ComponentChecklist.execute()method returnsComponentChecklistResultdirectly, not aPromise. The method is synchronous, so theasynckeyword on the action handler is unnecessary. The code works correctly as-is, but theasyncdeclaration 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 winAdd a test for error output without
--json.The graph-load failure path in
entrypoint.tsoutputs 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--jsongating 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 winError output without
--jsonis untested.The entrypoint outputs graph-load failures unconditionally (no
--jsoncheck), but both error tests pass--json. If error output were accidentally gated on--jsonin the future, these tests would still pass.Add a companion test without
--jsonto lock in the unconditional-output contract, mirroring the same gap inentry-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 winPrefer
vi.spyOnover direct prototype mutation.Directly assigning to
queryClass.prototype.traceFlowbypasses TypeScript's type-checking on the replacement function and requires manualtry/finallycleanup.vi.spyOnis type-safe, self-documenting, and letsvi.restoreAllMocks()handle teardown automatically — whichsetupCommandTestlikely already calls inafterEach.♻️ 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 | 🔴 CriticalAdd missing
exportkeywords and@riviere-roleannotations toSuccessOutputandErrorOutputinterfaces.
output-writer.tsline 3 importsSuccessOutput(import { type SuccessOutput }), but the interfaces inoutput.tsare not exported. This is a TypeScript compilation error. Both interfaces must be exported and must include the required@riviere-roleannotation 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.errorspy not restored on assertion failure.If either assertion at lines 80–81 fails,
errorSpy.mockRestore()is skipped and theconsole.errormock leaks into subsequent tests, potentially suppressing real errors or causing false results. Explicitly scope teardown outside the assertion path using abeforeEach/afterEachpair or check whetherrestoreMocks: trueis 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 valueConsider 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 callsconsole.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
📒 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.tsdocs/architecture/adr/ADR-002-allowed-folder-structures.mdeslint.config.mjspackages/riviere-cli/src/features/builder/entrypoint/add-component/add-component-custom.spec.tspackages/riviere-cli/src/features/builder/entrypoint/add-component/add-component.spec.tspackages/riviere-cli/src/features/builder/entrypoint/add-component/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/add-domain/add-domain.spec.tspackages/riviere-cli/src/features/builder/entrypoint/add-domain/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/add-source/add-source.spec.tspackages/riviere-cli/src/features/builder/entrypoint/add-source/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/add-source/graph-error-coverage.spec.tspackages/riviere-cli/src/features/builder/entrypoint/check-consistency/check-consistency.spec.tspackages/riviere-cli/src/features/builder/entrypoint/check-consistency/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/component-checklist/component-checklist.spec.tspackages/riviere-cli/src/features/builder/entrypoint/component-checklist/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/component-summary/component-summary.spec.tspackages/riviere-cli/src/features/builder/entrypoint/component-summary/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/define-custom-type/define-custom-type.spec.tspackages/riviere-cli/src/features/builder/entrypoint/define-custom-type/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/enrich/enrich.signature.spec.tspackages/riviere-cli/src/features/builder/entrypoint/enrich/enrich.spec.tspackages/riviere-cli/src/features/builder/entrypoint/enrich/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/finalize/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/finalize/finalize.spec.tspackages/riviere-cli/src/features/builder/entrypoint/init/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/init/init-options.spec.tspackages/riviere-cli/src/features/builder/entrypoint/init/init.spec.tspackages/riviere-cli/src/features/builder/entrypoint/link-external/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/link-external/link-external.spec.tspackages/riviere-cli/src/features/builder/entrypoint/link-http/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/link-http/link-http.spec.tspackages/riviere-cli/src/features/builder/entrypoint/link/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/link/link.spec.tspackages/riviere-cli/src/features/builder/entrypoint/validate/entrypoint.tspackages/riviere-cli/src/features/builder/entrypoint/validate/validate.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/create-command-inputs.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/create-enrich-draft-components-input.tspackages/riviere-cli/src/features/extract/entrypoint/extract/create-extract-draft-components-input.tspackages/riviere-cli/src/features/extract/entrypoint/extract/entrypoint.tspackages/riviere-cli/src/features/extract/entrypoint/extract/extract.connections.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/extract.dry-run.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/extract.enrichment.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/extract.pr-extraction.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/extract.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/present-extraction-result.spec.tspackages/riviere-cli/src/features/extract/entrypoint/extract/present-extraction-result.tspackages/riviere-cli/src/features/query/entrypoint/components/components.errors.spec.tspackages/riviere-cli/src/features/query/entrypoint/components/components.spec.tspackages/riviere-cli/src/features/query/entrypoint/components/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/components/query-entrypoint-error-rethrow.spec.tspackages/riviere-cli/src/features/query/entrypoint/domains.tspackages/riviere-cli/src/features/query/entrypoint/domains/domains.spec.tspackages/riviere-cli/src/features/query/entrypoint/domains/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/entry-points.tspackages/riviere-cli/src/features/query/entrypoint/entry-points/entry-points.spec.tspackages/riviere-cli/src/features/query/entrypoint/entry-points/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/orphans.tspackages/riviere-cli/src/features/query/entrypoint/orphans/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/orphans/orphans.spec.tspackages/riviere-cli/src/features/query/entrypoint/search.tspackages/riviere-cli/src/features/query/entrypoint/search/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/search/search.spec.tspackages/riviere-cli/src/features/query/entrypoint/trace.tspackages/riviere-cli/src/features/query/entrypoint/trace/entrypoint.tspackages/riviere-cli/src/features/query/entrypoint/trace/trace.spec.tspackages/riviere-cli/src/features/query/queries/detect-orphans-result.tspackages/riviere-cli/src/features/query/queries/detect-orphans.tspackages/riviere-cli/src/features/query/queries/list-components-result.tspackages/riviere-cli/src/features/query/queries/list-components.tspackages/riviere-cli/src/features/query/queries/list-domains-result.tspackages/riviere-cli/src/features/query/queries/list-domains.tspackages/riviere-cli/src/features/query/queries/list-entry-points-result.tspackages/riviere-cli/src/features/query/queries/list-entry-points.tspackages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.tspackages/riviere-cli/src/features/query/queries/query-graph-load-failure.tspackages/riviere-cli/src/features/query/queries/search-components-result.tspackages/riviere-cli/src/features/query/queries/search-components.tspackages/riviere-cli/src/features/query/queries/trace-flow-result.tspackages/riviere-cli/src/features/query/queries/trace-flow.tspackages/riviere-cli/src/platform/infra/cli/presentation/exit-with-cli-error.tspackages/riviere-cli/src/platform/infra/cli/presentation/global-error-handler.tspackages/riviere-cli/src/platform/infra/cli/presentation/output-writer.tspackages/riviere-cli/src/platform/infra/cli/presentation/output.tspackages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.spec.tspackages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-error-handler.tspackages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.spec.tspackages/riviere-cli/src/platform/infra/cli/presentation/query-graph-load-failure-output.tspackages/riviere-cli/src/shell/cli.tspackages/riviere-role-enforcement/src/features/enforcement/entrypoint/cli/entrypoint.tspackages/riviere-role-enforcement/src/shell/bin.tstools/dev-workflow-v2/.claude-plugin/plugin.jsontools/dev-workflow-v2/src/features/workflow/commands/workflow-cli-definition.tstools/dev-workflow-v2/src/features/workflow/domain/fixtures/review-command-test-fixtures.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/entrypoint.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-state-steps-test-fixtures.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-test-fixtures.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli-hooks.spec.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli-transitions.spec.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/workflow-cli.spec.tstools/dev-workflow-v2/src/shell/cli.tstools/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
Deploying living-architecture with
|
| 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 |
|
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
packages/riviere-cli/src/features/builder/entrypoint/init/init.spec.tspackages/riviere-cli/src/features/query/queries/query-graph-load-failure.spec.tspackages/riviere-cli/src/features/query/queries/query-graph-load-failure.tstools/dev-workflow-v2/src/features/workflow/entrypoint/workflow/fixtures/workflow-cli-test-fixtures.ts



Summary
entrypoint/{entrypoint}/entrypoint.tsfolders so openingentrypoint/shows available entrypoints.Verification
pnpm nx run @living-architecture/source:role-checkpnpm nx test riviere-clipnpm nx test dev-workflow-v2pnpm nx typecheck dev-workflow-v2pnpm exec nx affected -t lint test build typecheck check-generated-docs smoke-test --verboseCloses #371
Summary by CodeRabbit
Documentation
New Features
Bug Fixes & Refactors