Skip to content

Native Rust runtime for Annotate Lite evaluation - #27

Merged
backnotprop merged 12 commits into
mainfrom
feat/rust-lite
Aug 31, 2026
Merged

Native Rust runtime for Annotate Lite evaluation#27
backnotprop merged 12 commits into
mainfrom
feat/rust-lite

Conversation

@backnotprop

@backnotprop backnotprop commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add one Rust 2024 herdr-annotate binary with capture, copy-context, manage, editor, and manager subcommands
  • preserve Lite JSONL paths and bytes, pending/saved field order, action/pane contracts, capture precedence, Markdown export, archive workflows, clipboard adapters, process failures, terminal screens, and key handling
  • add a side-by-side lite-rs manifest plus checksummed native-binary installers, local staging, path-filtered CI, and six-target prerelease packaging
  • add a function-level TypeScript/Rust call-path map and a deterministic differential harness at scripts/parity-lite.sh

No TypeScript source changed.

Reproducible parity proof

Run:

bash scripts/parity-lite.sh

Result on both Ubuntu and macOS:

Parity Lite: 401 observables compared, 92 screens diffed, zero divergences / 1 deliberate

The harness runs both implementations against identical temporary inputs. It mechanically compares exit status, stderr, exact Herdr argv, clipboard bytes, pending files and modes, JSONL products and temporary/lock trees, cross-reading in both directions, and fixed-size real-PTY terminal grids after each scripted key step. Generated timestamps, UUIDs, PIDs, and temporary roots are normalized only at comparison time; product files are not rewritten or weakened.

Green matrix: rust-lite-ci run 33346820571 — Linux checks, Windows Clippy, and parity on Ubuntu and macOS all passed.

Verification

Automated:

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test --all-targets — 59 passed (55 unit + 4 subprocess integration)
  • cargo clippy --target x86_64-pc-windows-msvc --all-targets -- -D warnings
  • cargo check --target x86_64-apple-darwin --all-targets
  • bun test — original TypeScript suite unchanged, 58 passed
  • bun run typecheck
  • bash scripts/parity-lite.sh — summary above
  • shell syntax checks, workflow YAML parse, and git diff --check

Live on macOS:

  • HERDR_SESSION=rust-lite-test bash scripts/smoke-rust-lite.sh — zero failures
  • staged native binary and manifest resolved in a disposable Herdr session; native manager pane rendered
  • the prior Full annotate GitHub install was restored at commit ba4903b, and the disposable session was stopped

Not live-tested:

  • Windows execution. Windows builds and strict Clippy are covered by CI, but live Windows behavior remains part of the separate promotion track.
  • release-download installation, because no evaluation tag/release exists yet; installer and checksum behavior are tested.

Findings and deliberate divergence

The proof work found and fixed six observable differences: state-directory mode, direct-editor record key order, lossy handoff decoding, signal-time terminal restoration, forceful pending cleanup, and handoff removal failure propagation. Each fix is isolated in its own commit and mapped to a harness case in the call-path document.

The sole deliberate divergence is manager timestamp display on non-en-US hosts: Rust renders an en-US-style local timestamp, while TypeScript delegates to host toLocaleString(). Persisted timestamps, ordering, and exports are identical. CI pins UTC/en-US and compares the literal timestamp cells; this locale difference is not hidden by normalization.

The earlier reviewer findings for manager detail width and captured-record field order remain covered by exact cell/byte regression tests.

@backnotprop

Copy link
Copy Markdown
Contributor Author

Review (requesting changes; posted as a comment because the PR is under the same account).

Reviewed against the parity bar: a Lite user swapping to this build should notice exactly one thing, that Bun is gone. Close, with two small parity fixes needed.

