From a9edbbf6e9bc9f38e4bda260b652d6b863f6b9c1 Mon Sep 17 00:00:00 2001 From: Zack Story <2302315+rt2zz@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:34:02 -0700 Subject: [PATCH 1/3] feat(sync): gate a peer's publishing on review A peer whose committed .sidecar sets `review = "required"` publishes nothing until a review approves exactly what would leave the machine. Redaction catches secrets that look like secrets; a reviewer, usually a coding agent, catches what pattern matching cannot. - Gated peers never push local history: each approved review publishes one commit of the reviewed tree on the public base it was measured from, and the canonical merge is built only from what the remote already holds. Local main is never reset and is joined to each publish by a link merge, so later edits merge without spurious forks. Reviews are per repo family and cover local main, so a rejection holds every sibling worktree. - Publishing git runs hermetically (withPublicGit): fixed identity, no hooks, rerere, signing, push options, replacement objects, or local attributes, drivers, and filters. "Already public" comes only from sidecar's own refs/sidecar-public fetch. - Review records and packets live in the state dir. A detached runner starts the machine-local reviewer command; verdicts come from `sidecar review approve|reject`, taken under the sync lock, so a reviewer can hand a review off to a discussion and decide later. Reviews undecided past `review-timeout`, rejected, failed, or without a reviewer need attention in sync, status, and the health heartbeat, which never carries a reason. - .sidecar parsing is now strict: unknown keys and other versions are refused, so a policy key an older CLI doesn't know fails closed. - Add `sidecar review`, `init --review/--review-timeout`, optional `.sidecar-review` guidance, docs/review.md, and tests. --- README.md | 5 + docs/agents.md | 16 + docs/commands.md | 42 +- docs/review.md | 237 +++ docs/rules.md | 3 +- docs/sync.md | 5 + packages/sidecar/CLAUDE.md | 31 +- packages/sidecar/dist/cli.js | 1759 ++++++++++++++++---- packages/sidecar/src/cmd-init.ts | 20 +- packages/sidecar/src/cmd-refresh.ts | 109 +- packages/sidecar/src/cmd-review.ts | 239 +++ packages/sidecar/src/cmd-status.ts | 45 +- packages/sidecar/src/cmd-sync.ts | 85 +- packages/sidecar/src/commands.ts | 13 +- packages/sidecar/src/config.ts | 74 +- packages/sidecar/src/git.ts | 19 +- packages/sidecar/src/health.ts | 30 +- packages/sidecar/src/install.ts | 11 + packages/sidecar/src/review.ts | 599 +++++++ packages/sidecar/src/state.ts | 37 +- packages/sidecar/src/sync.ts | 599 ++++++- packages/sidecar/src/util.ts | 5 + packages/sidecar/tests/cli.test.ts | 30 + packages/sidecar/tests/cmd-sync.test.ts | 1 + packages/sidecar/tests/health.test.ts | 29 + packages/sidecar/tests/integration.test.ts | 159 +- packages/sidecar/tests/review.test.ts | 278 ++++ skills/sidecar/SKILL.md | 16 +- 28 files changed, 4035 insertions(+), 461 deletions(-) create mode 100644 docs/review.md create mode 100644 packages/sidecar/src/cmd-review.ts create mode 100644 packages/sidecar/src/review.ts create mode 100644 packages/sidecar/tests/review.test.ts diff --git a/README.md b/README.md index e77822a..ad2adb3 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ while a background daemon keeps every clone in sync. - **Secrets stay home.** Pasted API keys and tokens are redacted from pushes while your local files stay untouched; PII redaction is one flag away — [how redaction works](docs/redaction.md). +- **Optional review before publishing.** Turn on `review = "required"` and + nothing leaves the machine until a coding agent (or you) approves it, for + the sensitive content that pattern matching can't see — + [how review works](docs/review.md). ## Quickstart @@ -102,6 +106,7 @@ Every command acts on all peers unless `--peer` names one — - [Peers](docs/sync.md#peers-several-sidecars-in-one-repo) — several sidecars in one repo, one committed and one ignored - [Per-file rules](docs/rules.md) — glob-based merge and redaction policy for each peer - [Redaction](docs/redaction.md) — what's stripped from pushes and how to review it +- [Review before publishing](docs/review.md) — gate a peer's pushes on an agent's or your approval - [Editor search visibility](docs/editors.md) — making gitignored files searchable - [Global vs local installs](docs/install.md) — the newest install wins - [All commands](docs/commands.md) diff --git a/docs/agents.md b/docs/agents.md index 51148d1..10009b9 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -82,6 +82,22 @@ checkout: let that operation finish before retrying. Repeated failures need diagnosis using the reported error, `sidecar status`, `sidecar tail`, or `sidecar health`; force-refreshing or removing a lock is not routine recovery. +## When publishing is held for review + +A peer with `review = "required"` publishes nothing until a review approves +it. `sidecar sync` succeeds and says the work is held; that does not mean it +reached the remote. Report a held sync as held. Check `sidecar status` for the +review's state, and leave the decision to the reviewer or the person the user +named. + +An agent acting as the reviewer reads the packet it was given (start with +`$SIDECAR_REVIEW_PROMPT`). It decides with `sidecar review approve ` or +`sidecar review reject --reason ""`, without +repeating the sensitive value in the reason. When unsure, it leaves the review +open and asks. It does not approve its own changes to get a handoff through +faster, and it does not edit `.sidecar` to turn review off. See +[review.md](review.md). + ## Choose how concurrent work survives Use separate files for independent handoffs when possible. A single shared diff --git a/docs/commands.md b/docs/commands.md index 517988f..5bc98e8 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -16,6 +16,8 @@ sidecar clone # clone or update the configured sidecar repo sidecar refresh # delete the sidecar checkout and clone it again sidecar merge --fork-files # merge inbox branches and preserve conflicts sidecar redactions # preview what redaction changes on the next push +sidecar review # list the reviews gating a review-required peer +sidecar review approve # publish what a review covers sidecar instances # list known local sidecar checkouts sidecar daemon status # report daemon health, service state, settings paths sidecar daemon restart # restart the background auto-sync process @@ -51,9 +53,11 @@ repository yet becomes one when a remote is named: `sidecar init | `--inbox