Skip to content

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

Description

@vybe

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 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/dev cec2a64b, 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–29 should_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.

Technical Notes

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.

Re-prioritised to P1 for 1.0 the same day: #2105 · #2107. Linked, unchanged: #1703 · #2938 · abilityai/trinity-enterprise#142 · abilityai/trinity-enterprise#230.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions