Make infrastructure the GitHub <=> Forgejo coordination hub #489

Open
opened 2026-07-09 18:00:43 +00:00 by coilyco-ops · 4 comments
Member

Kai's direction: infrastructure should be the GitHub <=> Forgejo coordination hub.

Rationale:

  • infrastructure already owns host/systemd mirror units such as coilysiren-github-mirror and coilysiren-forgejo-mirror.
  • infrastructure owns SSM-backed credentials and token helpers such as the GitHub App installation token minting path.
  • infrastructure owns Forgejo bot grants, repo-creation permissions, and ops runbooks.
  • infrastructure is the right layer for stateful fleet convergence, timers, credentials, and audits.
  • ward should remain the reusable command surface and dispatch engine, not the place that owns fleet-specific GitHub/Forgejo state.

This should connect the existing narrower work:

  • coilyco-flight-deck/infrastructure#475 - centralized GitHub mirror sync.
  • coilyco-flight-deck/ward#823 - GitHub <=> Forgejo command hub design.
  • coilyco-flight-deck/ward#830 and coilysiren/inbox#184 - coilysiren/* GitHub-authoritative migration.
  • coilyco-flight-deck/ward#827 - out-of-band tracker source and Shortcut shadowing.

Proposed split:

  • infrastructure owns the coordination state, scheduled checks, mirror status, divergence detection, credential storage, and fleet-level sync jobs.
  • ward owns bounded verbs and dispatch mechanics that can call into GitHub/Forgejo safely.
  • inbox owns the personal-work migration tracker and policy decisions that affect Kai's workflow.

Acceptance:

  • A short design doc or issue comment defines the hub boundary between infrastructure, ward, and inbox.
  • The design names the first commands or scripts needed to answer: mirror status, divergent refs, authoritative side, issue/tracker mapping, and stale syncs.
  • The design decides whether infrastructure#475 becomes the first implementation slice or needs to be reframed.
  • Follow-up implementation issues are filed for the first small slice.
Kai's direction: infrastructure should be the GitHub <=> Forgejo coordination hub. Rationale: * infrastructure already owns host/systemd mirror units such as `coilysiren-github-mirror` and `coilysiren-forgejo-mirror`. * infrastructure owns SSM-backed credentials and token helpers such as the GitHub App installation token minting path. * infrastructure owns Forgejo bot grants, repo-creation permissions, and ops runbooks. * infrastructure is the right layer for stateful fleet convergence, timers, credentials, and audits. * ward should remain the reusable command surface and dispatch engine, not the place that owns fleet-specific GitHub/Forgejo state. This should connect the existing narrower work: * `coilyco-flight-deck/infrastructure#475` - centralized GitHub mirror sync. * `coilyco-flight-deck/ward#823` - GitHub <=> Forgejo command hub design. * `coilyco-flight-deck/ward#830` and `coilysiren/inbox#184` - `coilysiren/*` GitHub-authoritative migration. * `coilyco-flight-deck/ward#827` - out-of-band tracker source and Shortcut shadowing. Proposed split: * infrastructure owns the coordination state, scheduled checks, mirror status, divergence detection, credential storage, and fleet-level sync jobs. * ward owns bounded verbs and dispatch mechanics that can call into GitHub/Forgejo safely. * inbox owns the personal-work migration tracker and policy decisions that affect Kai's workflow. Acceptance: * A short design doc or issue comment defines the hub boundary between infrastructure, ward, and inbox. * The design names the first commands or scripts needed to answer: mirror status, divergent refs, authoritative side, issue/tracker mapping, and stale syncs. * The design decides whether `infrastructure#475` becomes the first implementation slice or needs to be reframed. * Follow-up implementation issues are filed for the first small slice.
Author
Member

Auth/identity doc split filed as coilyco-flight-deck/infrastructure#492. The hub design should explicitly distinguish the Forgejo coilyco-ops bot user from the GitHub App installation identity.

Auth/identity doc split filed as coilyco-flight-deck/infrastructure#492. The hub design should explicitly distinguish the Forgejo `coilyco-ops` bot user from the GitHub App installation identity.
Author
Member

🔎 ward agent advisor - cross-repo fan-out

