feat(team-mode): remote event-log adapter — Drive + LocalFolder backends (#277)#289
Merged
Conversation
…nds (#277) Closes #277. Implements v0 Productization §2: shifts team mode entirely off git as the inter-machine replication substrate, onto a pluggable backend with two ship-day implementations (LocalFolder, GoogleDrive). Pull-only sync; no daemons, no webhooks, no Bicameral server in the loop. What changes for users - Setup wizard team-mode branch now offers Create vs Join vs LocalFolder. Create: provisions a Drive folder under the operator's Google account, prints the literal share-text-to-teammates message. Join: paste folder ID/URL, OAuth, verify access (404 / read-only both block), confirm the resolved signer (default-No) before persisting. LocalFolder: single prompt for the path. - Drive integration uses Bicameral's bundled OAuth client (the same pattern gh / gcloud / cursor use). Scope: drive.file only — Bicameral's CLI can only see files it creates inside the team folder. Token cache at ~/.bicameral/google-drive-token.json mode 0600. - Colored security disclosure renders before the browser opens, walking the operator through what flows where, what we do and don't see, and the trust dependency. Mirrored on bicameral-ai.com/privacy (BicameralAI/bicameral PR #111). Architecture - events/backends/__init__.py — BackendAdapter ABC + get_backend factory. - events/backends/local_folder.py — sha256-idempotent LocalFolderAdapter. - events/backends/google_drive.py — Drive Files API adapter; bundled client_id + client_secret (RFC 8252 native-app pattern, no env override per Option A); FolderNotFoundError / ReadOnlyAccessError surface for Join verify_access; create_folder helper for Create branch. - events/team_adapter.py — TeamWriteAdapter accepts backend=, marks _dirty on every write, exposes flush_to_backend(). - adapters/ledger.py — _read_collaboration_mode refactored to _read_team_config(repo_path) -> dict; constructs backend and injects into TeamWriteAdapter. - handlers/sync_middleware.py — ensure_team_synced (30 s TTL pull) + flush_team_writes (post-handler push); errors swallowed at DEBUG. - server.py — wires both into the dispatch site (pull at top, flush in finally). - setup_wizard.py — Create/Join/LocalFolder dispatch + colored security disclosure + identity-confirmation prompt at Join time. Testing - 53 new tests, 1 platform-skip (Windows-only path): - LocalFolderAdapter: 6 tests (push idempotency, pull peer-files-only, list_peers, lock serialization) - TeamWriteAdapter ↔ backend: 3 tests (connect-pulls-then-replays, write-marks-dirty-then-flush-pushes, no-backend-noop) - Two-author round-trip: 2 tests - Sync middleware: 5 tests (TTL cache, no-backend-noop, error swallowing) - GoogleDriveAdapter: 11 tests (push idempotency on md5, pull own-file-skip + max-modifiedTime token, lock create-then-delete + cleanup on exception, verify_access 404 / read-only / can-edit, create_folder, placeholder-detection auto-skip when bundled client is published) - Setup wizard Create/Join: 11 tests including identity decline, OAuth-disclosure decline, folder-id URL extraction, unwritable-path rejection - All adjacent regression tests still pass (test_team_event_replay, test_event_writer). - Lint clean across events/ adapters/ handlers/sync_middleware.py setup_wizard.py + new test files. Security model (also documented at docs/team-mode-setup.md and on bicameral-ai.com/privacy) - Decision data flows your-CLI ↔ Google directly. Bicameral the company does NOT receive copies. No Bicameral server in the loop. - drive.file scope limits the CLI on the user's machine to files it creates in the team folder. The rest of the user's Drive is invisible to the CLI; Google enforces this server-side. - As OAuth app publisher, Bicameral receives aggregate API request counts and per-user OAuth consent records (which Google accounts authenticated, when). Not contents. - Trust dependency: same as any OAuth tool (gh, gcloud, Notion, Slack desktop) — open-source CLI behaves as advertised, mitigated by source visibility. OAuth verification submission text + GCP setup checklist: docs/google-oauth-verification-submission.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
8 tasks
Pure formatting — `ruff format` against the 10 files touched in #277. No semantic changes. CI's `ruff format --check .` now passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…endAdapter Mypy was failing on `events/backends/__init__.py:62,66` — the factory's return type is `BackendAdapter | None`, but the two concrete adapters were structurally compatible without declaring inheritance. Added explicit `BackendAdapter` base. Both classes already implemented all four abstract methods (push_events, pull_events, lock, list_peers) — runtime check (issubclass + concrete instantiation) passes. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jinhongkuan
pushed a commit
that referenced
this pull request
May 9, 2026
Triages 25 dev commits onto main (already on dev as of merge time): • #289 — team-mode remote event-log adapter (#277) • #285, #284, #283 — M2 grounding telemetry, eval harness, precision fix (#280) • #275 — README/SECURITY surface • plus assorted fixes flowing through dev Resolved conflicts in CHANGELOG.md (kept dev's [Unreleased] block, inserted v0.14.2's release entry from main below it, then renamed [Unreleased] → v0.14.3) and README.md (kept dev's Solo-vs-Team mode section + extended setup-writes table from #289 — main was missing both because PR #289 hadn't backflowed yet). pyproject.toml: 0.14.2 → 0.14.3 RECOMMENDED_VERSION: 0.14.1 → 0.14.3 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
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.
Closes #277.
Summary
BackendAdapterwith two ship-day implementations:LocalFolderAdapter(NFS / Dropbox / syncthing) andGoogleDriveAdapter(per-team Drive folder + bundled OAuth client). Pull-only sync — no daemons, no webhooks, no Bicameral server in the loop.signer_email_fallbackpolicy at the moment it matters.gh/gcloud/cursoruse; RFC 8252 native-app pattern). Scope:drive.fileonly. Token cache at~/.bicameral/google-drive-token.jsonmode 0600.Architecture
events/backends/__init__.pyBackendAdapterABC +get_backend(config)factoryevents/backends/local_folder.pyLocalFolderAdapterevents/backends/google_drive.pyclient_id+client_secret;FolderNotFoundError/ReadOnlyAccessErrorfor Join verify;create_folderfor Createevents/team_adapter.pyTeamWriteAdapteracceptsbackend=, marks_dirtyon writes, exposesflush_to_backend()adapters/ledger.py_read_collaboration_mode → _read_team_config(repo_path) -> dict; constructs backend, injectshandlers/sync_middleware.pyensure_team_synced(30 s TTL pull) +flush_team_writes(post-handler push); errors swallowed at DEBUGserver.pyfinally)setup_wizard.pyWhy bundled OAuth client (not operator-supplied)
Originally drafted with operator-supplied OAuth client (env var or
~/.bicameral/google-drive-client.json) per overcautious audit reading of OWASP A05. UX cost was a 5-step GCP console dance per user, killing onboarding. Pivoted to bundled-client per RFC 8252 native-app pattern (industry standard:gh,gcloud,cursor, every dev-tool OAuth client). Justification + threat-model indocs/google-oauth-verification-submission.md.The
client_secretin source is not a confidentiality boundary — it's a shared identifier. The actual security model is the consent screen + Google's verified-app badge + the open-source CLI auditability. (GitHub secret-scanning push protection flagged the secret on push; deliberately unblocked.)Security model (mirrored at bicameral-ai.com/privacy)
drive.filescope limits the CLI on the user's machine to files it creates in the team folder. Rest of user's Drive is invisible to the CLI; Google enforces server-side.Test plan
pytest tests/test_backends_local_folder.py tests/test_backends_google_drive_unit.py tests/test_team_adapter_with_backend.py tests/test_team_round_trip_local_folder.py tests/test_sync_middleware_team.py tests/test_setup_wizard_team_backend.py— 53 pass, 1 platform-skippytest tests/test_team_event_replay.py tests/test_event_writer.py— adjacent regression, 15 pass + 1 platform-skipruff check events/ adapters/ledger.py handlers/sync_middleware.py setup_wizard.py tests/test_*team*.py tests/test_backends_*.py tests/test_sync_middleware_team.py— cleanjin@bicameral-ai.com): runbicameral-mcp setup → team → Create, complete browser OAuth, verify folder is created in Drivedocs/google-oauth-verification-submission.mdso non-test-user flows don't see the unverified-app warningOut of scope (separate issues)
Companion PRs
BicameralAI/bicameral#111— privacy page rewrite mirroring the corrected CLI disclosure🤖 Generated with Claude Code