fix: crystallize exits 0 on total approval failure (#56)#62
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe CLI crystallize command now inspects the JSON result to compute approved/failed counts, prints an error and exits 1 if all proposals failed, or prints a warning if some failed. Tests verify failure collection and both CLI exit/warning behaviors under mocked approval failures. ChangesFailure Visibility and Exit Code Handling
Sequence DiagramsequenceDiagram
participant CLI
participant sessions_crystallize as sessions.crystallize
participant approve as vouch.sessions.approve
participant Storage
CLI->>sessions_crystallize: call crystallize(session_id)
sessions_crystallize->>approve: attempt approve(proposal)
approve->>Storage: persist proposal
Storage->>approve: error/exception (when failing)
approve-->>sessions_crystallize: raise Exception or return success
sessions_crystallize-->>CLI: {"approved": [...], "failures": [...]}
CLI->>CLI: compute approved/failed counts
CLI->>CLI: print JSON to stdout
CLI->>CLI: if all failed -> print stderr error and SystemExit(1)
CLI->>CLI: if partial failures -> print stderr warning
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_sessions.py (1)
49-63: ⚡ Quick winAdd CLI-level coverage for the new failure signaling contract.
This test verifies
sess_mod.crystallizefailure collection, but the new behavior insrc/vouch/cli.py(stderr warning/error + exit code semantics) is still untested. Please add aCliRunnertest for: (1) all approvals fail → exit code1+error:on stderr, and (2) partial failures → exit code0+warning:on stderr.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sessions.py` around lines 49 - 63, Add a CLI-level test using Click's CliRunner to exercise the crystallize command (invoke the CLI entrypoint in vouch.cli, e.g. the cli/main function) and assert stderr/exit semantics: create a session and two proposed claims as in test_crystallize_collects_approval_failures, then (1) patch vouch.sessions.approve to always raise ValueError and call the CLI crystallize command, assert exit code == 1 and stderr contains "error:", and (2) patch approve to raise for one approval but succeed for the other, call the CLI, assert exit code == 0 and stderr contains "warning:". Ensure the test references sess_mod.crystallize/session_start/session_end setup and uses CliRunner.invoke against the same session id and approver arguments so behavior matches the existing unit test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/test_sessions.py`:
- Around line 49-63: Add a CLI-level test using Click's CliRunner to exercise
the crystallize command (invoke the CLI entrypoint in vouch.cli, e.g. the
cli/main function) and assert stderr/exit semantics: create a session and two
proposed claims as in test_crystallize_collects_approval_failures, then (1)
patch vouch.sessions.approve to always raise ValueError and call the CLI
crystallize command, assert exit code == 1 and stderr contains "error:", and (2)
patch approve to raise for one approval but succeed for the other, call the CLI,
assert exit code == 0 and stderr contains "warning:". Ensure the test references
sess_mod.crystallize/session_start/session_end setup and uses CliRunner.invoke
against the same session id and approver arguments so behavior matches the
existing unit test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8b1b5cdd-3e5f-4770-bdb4-fc2afd760b87
📒 Files selected for processing (2)
src/vouch/cli.pytests/test_sessions.py
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
tests/test_cli.py (2)
136-141: ⚡ Quick winConsider using context manager for patch lifecycle.
The manual
start()/stop()pattern forembedder_patchis verbose and risks leaving the patch active if an exception occurs between start and stop (though thefinallyblock mitigates this here). Using the patch as a context manager would be more idiomatic and safer:with patch("vouch.embeddings.get_embedder", return_value=_MockEmbedder()): src = store.put_source(b"e")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_cli.py` around lines 136 - 141, Replace the manual patch.start()/patch.stop() pattern with a context manager to ensure the patch lifecycle is safe and concise: use patch("vouch.embeddings.get_embedder", return_value=_MockEmbedder()) as a context manager around the call to store.put_source so the mock embedder (_MockEmbedder) is applied only for the scope where src = store.put_source(b"e") is executed, avoiding explicit embedder_patch.start()/embedder_patch.stop().
158-163: ⚡ Quick winConsider using context manager for patch lifecycle.
Same as the previous test: the manual
start()/stop()pattern forembedder_patchwould be clearer as a context manager:with patch("vouch.embeddings.get_embedder", return_value=_MockEmbedder()): src = store.put_source(b"e")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_cli.py` around lines 158 - 163, Replace the manual patch.start()/patch.stop() pattern with a context manager: use patch("vouch.embeddings.get_embedder", return_value=_MockEmbedder()) as a context manager around the call to store.put_source so the embedder mock is automatically applied and torn down; update the block that currently creates embedder_patch and calls embedder_patch.start()/embedder_patch.stop() and instead wrap the store.put_source(b"e") invocation inside the with-patch context to ensure proper lifecycle management.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/test_cli.py`:
- Line 31: The return expression in tests/test_cli.py (the line returning
np.zeros((len(texts), self.dim), dtype=np.float32) if texts else np.zeros((0,
self.dim), dtype=np.float32)) exceeds the 100-char limit; split it across
multiple lines by using parentheses and line breaks (e.g., assign the two
np.zeros calls to clearly wrapped lines or use a conditional expression on
separate lines) so the same logic in the function/method containing this line
remains intact but each line is under 100 characters.
---
Nitpick comments:
In `@tests/test_cli.py`:
- Around line 136-141: Replace the manual patch.start()/patch.stop() pattern
with a context manager to ensure the patch lifecycle is safe and concise: use
patch("vouch.embeddings.get_embedder", return_value=_MockEmbedder()) as a
context manager around the call to store.put_source so the mock embedder
(_MockEmbedder) is applied only for the scope where src = store.put_source(b"e")
is executed, avoiding explicit embedder_patch.start()/embedder_patch.stop().
- Around line 158-163: Replace the manual patch.start()/patch.stop() pattern
with a context manager: use patch("vouch.embeddings.get_embedder",
return_value=_MockEmbedder()) as a context manager around the call to
store.put_source so the embedder mock is automatically applied and torn down;
update the block that currently creates embedder_patch and calls
embedder_patch.start()/embedder_patch.stop() and instead wrap the
store.put_source(b"e") invocation inside the with-patch context to ensure proper
lifecycle management.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/test_cli.py (1)
26-33:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMake NumPy optional for test collection (tests/test_cli.py + embeddings tests)
tests/test_cli.pystill has hardimport numpy as npstatements (including around lines 28 and 31), so pytest collection will fail if NumPy isn’t installed.- Other test modules (
tests/embeddings/test_core.py,tests/embeddings/test_dedup.py,tests/embeddings/test_integration.py,tests/embeddings/_fakes.py) also import NumPy at module import time, whilenumpyis only declared via optionalembeddings/embeddings-fastextras—guard these imports or usepytest.importorskip/conditional skipping so the suite can collect without NumPy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_cli.py` around lines 26 - 33, The tests import NumPy at module scope causing collection to fail when NumPy is not installed; modify the test modules (e.g., tests/test_cli.py and tests/embeddings/* and _fakes.py) to guard NumPy usage by either calling pytest.importorskip("numpy") at the top of tests that require it or by deferring the import inside helper classes/functions (e.g., inside _MockEmbedder.encode and _MockEmbedder.encode_batch) using a local try/except ImportError that raises pytest.skip or falls back to a pure-Python/empty-array alternative; update references to np.zeros accordingly and keep the public API of _MockEmbedder (methods encode and encode_batch) unchanged so test logic continues to work when NumPy is available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tests/test_cli.py`:
- Around line 26-33: The tests import NumPy at module scope causing collection
to fail when NumPy is not installed; modify the test modules (e.g.,
tests/test_cli.py and tests/embeddings/* and _fakes.py) to guard NumPy usage by
either calling pytest.importorskip("numpy") at the top of tests that require it
or by deferring the import inside helper classes/functions (e.g., inside
_MockEmbedder.encode and _MockEmbedder.encode_batch) using a local try/except
ImportError that raises pytest.skip or falls back to a pure-Python/empty-array
alternative; update references to np.zeros accordingly and keep the public API
of _MockEmbedder (methods encode and encode_batch) unchanged so test logic
continues to work when NumPy is available.
Leftover from the merge of main (which brought PR vouchdev#62's test_sessions.py changes); ruff F401 was failing CI on PR vouchdev#77.
Resolves conflicts in CHANGELOG.md, tests/test_sessions.py, and auto-merges src/vouch/sessions.py with the recently-merged: - vouchdev#61 (FTS5 indexing of crystallize summary page) — adds index_db.index_page(...) right after store.put_page(...) inside crystallize. Composes cleanly with this PR's strict-derivation _build_summary_body and the summary_page_id audit fix. - vouchdev#62 (test_crystallize_collects_approval_failures) — preserved as a sibling test. - vouchdev#75 (bundle import sha256 verification) — CHANGELOG entry only. - vouchdev#73 (bundle POSIX separators) — CHANGELOG entry only. Both regression tests added by this PR (test_crystallize_summary_page_does_not_leak_agent_controlled_fields and test_crystallize_audit_event_records_summary_page_id) still pass against the merged sessions.crystallize.
Fixes #56
Summary
crystallize()silently swallows all approval failures and exits 0, giving no indication that the operation completely failed.Changes
test_crystallize_collects_approval_failuresSummary by CodeRabbit