Skip to content

Pre-flight checks - #16

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-4
Sep 25, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-4

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

Every pre-flight check now runs before a Run creates anything. Each one fails with a clear message and a non-zero exit.

 main
+  parse Issue URL                  → usage error (exit 2) if missing or malformed
   run
+    preflight::check
+      Origin match (raw origin URL)  → "origin mismatch"
+      gh issue view                  → "issue #<n> is closed"
+      git user.name / user.email     → "git <key> is not set"
+      branch --show-current          → "HEAD is detached"
+      ls-remote origin <base>        → "base branch <base> does not exist on origin"
+      fetch; rev-list origin/<base>..<base> → "local <base> is <k> commit(s) ahead of origin/<base>"
     Worktree::create_fresh   (still from origin/<base>, so uncommitted changes are left out)
     Plugin::write
     session::run

The git identity check comes from the finding in the issue comment. Global config and repo config both count.

Tested seam: the thirdshift CLI only, with real git and fake gh/claude (tests/preflight.rs). Every rejection goes through assert_rejected_before_any_work, which checks for no worktree, an empty $TMPDIR, and a fake claude that was never invoked. The fake gh now supports issue view.

Evidence

  • Before: a_closed_issue_is_rejected → thirdshift: no PR found (the run went ahead). A detached HEAD gave git symbolic-ref --short HEAD failed: …. A local Base branch ahead of origin went ahead. A missing identity went ahead.
    After: cargo test → fresh_run 8 passed, preflight 11 passed. cargo clippy --all-targets -D warnings and cargo fmt --check are clean.
a_missing_argument_is_a_usage_error
an_argument_that_is_not_a_github_issue_url_is_a_usage_error   (non-github host, /pull/, no /issues/<n>, non-numeric)
the_origin_match_accepts_https_and_ssh_with_or_without_git_in_any_case
an_issue_in_another_owners_or_repos_repository_is_an_origin_mismatch
a_closed_issue_is_rejected
a_detached_head_is_rejected
a_base_branch_missing_on_origin_is_rejected
a_local_base_branch_ahead_of_origin_is_rejected
uncommitted_changes_in_the_launch_directory_are_allowed_and_left_out
a_missing_git_identity_is_rejected_naming_the_missing_key
a_git_identity_set_only_in_the_launch_repository_is_enough

Merge Danger

Door: two-way

Only new checks and a small refactor. Reverting is a single revert.

Blast Radius: small

Runs that used to go ahead now stop early: local Base branch ahead of origin, closed issue, missing identity. A malformed argument now exits 2 instead of 1. Pre-flight adds one gh issue view and one git ls-remote per run.

Unaddressed findings

Standards

  • Primitive Obsession ("OPEN" strings, String for the Base branch, issue_is(n, &str) in tests): a state enum or Base branch type isn't worth it with only two comparisons. It is better introduced with Issue branch selection (a later ticket), where the Base branch can be redefined.
  • Mysterious Name (arg in main.rs): cosmetic. It is used on the next two lines only, and it isn't yet an Issue URL at that point.

Spec

  • Issue URL host casing (https://github.com/... is rejected): the spec lists "any casing" under the Origin match of the origin URL, not the Issue URL. The rejection is a fast usage error.
  • Issue URLs with a #fragment or ?query are rejected: not asked for. It fails fast as a usage error and could be a follow-up if it proves annoying.

Closes #4

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits September 25, 2026 04:38
A Run now stops with a clear message and a non-zero exit, before any
worktree, temp directory or session exists, when:

- the argument is missing or not a GitHub issue URL (a usage error)
- the Issue URL fails the Origin match
- the issue is closed
- HEAD is detached
- the Base branch doesn't exist on origin
- the local Base branch is ahead of origin
- git user.name or user.email is not set

Uncommitted changes in the launch directory are allowed and left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Worktree::create_fresh fetches the Base branch itself again, so it no
  longer relies on pre-flight having fetched it first.
- Share one gh-JSON helper between the issue and pull request lookups.
- Check the git identity before the Base branch checks, which fetch.
- Test that a repo-local git identity is accepted.
- In the test harness, share one fallible git helper, and let the
  origin-spelling test add its own insteadOf rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit e69dbfd into main Sep 25, 2026
1 check passed
@JacobStephens2
JacobStephens2 deleted the issue-4 branch September 25, 2026 11:11
JacobStephens2 added a commit that referenced this pull request Sep 25, 2026
Resolve src/main.rs and src/run.rs against the pre-flight checks (#15/#16)
and the Base branch merge and conflict Repair (#17). Keep main's run
structure and carry the progress lines into it: the session log line
moves into run_session, the push line into Worktree::push, and the newly
built steps (merging the Base branch, starting a Repair) print their
lines as issue #10 asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JacobStephens2 added a commit that referenced this pull request Sep 25, 2026
Brings in pre-flight checks (#16), the README (#15) and merging the Base
branch with conflict Repair (#17) on top of the Failed run path.

- run.rs: preflight::check replaces the inline origin check; the Repair
  session and final push sit inside implement(), so a failed Repair goes
  down the Failed run path. The session log reported on failure is the
  most recent one (repair-1 if it ran).
- main.rs: parse the Issue URL up front (exit 2) as main does, then
  install the interrupt handler and report a FailedRun.
- github.rs: main's gh_json/string_field helpers, plus isDraft, is_open,
  mark_ready and convert_to_draft.
- failed_run.rs: reuses Worktree::merge_in_progress.
- tests/mergeable.rs: a failed Repair is now a Failed run, so it prints
  the open PR's URL and sends the PR back to draft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Pre-flight checks

1 participant