fix(git): propagate exit codes in push/pull/fetch/stash/worktree - #234
Merged
pszymkowiak merged 1 commit intoMar 2, 2026
Merged
Conversation
run_push, run_pull, run_fetch, run_stash (pop/apply/drop/push and default), and run_worktree all printed FAILED messages on error but returned Ok(()) instead of propagating git's exit code. This breaks CI/CD pipelines and shell scripts that rely on exit code semantics (e.g. `git push || handle_error`). Fix: call std::process::exit(output.status.code().unwrap_or(1)) in each failure branch, matching the pattern already used in run_log, run_diff, run_add, and run_branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collaborator
|
Hi, Thanks for the fix! LGTM |
maxkulish
added a commit
to maxkulish/rtk
that referenced
this pull request
Mar 18, 2026
…og limits Port 5 critical bug fixes from upstream rtk-ai/rtk (v0.29.0): **P1.1a: Exit code propagation (rtk-ai#234)** - Add std::process::exit(code) to push/pull/fetch/stash/worktree/commit - Fixes CI/CD pipelines that rely on exit code semantics - Pattern: eprintln error, then exit with git's actual exit code **P1.1b: git commit -am, --amend support (rtk-ai#327/rtk-ai#360)** - Change Commit enum from {messages: Vec<String>} to unit variant - Update main.rs GitCommands::Commit to use trailing_var_arg - Update build_commit_command() to accept all args, not just -m messages - Now supports: -a, -am, --amend, --no-edit, --allow-empty, etc. - Add tests for --amend, -am flags **P1.1c: git branch creation fix (rtk-ai#194)** - Already implemented in our fork (lines 961-1019) - Detects positional args without list flags → routes to passthrough - Prevents `rtk git branch newbranch` from silently becoming a list **P1.1d: git log limit injection fix (rtk-ai#461/rtk-ai#478)** - Add parse_user_limit() to parse -N, -n N, --max-count=N/N formats - Update has_limit_flag detection to handle all limit formats - Fix: don't inject -10 when user provides their own limit - Fix: when user provides --oneline without limit, use -50 not -10 - Add 7 tests for parse_user_limit() edge cases **P1.1e: Multiple -m flags (#c18553a)** - Already supported by new trailing_var_arg implementation - Tests verify `git commit -m "title" -m "body"` works All tests pass (510 passed, 2 ignored). Zero clippy warnings. Upstream commits ported: - 5cfaecc (exit codes) - 409aed6 (commit flags) - 88dc752 (branch creation - already had) - c18553a (multiple -m - already had) - Related: rtk-ai#461, rtk-ai#478 (log limit)
thehoff
pushed a commit
to thehoff/contextcrawler
that referenced
this pull request
May 14, 2026
…-ai#234) run_push, run_pull, run_fetch, run_stash (pop/apply/drop/push and default), and run_worktree all printed FAILED messages on error but returned Ok(()) instead of propagating git's exit code. This breaks CI/CD pipelines and shell scripts that rely on exit code semantics (e.g. `git push || handle_error`). Fix: call std::process::exit(output.status.code().unwrap_or(1)) in each failure branch, matching the pattern already used in run_log, run_diff, run_add, and run_branch. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
… coexist (rtk-ai#234) P0 regression from 0.4.4 (rtk-ai#219, 65cf208). The runtime integrity gate treated ANY non-contextcrawler PreToolUse Bash hook as tampering and refused to run, disabling Tirith + supply-chain gating for anyone with a real multi-hook settings.json (git-hygiene, lab-repo-guard, council-review-gate, ...). Posture A (own-boundary): contextcrawler validates ITS registration and flags entries that masquerade as ours; it ignores unrelated third-party hooks — that is Claude Code's settings-trust boundary, not ours. - inspect_registration_surface: only a contextcrawler/rtk-hook-shaped command that FAILS is_expected_hook_command is `unexpected` (repoint/masquerade). Unrelated + malformed sibling entries are ignored. - A valid, non-masquerading registration with no baseline now RUNS instead of hard-bailing (bricked every upgrade + re-bricked on each hook edit). Removed the now-unreachable BinaryHookStatus::NoBaseline. - registration_surface_hash covers only contextcrawler-owned entries, so a legitimate edit to some other tool's hook never invalidates our baseline. Preserved: closed-argv validation, trusted-install-path for the absolute form, masquerade/repoint detection, hash change-detection when a baseline exists. Verified against the real settings.json (5 sibling hooks): clean auto-allow, empty stderr. Reverses three rtk-ai#219 posture tests (-> council). Refs: rtk-ai#234 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
…#234) Addresses council findings on the round-1 posture change: - Bind the matcher into the identity hash (council blocker): moving our hook from Bash to another matcher (dropping Bash gating) with an unchanged command is now caught as Tampered. Sibling entries stay excluded so unrelated hook edits don't invalidate the baseline. - Ownership by parsed argv[0] basename, not substring (council medium): a sibling like ~/.claude/hooks/smartkit.sh (contains "rtk" + path has "hook") is no longer mis-flagged as tampering. Genuine repoints keeping our binary name are still caught. - Trust-on-first-use baseline (council blocker): a valid registration with no identity is RegisteredNoBaseline (non-fatal) — the runtime gate records the identity on first clean run, so a later swap of our own binding to any other valid form/matcher is then caught as Tampered. Replaces the old fatal NoBaseline (which bricked upgrades) without leaving the no-baseline case unprotected. Rejected (Law-2): council blocker "a sibling with ; or && passes is_expected_hook_command" — false; that validator forbids ;|&<>`$ newline and requires exactly `<exe> hook claude` (integrity.rs is_expected_hook_command). 5 new/updated regression tests (matcher-move, smartkit FP, TOFU record+enforce, parsed-substring). Full lib suite 3252 pass/0 fail; clippy clean. Refs: rtk-ai#234 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
…rtk-ai#234) Second-round council raised two real correctness gaps in the round-2 hash: - Hash only `type == "command"` owned hooks: a `command` -> `prompt` type swap on our entry (command string unchanged) now drops it from the identity set and is caught as Tampered, even when a second owned entry keeps has_expected. - Serialise the sorted (matcher, command) tuples as JSON instead of a manual NUL-join: the previous encoding was forgeable via NUL/newline inside a matcher (two distinct registrations could collide). Structured serialization is unambiguous. - Surface a stderr warning (never stdout — hook JSON protocol) when the TOFU baseline record fails, so degraded tamper-detection is not silent. Rejected (Law-2, with evidence): "absolute paths outside trusted prefixes not flagged" — already Tampered via is_expected_hook_command + trusted-path check (test_binary_hook_foreign_absolute_path_is_tampered); "$VAR shell-expansion bypass" — is_expected_hook_command forbids `$`, such forms are ignored and can only ever run our genuine binary. init records the identity at install (init.rs), so the TOFU pre-first-run window is a narrow upgrade-without-init residual, not a fatal gap. 2 new tests (type-swap, adversarial-matcher). Full lib suite 3254 pass/0 fail; clippy clean. Refs: rtk-ai#234 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
…self-disable) 0.4.4's rtk-ai#219 integrity gate disabled contextcrawler (Tirith + supply-chain included) whenever any sibling PreToolUse hook coexisted. Posture A own-boundary fix + 2 council rounds (parsed-basename ownership, matcher+type-bound identity hash, TOFU baseline). Refs: rtk-ai#234
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
P0 hotfix: rtk-ai#234 — the 0.4.4 hook-integrity gate (rtk-ai#219) disabled ContextCrawler (Tirith + supply-chain gating included) whenever any sibling PreToolUse hook coexisted. Own-boundary fix, hardened over two council rounds. See CHANGELOG. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff
added a commit
to thehoff/contextcrawler
that referenced
this pull request
Jul 14, 2026
…ts` run
The security releases (0.4.3-0.4.7) verified with `cargo test --lib`, which
skips integration tests in tests/ and #[ignore] tests — so these were red
undetected. All test/infra only; no production behaviour change.
- branding_lint: add a rule for the match-arm binary-name alternation
`Some("contextcrawler" | "rtk")`. The lint had been RED since 0.4.4 (rtk-ai#219's
is_trusted_install_path added this form with no allowlist rule).
- integrity_cargo_bin: compute the registration identity hash the rtk-ai#234 way
(sorted owned (matcher, command) tuples), not the old whole-surface hash, so
the cargo-bin acceptance test matches the shipped verifier.
- read.rs / git.rs ignored tests: the debug binary is `contextcrawler`, not the
pre-rebrand `ctxcrl` — fix the hard-coded path.
- gain sigpipe test: seed the temp DB UNDER $HOME. The tracking DB path is
confined to $HOME (#111), so a /tmp DB was refused (exit 1) before the
broken-pipe path ran; the broken-pipe behaviour itself was already correct.
Full `cargo test --all-targets`: 3406 pass / 0 fail; `--ignored`: 7/7.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
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.
Problem
`rtk git push`, `rtk git pull`, `rtk git fetch`, `rtk git stash` (pop/apply/drop/push/default), and `rtk git worktree` all print a `FAILED:` message on error but return exit code 0 to the caller.
This breaks CI/CD pipelines and shell scripts that rely on exit code semantics:
```bash
rtk git push || handle_error # handle_error never called
rtk git pull && deploy # deploy runs even on pull failure
```
Root cause
The failure branches in these functions print the error but fall through to `Ok(())` instead of propagating git's exit code via `std::process::exit()`.
Fix
Add `std::process::exit(output.status.code().unwrap_or(1))` in each failure branch, matching the pattern already used correctly in `run_log`, `run_diff`, `run_add`, and `run_branch`.
Per the Rust docs, `status.code()` returns `Option` — `None` when the process was killed by a signal (Unix). The `.unwrap_or(1)` fallback handles that case correctly.
Functions fixed
Testing
All 412 existing tests pass. No new behavior on the success path.
🤖 Generated with Claude Code