Documentation
GitHub Sync — Backlog ↔ Issues
How the Yoke backlog mirrors to GitHub issues, and the per-project switch that turns that mirroring off.
What syncs
Every backlog→GitHub surface routes through the yoke_core.domain.backlog_github_sync helper family (issue create, body/title updates, status comments, state close/reopen, status and flag labels, done-transition closeout, epic-task issues, progress notes) or the resync engine (yoke resync, yoke_core.engines.resync), which detects and repairs drift between the DB and the linked issues. Repository authority and a short-lived installation token resolve per project through yoke_core.domain.project_github_auth.resolve_project_github_auth (an active, verified project_github_repo_bindings row). The binding is the sole outbound repository authority; projects.github_repo is a compatibility display projection and never overrides it. The resolver fails closed when the App installation is suspended, the exact repository is unavailable, or required permissions are missing; GitHub authentication never falls back to a project capability secret or host credential.
The per-project switch: projects.github_sync_mode
One column is the authority for whether a project's backlog mirrors to GitHub issues at all:
| Value | Meaning |
|---|---|
enabled |
Backlog items and epic tasks mirror to GitHub issues. An explicit enable requires an active, verified GitHub App repository binding. |
disabled |
The backlog lives only in the Yoke DB. Every GitHub issue sync surface skips or refuses for the project. |
New projects default to disabled. A legacy NULL, empty, or unrecognized stored value also resolves to disabled and is normalized during schema initialization or by the repair command.
Reader: yoke_core.domain.projects_github_sync_mode. The mode vocabulary is single-sourced in yoke_contracts.project_contract.github_sync_mode.
Read and flip through the registered projects surface:
yoke projects get --project <slug> --field github_sync_mode
yoke projects update --slug <slug> --name <Name> --github-sync-mode disabled
yoke projects github-sync-mode repair
yoke projects github-sync-mode repair --apply
The repair command is a dry-run unless --apply is explicit. It finds projects with a legacy, empty, or unrecognized stored mode, enabled projects without an active verified binding, and stale repository/capability projections. It normalizes the affected modes to disabled. Use --project <slug> to inspect or repair one project.
disabled is independent of the GitHub App repo binding: a project can keep the binding for code delivery (pushes, CI, deploys) while never mirroring backlog content to that repo's issue tracker. This is the "repo connection optional — sync off" posture.
Disabled semantics
- Item flows skip silently-and-logged. Sync helpers invoked from item
lifecycle flows (create, body/title sync, status comments, labels, close/reopen, done closeout, epic-task sync, progress notes) return success and print one canonical mode-language line (GitHub <operation> skipped for project '<slug>': github_sync_mode=disabled ...). The flow continues; nothing reaches GitHub. A disabled project resolves no GitHub App token — the skip fires before auth resolution and is never reported as an auth failure.
- **Structured-field writes with
options.sync_github_body=trueno-op
cleanly.** The body-sync step reports success (no sync_warning); the DB write proceeds as normal.
yoke resyncnames the exclusion. Disabled projects are
excluded from the GitHub fetch and from classification: their items are never local orphans (so --fix can never mass-create them as issues), never drift, never repair. The report prints a === GitHub Sync Disabled (per-project) === section naming each excluded project; the exit code reflects the enabled projects only.
- Explicit issue-creating operations refuse.
migrate_issue_to_repo
(cross-repo issue migration) returns non-zero with the mode-language message when the target project is disabled, instead of creating an issue there.
Rebinding a project repository — ordering
Changing the verified App binding does not move existing issues. If the backlog should not immediately sync into the replacement repository, use this order:
- Sync off first: set
github_sync_mode=disabledfor the
project and verify (yoke projects get --project <slug> --field github_sync_mode).
- Bind verified App access: run
yoke projects github-binding bindwith
the project, installation id, repository id, and new owner/repo. If the checkout has no GitHub remote yet, create a private repo and bind it with yoke project git bootstrap CHECKOUT --project <slug> --yes instead.
- Migrate intentionally or keep history: existing
items.github_issue/
epic_tasks.github_issue numbers keep pointing at the old repo's issues until the explicit issue-migration flow moves them. No sync writes land while the project remains disabled.
Re-enable sync only after the binding and issue disposition are verified; the registered project update rejects enabled while that binding is unavailable. Rebinding before step 1 leaves a window where the next sync can create backlog issues in the new repository.
GitHub Sync — Backlog ↔ Issues