You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
bug(git-sync): the auto-sync toggle is not authoritative — OFF never takes effect (the baked env is OR'd forward at recreate), ON waits for the next recreate #3010
PUT /api/agents/{name}/git/auto-sync {enabled:false} writes the DB row and nothing else. The container gates the 15-minute heartbeat purely on the GIT_SYNC_AUTO env var, and the only place the two converge — _apply_git_env_from_db on recreate — derives the env as DB flag OR baked env with a deliberate no-write-back. Result: toggling ON takes effect only at the next container recreate; toggling OFF never takes effect at all on an agent that baked the env at creation. The toggle is a silent no-op in one direction and a delayed one in the other.
Scope: Trinity 1.0 · project:tandem (operator ruling 2026-09-24 — the repository is the agent; the container is a cache of it; work that exists only on a container disk does not exist). This is precondition 1 of 3 for making auto-sync the default for agents created as agents (abilityai/trinity-enterprise, the agents-created-as-agents default issue); the other two are the fetch-and-rebase heartbeat and #2107.
Mechanism (verified at origin/devcec2a64b, 2026-09-24)
Writer 1 — creation.services/agent_service/crud.py:1770 bakes GIT_SYNC_AUTO='true' into the container env when _git_auto_sync_baked() holds (services/git_service/gitignore_clone.py:44–70: github_repo and github_pat and (not source_mode or fork_upstream)), and crud.py:2462 sets auto_sync_enabled=1 in agent_git_config — inside a swallowing try/except that additionally excludes ephemeral agents, so env and DB can already disagree at birth.
Writer 2 — the toggle.routers/git.py:1175–1185 (set_auto_sync_config) calls db.set_git_auto_sync_enabled and returns. No container env change, no recreate, no signal to the agent server.
The gate.docker/base-image/agent_server/auto_sync.py:27–29should_run_auto_sync() reads only os.getenv("GIT_SYNC_AUTO"), evaluated once at agent-server startup.
The convergence, and why OFF cannot stick.services/agent_service/lifecycle.py:799–836: if _db_auto or _baked_auto: env_vars["GIT_SYNC_AUTO"] = "true". The inline comment explains the OR — a transient DB hiccup at creation or an ephemeral agent can leave env-true/DB-0, and deriving from the DB alone would silently stop auto-push for that slice — and then names the fix this issue is: "Making the Sync health observability (S1) #389 toggle authoritative (one writer, env re-baked on toggle) is the separate follow-up that retires this OR honestly." The same comment records a second reason the write-back was refused: PUT .../auto-sync is owner-only while POST .../start (the recreate trigger) is authorized-by-name, so a backfill would let a shared non-owner flip an owner-only flag.
Sibling flag, same shape.freeze_schedules_if_sync_failing (routers/git.py:1201–1213) is DB-only too, but it is read from the DB by the scheduler (routers/internal.py:283–305), so it does take effect live. Neither flag has a UI control — SettingsPanel.vue:12–14 lists both endpoints under "Growth path (each a follow-up PR)".
Seam history (closed, cited for context):#389 built the flag, the heartbeat and the sync-state stack; abilityai/trinity-enterprise#109 built _apply_git_env_from_db as the single owner of the env gate; #2069 gated its .gitignore merge on the DB flag and documented the env-vs-DB disagreement.
Acceptance Criteria
One writer.auto_sync_enabled in agent_git_config is the only source of truth. _apply_git_env_from_db derives GIT_SYNC_AUTO from the DB flag alone; the OR baked env branch and its log line are removed.
Live on toggle.PUT .../git/auto-sync takes effect within one heartbeat interval without a manual recreate: either the agent server re-reads the flag each cycle (a /api/git/auto-sync push or a poll of the platform), or the platform recreates the container with the re-baked env. Whichever is chosen, an OFF toggle stops the next cycle and an ON toggle starts one.
Migration for the env-true/DB-0 slice. A one-shot backfill sets auto_sync_enabled=1 where the creation-time predicate (_git_auto_sync_baked) holds and the row is 0 — so no agent that auto-pushes today silently stops. The backfill is keyed on the predicate, not on the container env, so an owner's explicit disable made after creation is not overwritten (the ambiguity the comment names is resolved by choosing creation-time truth once, then making the toggle the only writer thereafter).
Authorization aligned. The toggle and the recreate path agree on who may flip the flag (the comment's owner-vs-authorized mismatch is closed, not carried).
GET .../git/auto-sync returns the value the container is actually running with, and the agent-side /api/git/status reports auto_sync_enabled from the same source.
Both flags get the settings-panel control the growth-path comment promises, so the toggle is reachable without the API.
Tests: toggle OFF → next cycle skipped; toggle ON → next cycle runs; recreate after OFF stays OFF; ephemeral/ghost agents keep auto-push where the predicate held.
The fleet-convention half (which branch an agent writes, what is tracked vs ignored) is a convention document on the fleet side, not this issue.
Journey Impact: existing — operator toggles auto-sync on an agent (docs/memory/feature-flows/github-sync.md); the flow's stated behaviour becomes true.
Siblings — the git-sync set, filed 2026-09-24 (operator rulings relayed by corbin)
Platform half of the repository is the agent; the container is a cache of it (Trinity 1.0 · project:tandem · epic abilityai/trinity-enterprise#497). Fleet-convention half: corbin's canon/protocols/agent-git-sync.md.
abilityai/trinity-enterprise#706 — f. divergence-age semantics (red >24h ahead or behind; freeze keys on it for work agents); dirty count + last successful push persisted
abilityai/trinity-enterprise#707 — g. sync health surfaces: fleet health sync block, agent card, sync-audit over MCP
abilityai/trinity-enterprise#708 — h. narrow the .claude/settings.json ignore; hooks-vs-heartbeat owner
Re-prioritised to P1 for 1.0 the same day: #2105 · #2107. Linked, unchanged: #1703 · #2938 · abilityai/trinity-enterprise#142 · abilityai/trinity-enterprise#230.
Summary
PUT /api/agents/{name}/git/auto-sync {enabled:false}writes the DB row and nothing else. The container gates the 15-minute heartbeat purely on theGIT_SYNC_AUTOenv var, and the only place the two converge —_apply_git_env_from_dbon recreate — derives the env asDB flag OR baked envwith a deliberate no-write-back. Result: toggling ON takes effect only at the next container recreate; toggling OFF never takes effect at all on an agent that baked the env at creation. The toggle is a silent no-op in one direction and a delayed one in the other.Scope: Trinity 1.0 ·
project:tandem(operator ruling 2026-09-24 — the repository is the agent; the container is a cache of it; work that exists only on a container disk does not exist). This is precondition 1 of 3 for making auto-sync the default for agents created as agents (abilityai/trinity-enterprise, the agents-created-as-agents default issue); the other two are the fetch-and-rebase heartbeat and #2107.Mechanism (verified at
origin/devcec2a64b, 2026-09-24)services/agent_service/crud.py:1770bakesGIT_SYNC_AUTO='true'into the container env when_git_auto_sync_baked()holds (services/git_service/gitignore_clone.py:44–70:github_repo and github_pat and (not source_mode or fork_upstream)), andcrud.py:2462setsauto_sync_enabled=1inagent_git_config— inside a swallowing try/except that additionally excludes ephemeral agents, so env and DB can already disagree at birth.routers/git.py:1175–1185(set_auto_sync_config) callsdb.set_git_auto_sync_enabledand returns. No container env change, no recreate, no signal to the agent server.docker/base-image/agent_server/auto_sync.py:27–29should_run_auto_sync()reads onlyos.getenv("GIT_SYNC_AUTO"), evaluated once at agent-server startup.services/agent_service/lifecycle.py:799–836:if _db_auto or _baked_auto: env_vars["GIT_SYNC_AUTO"] = "true". The inline comment explains the OR — a transient DB hiccup at creation or an ephemeral agent can leave env-true/DB-0, and deriving from the DB alone would silently stop auto-push for that slice — and then names the fix this issue is: "Making the Sync health observability (S1) #389 toggle authoritative (one writer, env re-baked on toggle) is the separate follow-up that retires this OR honestly." The same comment records a second reason the write-back was refused:PUT .../auto-syncis owner-only whilePOST .../start(the recreate trigger) is authorized-by-name, so a backfill would let a shared non-owner flip an owner-only flag.freeze_schedules_if_sync_failing(routers/git.py:1201–1213) is DB-only too, but it is read from the DB by the scheduler (routers/internal.py:283–305), so it does take effect live. Neither flag has a UI control —SettingsPanel.vue:12–14lists both endpoints under "Growth path (each a follow-up PR)".Seam history (closed, cited for context): #389 built the flag, the heartbeat and the sync-state stack; abilityai/trinity-enterprise#109 built
_apply_git_env_from_dbas the single owner of the env gate; #2069 gated its.gitignoremerge on the DB flag and documented the env-vs-DB disagreement.Acceptance Criteria
auto_sync_enabledinagent_git_configis the only source of truth._apply_git_env_from_dbderivesGIT_SYNC_AUTOfrom the DB flag alone; theOR baked envbranch and its log line are removed.PUT .../git/auto-synctakes effect within one heartbeat interval without a manual recreate: either the agent server re-reads the flag each cycle (a/api/git/auto-syncpush or a poll of the platform), or the platform recreates the container with the re-baked env. Whichever is chosen, an OFF toggle stops the next cycle and an ON toggle starts one.auto_sync_enabled=1where the creation-time predicate (_git_auto_sync_baked) holds and the row is 0 — so no agent that auto-pushes today silently stops. The backfill is keyed on the predicate, not on the container env, so an owner's explicit disable made after creation is not overwritten (the ambiguity the comment names is resolved by choosing creation-time truth once, then making the toggle the only writer thereafter).GET .../git/auto-syncreturns the value the container is actually running with, and the agent-side/api/git/statusreportsauto_sync_enabledfrom the same source.Technical Notes
ahead_workingreports ahead-of-mainfor any non-trinity/*branch, and fleet audit reads it as unpushed #2105 (ahead counts misreport on non-trinity/*branches — every source-mode agent), bug: git-sync write access is never validated — a read-only credential fails silently forever (ls-remoteand the RESTpermissions.pushfield both lie) #2107 (write access never validated — with auto-sync defaulted on, a read-only credential would freeze an agent for reasons that look like sync failure), bug(git-sync): initialize places a new repo in workspace/ when it is non-empty, but the agent server only looks at /home/developer — init reports success, everything after says "not enabled" #2938 (initialize places the repo where the agent server does not look), refactor: agent git repo root is $HOME — 22 gitignore patterns exist only to compensate, and every new home-writing feature repeats the dance #1703 (repo root is$HOME— the reasonadd -Aneeds 22 ignore patterns).Journey Impact: existing — operator toggles auto-sync on an agent (
docs/memory/feature-flows/github-sync.md); the flow's stated behaviour becomes true.Siblings — the git-sync set, filed 2026-09-24 (operator rulings relayed by corbin)
Platform half of the repository is the agent; the container is a cache of it (Trinity 1.0 ·
project:tandem· epic abilityai/trinity-enterprise#497). Fleet-convention half: corbin'scanon/protocols/agent-git-sync.md.ls-remoteand the RESTpermissions.pushfield both lie) #2107)syncblock, agent card, sync-audit over MCP.claude/settings.jsonignore; hooks-vs-heartbeat ownerRe-prioritised to P1 for 1.0 the same day: #2105 · #2107. Linked, unchanged: #1703 · #2938 · abilityai/trinity-enterprise#142 · abilityai/trinity-enterprise#230.