Repository navigation
feat(review): open on a caller-specified base and diff type (--base / --diff-type) - #1484
Merged
Merged
Conversation
Adds two session-only flags to plannotator review, parsed in the shared parser so every host inherits them together: - --base <ref> opens the session against a caller-chosen compare target (branch, origin/<branch>, tag, SHA, HEAD~N), probed with git rev-parse --verify --end-of-options before the server starts so a typo'd ref is a startup error with near-match suggestions instead of a silently mislabelled merge-base->HEAD diff. - --diff-type <id> opens the session in one of the nine flat git diff modes (REVIEW_OPEN_DIFF_TYPES, pinned against GIT_DIFF_TYPES). The flags are a seed, never a setting: nothing writes config.json or any review cookie, and the UI stays fully mutable. Validation is pure in packages/shared/review-open-state.ts (provider matrix errors on jj/GitButler/P4/workspace/PR mode, promote-with-notice when the saved default is base-irrelevant, fatal explicit contradiction). A flagged base rides explicitBase semantics: the new initialBaseExplicit server option (both runtimes) seeds baseExplicitlyChosen, suppressing the startup origin/* upgrade and canonicalization, and openStatePinned rides /api/diff so the client neither offers the first-run setup dialog (its one-time cookie is NOT consumed) nor runs the panel-pair self-heal for a pinned session. The since-base dropdown label now renders from the live active base, matching the adjacent base picker. Coverage: Bun CLI, opencode-review bridge, OpenCode embedded plugin, and the Pi extension (re-vendored; strict validation on the slash-command path only, programmatic callers unchanged). Skills, command stubs, help text, and docs updated across every host surface.
backnotprop
force-pushed
the
feat/review-open-state
branch
from
September 10, 2026 03:51
18880b0 to
d393121
Compare
1 task
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.
What
Implements the review open-state feature:
plannotator reviewcan open on a caller-specified base ref and diff type, as per-invocation session state that persists nothing.Built on #1483 (strict argument parsing), which is this PR's base branch.
Parser (
packages/shared/review-args.ts)--base <ref>/--diff-type <id>, space-separated, at most once, value consumed before the positional collector (so--base main <PR_URL>keeps the URL). Missing/duplicate/invalid values and unknown diff ids areerrors[]entries.REVIEW_OPEN_DIFF_TYPESexported and test-pinned againstGIT_DIFF_TYPES(now exported from vcs-core).Pure validator (
packages/shared/review-open-state.ts, new)--basewith a base-irrelevant saved default promotes the session tosince-basewith one stderr notice; the explicit--base X --diff-type uncommittedcontradiction is fatal.Base ref not found+ up to five near-match suggestions) — the guard against review-core's silentmerge-base → HEADdegrade under a confident label.--baseis given, the base-relative diff type is requested explicitly, so--base trunkmakes since-base work even on repos wheregetGitContextcannot offer it (spec §4.6).CLI (
apps/hook/server/index.ts, both thereviewbranch and theopencode-reviewbridge)git rev-parse --verify --quiet --end-of-options "<ref>^{commit}"probe before the server starts; suggestions come fromlistBranchesonly on the failure path.requestedBase/requestedDiffTypeintoprepareLocalReviewDiffand forwardsinitialBase(the base the patch was actually computed against) +initialBaseExplicit+openStatePinnedintostartReviewServer.Server (both runtimes:
packages/server/review.ts+apps/pi-extension/server/serverReview.ts)initialBaseExplicitoption seedsbaseExplicitlyChosen: no bare-local-name →origin/*canonicalization, no startup base upgrade, no "Baseline is behind" nagging —--base mainmeans the local ref on purpose. Opt-in and additive: Pi's documented forward-the-local-name-and-let-it-upgrade behavior is unchanged without it (test-pinned).openStatePinnedoption rides/api/diffonly (mount is the only place it matters).Client (
packages/review-editor)shouldOfferReviewSetuppredicate, composed beforeinitializeReviewSetup()in the&&chain — the one-time setup cookie is consumed by calling that function, so a pinned session skips the dialog without consuming it (order pinned by a source-level test).shouldRepairPanelPairguard: the panel-pair self-heal fully bails for a pinned session (its other half is a config.json write).--diff-type commit:<sha>(no behavior change in v1).--baselaunch. The two comments claiming a "live header" drives this (DiffTypePicker.tsx,review-core.ts) are deleted — there is no such on-screen header.Pi + OpenCode
review-open-stateadded tovendor.sh);/plannotator-reviewforwardsbase/diffTypewith a newopenStateFromFlagsopt-in onCodeReviewOptionsthat turns on strict validation + the explicit/pinned bits for the slash-command path only; parse/validator errors surface through Pi's notifier. ProgrammaticopenCodeReviewcallers are byte-identical.startReviewServerinjectable throughCommandDepsfor tests.rawArgsforward + the bridge branch above.Skills / docs (§10 sweep run at implementation time)
git grep -ln "plannotator review\|plannotator-review"over apps/skills, apps/kiro-cli, apps//commands, apps//skills — every hit updated: core knowledge skill (usage line, session-only semantics, the stacked-branch hint, git-only note), core + Claude + Kiro review skills, Kiro agent prompt, OpenCode/Copilot/Gemini command stubs, Droid README, Amp helpText, Pi command description. Pluscli.tshelp +cli.test.tsliterals, marketing docs (code-review.mdnew "Opening on a specific base" section incl. the no-behind-banner note;configuration.md;opencode.md), and the CLAUDE.md "review OPENS on" sentence.apps/kiro-cli/README.mdhits name the skills without enumerating flags — no change needed.droid-plugin/commands/plannotator-review.jsverified as pure passthrough.Nothing is persisted (the maintainer's constraint)
No
POST /api/config, no cookie writes, nodefaultDiffType, noreviewPanelView, no last-used memo, and the review-setup-seen cookie survives untouched. Pinned-session no-persistence is test-pinned (config.json byte-identical after a flagged session), and the smoke run confirmed no config.json is created.Tests
review-args.test.ts: value parsing, URL preservation, missing/dup values, unknown diff id (lists the set), range-syntax rejection,REVIEW_OPEN_DIFF_TYPES == GIT_DIFF_TYPES.review-open-state.test.ts(new, pure): full provider/PR/workspace matrix, promote-with-notice, contradiction, base-not-found, near-match suggestions.packages/server/review-open-state.test.ts(new, dual-runtime Bun + Pi): explicit base served verbatim; startup upgrade suppressed for an explicitmainon a repo withorigin/main(with the unpinned control proving the probe fires); canonicalization off on a non-explicit switch echo;openStatePinnedpresent only when set; config.json byte-identical after a pinned session.shouldOfferReviewSetup/shouldRepairPanelPairpure tests, cookie-not-consumed composition test, source-level pin of the&&-chain order; DOM-gatedDiffTypePickerlive-label tests (incl. SHA shortening and the no-live-base fallback).handleReviewCommandtests (flag base lands in both the patch and the server options; unresolvable base refuses to start and never reachesstartReviewServer).Revert-verify evidence: server tests fail 4/10 without the server change; parser tests fail to load on the pre-flag parser; the App source-order pin and the DiffTypePicker DOM tests fail against the pre-change files.
bun run typecheckclean (includes Pi re-vendor); builds verified in order (review → hook → opencode);check:manifestin sync (viewer graph untouched);DOM_TESTS=1 bun test --isolategreen on the touched DOM suites; fullbun testunder a tempPLANNOTATOR_DATA_DIR: 4413 pass / 899 skip / 0 fail (5312 tests, 473 files).Out of scope (per spec §1.1)
--branch,--hide-whitespace,commit:<sha>/worktree/gitbutler/jj diff-type values, jj--base(the error message namesjj-lineas the future), persisting the flags, a "reset to launch state" affordance, PR-URL mode support.