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
Closed
feat: Commit Review, per-repo .gitwandrc opt-in + pre-commit hook (v3.7.0 part 3)#163devlint wants to merge 7 commits into
devlint wants to merge 7 commits into
Conversation
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.
5 tasks
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
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.
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.
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:
.gitwandrcper-repo opt-in — acommitReviewblock (enabled,minConfidence,maxFindings,maxFiles) inpackages/core's config schema, validated defensively like the existingsecretsblock (zero Node.js imports, browser-compatible). A repo's.gitwandrccan force the feature on or off independently of the user's global Setting, in both directions.pre-commitscript 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 scaninvocation is byte-identical to the shipped version. The review section can never fail a commit (always exits 0).HooksPanel.vuegets 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
.gitwandrcoverride 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 reactiveeffectiveEnabledvalue throughApp.vueand 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:.gitwandrccommitReview block parsing/validation)cd apps/desktop && pnpm test -- --run— 859/859 passing (104 files, +28 vs PR2: rc-override gating, theeffectiveEnabledUI-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— cleanvue-tsc --noEmit+vite buildsecretsHook.tsinvocationpnpm test:parityunaffected — zero new Tauri commands, zero Rust files touched