ward agent advisor --harness codex ran a one-shot standard research pass on this question:

Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue.

Recommendation

Treat infrastructure as the bridge control plane, ward as the typed command client, and inbox as policy only.

That matches what already exists in the repo better than pushing hub ownership into ward:

  • infrastructure already owns the live mirror machinery and its migration path: the legacy host timers (systemd/coilysiren-{forgejo,github}-mirror.*), the new safe reconcile script (scripts/coilysiren-mirror-reconcile.sh), and the staged Forgejo Actions workflow (.forgejo/workflows/mirror-sync.yml)
  • infrastructure already owns the sensitive credentials and minting path: SSM-backed Forgejo tokens and the GitHub App installation token broker (scripts/github_app_token.py, ward exec github-app-token)
  • infrastructure already documents mirror-sync as an operator skill and explicitly says the old push --mirror pair is unsafe and being retired (skills/mirror-sync/SKILL.md)
  • ward already has the right seam for a forge/tracker command surface, but its own docs still describe GitHub coordination as a consumer-facing surface, not the fleet state owner (/substrate/ward/docs/compat-surface.md, /substrate/ward/README.md)

Boundary

Infrastructure should own:

  • the machine-readable repo bridge registry: repo slug, authoritative side, mirror target, tracker authority, and whether the repo is PR-gated or direct-to-main
  • scheduled reconciliation and freshness checks
  • divergence detection artifacts
  • credential storage and token minting
  • audit history of last successful sync / last divergent state

Ward should own:

  • read-only verbs over that state, for example ward ops bridge authoritative-side, mirror-status, divergent-refs, stale-syncs, map-issue
  • explicit write verbs later, only after the read path is proven, for example carry-pr
  • auth and dispatch mechanics needed to call GitHub and Forgejo safely

Inbox should own:

  • personal-work migration policy
  • decisions about how coilysiren/* should route issues and close semantics
  • Shortcut shadowing policy from ward#827

Inbox should not own mirror state, schedules, or fleet credentials.

Key decisions

  1. Per-repo authority must be first-class. Do not hardcode "Forgejo canonical" globally. ward#830 changes coilysiren/* toward GitHub-authoritative, so the hub needs a per-repo authority registry, not a namespace-wide assumption baked into scripts.
  2. Reframe infrastructure#475 as the first implementation slice. The issue body still says "force-push main + tags", but this repo already contains a safer design that avoids default force-sync and auto-merges GitHub PR commits into Forgejo without force-pushing. The first slice should be: registry + read-only mirror status + stale-sync detection + the existing safe reconcile pilot. Do not start with a force-push fleet job.
  3. Make read-only status the first public contract. Before carry-pr, issue mapping, or Shortcut shadowing, land a status surface that answers: authoritative side, last sync age, head SHAs on both sides, and whether refs are in-sync / FF / diverged / conflicted.
  4. Keep ward stateless about bridge ownership. ward should consume infrastructure-published facts, not grow its own separate repo-mapping table or mirror scheduler.

Infrastructure-backed implementation first:

  • bridge-repo-registry - outputs the authoritative mapping for one repo or the fleet
  • bridge-mirror-status - reports authoritative side, both default-branch heads, and state (in-sync, forgejo-ahead, github-ahead, diverged, conflict)
  • bridge-stale-syncs - reports repos whose last successful reconcile is older than threshold
  • bridge-map-work-item - initially read-only mapping stub for Forgejo issue / GitHub issue / GitHub PR / external tracker ids

Ward wrapper surface after that:

  • ward ops bridge authoritative-side <owner/repo>
  • ward ops bridge mirror-status [owner/repo]
  • ward ops bridge divergent-refs [owner/repo]
  • ward ops bridge stale-syncs
  • ward ops bridge map-issue <ref>

Options considered

Option A - ward owns the hub

Rejected.

ward is the right place for verbs and dispatch, but the wrong place for fleet-specific state, timers, SSM-backed creds, and ops audits. That would duplicate logic already living in infrastructure and violate the repo split the issue body is trying to enforce.

Option B - infrastructure owns everything including user-facing verbs

Rejected.

That would make operators shell directly into infra scripts and leave ward blind to a core cross-forge capability. The better split is infrastructure owns facts and jobs, ward owns the stable command UX.

Option C - infrastructure owns hub state, ward consumes it, inbox owns policy

Recommended.

This is closest to the current code and lowest-risk migration path.

Open questions

  • What is the storage format for the bridge registry and status artifacts: tracked YAML/JSON in-repo, generated JSON committed by workflow, or host-local state plus readout command? My recommendation is a tracked registry plus a generated status artifact.
  • Is tracker authority always the same as git authority? coilysiren/* likely says GitHub for both, but the schema should not assume they are identical.
  • What is the minimum viable mapping for map-issue? Recommendation: start with repo/ref metadata and links only. Defer durable bidirectional issue sync until after authority policy is settled.
  • How should stale syncs alert? Recommendation: start with workflow red + command output, then add timer/heartbeat alerting only after the status model is stable.

Risks

  • Split-brain during migration: if the old kai-server timers and the new workflow both run live, they can race each other. The staged cutover in skills/mirror-sync/SKILL.md should stay the source of truth.
  • Wrong default authority: a hub that assumes Forgejo-first everywhere will be invalid as soon as coilysiren/* becomes GitHub-authoritative.
  • Unsafe sync semantics: the older push --mirror design can overwrite divergent history. The repo already contains the safer reconcile path, so the design should bless that and retire the older model.
  • Tracker mapping too early: if Shortcut shadowing or cross-forge issue mapping lands before the authority registry, the system will hardcode the wrong source-of-truth assumptions.

Advisory close

The shortest path is:

  1. Use this issue to define the boundary.
  2. Reframe infrastructure#475 as the first infra slice: repo registry, mirror status, stale-sync detection, safe reconcile pilot.
  3. File a ward follow-up for read-only bridge verbs that consume infra state.
  4. Keep inbox policy work on coilysiren/inbox#184 plus a focused follow-up for coilysiren/* authority and tracker-migration rules.

I would explicitly defer carry-pr and Shortcut shadow writes until the read-only status surface and per-repo authority registry are landed.


The work spans multiple repos, so it was filed as 2 tracked issue(s), in dependency order:

  1. coilyco-flight-deck/ward#834 - Add read-only bridge verbs backed by infrastructure's coordination hub
  2. coilysiren/inbox#185 - Define coilysiren tracker authority and migration policy for the bridge hub

Researched and posted automatically by ward agent advisor --harness codex (ward#424). This is one-shot research, not a carried change - verify before acting on it.

— Codex, via ward agent

### 🔎 ward agent advisor - cross-repo fan-out `ward agent advisor --harness codex` ran a one-shot **standard** research pass on this question: > Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue. ## Recommendation Treat **infrastructure as the bridge control plane**, **ward as the typed command client**, and **inbox as policy only**. That matches what already exists in the repo better than pushing hub ownership into ward: - infrastructure already owns the live mirror machinery and its migration path: the legacy host timers (`systemd/coilysiren-{forgejo,github}-mirror.*`), the new safe reconcile script (`scripts/coilysiren-mirror-reconcile.sh`), and the staged Forgejo Actions workflow (`.forgejo/workflows/mirror-sync.yml`) - infrastructure already owns the sensitive credentials and minting path: SSM-backed Forgejo tokens and the GitHub App installation token broker (`scripts/github_app_token.py`, `ward exec github-app-token`) - infrastructure already documents mirror-sync as an operator skill and explicitly says the old `push --mirror` pair is unsafe and being retired (`skills/mirror-sync/SKILL.md`) - ward already has the right seam for a forge/tracker command surface, but its own docs still describe GitHub coordination as a consumer-facing surface, not the fleet state owner (`/substrate/ward/docs/compat-surface.md`, `/substrate/ward/README.md`) ## Boundary **Infrastructure should own:** - the machine-readable repo bridge registry: repo slug, authoritative side, mirror target, tracker authority, and whether the repo is PR-gated or direct-to-main - scheduled reconciliation and freshness checks - divergence detection artifacts - credential storage and token minting - audit history of last successful sync / last divergent state **Ward should own:** - read-only verbs over that state, for example `ward ops bridge authoritative-side`, `mirror-status`, `divergent-refs`, `stale-syncs`, `map-issue` - explicit write verbs later, only after the read path is proven, for example `carry-pr` - auth and dispatch mechanics needed to call GitHub and Forgejo safely **Inbox should own:** - personal-work migration policy - decisions about how `coilysiren/*` should route issues and close semantics - Shortcut shadowing policy from `ward#827` Inbox should **not** own mirror state, schedules, or fleet credentials. ## Key decisions 1. **Per-repo authority must be first-class.** Do not hardcode "Forgejo canonical" globally. `ward#830` changes `coilysiren/*` toward GitHub-authoritative, so the hub needs a per-repo authority registry, not a namespace-wide assumption baked into scripts. 2. **Reframe `infrastructure#475` as the first implementation slice.** The issue body still says "force-push main + tags", but this repo already contains a safer design that avoids default force-sync and auto-merges GitHub PR commits into Forgejo without force-pushing. The first slice should be: registry + read-only mirror status + stale-sync detection + the existing safe reconcile pilot. Do not start with a force-push fleet job. 3. **Make read-only status the first public contract.** Before `carry-pr`, issue mapping, or Shortcut shadowing, land a status surface that answers: authoritative side, last sync age, head SHAs on both sides, and whether refs are in-sync / FF / diverged / conflicted. 4. **Keep ward stateless about bridge ownership.** ward should consume infrastructure-published facts, not grow its own separate repo-mapping table or mirror scheduler. ## Recommended first commands / scripts Infrastructure-backed implementation first: - `bridge-repo-registry` - outputs the authoritative mapping for one repo or the fleet - `bridge-mirror-status` - reports authoritative side, both default-branch heads, and state (`in-sync`, `forgejo-ahead`, `github-ahead`, `diverged`, `conflict`) - `bridge-stale-syncs` - reports repos whose last successful reconcile is older than threshold - `bridge-map-work-item` - initially read-only mapping stub for Forgejo issue / GitHub issue / GitHub PR / external tracker ids Ward wrapper surface after that: - `ward ops bridge authoritative-side <owner/repo>` - `ward ops bridge mirror-status [owner/repo]` - `ward ops bridge divergent-refs [owner/repo]` - `ward ops bridge stale-syncs` - `ward ops bridge map-issue <ref>` ## Options considered ### Option A - ward owns the hub Rejected. ward is the right place for verbs and dispatch, but the wrong place for fleet-specific state, timers, SSM-backed creds, and ops audits. That would duplicate logic already living in infrastructure and violate the repo split the issue body is trying to enforce. ### Option B - infrastructure owns everything including user-facing verbs Rejected. That would make operators shell directly into infra scripts and leave ward blind to a core cross-forge capability. The better split is infrastructure owns facts and jobs, ward owns the stable command UX. ### Option C - infrastructure owns hub state, ward consumes it, inbox owns policy Recommended. This is closest to the current code and lowest-risk migration path. ## Open questions - What is the storage format for the bridge registry and status artifacts: tracked YAML/JSON in-repo, generated JSON committed by workflow, or host-local state plus readout command? My recommendation is a **tracked registry** plus a **generated status artifact**. - Is tracker authority always the same as git authority? `coilysiren/*` likely says GitHub for both, but the schema should not assume they are identical. - What is the minimum viable mapping for `map-issue`? Recommendation: start with repo/ref metadata and links only. Defer durable bidirectional issue sync until after authority policy is settled. - How should stale syncs alert? Recommendation: start with workflow red + command output, then add timer/heartbeat alerting only after the status model is stable. ## Risks - **Split-brain during migration**: if the old kai-server timers and the new workflow both run live, they can race each other. The staged cutover in `skills/mirror-sync/SKILL.md` should stay the source of truth. - **Wrong default authority**: a hub that assumes Forgejo-first everywhere will be invalid as soon as `coilysiren/*` becomes GitHub-authoritative. - **Unsafe sync semantics**: the older `push --mirror` design can overwrite divergent history. The repo already contains the safer reconcile path, so the design should bless that and retire the older model. - **Tracker mapping too early**: if Shortcut shadowing or cross-forge issue mapping lands before the authority registry, the system will hardcode the wrong source-of-truth assumptions. ## Advisory close The shortest path is: 1. **Use this issue to define the boundary.** 2. **Reframe `infrastructure#475` as the first infra slice**: repo registry, mirror status, stale-sync detection, safe reconcile pilot. 3. **File a ward follow-up** for read-only bridge verbs that consume infra state. 4. **Keep inbox policy work on `coilysiren/inbox#184` plus a focused follow-up** for `coilysiren/*` authority and tracker-migration rules. I would explicitly defer `carry-pr` and Shortcut shadow writes until the read-only status surface and per-repo authority registry are landed. --- The work spans multiple repos, so it was filed as 2 tracked issue(s), in dependency order: 1. [coilyco-flight-deck/ward#834](https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/834) - Add read-only bridge verbs backed by infrastructure's coordination hub 2. [coilysiren/inbox#185](https://forgejo.coilysiren.me/coilysiren/inbox/issues/185) - Define coilysiren tracker authority and migration policy for the bridge hub --- Researched and posted automatically by `ward agent advisor --harness codex` (ward#424). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Codex, via `ward agent`
Owner

This ticket pushed a bunch of logic into coilyco-flight-deck/ward#834 that definitely does not belong there. ward shouldn't be hardcoding the expectation of syncing git state between platforms

This ticket pushed a bunch of logic into https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/834 that definitely does not belong there. ward shouldn't be hardcoding the expectation of syncing git state between platforms
coilyco-ops added
P3
and removed
P2
labels 2026-07-10 08:59:53 +00:00
Author
Member

🔎 ward agent advisor

ward agent advisor --harness codex ran a one-shot standard research pass on this question:

Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue.


Recommendation

Keep this issue as the hub-boundary decision and keep the first implementation slice in infrastructure, not ward.

Kai's correction on ward#834 is right: ward should not hardcode git sync expectations between Forgejo and GitHub. The durable split should be:

  • infrastructure owns the bridge control plane: desired repo registry, per-repo authority, scheduled checks, status snapshots, divergence detection, sync jobs, stale-sync alerts, credentials, and audit trails.
  • ward owns bounded operator verbs only after infra has a stable contract: it may call infra-owned commands or read infra-owned JSON, but it should not carry its own mirror registry or sync logic.
  • inbox owns personal workflow policy: tracker migration, Shortcut shadowing policy, and which human workflow decisions apply to coilysiren/*. It should not own forge credentials, mirror state, or schedulers.

Repo evidence

The current infrastructure clone already supports that boundary:

  • .ward/ward.yaml exposes forgejo-github-mirror-sync, forgejo-admin-token, and github-app-token as infra verbs.
  • scripts/forgejo_github_mirror_sync.py and .forgejo/workflows/github-mirror-sync.yml are a fleet-level Forgejo-to-GitHub mirror path. They still assume Forgejo is canonical and force-update GitHub.
  • skills/mirror-sync/SKILL.md, .forgejo/workflows/mirror-sync.yml, and scripts/coilysiren-mirror-reconcile.sh document a safer reconcile path that avoids force-push and handles GitHub PR commits, but it still assumes Forgejo is canonical.
  • docs/forgejo-github-automation-identity.md already makes the key identity distinction requested by infrastructure#492: Forgejo automation is the coilyco-ops bot user, while GitHub automation is a GitHub App installation token.
  • docs/FEATURES.md already says infrastructure owns the identity split, GitHub App token helper, Forgejo bot grants, and fleet mirror pieces.

That means the missing piece is not a ward-side design. The missing piece is an infrastructure-owned model that tells every caller what the bridge truth is for a repo.

Decisions

  1. Per-repo authority must be explicit. Do not assume Forgejo-first globally. coilyco-flight-deck/*, coilyco-bridge/*, coilyco-gaming/*, coilysiren/*, and special cases like coilyco-flight-deck/homebrew-tap can have different authority rules.

  2. Git authority and tracker authority must be separate fields. A repo can be GitHub-authoritative for git while still having Forgejo issues during migration, or vice versa. Shortcut shadowing should depend on tracker policy, not git mirror policy.

  3. Status and writing must be separate. The first public contract should answer what is true, not change either forge. Write sync can come after the status model proves itself.

  4. Infrastructure should publish the contract ward consumes. Ward may eventually offer ward ops bridge ... commands, but those commands should shell out to infra-owned scripts, call an infra-owned endpoint, or read an infra-owned JSON schema. Ward should not duplicate the registry or encode sync direction.

  5. infrastructure#475 should be reframed, not discarded. It is still the right first implementation track, but its acceptance should shift away from broad force-mirroring. The first slice should be registry + read-only status + stale detection + a decision on how to retire or quarantine the force-push mirror path.

These should live in infrastructure first:

  • bridge-registry - validate and print the desired repo bridge registry. Fields should include Forgejo repo, GitHub repo, git authority, tracker authority, mirror mode, default branch, PR/direct-to-main policy, and credential identity.
  • bridge-authoritative-side <repo> - answer git authority and tracker authority for one repo.
  • bridge-mirror-status [repo] - fetch both forges and report default-branch SHAs plus state: in-sync, forgejo-ahead, github-ahead, diverged, missing-github, missing-forgejo, or unknown.
  • bridge-divergent-refs [repo] - list refs where both sides have advanced incompatibly. Start with default branch only, then expand to tags and release branches once the default-branch model is stable.
  • bridge-stale-syncs - report repos whose last successful status or reconcile check is older than a threshold.
  • bridge-map-work-item <ref> - read-only mapping from Forgejo issue, GitHub issue, GitHub PR, or external tracker id to known related refs. Start as registry-backed metadata and links only. Do not implement bidirectional tracker sync in the first slice.

Ward follow-up should wait until these infra commands exist. Then ward's issue can be narrowed to thin wrappers like ward ops bridge mirror-status, with no embedded sync rules.

Options

Option A: ward owns the bridge hub

Reject. This is the failure mode Kai flagged. Ward is reusable command and dispatch machinery. It should not own fleet-specific repo authority, sync schedules, SSM-backed credentials, or mirror audits.

Option B: inbox owns the bridge hub

Reject. Inbox is the right place for personal-work policy and migration decisions, but it is the wrong layer for fleet credentials, scheduled jobs, and git divergence checks.

Option C: infrastructure owns the hub, ward consumes it, inbox owns policy

Recommend. It matches the existing repo ownership and keeps the reusable ward surface clean.

Option D: keep infrastructure#475 as a force mirror rollout

Reject as written. The existing forgejo_github_mirror_sync.py path force-pushes and assumes Forgejo canonical. That is too narrow for GitHub-authoritative migration and risky under divergence. Keep the issue, but reframe it as the first read-only bridge-control-plane slice.

Open questions

  • Where should the desired registry live? Recommendation: tracked YAML or JSON in infrastructure, with a schema test.
  • Where should generated status live? Recommendation: start with live readout command output and Forgejo Actions artifacts. Add durable state only when stale-sync alerting needs it.
  • Which repos are in the first registry seed? Recommendation: include the known mirror fleet plus explicit exceptions like coilyco-flight-deck/homebrew-tap.
  • What is the exact coilysiren/* migration policy? Recommendation: inbox decides policy, infrastructure stores only the resulting per-repo authority fields.
  • What should happen to ward#834? Recommendation: rewrite it to depend on the infra contract, or close it if it already contains bridge-state ownership that belongs here.
  • Is the GitHub App installed on every repo that needs status checks? If not, the registry should record missing installation as a status problem rather than silently falling back to a personal PAT.

Risks

  • Split brain: old kai-server timers, github-mirror-sync, and mirror-sync can race if more than one writer is active.
  • Unsafe force-push semantics: the current forgejo_github_mirror_sync.py force-updates GitHub and does not fit a mixed-authority future.
  • Wrong default authority: a global Forgejo-canonical assumption will break the coilysiren/* GitHub-authoritative migration.
  • Identity confusion: Forgejo coilyco-ops and the GitHub App installation are different actors with different rotation and permission models.
  • Premature tracker sync: Shortcut or issue shadowing before the authority registry will bake in the wrong source of truth.
  • Ward drift: if ward grows its own repo table, infra and ward will disagree about authority during migration.

Advisory close

Use this issue to ratify the boundary. Reframe infrastructure#475 as the first infra implementation slice: tracked bridge registry, read-only mirror status, divergent-ref detection, stale-sync reporting, and an explicit plan to retire or quarantine unsafe force mirror paths. Do not add more bridge-state logic to ward until infrastructure publishes the contract ward should consume.


Researched and posted automatically by ward agent advisor --harness codex (ward#179). This is one-shot research, not a carried change - verify before acting on it.

— Codex, via ward agent

### 🔎 ward agent advisor `ward agent advisor --harness codex` ran a one-shot **standard** research pass on this question: > Read the issue title, body, and comment thread below as the research brief. Identify the decisions, options, open questions, and risks, then give a clear advisory recommendation that can be posted back on the issue. --- ## Recommendation Keep this issue as the hub-boundary decision and keep the first implementation slice in **infrastructure**, not ward. Kai's correction on `ward#834` is right: ward should not hardcode git sync expectations between Forgejo and GitHub. The durable split should be: * **infrastructure owns the bridge control plane**: desired repo registry, per-repo authority, scheduled checks, status snapshots, divergence detection, sync jobs, stale-sync alerts, credentials, and audit trails. * **ward owns bounded operator verbs only after infra has a stable contract**: it may call infra-owned commands or read infra-owned JSON, but it should not carry its own mirror registry or sync logic. * **inbox owns personal workflow policy**: tracker migration, Shortcut shadowing policy, and which human workflow decisions apply to `coilysiren/*`. It should not own forge credentials, mirror state, or schedulers. ## Repo evidence The current infrastructure clone already supports that boundary: * `.ward/ward.yaml` exposes `forgejo-github-mirror-sync`, `forgejo-admin-token`, and `github-app-token` as infra verbs. * `scripts/forgejo_github_mirror_sync.py` and `.forgejo/workflows/github-mirror-sync.yml` are a fleet-level Forgejo-to-GitHub mirror path. They still assume Forgejo is canonical and force-update GitHub. * `skills/mirror-sync/SKILL.md`, `.forgejo/workflows/mirror-sync.yml`, and `scripts/coilysiren-mirror-reconcile.sh` document a safer reconcile path that avoids force-push and handles GitHub PR commits, but it still assumes Forgejo is canonical. * `docs/forgejo-github-automation-identity.md` already makes the key identity distinction requested by `infrastructure#492`: Forgejo automation is the `coilyco-ops` bot user, while GitHub automation is a GitHub App installation token. * `docs/FEATURES.md` already says infrastructure owns the identity split, GitHub App token helper, Forgejo bot grants, and fleet mirror pieces. That means the missing piece is not a ward-side design. The missing piece is an infrastructure-owned model that tells every caller what the bridge truth is for a repo. ## Decisions 1. **Per-repo authority must be explicit.** Do not assume Forgejo-first globally. `coilyco-flight-deck/*`, `coilyco-bridge/*`, `coilyco-gaming/*`, `coilysiren/*`, and special cases like `coilyco-flight-deck/homebrew-tap` can have different authority rules. 2. **Git authority and tracker authority must be separate fields.** A repo can be GitHub-authoritative for git while still having Forgejo issues during migration, or vice versa. Shortcut shadowing should depend on tracker policy, not git mirror policy. 3. **Status and writing must be separate.** The first public contract should answer what is true, not change either forge. Write sync can come after the status model proves itself. 4. **Infrastructure should publish the contract ward consumes.** Ward may eventually offer `ward ops bridge ...` commands, but those commands should shell out to infra-owned scripts, call an infra-owned endpoint, or read an infra-owned JSON schema. Ward should not duplicate the registry or encode sync direction. 5. **`infrastructure#475` should be reframed, not discarded.** It is still the right first implementation track, but its acceptance should shift away from broad force-mirroring. The first slice should be registry + read-only status + stale detection + a decision on how to retire or quarantine the force-push mirror path. ## Recommended first infra commands or scripts These should live in infrastructure first: * `bridge-registry` - validate and print the desired repo bridge registry. Fields should include Forgejo repo, GitHub repo, git authority, tracker authority, mirror mode, default branch, PR/direct-to-main policy, and credential identity. * `bridge-authoritative-side <repo>` - answer git authority and tracker authority for one repo. * `bridge-mirror-status [repo]` - fetch both forges and report default-branch SHAs plus state: `in-sync`, `forgejo-ahead`, `github-ahead`, `diverged`, `missing-github`, `missing-forgejo`, or `unknown`. * `bridge-divergent-refs [repo]` - list refs where both sides have advanced incompatibly. Start with default branch only, then expand to tags and release branches once the default-branch model is stable. * `bridge-stale-syncs` - report repos whose last successful status or reconcile check is older than a threshold. * `bridge-map-work-item <ref>` - read-only mapping from Forgejo issue, GitHub issue, GitHub PR, or external tracker id to known related refs. Start as registry-backed metadata and links only. Do not implement bidirectional tracker sync in the first slice. Ward follow-up should wait until these infra commands exist. Then ward's issue can be narrowed to thin wrappers like `ward ops bridge mirror-status`, with no embedded sync rules. ## Options ### Option A: ward owns the bridge hub Reject. This is the failure mode Kai flagged. Ward is reusable command and dispatch machinery. It should not own fleet-specific repo authority, sync schedules, SSM-backed credentials, or mirror audits. ### Option B: inbox owns the bridge hub Reject. Inbox is the right place for personal-work policy and migration decisions, but it is the wrong layer for fleet credentials, scheduled jobs, and git divergence checks. ### Option C: infrastructure owns the hub, ward consumes it, inbox owns policy Recommend. It matches the existing repo ownership and keeps the reusable ward surface clean. ### Option D: keep `infrastructure#475` as a force mirror rollout Reject as written. The existing `forgejo_github_mirror_sync.py` path force-pushes and assumes Forgejo canonical. That is too narrow for GitHub-authoritative migration and risky under divergence. Keep the issue, but reframe it as the first read-only bridge-control-plane slice. ## Open questions * Where should the desired registry live? Recommendation: tracked YAML or JSON in infrastructure, with a schema test. * Where should generated status live? Recommendation: start with live readout command output and Forgejo Actions artifacts. Add durable state only when stale-sync alerting needs it. * Which repos are in the first registry seed? Recommendation: include the known mirror fleet plus explicit exceptions like `coilyco-flight-deck/homebrew-tap`. * What is the exact `coilysiren/*` migration policy? Recommendation: inbox decides policy, infrastructure stores only the resulting per-repo authority fields. * What should happen to `ward#834`? Recommendation: rewrite it to depend on the infra contract, or close it if it already contains bridge-state ownership that belongs here. * Is the GitHub App installed on every repo that needs status checks? If not, the registry should record missing installation as a status problem rather than silently falling back to a personal PAT. ## Risks * **Split brain**: old kai-server timers, `github-mirror-sync`, and `mirror-sync` can race if more than one writer is active. * **Unsafe force-push semantics**: the current `forgejo_github_mirror_sync.py` force-updates GitHub and does not fit a mixed-authority future. * **Wrong default authority**: a global Forgejo-canonical assumption will break the `coilysiren/*` GitHub-authoritative migration. * **Identity confusion**: Forgejo `coilyco-ops` and the GitHub App installation are different actors with different rotation and permission models. * **Premature tracker sync**: Shortcut or issue shadowing before the authority registry will bake in the wrong source of truth. * **Ward drift**: if ward grows its own repo table, infra and ward will disagree about authority during migration. ## Advisory close Use this issue to ratify the boundary. Reframe `infrastructure#475` as the first infra implementation slice: tracked bridge registry, read-only mirror status, divergent-ref detection, stale-sync reporting, and an explicit plan to retire or quarantine unsafe force mirror paths. Do not add more bridge-state logic to ward until infrastructure publishes the contract ward should consume. --- Researched and posted automatically by `ward agent advisor --harness codex` (ward#179). This is one-shot research, not a carried change - verify before acting on it. <!-- ward-agent-reply --> <!-- ward-agent-signature --> — Codex, via `ward agent`
Sign in to join this conversation.
No description provided.