Skip to content

fix(bridge): colocated repos stay native until they opt in to the Git bridge - #212

Merged
vinceblock99 merged 1 commit into
feat/atomic-sidecar-for-gitfrom
fix/bridge-opt-in-workspace-gate
Oct 1, 2026
Merged

vinceblock99 merged 1 commit into
feat/atomic-sidecar-for-gitfrom
fix/bridge-opt-in-workspace-gate

Conversation

@vinceblock99

Copy link
Copy Markdown
Contributor

Problem

On feat/atomic-sidecar-for-git (#207), a repository that has a .git directory next to .atomic cannot run ordinary Atomic commands unless it has been anchored to the Git bridge. Starting from scratch, which is the normal way to begin a new project:

$ git init && atomic init
$ echo hello > f.txt
$ atomic add f.txt
✗ workspace reconciliation required: workspace is not anchored to a verified Git baseline: UnbornHead { symref: "refs/heads/main" }

Hint: Follow the exact remediation commands in the stale-baseline report before retrying the refused operation.
$ atomic status
✗ … UnbornHead { symref: "refs/heads/main" }

With existing Git history the refusal becomes MissingCheckpoint. add, record, status, status --json (the IDE extension), diff, mv, restore, push, pull, stash, tag, view switch and the agent turn-end hook are all refused.

Root cause

  • begin_workspace_txn (CB-5B, workspace_txn.rs) decides whether to reconcile with Git from the presence of a .git directory alone. classify_git_state then reports UnbornHead or MissingCheckpoint because only explicit bridge commands ever write .atomic/bridge/workspace.json.
  • tolerable_remediation_for_repair exempts only the bridge's own repair boundary, and its allowlist has no UnbornHead.
  • The classifier never reads the opt-in flag. [git.bridge] enabled is read only for telemetry consent (observability.rs) and the watch daemon (watch.rs).
  • The report prints the enum's Debug form. The hint tells the user to follow "the exact remediation commands", but the report contains none.

No way out for a new repository

  • atomic git bridge enable records the opt-in and installs five hooks, but it doesn't write a checkpoint without --binding-key-file, so the refusal stays.
  • atomic git import fails on an unborn HEAD with "Could not determine default branch".
  • The only working sequence is git commit → atomic git bridge reconcile, and nothing tells the user that.

The same .git-presence check also switches on other colocated-mode side effects without consent:

  • atomic add writes intent-to-add entries into the user's Git index.
  • atomic tag create writes refs/tags/*.
  • atomic status mixes Git index state into native status.
  • The stash guard applies the same refusal.

What the RFC and guide specify

  • RFC §2: "Atomic replaces the Git Shadow model with an Atomic Git causal bridge for explicitly configured colocated workspaces."
  • RFC §13 Phase 13 task 4: "opt-in flag; default flip criteria." Phase 0 acceptance: "Non-Git repositories unchanged."
  • docs/bridge-operating-guide.md: "opt-in, advisory-evidence bridge; default rollout is blocked" … "The bridge is explicit per-repository opt-in."
  • GitBridgeConfig (CB-13C): "enabled defaults to false, and no code path flips it."

The workspace boundary was the one place that didn't honor this.

The fix

One participation predicate, Repository::bridge_workspace_active(). A working copy is in the bridge if either of these holds:

  • [git.bridge] enabled = true, recorded by atomic git bridge enable.
  • A verified checkpoint exists. Only explicit bridge commands write one: git bridge reconcile, git import, anchoring, clone bootstrap / --adopt-git. The guide already treats these as "their own consent".

Everything that inferred colocated mode from .git alone now asks this predicate:

Path Before After (not enrolled) After (enrolled)
Ordinary begin_workspace_txn (status/add/record/diff/…/agent turn-end) refuses UnbornHead/MissingCheckpoint observes as native (NoGit) unchanged: full Git reconciliation
stash guard (guard_working_copy) refuses passes (BridgeNotEnabled) unchanged
atomic add intent-to-add writes Git index Atomic tracking only unchanged (RFC §3.4)
atomic tag create Git export writes refs/tags/* tag stays Atomic-only unchanged (RFC §8.4)
atomic status Git overlays mixes Git index state native status unchanged (RFC §9)

The following are unchanged, because the user asks for Git behavior explicitly when running them: the bridge repair boundary (begin_remediation_txn, which still observes Git), atomic stage/unstage, diff --git, status --git, and status --no-reconcile.

Enrolled workspaces that are still unanchored now get an actionable report:

✗ workspace reconciliation required: workspace is not anchored to a verified Git baseline: MissingCheckpoint

The Git bridge is enabled for this repository, but this workspace has no verified Git baseline yet. Anchor it to the current Git HEAD (imports the Git history into the current view):
  atomic git bridge reconcile
To use Atomic without the Git bridge in a workspace that was never anchored:
  atomic git bridge disable

For UnbornHead, the report says to create the first Git commit and then run atomic git bridge reconcile.

Why this is the Atomic way, not the Git way

The bridge is optional interop. Atomic's model doesn't depend on it:

  • The graph is the source of truth.
  • Views are filters over it.
  • add means durable Atomic tracking.

Before this change, a stray .git directory made Atomic commands depend on Git HEAD/index state and write into Git's index and refs. After it, a repository that never enrolled behaves exactly like one with no Git at all. Git stays the user's own tool until they opt in.

This change doesn't touch the graph, views, insert, record, materialization, TREE/PATH_CLAIMS, or the change format. It only decides whether ordinary boundaries interpret Git metadata.

Tests

Test-first. Every new test was written and observed failing before the implementation.

New: atomic-cli/tests/git_bridge_opt_in_test.rs (6 end-to-end tests; before the fix 0/6 passed, with exactly the errors above; after the fix 6/6 pass):

  • fresh_git_init_without_bridge_keeps_native_commands_working: git init + atomic init, then add, status, status --json, record, diff, stash push/pop and tag create all succeed. The Git index, tags and hooks are untouched and no checkpoint is written.
  • existing_git_history_without_bridge_keeps_native_commands_working: the same flow with prior Git history.
  • status_does_not_mix_git_index_state_without_bridge_opt_in: a file staged with git add stays ?? in native status.
  • opted_in_workspace_without_anchor_refuses_with_actionable_remediation: the report names reconcile and disable, and reconcile then unblocks.
  • opted_in_unborn_head_names_the_first_commit_remediation
  • disabling_an_unanchored_bridge_restores_native_behavior

New in workspace_txn_tests.rs (4 tests):

  • A native entry for unborn HEAD and for existing history, in both Observe and Reconcile modes.
  • Opt-in without a checkpoint still refuses, with the remediation text.
  • Opt-in on an unborn HEAD names the first-commit remediation.
  • A checkpoint without the opt-in keeps the stale-baseline guard.

Updated fixture: record_stale_conflict_cleanup_test.rs. It models an enrolled workspace whose checkpoint was removed, so it now records [git.bridge] enabled = true explicitly. It writes the flag directly rather than running bridge enable, so no hooks are installed. All 7 assertions are unchanged and pass.

Regression: Run locally on macOS. For every failure, the same test was also run on this branch's base (Aaron's head, without this change):

Suite Result Failures, all identical on the base
atomic-repository lib 1413 / 0 —
atomic-repository integration all pass except 2 files database_open_wait_test (macOS /var↔/private/var alias) and staging_cb11a_test 13/13 (PlatformMismatch: expects a case-sensitive FS)
atomic-agent 1373 / 0 —
atomic-cli 2394 / 6 git_binding_cb6b (1), git_binding_cb6c (2), git_transport_cb10b::clone_unbound_… (1): the bare remote's HEAD names a missing branch, so the fresh clone has no default branch. crash_during_effect… and graph_only_export_matrix… need --features atomic-cli/adoption-test-injection (as in CI) and pass with it.

Graph and view harnesses, run in native and colocated repositories. Each harness was run twice:

  • in plain Atomic repositories;
  • in a mode where every atomic init is preceded by git init, making each repository colocated but never enrolled (16 such repositories were created).

The results are identical in both modes and match the base:

Harness base native colocated, not enrolled
41_view_switch_name_conflict (#199) — 8/8 8/8
42_view_create_parent (#200) — 6/6 6/6
43_record_status_name_conflict (#203) 64/79 64/79 64/79
39_ambient_inode_views 51/53 51/53 51/53
40_switch_transactionality (failpoints) 22/25 22/25 22/25

Separate finding, not caused by this PR. #203's harness passes 79/79 on dev, but 15 checks fail on #207 with resolved name conflict at 'f.txt' matches 0 claimants, identically with and without this change. The #203 namespace-patch resolution appears to have regressed in #207's reconciliation with dev and should be looked at on #207 itself.

Out of scope / follow-ups

  • Agent turn classification (atomic-agent/src/record/mod.rs, orchestrator/mod.rs) still calls observe_git_metadata directly. It can mark a session Incomplete after a git commit inside a turn even in a repository that never enrolled. The same predicate should gate it, but that is agent-lifecycle behavior and deserves its own PR.
  • atomic git bridge disable on a workspace that already has a checkpoint keeps the stale-baseline guard for that baseline, because the predicate counts the checkpoint. Whether disable should also retire the checkpoint is an owner decision.
  • atomic git import on an unborn HEAD still says "Could not determine default branch".

Stacked on #207 (feat/atomic-sidecar-for-git).

… bridge

A `.git` directory next to `.atomic` made every ordinary command (status,
add, record, diff, stash, tag, agent turn-end, ...) refuse with UnbornHead
or MissingCheckpoint until the workspace was anchored, and made add, tag and
status write or read Git index/ref state without consent. RFC §2 and CB-13C
scope the bridge to explicitly configured workspaces ([git.bridge] enabled,
default false).

- Repository::bridge_workspace_active(): the explicit opt-in, or a verified
  checkpoint written by an explicit bridge command (reconcile, import,
  anchoring, clone bootstrap).
- Ordinary workspace boundaries observe Git only for bridge workspaces; the
  repair boundary (begin_remediation_txn) is unchanged.
- The stash guard, `add` intent-to-add, `tag create` Git export and status
  Git overlays use the same predicate.
- Unanchored MissingCheckpoint/UnbornHead reports name the exact commands
  that resolve them.
- The operating guide documents the opt-in contract.

Tests: new git_bridge_opt_in_test (6, written first and failing), four
workspace_txn tests, and record_stale_conflict_cleanup_test now opts in
explicitly because it models an enrolled workspace with a missing checkpoint.
@vinceblock99
vinceblock99 merged commit 6f636f3 into feat/atomic-sidecar-for-git Oct 1, 2026
@vinceblock99
vinceblock99 deleted the fix/bridge-opt-in-workspace-gate branch October 1, 2026 20:08
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