Skip to content

Remove legacy-ts and apply Rust review fixes#18

Merged
willwashburn merged 2 commits into
mainfrom
claude/cleanup-rust-review-gx8iD
May 8, 2026
Merged

Remove legacy-ts and apply Rust review fixes#18
willwashburn merged 2 commits into
mainfrom
claude/cleanup-rust-review-gx8iD

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Summary

Drops the retired legacy-ts/ tree (the Rust crate has been the source of truth since the rewrite) and applies a handful of focused fixes from a review of the crate.

Cleanup

  • Delete legacy-ts/ (31 files, ~3000 lines) and its README references.

Code fixes

  • Compact JSON in tool results (mcp/server.rs). The text block — which is what the model actually reads — was being pretty-printed. For nested results, that roughly doubles whitespace tokens, which directly contradicts the project's stated goal. Single biggest token win in this PR. The structuredContent mirror is unchanged.
  • Atomic file writes in Edit (tools/edit.rs). std::fs::write truncates first; a crash mid-write left files corrupt. Now stages to a sibling .tmp and renames atomically (with sync_all before rename, cleanup on rename failure).
  • Search: cache the relativized path per file (was recomputed per snippet) and drop a redundant sort_unstable over already-sorted BTreeMap keys.
  • Drop duplicated meta_for_tool helper in tools/search.rs; Meta is already Clone.
  • Cache strip_inline_comments's block-comment regex in a OnceLock — was recompiled on every line of every signature extraction.

Follow-ups (filed as separate issues)

The review surfaced larger items that deserve their own PRs — Edit rollback semantics, parallel walking/searching, lazy Read, signature line-map matching, MCP tool-error semantics, profile cache reload, redirect-pattern alignment, GhPR repo resolution, Search size caps, and streaming Ledger summaries.

Test plan

  • cargo build --release clean
  • cargo test --release — 83 unit + 2 stdio integration tests pass
  • Smoke-test against a real Claude Code session to confirm the compact-JSON change doesn't regress any host that relied on the pretty form

https://claude.ai/code/session_01SmRqMmBjQXM4KyDod2Mrfw


Generated by Claude Code

- Drop legacy-ts/ directory and README references; the Rust crate has been
  the source of truth since the rewrite.
- mcp/server: emit compact JSON in `content[].text`. Pretty-printed JSON
  roughly doubles whitespace tokens in nested results, defeating the point
  of relaywash. The `structuredContent` mirror is unchanged.
- tools/edit: write atomically (temp file + rename) so a crash mid-write
  cannot corrupt the target. `std::fs::write` truncates first.
- search: cache the relativized path per file (was recomputed per snippet)
  and drop a redundant sort over BTreeMap keys (already sorted).
- tools/search: drop the duplicated `meta_for_tool` helper; clone the Meta
  the json! macro needs instead of building it twice.
- ast/line_regex: cache `strip_inline_comments`'s block-comment regex in a
  OnceLock — was recompiled on every call.
@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06675cd1-11c3-404e-801d-bcbfb5d3214a

📥 Commits

Reviewing files that changed from the base of the PR and between b5ec72b and 6af12ec.

📒 Files selected for processing (1)
  • crates/wash/src/tools/edit.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/wash/src/tools/edit.rs

📝 Walkthrough

Walkthrough

This PR performs targeted optimizations on the Rust crate while undertaking a comprehensive cleanup/migration of the legacy TypeScript codebase. The Rust changes focus on performance (regex caching, compact JSON, search optimization) and durability (atomic file writes). The TypeScript changes remove deprecated hook scripts and test suites while introducing a file-backed ledger and measurement infrastructure.

Changes

Rust Crate Optimizations

Layer / File(s) Summary
Regex Caching
crates/wash/src/ast/line_regex.rs
Block-comment regex compiled once and cached via OnceLock instead of on every call.
JSON Compaction
crates/wash/src/mcp/server.rs
Tool result serialization uses compact JSON (to_string) instead of pretty-printed (to_string_pretty).
Search Optimization
crates/wash/src/search.rs
Relative path computed once per file and reused across all SearchHits; BTreeMap iteration used directly for ordering without redundant sort.
Atomic Writes
crates/wash/src/tools/edit.rs
New atomic_write helper stages output to a temp file, fsyncs, optionally preserves permissions, and renames atomically; Unix test added for executable-bit preservation.
Search Meta Refactoring
crates/wash/src/tools/search.rs
Meta field construction centralized using cloned Meta instance for response and ToolResult.
Documentation
README.md
Minor wording/placement edits: removed inline legacy-ts/ reference and rephrased Rust subcommand paragraph.

Legacy TypeScript Cleanup & Ledger Infrastructure

