Skip to content

feat(team-sync): #279 Phase 2 — team-mode integration for sync-and-brief#321

Merged
Knapp-Kevin merged 1 commit into
devfrom
feat/279-phase2-team-mode-integration
May 14, 2026
Merged

feat(team-sync): #279 Phase 2 — team-mode integration for sync-and-brief#321
Knapp-Kevin merged 1 commit into
devfrom
feat/279-phase2-team-mode-integration

Conversation

@Knapp-Kevin

Copy link
Copy Markdown
Collaborator

Summary

Phase 2 of #279. Wires the existing `BackendAdapter` interface (shipped in #277) into the `sync-and-brief` CLI flow. The team-mode complement to Phase 1's solo-mode session magic (PR #318, merged).

Order invariants

  1. BEFORE source pull: `backend.pull_events()` copies every peer's `.jsonl` into the local `.bicameral/events/` cache. The materializer's existing `*.jsonl` glob picks them up alongside the operator's own events. Peer ingest is visible in the brief without manual action.
  2. AFTER source ingest succeeds: `backend.push_events()` uploads each local `.jsonl` to the shared backend. LocalFolderAdapter's sha-match skip keeps re-invocations idempotent.

Failure modes (non-blocking by design)

Scenario Behavior
`team:` absent from config Solo mode. No backend constructed.
`team.backend` set, `team.author` empty Warning to stderr; team sync skipped; CLI continues local-only.
Backend `pull_events` raises Logged; continues with current local events_dir state.
Backend `push_events` raises for one file Logged; other files still pushed.
Source ingest raises Watermark NOT advanced (Phase 1 invariant); push still runs for unrelated files.

Brief renderer extension

The brief gains an optional `## Team sync` section showing `peer_files_pulled` + `my_file_pushed`. Omitted entirely when `team_sync=None` so solo-mode briefs render byte-identically to pre-Phase-2.

Per-#205 doctrine alignment

The only operator-facing default in this new code path — "no team backend when `team:` config is absent" — is a deterministic gate at `events/backends/init.py::get_backend` (returns `None`), NOT a skill-text-only claim. Backend errors return `None` safely. Per the doctrine just shipped in PR #320.

Test plan

  • `python -m pytest tests/test_sync_and_brief_team_mode.py -v` — 12 tests sociable per CLAUDE.md (real `LocalFolderAdapter`, real `BicameralContext`, real file system; no backend mocks)
  • `python -m pytest tests/test_brief_renderer.py -v` — 3 new tests for the `## Team sync` section
  • Full Phase 1 + Phase 2 + brief + sessionstart sweep — 56 passed
  • Manual: configure two real repos with shared `remote_root` + matching `team:` blocks; run sync-and-brief on machine A, then machine B; verify A's events appear in B's brief

Headline test: two-machine round-trip

`test_team_sync_round_trip_alice_to_bob_via_local_folder` — spans Alice's repo → shared `remote_root` → Bob's repo and verifies Alice's events land in Bob's cache after his next `sync-and-brief`. End-to-end across two `BicameralContext` instances + a real shared `LocalFolderAdapter`. No mocks on the backend, materializer, or file system.

Files

Path Change
`cli/sync_and_brief_cli.py` Add `_resolve_team_backend`, `_team_sync_pull`, `_team_sync_push`; wire into `_run` around the source-pull loop
`cli/brief_renderer.py` Optional `team_sync` kwarg; `## Team sync` section
`tests/test_sync_and_brief_team_mode.py` 12 new integration tests including the two-machine round-trip
`tests/test_brief_renderer.py` 3 new tests for the team-sync section
`docs/policies/sources-config.md` New "Team backend" section documenting config schema + failure modes

🤖 Generated with Claude Code

Wires the existing BackendAdapter interface (shipped in #277, closed) into
the sync-and-brief CLI flow. Phase 2 of #279 — the team-mode complement to
Phase 1's solo-mode session magic (PR #318, merged).

Order invariants:
  1. BEFORE source pull: backend.pull_events() copies every peer's
     <email>.jsonl into the local .bicameral/events/ cache. The
     materializer's existing *.jsonl glob (events/materializer.py:73)
     picks them up alongside the operator's own events. Peer ingest is
     visible in the brief without manual action.

  2. AFTER source ingest succeeds: backend.push_events() uploads each
     local <email>.jsonl to the shared backend. LocalFolderAdapter's
     sha-match skip (events/backends/local_folder.py:44-46) keeps
     re-invocations idempotent.

Failure modes are non-blocking — backend pull/push errors log to stderr
+ ~/.bicameral/cli-errors.log but never block the brief. The hook
wrapper's exit 0 framing makes this invisible to SessionStart users on
a network outage. Solo mode (no team: config) is completely unaffected
— same code path as before.

The brief renderer gains an optional ## Team sync section showing
peer_files_pulled + my_file_pushed. Omitted entirely when team_sync=None
so solo-mode briefs render byte-identically to pre-Phase-2.

Per #205 doctrine — the only operator-facing default in this new path
(no team backend when team: config is absent) is a deterministic gate
at events/backends/__init__.py::get_backend, not a skill-text-only
claim. Backend errors return None safely.

12 tests in tests/test_sync_and_brief_team_mode.py — sociable per
CLAUDE.md (real LocalFolderAdapter, real BicameralContext, real file
system; no backend mocks). The headline two-machine round-trip test
spans Alice's repo → shared remote_root → Bob's repo and verifies
Alice's events land in Bob's cache after his next sync-and-brief.

3 new tests in tests/test_brief_renderer.py covering the Team sync
section (present/absent/zero-counts).

Full Phase 1 + Phase 2 sweep: 56 passed, 1 skipped (mcp[cli] env probe
unchanged).

docs/policies/sources-config.md extended with a "Team backend" section
documenting config schema + failure modes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5aa9ffad-9f87-4e41-85b9-5f9dbc548ef8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/279-phase2-team-mode-integration

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Knapp-Kevin Knapp-Kevin added flow:feature Standard feature/fix PR targeting BicameralAI/dev (the default flow) P1 High: ship this milestone; user-impacting bug or committed feature feat Feature work or user-visible capability python Pull requests that update python code test Test infrastructure, fixtures, or coverage work infra Infrastructure / build / CI / repo-admin work labels May 14, 2026
@Knapp-Kevin Knapp-Kevin merged commit ffa1b22 into dev May 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat Feature work or user-visible capability flow:feature Standard feature/fix PR targeting BicameralAI/dev (the default flow) infra Infrastructure / build / CI / repo-admin work P1 High: ship this milestone; user-impacting bug or committed feature python Pull requests that update python code test Test infrastructure, fixtures, or coverage work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant