Skip to content

feat: Commit Review, per-repo .gitwandrc opt-in + pre-commit hook (v3.7.0 part 3) - #163

Closed
devlint wants to merge 7 commits into
feat/v3.7-commit-review-pr2from
feat/v3.7-commit-review-pr3
Closed

feat: Commit Review, per-repo .gitwandrc opt-in + pre-commit hook (v3.7.0 part 3)#163
devlint wants to merge 7 commits into
feat/v3.7-commit-review-pr2from
feat/v3.7-commit-review-pr3

Conversation

@devlint

@devlint devlint commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Summary

Third and final slice of ROADMAP.md's v3.7.0 "Commit Review" feature, stacked on #160 (part 2) and #159 (part 1). This PR adds:

  • .gitwandrc per-repo opt-in — a commitReview block (enabled, minConfidence, maxFindings, maxFiles) in packages/core's config schema, validated defensively like the existing secrets block (zero Node.js imports, browser-compatible). A repo's .gitwandrc can force the feature on or off independently of the user's global Setting, in both directions.
  • Composable pre-commit hook — the existing v3.5.0 secrets hook and this feature's new warn-only review reminder now share a single GitWand-managed pre-commit script with parseable sections, instead of one clobbering the other. An installed v1 secrets-only hook is correctly recognized and migrated. The secrets section's enforcing @gitwand/cli scan invocation is byte-identical to the shipped version. The review section can never fail a commit (always exits 0). HooksPanel.vue gets two independent Install/Remove rows.

This PR went through two rounds of implementation + adversarial verification. The first round's fix was structurally correct but left the .gitwandrc override completely disconnected from the UI: forcing the feature on via config did nothing (no button ever appeared), and forcing it off left a dead button plus the Review/Vouch/Skip decision modal still popping open on every commit with a misleading "no AI provider" error. The second round wired a proper reactive effectiveEnabled value through App.vue and fixed an unrelated reactivity regression the same fix had introduced (Settings changes to the review threshold/cap stopped live-updating the findings list). A final trivial dead-code cleanup (two unused config fields left over from the reactivity fix) was applied directly after the second verification pass confirmed everything else.

Test plan

  • pnpm --filter @gitwand/core run test -- --run — 1066/1066 passing (+10 vs PR2's baseline: .gitwandrc commitReview block parsing/validation)
  • cd apps/desktop && pnpm test -- --run — 859/859 passing (104 files, +28 vs PR2: rc-override gating, the effectiveEnabled UI-gate fix in both directions, live threshold/cap reactivity, hook script round-trip for all 4 section combinations, v1 migration, foreign-hook safety)
  • cd apps/desktop && pnpm build — clean vue-tsc --noEmit + vite build
  • Manual verification (by the verifier, reading code side-by-side) that the rewritten hook's secrets section is byte-for-byte identical to the shipped v3.5.0 secretsHook.ts invocation
  • pnpm test:parity unaffected — zero new Tauri commands, zero Rust files touched

Laurent Guitton added 7 commits August 19, 2026 10:46
… pass

Adds a commitReview block to GitWandrcConfig (enabled/minConfidence/
maxFindings/maxFiles), validated with the same defensive style as the
existing secrets block: booleans type-checked, numbers range-checked,
unknown keys ignored, empty block omitted.

useCommitReview resolves this block the same way useSecretsScanner
resolves its own gitwandrc block, cached per repo, and lets it override
the app Settings in both directions (a repo can force the review pass
on or off, or tune its confidence threshold / caps, regardless of the
global setting). run() and the coverage-recompute helpers now gate on
this resolved config instead of reading settings.commitReviewEnabled
directly.
…iew sections)

.git/hooks/pre-commit can only hold one script, and the v3.5.0 secrets
hook already owned it outright via gitHookCreate's overwrite semantics.
Introduces gitwandHook.ts: a single GitWand-managed script builder with
a v2 marker and parseable section boundaries, so a warn-only Commit
Review reminder section can be installed alongside (or independently
of) the secrets-scanning section without either clobbering the other.

parseGitwandHookSections recognizes a previously-installed v1
secrets-only script (secretsHook.ts, now marked deprecated but kept for
this migration) as secrets:true/review:false, and returns null for any
foreign hook. The secrets section's npx --no-install @gitwand/cli scan
--staged --strict --json invocation is byte-identical to the shipped
v3.5.0 script; the new review section always exits 0.
HooksPanel's single "Secrets pre-commit hook" row becomes two
independent rows (Secrets / Commit review), each driven by
parseGitwandHookSections and each with its own install/remove flow.
Both write through the same buildGitwandHookScript builder so
installing or removing one section never clobbers the other; removing
the last remaining section deletes the hook file entirely. Reuses the
existing askConfirm modal gate for both rows, matching the prior
secrets-only confirm copy style.

Adds the settings.commitReview.rcOverrideHint line to the AI tab
noting that a repo's .gitwandrc can force Commit Review on or off, and
adds every new user-visible string (hook row labels/confirms/errors,
the rc override hint) to all 5 locales.
…threshold/cap

HIGH: App.vue read settings.value.commitReviewEnabled directly at three
call sites (the Review staged changes button, the n/p/x shortcut guard,
and the commit-time decision gate), so a .gitwandrc override never
reached the UI in either direction. A repo forcing the feature on had
no reachable path into run(), and worse, a repo forcing it off still
showed the button, still popped the Review/Vouch/Skip modal on every
commit, and still wrote a GitWand-Review trailer.

useCommitReview now exposes effectiveEnabled, a computed that resolves
.gitwandrc's commitReview.enabled against the app setting for whatever
repo is currently active, refreshed by onStagedSetChanged (the same
per-repo refresh App.vue's existing repoFolderPath/staged-count watcher
already triggers) and by run(). App.vue's three call sites now read
this instead of the raw setting.

MEDIUM: effectiveThreshold/effectiveCap were one-shot refs written only
inside run(), so changing the Review AI confidence threshold or max
findings cap in Settings after a run had no visible effect until the
next full review. They are now computeds reading the same rc-override
state reactively, so the findings list re-filters immediately on a
Settings change.

Adds effectiveEnabled direction tests (rc forces on/off against the
opposite app setting), a live-refresh test via onStagedSetChanged with
no LLM call, a no-repo-open fallback test, and two reactivity tests
proving a live Settings change re-filters findings without a new run.
Also adds the previously-missing maxFiles/maxFindings override tests.
…mit script

LOW #1: hooks.secretsInstallConfirmMessage and secretsRemoveConfirmMessage
in all 5 locales still described the old single-hook-file behavior
(any existing hook overwritten, file always deleted on removal). Now
that installing/removing writes through the shared sectioned script
builder, installing over a GitWand-managed hook merges sections rather
than overwriting, and removing secrets while commit review stays
installed rewrites the file to review-only instead of deleting it.
Updated the copy in all 5 locales to describe this accurately.

LOW #3: the pre-commit hook's warn-only review reminder claimed "this
commit was made from the terminal", which is wrong for a GUI commit
that reaches the hook without --no-verify (harmless since GitWand
discards hook stdout on a successful GUI commit, but still inaccurate).
Reworded to a generic "commit review did not run for this commit"
that does not assume the commit's origin. This text lives in the
generated bash script itself, not in the i18n locale files, so there
is no per-locale variant to update for it.
…eCommitReviewConfig

Third verifier pass, LOW-2: the fix that made effectiveThreshold/
effectiveCap into computeds removed their only readers (the one-shot
assignments inside run()), leaving resolveEffectiveConfig's threshold
and cap fields produced but never consumed anywhere.
… fix stale zero-IPC doc

Fourth verifier pass (direct review), three confirmed findings:

- HIGH: rcOverride.value was written unconditionally in refreshRcOverride,
  unlike coverage.value which is guarded by coverageGeneration. A slow
  first-visit .gitwandrc read for a repo the user already left could
  resolve after a newer repo's read and overwrite effectiveEnabled with
  the wrong repo's config. Now guarded by the same coverageGeneration
  counter already protecting coverage.value.

- MEDIUM: HooksPanel.vue's install/remove handlers for the secrets and
  review hook sections used two independent busy flags but all
  read-modify-write the same hookSections.value snapshot through a single
  full-file rewrite. Installing both in quick succession could let one
  write silently clobber the other. Both sections now share one busy
  gate (hookWriteBusy), checked both in the template and at each
  handler's entry.

- The file's own doc comment claimed zero IPC when Commit Review is
  disabled; that's no longer true since .gitwandrc must be read to
  support a per-repo force-on override. Corrected the comment and the
  two tests that were asserting a stale "zero IPC" claim without ever
  checking readGitwandrcMock.
@devlint

devlint commented Aug 19, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #164, which combines all three PRs into a single review surface for the full v3.7.0 Commit Review feature.

@devlint devlint closed this Aug 19, 2026
devlint added a commit that referenced this pull request Aug 19, 2026
Combines the full v3.7.0 "Commit Review" feature, originally built and adversarially reviewed across three stacked PRs (#159, #160, #163), then further product- and code-reviewed as a whole and fixed here (see commit history for the detailed round-by-round verification trail).

- Review staged changes: AI pass over the staged diff, inline findings with severity badges, n/p/x navigation
- Fix with agent: pipe findings into a terminal AI agent session
- Iterations & coverage tracking, bound to HEAD
- Review / Vouch / Skip commit-time decision, recorded as a GitWand-Review trailer
- Per-repo .gitwandrc opt-in overriding the global setting in either direction
- Composable pre-commit hook merging the shipped secrets-scanner section with a new warn-only review reminder
- Fixed a core reactivity bug where routine background polling silently wiped findings, plus 13 other findings from a dedicated product/code review round

1067 core + 959 desktop tests, parity suite green, clean build, zero new Tauri commands.
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