Skip to content

Release: gitgrip 1.3.0 - #916

Merged
laynepenney merged 8 commits into
mainfrom
promote/v1.3.0
Aug 25, 2026
Merged

Release: gitgrip 1.3.0#916
laynepenney merged 8 commits into
mainfrom
promote/v1.3.0

Conversation

@laynepenney

Copy link
Copy Markdown
Member

Promotes the current dev tip to main for gitgrip 1.3.0. The promotion branch adds no bytes. Release metadata was independently ratified by Sentinel, with cargo formatting, 739 library tests, all-target checking, and crate packaging green.

laynepenney and others added 8 commits August 22, 2026 09:37
An unknown --repo name matched zero repositories and the command reported
success having done nothing. With a modified file present in the worktree,
`gr add . --repo missing` printed "No changes to stage." and exited 0. There
was a change to stage; gr said there was not. That is a false claim about the
working tree, which is what justifies a fix rather than a documentation note.

validate_repo_filters_known already existed in src/core/repo.rs, already
produced the right diagnostic, and already carried a basename-matching branch
that names the intended repository. It was not called from add, commit, or
push. This calls it from all three.

Wired to CliOutcomeError::refusal. outcome.rs defines EXIT_REFUSED = 2 and
carries a test named refusal_is_distinct_from_operational_failure, so the
codebase already separates a refusal from an operational failure. pr/merge
uses the same mapping.

Three regression tests assert that an unknown --repo name exits 2 with the
diagnostic, one per verb. A control asserts that a KNOWN name still reaches
the work: it writes a file, runs the command, and asserts that file is present
in the repository's index. It asserts the destination rather than the absence
of a message.

Mutation-verified in both directions. Removing add.rs's validation call turns
exactly the add rejection test red while commit and push stay green. Making
run_add return early after validation turns exactly the control red while the
three rejection tests stay green.

Other commands accept a repo filter without validating it. Identifying which
requires reading each command's behavior rather than grepping an identifier,
and is deliberately left to a follow-on so each batch is reviewed on its own.

Ref #886 — closes at promotion.

Premium boundary: grip is OSS — local workspace orchestration, no identity,
org, or entitlement semantics.
fix(cli): refuse an unknown --repo filter on add, commit, and push
…arget

`gr prune --execute` deleted the local `main` on any repo whose manifest
target is not `main`. The guard skipped exactly two branches -- the one
checked out and `repo.target_branch()` -- so once the integration target
moved to `dev`, `main` was neither and fell out of protection. Nothing
was edited to cause this: `main` had been protected only by coincidence,
because target and default used to be the same value.

Scope of the defect, measured rather than assumed: LOCAL ONLY. The
`--remote` path is `git fetch --prune`, which prunes stale remote-tracking
refs and does not delete remote branches, so the worst outcome was a local
branch recreated from the remote.

The fix protects a SET of three: current, target, and the remote's default
branch read from `refs/remotes/<remote>/HEAD`. The default is the only one
of the three that asserts "permanent" rather than "currently interesting",
which is the property a cleanup rule needs. Resolution is local -- a
cleanup verb should not acquire a network failure mode.

When the default cannot be resolved, the protected set GROWS rather than
shrinks: `main` and `dev` are protected by name and the run says so. A
resolution failure that silently dropped a branch would reproduce this
exact defect inside its own fix, and would do it with nothing going red.

Two witnesses, each killed by exactly one mutation:

- target=dev with `main` present: `main` and `dev` survive while a genuinely
  merged branch is still deleted. That last assertion is a positive control;
  without it the test would pass against a guard that protected everything.
- `origin/HEAD` deleted from a real clone, so resolution genuinely fails
  rather than being stubbed: both branches survive AND the output states
  the default could not be determined. A protected-more that is silent
  still reads as "the default resolved fine" to the next reader.

The pre-existing `test_prune_skips_current_and_default` is left in place but
does not cover any of this: its fixture has current == target == `main`, so
`main` is protected twice over and the test passes with EITHER clause of the
old guard removed. It kills no mutant while carrying the name of the
guarantee it fails to check.

Co-Authored-By: Claude <noreply@anthropic.com>
…anch

fix(prune): protect the remote default branch, not just current and target
feat(spawn): pass Codex startup prompts as developer instructions
chore(release): prepare gitgrip 1.3.0
@laynepenney
laynepenney merged commit 373b44b into main Aug 25, 2026
7 of 13 checks passed
@laynepenney
laynepenney deleted the promote/v1.3.0 branch August 25, 2026 20:18
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