fix(pull-base): word-match the clear hook; defer no-identity refusal to diverged pulls #153

Merged
dominik.polakovics merged 2 commits from afk/151 into main 2026-07-13 15:46:02 +02:00

Closes #151

Two narrow gaps in the /pull-base lab command (#149), each degrading to pre-#149 behaviour in cases the design intended to cover.

1. Arg-bearing clear silently skipped the pull

isClearCommand matched the provider's role=clear entry by exact string equality, so /clear my-session (claude's clear carries ArgHint "[name]", and the autocomplete gesture model #122 actively invites typing one) fell through to the ordinary reply path — forwarded with no pull and no notice.

Fix: a commandWord helper (first whitespace-delimited token, unicode.IsSpace so tabs count) now drives both dispatch paths, so they agree on what counts as a command with a trailing argument:

  • the clear hook matches on the word; the argument survives the forward verbatim (pull-then-forward, as intended);
  • /pull-base bare executes, any argument — space- or tab-separated (previously leaked to the provider) — is the 400 refusal.

2. No-identity refusal blocked even a fast-forward

The ErrNoAuthorIdentity refusal ran before any git did (inherited from crmerge, where every merge authors a commit), so a repo with no configured author identity could never /pull-base at all — even when the pull would trivially fast-forward, which authors no commit. Since ff-ness is unknowable before the fetch, the check had to move below it: gitx.PullBase now refuses with a new ErrAuthorIdentityRequired sentinel only on the diverged path, after the fetch but before the merge touches the worktree (untouched on refusal); the pull service maps that to ErrNoAuthorIdentity, so the HTTP classification (409) is unchanged.

Tests

  • TestAPI_ClearHook_argBearingClearPulls/clear my-session pulls first, forwards verbatim.
  • TestAPI_ClearHook_prefixIsNotClear/clearx is not a clear (word match, not prefix).
  • TestAPI_PullBase_tabArgRejected — tab-separated argument 400s.
  • gitx.TestPullBase_fastForwardEmptyIdentity / TestPullBase_divergedEmptyIdentityRefused — identity needed only when a merge commit is authored; refusal leaves HEAD unmoved, no MERGE_HEAD, clean tree.
  • pull.TestPullBase_noIdentityFastForwardSucceeds / TestPullBase_noIdentityDivergedRefused — service-level acceptance, replacing the old up-front-refusal test.

Full go test ./... green (gofmt/vet clean).

🤖 Generated with Claude Code

https://claude.ai/code/session_01SG5V7rCpXuWvkD6Uv4BjHB

Closes #151 Two narrow gaps in the `/pull-base` lab command (#149), each degrading to pre-#149 behaviour in cases the design intended to cover. ## 1. Arg-bearing clear silently skipped the pull `isClearCommand` matched the provider's role=clear entry by exact string equality, so `/clear my-session` (claude's clear carries `ArgHint "[name]"`, and the autocomplete gesture model #122 actively invites typing one) fell through to the ordinary reply path — forwarded with no pull and no notice. Fix: a `commandWord` helper (first whitespace-delimited token, `unicode.IsSpace` so tabs count) now drives **both** dispatch paths, so they agree on what counts as a command with a trailing argument: - the clear hook matches on the word; the argument survives the forward verbatim (pull-then-forward, as intended); - `/pull-base` bare executes, any argument — space- **or tab**-separated (previously leaked to the provider) — is the 400 refusal. ## 2. No-identity refusal blocked even a fast-forward The `ErrNoAuthorIdentity` refusal ran before any git did (inherited from crmerge, where every merge authors a commit), so a repo with no configured author identity could never `/pull-base` at all — even when the pull would trivially fast-forward, which authors no commit. Since ff-ness is unknowable before the fetch, the check had to move below it: `gitx.PullBase` now refuses with a new `ErrAuthorIdentityRequired` sentinel only on the diverged path, after the fetch but before the merge touches the worktree (untouched on refusal); the pull service maps that to `ErrNoAuthorIdentity`, so the HTTP classification (409) is unchanged. ## Tests - `TestAPI_ClearHook_argBearingClearPulls` — `/clear my-session` pulls first, forwards verbatim. - `TestAPI_ClearHook_prefixIsNotClear` — `/clearx` is not a clear (word match, not prefix). - `TestAPI_PullBase_tabArgRejected` — tab-separated argument 400s. - `gitx.TestPullBase_fastForwardEmptyIdentity` / `TestPullBase_divergedEmptyIdentityRefused` — identity needed only when a merge commit is authored; refusal leaves HEAD unmoved, no `MERGE_HEAD`, clean tree. - `pull.TestPullBase_noIdentityFastForwardSucceeds` / `TestPullBase_noIdentityDivergedRefused` — service-level acceptance, replacing the old up-front-refusal test. Full `go test ./...` green (gofmt/vet clean). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SG5V7rCpXuWvkD6Uv4BjHB
fix(pull-base): word-match the clear hook; defer no-identity refusal to diverged pulls
All checks were successful
ci / native (pull_request) Successful in 5m20s
0ebca59728
Two gaps from #149, both defeating the pull in cases the design covers:

- isClearCommand matched the provider's role=clear entry by exact string,
  so an arg-bearing clear (claude's "/clear my-session" — its ArgHint
  invites one) fell through to the plain reply path with no pull and no
  notice. Dispatch now matches on the command word (first whitespace-
  delimited token) for both the clear hook and /pull-base, so the two
  paths agree; the argument survives the forward verbatim, and a
  tab-separated /pull-base argument now 400s instead of leaking to the
  provider.

- The no-author-identity refusal fired before any git ran, blocking even
  a fast-forward pull that authors no commit and needs no identity. gitx
  now refuses (ErrAuthorIdentityRequired) only on the diverged path,
  after the fetch but before the merge touches the worktree; the pull
  service maps it to ErrNoAuthorIdentity so the HTTP seam is unchanged.

Closes #151

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SG5V7rCpXuWvkD6Uv4BjHB
Merge branch 'main' into afk/151
All checks were successful
ci / native (pull_request) Successful in 5m8s
bc084c9e7f
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!153
No description provided.