Verified (macOS, live in a disposable Herdr session, previous install and the real store restored byte-for-byte afterwards)

  • Full untouched: zero diff under src/, root manifest, scripts/, plannotator-tui.version, README; Full reinstalls and works after the evaluation.
  • cargo fmt, cargo clippy --all-targets -- -D warnings (crate config) clean; 52 Rust tests; the 58 TypeScript tests unchanged; no production unwrap/expect.
  • copy-context: Markdown output byte-identical to export.ts over a fixture store (backticks in selections, wide chars, multi-line comments, with/without Source) and over a store containing records written by both editors; notification text identical.
  • capture: identical herdr plugin pane open argv, identical pending JSON (extra context keys dropped, same key order), mode 0600, identical "No supported clipboard reader is available" path.
  • Editor: screen text identical to the TypeScript editor at the same size, empty and after typing two lines; Ctrl+S save lands a record the TypeScript build reads, and vice versa.
  • Manager: active view identical, including the herdr / main · 8/29/2026, 6:27:35 PM metadata line (en-US host).

Needs fixing before this can stand in for lite/

  1. Detail pane one cell too wide (rust/src/manager.rs:114). TypeScript computes detailWidth = cols - detailLeft - 1 with a 1-based detailLeft = listWidth + 3; the port keeps the correct 0-based detail_left = list_width + 2 but reuses the same subtraction, so detail_width is one larger. Visible in the archive detail list: Rust renders …lines mo… where TypeScript renders …lines m… on every row. It also widens wrapping of long selections/comments and the metadata line. Fix: cols.saturating_sub(detail_left + 2).max(1), and pin it with a test that asserts the TypeScript width.
  2. Saved-record key order differs. editor.ts builds the record from parsePendingAnnotation, which yields selectedText, capturedAt, context, id, comment, createdAt; the Rust Annotation serializes selectedText, context, capturedAt, … (the capture pending-file order). Both parse fine in both directions, so data is safe, but the parity doc and serialization_matches_the_typescript_field_names_and_order assert the wrong reference. Either match the editor's order or correct the claim; matching is a struct reorder.

Smaller

  • rust-lite-ci.yml runs on every PR; add a paths: filter (rust/**, lite-rs/**, the two workflows) so TypeScript-only PRs don't pay for a Windows Rust build.
  • Naming the macOS/Linux binary herdr-annotate.exe to share one manifest works, but shows up in ps and error text. The Full install solves the same problem with a one-line shell wrapper; consider that before this leaves evaluation.
  • Windows is CI-clippy only; state it stays that way until someone runs it on a Windows host.
  • Timestamp locale: identical on en-US hosts; non-en-US users would see a different format than Bun's toLocaleString. Acceptable for evaluation, note it in the PR body.

Harness used for the screen diffs: overlay panes opened via herdr plugin pane open, keys sent through the session pty, herdr pane read captured after each step, both builds over the same store; happy to rerun it on the fix.

@backnotprop

Copy link
Copy Markdown
Contributor Author

Re-verified 0c5231f with the same harness (disposable session, both builds over an identical store, real store and prior install restored byte-for-byte afterwards):

  • clippy -D warnings clean, 53 Rust tests, 58 TypeScript tests, no Full-side files touched.
  • Manager archive detail view: now identical to TypeScript on the rows that differed before (the one-cell overflow is gone); editor screens identical.
  • A record saved from the Rust editor serializes as selectedText, capturedAt, context, id, comment, createdAt, matching what editor.ts writes.
  • CI path filter present.

Parity holds for macOS/Linux on everything I can exercise. Remaining, as documented: Windows is CI-only, the .exe naming on Unix, and en-US-only timestamp formatting. From my side this is approved for evaluation; the swap decision (replacing lite/) should wait for a Windows run.

Parity proof: Filesystem effects / store directory creation.
Parity proof: Filesystem effects / saved annotation field order.
Parity proof: Filesystem effects / handoff take.
Parity proof: Process effects / terminal signal cleanup.
Parity proof: Filesystem effects / pending handoff deletion.
Parity proof: Filesystem effects / handoff take and removal.
Compare Bun and Rust process, PTY screen, and JSONL store observables on Ubuntu and macOS CI.
@backnotprop
backnotprop merged commit 5f118a9 into main Aug 31, 2026
4 checks passed
@backnotprop
backnotprop deleted the feat/rust-lite branch August 31, 2026 04:48
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.

1 participant