Layer / File(s) Summary
Build & Server Bootstrap
legacy-ts/build/build.mjs, legacy-ts/servers/relaywash-server.js
Build script and server entrypoint removed; dynamic validation and thin loader logic deleted.
Hook Scripts Removal
legacy-ts/scripts/builtin-block-hook.js, legacy-ts/scripts/session-start-hook.js, legacy-ts/scripts/session-stop-ingest-hook.js, legacy-ts/scripts/tool-redirect-hook.js
CLI hooks for tool redirection, session management, and filtering removed.
Fixture & Utility Scripts
legacy-ts/scripts/generate-large-fixture.mjs, legacy-ts/scripts/relaywash-savings/run.js, legacy-ts/scripts/burn-compare.js
Large fixture generator, savings report CLI, and burn comparison script removed.
Core Modules Removal
legacy-ts/src/ast/index.js, legacy-ts/src/burn/meta.js, legacy-ts/src/index.js, legacy-ts/src/mcp/server.js
AST utilities, meta builder, main entrypoint, and MCP server wiring removed from legacy TypeScript.
Test Suite Removal
legacy-ts/test/build-tool.test.js, legacy-ts/test/burn-sdk.test.js, legacy-ts/test/edit.test.js, legacy-ts/test/git-state.test.js, legacy-ts/test/read.test.js, legacy-ts/test/search.test.js
All legacy TypeScript test coverage removed.
File-Backed Ledger
legacy-ts/src/burn/sdk.js
New ledger stub emulates relayburn SDK: file-backed JSONL storage per session, Ledger class for recording tool use, ingest for session finalization, summary for aggregation.
Measurement Infrastructure
legacy-ts/src/measure.js
New opt-in measurement module logs replacement-tool calls with shadow equivalents (file reads, ripgrep, git) for size/performance comparison.
Fuzzy Matching
legacy-ts/src/fuzzy/index.js
Text normalization and fuzzy search utilities for whitespace/punctuation-tolerant code matching.
Tool Implementations
legacy-ts/src/tools/build.js, legacy-ts/src/tools/read.js, legacy-ts/src/tools/search.js, legacy-ts/src/tools/gh-pr.js, legacy-ts/src/tools/test-run.js, legacy-ts/src/tools/edit.js, legacy-ts/src/tools/git-state.js
Core tools: build command detection with error parsing, AST-aware read with signature extraction, ripgrep search with JS fallback, GitHub PR via gh CLI, test-run detection and failure aggregation, edit batching with fuzzy matching, git state queries.
Edit Batching Hook
legacy-ts/scripts/edit-batching-nudge.js
CLI hook tracks per-session single-edit history and suggests batching when detected.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • AgentWorkforce/wash#17: Refines the same Rust modules (line_regex.rs, mcp/server.rs, search.rs, tools/edit.rs) addressed in this PR.

Poem

🐰 I cached a regex, tuned a write,

legacy scripts fell out of sight,
Ledger lines now mark each call,
Tiny writes land safe for all,
A happy hop — code tidy, bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Remove legacy-ts and apply Rust review fixes' clearly and concisely summarizes the two main components of this changeset: deletion of the legacy-ts directory and application of focused Rust code improvements.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the cleanup (legacy-ts removal), code fixes with file-level specifics, and follow-up work. It provides clear context for the changes throughout the Rust crate.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ 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 claude/cleanup-rust-review-gx8iD

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5ec72b076

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/wash/src/tools/edit.rs Outdated
Comment on lines +308 to +312
let mut f = std::fs::File::create(&tmp)?;
f.write_all(contents.as_bytes())?;
f.sync_all().ok();
drop(f);
if let Err(e) = std::fs::rename(&tmp, target) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve file permissions during atomic write

Creating a brand-new temp file and then rename-ing it over the target replaces the original inode with one that has default permissions, so editing an executable or otherwise non-default-mode file will silently drop its mode bits (for example, 0755 scripts become non-executable). This is a regression from std::fs::write (truncate-in-place), and it can break files immediately after a successful Edit operation unless atomic_write copies the original metadata/permissions onto the temp file before rename.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 6af12ec. atomic_write now reads metadata().permissions() before opening the temp file and applies it via set_permissions before the rename, so the executable bit (and any other mode bits) survives. Added a Unix test that creates a 0755 .sh file, edits it, and asserts the mode is still 0755 afterward.


