Repository navigation
fix(bridge): colocated repos stay native until they opt in to the Git bridge - #212
Merged
vinceblock99 merged 1 commit intoOct 1, 2026
Conversation
… 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.
This was referenced Sep 24, 2026
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.
Problem
On
feat/atomic-sidecar-for-git(#207), a repository that has a.gitdirectory next to.atomiccannot 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:With existing Git history the refusal becomes
MissingCheckpoint.add,record,status,status --json(the IDE extension),diff,mv,restore,push,pull,stash,tag,view switchand 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.gitdirectory alone.classify_git_statethen reportsUnbornHeadorMissingCheckpointbecause only explicit bridge commands ever write.atomic/bridge/workspace.json.tolerable_remediation_for_repairexempts only the bridge's own repair boundary, and its allowlist has noUnbornHead.[git.bridge] enabledis read only for telemetry consent (observability.rs) and the watch daemon (watch.rs).Debugform. 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 enablerecords the opt-in and installs five hooks, but it doesn't write a checkpoint without--binding-key-file, so the refusal stays.atomic git importfails on an unborn HEAD with "Could not determine default branch".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 addwrites intent-to-add entries into the user's Git index.atomic tag createwritesrefs/tags/*.atomic statusmixes Git index state into native status.stashguard applies the same refusal.What the RFC and guide specify
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): "enableddefaults tofalse, 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 byatomic git bridge enable.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
.gitalone now asks this predicate:begin_workspace_txn(status/add/record/diff/…/agent turn-end)UnbornHead/MissingCheckpointNoGit)stashguard (guard_working_copy)BridgeNotEnabled)atomic addintent-to-addatomic tag createGit exportrefs/tags/*atomic statusGit overlaysThe 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, andstatus --no-reconcile.Enrolled workspaces that are still unanchored now get an actionable report:
For
UnbornHead, the report says to create the first Git commit and then runatomic 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:
addmeans durable Atomic tracking.Before this change, a stray
.gitdirectory 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, thenadd,status,status --json,record,diff,stash push/popandtag createall 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 withgit addstays??in native status.opted_in_workspace_without_anchor_refuses_with_actionable_remediation: the report namesreconcileanddisable, andreconcilethen unblocks.opted_in_unborn_head_names_the_first_commit_remediationdisabling_an_unanchored_bridge_restores_native_behaviorNew in
workspace_txn_tests.rs(4 tests):ObserveandReconcilemodes.Updated fixture:
record_stale_conflict_cleanup_test.rs. It models an enrolled workspace whose checkpoint was removed, so it now records[git.bridge] enabled = trueexplicitly. It writes the flag directly rather than runningbridge 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):
atomic-repositorylibatomic-repositoryintegrationdatabase_open_wait_test(macOS/var↔/private/varalias) andstaging_cb11a_test13/13 (PlatformMismatch: expects a case-sensitive FS)atomic-agentatomic-cligit_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…andgraph_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:
atomic initis preceded bygit init, making each repository colocated but never enrolled (16 such repositories were created).The results are identical in both modes and match the base:
41_view_switch_name_conflict(#199)42_view_create_parent(#200)43_record_status_name_conflict(#203)39_ambient_inode_views40_switch_transactionality(failpoints)Separate finding, not caused by this PR. #203's harness passes 79/79 on
dev, but 15 checks fail on #207 withresolved 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
atomic-agent/src/record/mod.rs,orchestrator/mod.rs) still callsobserve_git_metadatadirectly. It can mark a sessionIncompleteafter agit commitinside 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 disableon a workspace that already has a checkpoint keeps the stale-baseline guard for that baseline, because the predicate counts the checkpoint. Whetherdisableshould also retire the checkpoint is an owner decision.atomic git importon an unborn HEAD still says "Could not determine default branch".Stacked on #207 (
feat/atomic-sidecar-for-git).