Make invalid states unrepresentable: ScopedPath, MemoryConfig, TruncationReason, NonZero caps - #9
Merged
Merged
Conversation
…tionReason, NonZero caps/rates - Scope::check now returns ScopedPath, a newtype constructible only by scope validation; grep/find/cat require it, so skipping the check is a compile error rather than a security hole - MemoryConfig enum replaces (Option<PathBuf>, Option<String>): the impossible 'instructions without a memory dir' state is no longer constructible - TruncationReason enum replaces freeform truncation-reason strings; wire contract (byte_cap/line_cap/max_results) unchanged - PeerLimiter::new validates capacity/refill (guards the Duration::from_secs_f64 panic on non-finite input) and per_minute takes NonZeroU32; --initialize-rate-per-min is NonZeroU32 - max_results/max_bytes/max_lines are NonZeroUsize through args -> options -> tools, so zero caps are rejected at the JSON boundary 44 tests pass; clippy and fmt clean; wire smoke-tested via streamable HTTP (initialize, grep, cat truncation reason, zero-cap and out-of-scope rejection)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements findings 1–4 from the type-safety review: move runtime validation into the compile-time type system so whole classes of bugs become compile errors.
1.
ScopedPathnewtype (scope enforcement as a type)Scope::checknow returnsScopedPath, constructible only by scope validation.grep/find/catrequire&ScopedPath(viaAsRef`), so a tool handler that forgets the scope check no longer compiles — previously it compiled fine and was a security hole.2.
MemoryConfigenumDisabled | Enabled { dir, extra }replacesCodeMcpServer's(Option<PathBuf>, Option<String>)pair. The impossible state "extra instructions without a memory dir" is no longer constructible; thememorieshandler andget_infomatch one source of truth.3.
TruncationReasonenumReplaces freeform truncation-reason strings (
"byte_cap","line_cap","max_results") scattered as literals acrosscat/grep. Snake_case serialization keeps the wire contract byte-identical.4. Validated rates and caps
PeerLimiter::newreturnsResult<_, InvalidRate>, rejecting zero/negative/NaN capacity/refill — the inputs that would panicDuration::from_secs_f64in release builds where the olddebug_assertis compiled out.per_minutetakesNonZeroU32;--initialize-rate-per-minisNonZeroU32.max_results/max_bytes/max_linesareNonZeroUsizeend to end (JSON args → options →drain_capped), so a zero cap is rejected at deserialization instead of producing empty "truncated" output.Verification
cargo test: 44 passed, 0 failed (43 prior + newInvalidRatecontract test)cargo clippy --all-targets: 0 warnings;cargo fmt --check: cleangrepcall,cat max_lines: 2→"truncation_reason": "line_cap"(enum serializes to the exact old string),max_results: 0→ deserialization error,--initialize-rate-per-min 0→ clap error,/etc/passwd→Out of project scopeNotes
scoped_in()(testutil), which routes tempdirs through a realScope— the same path production takes.load_memoryintentionally keeps&Path: the memory dir is server-configured, not client input under project scope, soScopedPathwould assert a false proof.grep_count,Option<StringOrVec>collapse) not included.