Generated by Claude Code

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/wash/src/tools/edit.rs`:
- Around line 294-315: The atomic_write function currently discards sync errors
and can change file permissions on replace; modify atomic_write to (1) read the
existing target's metadata (std::fs::metadata) and capture its permissions
before creating the temp file, (2) create the temp file with
OpenOptions::new().write(true).create_new(true) to avoid races, (3) apply the
captured permissions to the temp file via std::fs::set_permissions (or use
set_mode on Unix) before writing, (4) propagate any sync_all() error instead of
calling .ok(), and finally perform the rename and cleanup as before so that
durability failures are returned and original permissions are preserved on
replacement; use the local symbols target, dir, file_name, tmp, and f to locate
the changes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b2d07248-de8f-4905-9cd4-369f2757f94f

📥 Commits

Reviewing files that changed from the base of the PR and between 4f6e70d and b5ec72b.

📒 Files selected for processing (37)
  • README.md
  • crates/wash/src/ast/line_regex.rs
  • crates/wash/src/mcp/server.rs
  • crates/wash/src/search.rs
  • crates/wash/src/tools/edit.rs
  • crates/wash/src/tools/search.rs
  • legacy-ts/build/build.mjs
  • legacy-ts/scripts/builtin-block-hook.js
  • legacy-ts/scripts/burn-compare.js
  • legacy-ts/scripts/edit-batching-nudge.js
  • legacy-ts/scripts/generate-large-fixture.mjs
  • legacy-ts/scripts/relaywash-savings/run.js
  • legacy-ts/scripts/session-start-hook.js
  • legacy-ts/scripts/session-stop-ingest-hook.js
  • legacy-ts/scripts/tool-redirect-hook.js
  • legacy-ts/servers/relaywash-server.js
  • legacy-ts/src/ast/index.js
  • legacy-ts/src/burn/meta.js
  • legacy-ts/src/burn/sdk.js
  • legacy-ts/src/fuzzy/index.js
  • legacy-ts/src/index.js
  • legacy-ts/src/mcp/server.js
  • legacy-ts/src/measure.js
  • legacy-ts/src/tools/build.js
  • legacy-ts/src/tools/edit.js
  • legacy-ts/src/tools/gh-pr.js
  • legacy-ts/src/tools/git-state.js
  • legacy-ts/src/tools/ping.js
  • legacy-ts/src/tools/read.js
  • legacy-ts/src/tools/search.js
  • legacy-ts/src/tools/test-run.js
  • legacy-ts/test/build-tool.test.js
  • legacy-ts/test/burn-sdk.test.js
  • legacy-ts/test/edit.test.js
  • legacy-ts/test/git-state.test.js
  • legacy-ts/test/read.test.js
  • legacy-ts/test/search.test.js
💤 Files with no reviewable changes (31)
  • legacy-ts/src/tools/git-state.js
  • legacy-ts/src/ast/index.js
  • legacy-ts/src/tools/test-run.js
  • legacy-ts/src/burn/meta.js
  • legacy-ts/src/tools/ping.js
  • legacy-ts/scripts/builtin-block-hook.js
  • legacy-ts/build/build.mjs
  • legacy-ts/scripts/tool-redirect-hook.js
  • legacy-ts/test/git-state.test.js
  • legacy-ts/scripts/relaywash-savings/run.js
  • legacy-ts/src/mcp/server.js
  • legacy-ts/scripts/edit-batching-nudge.js
  • legacy-ts/test/search.test.js
  • legacy-ts/test/burn-sdk.test.js
  • legacy-ts/servers/relaywash-server.js
  • legacy-ts/scripts/session-start-hook.js
  • legacy-ts/scripts/session-stop-ingest-hook.js
  • legacy-ts/test/build-tool.test.js
  • legacy-ts/scripts/generate-large-fixture.mjs
  • legacy-ts/src/index.js
  • legacy-ts/src/tools/edit.js
  • legacy-ts/src/measure.js
  • legacy-ts/test/edit.test.js
  • legacy-ts/src/fuzzy/index.js
  • legacy-ts/src/tools/gh-pr.js
  • legacy-ts/src/tools/build.js
  • legacy-ts/src/tools/read.js
  • legacy-ts/src/burn/sdk.js
  • legacy-ts/scripts/burn-compare.js
  • legacy-ts/src/tools/search.js
  • legacy-ts/test/read.test.js

Comment thread crates/wash/src/tools/edit.rs
Addresses review feedback on PR #18:

- Capture the target file's permissions before opening the temp file and
  set_permissions() on the temp before write+rename. Without this, rename
  swaps in our fresh inode with default mode bits and (e.g.) a 0755 script
  silently becomes non-executable after one Edit. On Windows, MoveFileEx
  assigns the destination directory's default ACL on rename, so copying
  perms forward gives equivalent behavior on both platforms.
- Use OpenOptions::create_new to avoid the (very narrow, but real) race
  where another process recreates the temp filename between our format!()
  and File::create.
- Propagate sync_all() errors instead of swallowing with .ok(). A failed
  fsync should not look like a successful write.
- Bracket the write phase so any failure cleans up the temp file before
  returning.
- New Unix-only test asserts that a 0755 file keeps its executable bit
  through an Edit.
@willwashburn willwashburn merged commit dc5d5a7 into main May 8, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 8, 2026
4 tasks
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.

2 participants