Skip to content

Add cairn review CLI verb: list/show/merge for shared-tier review branches#40

Merged
quad341 merged 5 commits into
mainfrom
deploy/crn-ucxp-gate
Jul 23, 2026
Merged

Add cairn review CLI verb: list/show/merge for shared-tier review branches#40
quad341 merged 5 commits into
mainfrom
deploy/crn-ucxp-gate

Conversation

@quad341

@quad341 quad341 commented Jul 23, 2026

Copy link
Copy Markdown
Owner

What this changes

Adds cairn review {list,show,merge} — a CLI workflow for a librarian to
triage pending contributions on shared-tier remember/* branches before they
land on the default branch.

  • cairn review list [--tier <tier>] discovers pending remember/* branches
    and derives each entry's tier from its changed file path, so a librarian
    can see what's waiting without checking out anything.
  • cairn review show <branch> renders the diff plus the parsed Entry
    fields for one branch, so the librarian can read the actual content before
    deciding — this is the real gate, everything else is bookkeeping.
  • cairn review merge <branch> [--topic-key ...] [--scope ...] [--anchor-type ...] [--bead ...] [--allow-secret-pattern] curates the
    entry's frontmatter (patching only the fields the librarian specifies, not
    a full rewrite), merges with --no-ff, deletes the source branch, and
    reindexes. An obvious-secret-pattern check blocks the merge by default
    and requires an explicit flag to override.

Why one PR

This originally shipped as PR #36, but that PR is stale: main advanced twice
after #36 opened (once for unrelated helper functions, once for stale-branch
detection) and both times independently added a same-named helper that
collided with one already defined in this feature's own review.go. Both
collisions are resolved by renaming this feature's local helpers (not by
touching the other side), so this PR carries only this feature's 4 files, no
changes to the code it previously collided with. #36 is closed as
superseded by this PR.

Review notes

  • merge reuses the existing ValidatePathSegment guard on topic_key,
    scope, and anchor_type — the last one closes a newline-injection
    gap (an unvalidated --anchor-type value could corrupt the default
    branch's TOML frontmatter and break every read path store-wide) found and
    fixed during review; see TestMergeReviewBranchRejectsInvalidAnchorType.
  • On a merge conflict, merge runs git merge --abort and leaves the
    default branch exactly as it was — no partial state — even though a
    frontmatter curation commit may already have landed on the source branch
    itself (that commit is inert until a successful merge; a retry is
    idempotent).
  • The secret-pattern check is a narrow, named-pattern backstop (6 patterns:
    AWS/GitHub/Slack/Google/Stripe/private-key) on the single changed file —
    by design, not a substitute for cairn review show.

Test plan

  • go test ./... -race -count=1 — all 4 packages, zero regressions
  • gofmt -l ., go vet ./..., golangci-lint run ./... — all clean
  • Reviewer-verified acceptance criteria, including a full request-changes
    → fix → re-review PASS round trip on the anchor-type validation gap
  • Release gate: release-gates/cairn-review-cli-verb-gate.md

🤖 Deployed by actual-factory

quad341 and others added 5 commits July 22, 2026 06:35
…nches

Implements AF1/AF2 of crn-xw3's design: the review half of the propose/
review flow crn-419's remember command already writes one side of.
review list derives tier from the changed entry file's path (never the
branch name); review show renders the diff plus a parsed Entry so a
reviewer can inspect real content before any merge; review merge
surgically patches only reviewer-specified frontmatter fields, blocks
on an obvious secret pattern unless overridden, --no-ff merges with the
"librarian: merge <branch> — <title> (<bead>)" convention, deletes the
branch, and reindexes — aborting cleanly with no partial state on any
failure up through the merge itself.

Refs crn-ffm.
opts.AnchorType flowed straight into tomlQuote unvalidated, unlike
TopicKey/Scope. An embedded newline produced invalid TOML that merged
successfully before Reindex choked on it, corrupting the default
branch and breaking every store read path (IterEntries) until
hand-fixed. Validate AnchorType via ValidatePathSegment in
MergeReviewBranch, matching the existing TopicKey/Scope pattern, with
a regression test pinning the newline-injection case reported in
review.

Addresses the blocking finding from cairn/reviewer's crn-8emp review
(mail gm-wisp-9h17fbl).
…nches.go (crn-j1uh)

origin/main gained two independent features since this branch forked:
entry.go's own splitFrontmatter/tomlQuote (PR #24) and branches.go's own
ReviewBranch/ListReviewBranches for stale-branch tracking (PR #39, crn-0yv.1).
Both pairs collide with review.go's pre-existing identically-named
declarations once merged.

- tomlQuote: consolidated. entry.go's version is a strict, behavior-compatible
  superset for review.go's validated-input call sites (TopicKey/AnchorType/
  Scope, already passed through ValidatePathSegment); deleted review.go's
  duplicate, zero call-site changes needed.
- splitFrontmatter: renamed to splitFrontmatterForPatch in review.go. Not
  substitutable with entry.go's version -- different input type (string vs
  []byte), different return contract (bool-flag+body vs sentinel-error+
  closeAndBody), and review.go's closing-fence retention is required by
  patchFrontmatterFields for byte-exact surgical reassembly.
- ReviewBranch/ListReviewBranches: renamed to ReviewMergeBranch/
  ListReviewMergeBranches in review.go. branches.go's versions serve the
  librarian sweep's age/SHA-tracked notify-escalate bookkeeping; review.go's
  serve the interactive list/show/merge review flow. Different shapes for
  different concerns, developed independently -- not a merge candidate.

Verified: gofmt, go vet, go build, go test ./... -race -count=1, and
golangci-lint (cache-cleaned) all clean across every package, run from the
module root. Specifically confirmed both ListReviewBranches test families
(branches_test.go's, unchanged, and review_test.go's, renamed) pass side by
side with no cross-talk, and TestMergeReviewBranchRejectsInvalidAnchorType /
TestTomlQuoteEscapesBackslashAndDoubleQuote still pass against the shared
entry.go implementation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@quad341
quad341 merged commit f6970f5 into main Jul 23, 2026
2 checks passed
@quad341
quad341 deleted the deploy/crn-ucxp-gate branch July 23, 2026 05:30
quad341 added a commit that referenced this pull request Jul 23, 2026
PR #40 (review CLI verb) merged into origin/main after this branch's
merge base, adding review_test.go/cmd/review_test.go calls to
Find(store, id). This branch's Find takes a leading context.Context
(index-backed reads reconciliation). Update the 4 stale call sites to
pass t.Context(), matching sibling calls already in these files.
quad341 added a commit that referenced this pull request Jul 23, 2026
…ead of scanning entries (#42)

* fix(cairn): index-backed reads + entry_tags DDL race fix, squashed for rebase (crn-2xpm)

Squashes builder/crn-2xpm-reconcile's commits since merge-base f6970f5
(net diff only, unchanged) into one commit re-applied cleanly on current
origin/main, so a linear rebase no longer replays this branch's internal
merge commits' pre-resolution states as independent patches.

Deploy gate's attempt_bounded_self_rebase found a real conflict (rc=12)
rebasing this branch's history onto origin/main, even though a 3-way
git merge-tree of the same two trees was clean. Root cause: this branch
contains merge commits (9168234, 3479d32, cc6ceb2, 8d913c5) that already
3-way-resolved conflicts between parallel feature branches and against
origin/main. A linear rebase instead replays each original commit
individually -- including pre-resolution commits like 9724ae9, whose
lone-patch content still conflicts with what origin/main looks like now,
even though that conflict was already resolved once, downstream, via
those merge commits. Merging origin/main again (a third time) would not
fix this: it resolves the conflict at the merge point but leaves the
same problematic commit replayable-and-conflicting on any subsequent
rebase. Flattening the branch's own history is the only durable fix.

Net change (unchanged from e9cdcd2, verified identical via `git diff
--cached --stat` vs `git diff f6970f5 e9cdcd2 --stat`): the index-backed
reads stack (Status/Visible/Prime/Find all index-backed, ctx-threaded,
zero full-body TOML decode or hit_count writes on read paths), the
entry_tags DROP+CREATE DDL race fix + txlock=immediate (crn-j3k4), the
busy_timeout/WAL locking fix (crn-t250), crn-ln1's scopeMismatchWarnings
diagnostic reconciled onto the index-backed Status read, and the 4
review_test.go/cmd/review_test.go call sites updated to the new
Find(ctx, store, id) signature after PR #40 landed with the old one.

New SHA per crn-fie5 SHA-pinning mandate -- requires fresh reviewer
verdict; e9cdcd2's PASS does not carry over. Old branch
builder/crn-2xpm-reconcile (tip e9cdcd2, already-open PR #41) left
untouched as historical reference, same treatment as the PR it
superseded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore: release gate PASS for cairn-index-backed-reads-squashed

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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