Skip to content

Replace the Python fake gh and claude with a Rust helper - #218

Merged
JacobStephens2 merged 2 commits into
issue-174from
issue-207
Sep 30, 2026
Merged

JacobStephens2 merged 2 commits into
issue-174from
issue-207

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

The warm suite still missed #174's targets (249s wall, 650s user + sys), and the Python fakes' startup used about 352s of that CPU. They're now one std-only Rust executable. It acts as gh or claude depending on the name it's called by, and keeps the same contract: the same state and record JSON files, argv handling and output.

 tests/
-├── fakes/gh              # Python
-├── fakes/claude          # Python
+├── fakes.rs              # entry: dispatch on argv[0]; also a Cargo test target (clippy, rustfmt, JSON tests)
+├── fakes/gh.rs           # port of the fake gh, same contract
+├── fakes/claude.rs       # port of the fake claude, same contract
+├── fakes/json.rs         # std-only JSON, laid out as Python's json module does
 └── support/
+    ├── fakes.rs          # builds the fakes with rustc once, cached in target/tmp by source hash, symlinks them into bin/
     └── mod.rs            # Scenario installs them via support::fakes

The fakes aren't a [[bin]], so neither the crate nor a dist release ships them. No scenario changed. tests/release.rs only changed how its fixture installs the fakes, and it now uses mod support.

Evidence

All measurements are on a 4-core machine that wasn't idle. The full table with load is in the Result comment on #174.

  • Before: warm cargo test took 249s wall and 444 + 205 = 650s user + sys. Python fake self-CPU was 333s for gh and 20s for claude.
    After: warm cargo test took 107s / 129s / 113s wall and 188s / 202s / 206s user + sys. All 25 test binaries passed. The last two runs had a failing python3 stub first on PATH, and nothing called it.
  • Cold cargo test --no-run took 108s (target ≤ 300s). Building the fakes adds about 1.2s once, at the first test that needs them.
  • cargo fmt --check and cargo clippy --all-targets -- -D warnings are clean.

Merge Danger

Door: two-way

Test-only. Reverting brings the Python scripts back.

Blast Radius: tests

Running the suite now needs rustc on PATH (or $RUSTC) instead of python3. Stale target/tmp/fakes-* builds pile up as the fakes change, a few MB each.

Unaddressed findings

Standards

  • Hidden global state (static LOCK, state_path() from env): skipped. It mirrors how the fake lets go of the lock while a hook script runs, and threading a lock through every command adds noise to a test fake.
  • Primitive Obsession (untyped Json state): skipped. Untyped state passes unknown keys and key order through untouched, which the state-file contract relies on.
  • Old fakes-<hash> and failed .partial builds never cleaned up: skipped. They're small, cargo clean removes them, and pruning them could race with parallel test processes.

Spec

  • tests/fakes.rs is an extra test target (not asked for): kept. It's the only way the fakes get clippy and rustfmt.
  • release.rs pulls in all of support: kept. Only fixture setup changed, no assertions.
  • A hand-written 437-line JSON module: kept. A fake built with plain rustc can't use serde_json, and the module has its own tests.
  • pr view --jq .<object field> prints JSON, not a Python repr: skipped. Nothing selects a non-string field.
  • gh fake … rejects extra arguments; gh fake pr on an unknown head says "no pull requests found" instead of a traceback: skipped. Both still fail (exit 1 or 2), and no caller passes extras.
  • run list treats a run missing branch/workflow/event as not matching (Python crashed); claude no longer turns \r\n into \n in stdin and the plugin snapshot: skipped. No test or script records such a run or uses CRLF.
  • The merge scratch dir is named tmp-merge-<pid>, not tmpXXXX: skipped. It's removed before any error, and nothing lists that directory.

Closes #207

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits September 30, 2026 22:42
The warm suite missed both of #174's targets (249s wall, 650s user + sys
against a 701s baseline), and the Python fakes' own CPU was about 352s of
that: 4,940 gh calls and 381 claude calls. The fakes are now one std-only
Rust executable, built from tests/fakes.rs with rustc the first time a
test needs it and cached in CARGO_TARGET_TMPDIR by a hash of its sources.
It acts as gh or claude by the name it is called by, under the same
contract: the same state and record files, argv handling and output.

tests/fakes.rs is also a Cargo test target, so clippy and rustfmt cover
the fakes and the JSON layout is checked against Python's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pull the repeated pieces of the fakes into one place each: the --jq
refusal, the "not open" check, the branch tip on origin, record appends
and the lock file. Move the JSON tests next to the JSON module. Build the
fakes without -D warnings, so a lint a newer rustc adds can't fail the
suite: clippy covers them through the fakes test target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit dde1cee into issue-174 Sep 30, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-207 branch September 30, 2026 22:53
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