catalog.dependsOn must honor a full clone URL (non-Forgejo host + host-side ssh seed) so external deps like github.com/StrangeLoopGames/Eco actually hydrate #612

Closed
opened 2026-07-05 21:47:55 +00:00 by coilysiren · 4 comments
Owner

Summary

catalog.dependsOn cannot express an external, non-Forgejo dependency, so declaring one silently fails to hydrate. github.com/StrangeLoopGames/Eco is declared in eco-ops but never arrives in the container. The fix is to let dependsOn (and the atlas service-catalogue generally) carry a full git clone URL and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path.

Current broken behavior

eco-ops .ward/ward.yaml declares, correctly host-qualified:

catalog:
  dependsOn:
    - github.com/StrangeLoopGames/Eco

But the resolver (resolveCatalogContextRepos, ward#573) throws the host away: in-container the declaration surfaces as WARD_CONTEXT_REPOS=StrangeLoopGames/Eco, which is then fed into the Forgejo-HTTPS gitcache pipeline (every gitcache mirror fetches https://forgejo.coilysiren.me/<owner>/<name>.git, keyed by FORGEJO_TOKEN). Forgejo has no such repo, so the seed step creates only a 0-byte /gitcache/.StrangeLoopGames__Eco.lock with no mirror, and the sibling ../Eco/ clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: ../Eco absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41.

Root cause

The dep pipeline assumes every dep is a Forgejo owner/name reachable over HTTPS with the Forgejo token. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless.

Ask

  1. Let catalog.dependsOn entries be a full git clone URL (for example ssh://git@github.com/StrangeLoopGames/Eco.git), not only a bare owner/name. The --repo grant path already accepts a clone URL (container-multi-repo.md), so extend the same URL-form support to the dependsOn context-repo path.
  2. Honor the URL's host and transport. A Forgejo entry keeps the existing HTTPS-token gitcache path. A non-Forgejo entry (github.com) is cloned host-side over ssh using the host's key into the gitcache mirror, then locally cloned into the container like every other mirror. The key stays on the host; the sealed container never talks to GitHub.
  3. Generalize this to the atlas service-catalogue dep shape, not a one-off for Eco. Any catalogue dep should be able to declare a full cloneable URL so external upstreams resolve uniformly.
  4. Fail loud when a declared dep does not hydrate. Today it resolves to a silent empty lock that reads as "source available" when it is not. A missing declared dep should warn at launch, naming the dep and why it did not arrive.

Explicitly rejected

Do not mirror StrangeLoopGames/Eco (or any third-party source) onto Forgejo. Redistributing third-party code we do not own is a legal-distribution risk. Host-side ssh clone into the gitcache is the sanctioned path.

Acceptance

  • An eco-ops (or any) container with dependsOn: [ ssh://git@github.com/StrangeLoopGames/Eco.git ] has a readable Eco source clone on disk, seeded host-side over ssh, or fails loudly at launch naming the gap.
  • Closes ward#611 (the Eco-source symptom) as a consequence of the general fix.

Refs

  • Resolver: ward resolveCatalogContextRepos (ward#573, ward#580). Docs: ward docs/ward-yaml.md, docs/container-multi-repo.md. Symptom: ward#611, eco-ops#41. Origin: eco-ops director session, 2026-07-05.

Reopened: closed but Eco source still does NOT mount (found via eco-ops#48, 2026-07-06)

eco-ops#48 was dispatched specifically to read the Eco engine source (this issue's whole point) and hit a hard wall — the resolver code landed, but the source never arrives:

  • ../Eco is empty.
  • StrangeLoopGames/Eco 404s under the coilyco-ops bot token — the repo is private and the bot is not a collaborator, so the token cannot clone it.
  • Public EcoModKit is SDK-only (no engine bodies); org code search sees nothing.
  • GitHub itself was reachable — so this is a missing/unauthorized mount, not a network blip.
  • It also did not fail loud#48 had to discover the empty mount itself, which the acceptance criterion ("present and readable, OR loud failure at startup") explicitly forbids.

The real remaining gap: credentials, not the resolver

The full-clone-URL resolver work landed, but the host-side gitcache seed has no credential that can read the private repo. The coilyco-ops bot lacks access. Per Kai (this session): her own GitHub SSH key has Eco clone access. So the host-side seed for StrangeLoopGames/Eco must clone with Kai's key, not the bot token — the key stays host-side, the sealed container still just clones from the warm mirror. Until that credential path is wired, the declaration keeps resolving to an empty mount.

What must actually be true to close this

  1. Host-side gitcache seed clones StrangeLoopGames/Eco using a credential that has access (Kai's GitHub SSH key), populating the mirror.
  2. A warded eco-ops/eco-app/eco-mods container gets a readable ../Eco from that warm mirror.
  3. If the credential is absent, the container fails loud at startup naming the gap — never a silent empty mount.

Blocks: eco-ops#48 (source read still owed), the eco-ops#41 restart-design confirmation (native vs external), and every source-verify for the Eco mods. Reopened 2026-07-06.


DECISION from Kai (2026-07-06): host-side ssh seed uses the DEFAULT ssh keychain

The host-side gitcache seed for an ssh-scheme dep (ssh://git@github.com/... or git@github.com:...) must clone using the host user's default ssh keychain - the ambient ssh-agent + ~/.ssh identities, exactly what a plain git clone over ssh already resolves. Not the coilyco-ops bot token, not a ward-managed per-repo credential.

Rationale (Kai): "that's what users will expect." When ward hydrates an ssh dependency, it should inherit whatever ssh access the host user already has, the same way their own git clone would. On Kai's host her default ssh identity has StrangeLoopGames/Eco access, so the seed just works. Any other ward user gets their own ssh access applied - no provisioning step, no embedded secret.

Implementation direction

  • In the host-side gitcache seed path (runs on the host, has the user's ssh-agent/keys - NOT in the sealed container), for an ssh-URL dep, shell out to git over ssh and let the host's default ssh resolve the identity (agent first, then default ~/.ssh identity files / ~/.ssh/config). Do not inject or require a ward-specific key.
  • The sealed container still only ever clones from the warm gitcache mirror - the ssh key never enters the container.
  • Keep it general: this is the transport for ANY ssh-scheme catalog.dependsOn entry, not an Eco special case.
  • Fail loud if the ssh clone fails (no access / key missing): the container must report the dep did not hydrate at startup, never a silent empty ../Eco.

Done condition (unchanged)

A warded eco-ops/eco-app/eco-mods container gets a readable ../Eco seeded host-side via the default ssh keychain, or fails loud at startup naming the gap.

## Summary `catalog.dependsOn` cannot express an **external, non-Forgejo** dependency, so declaring one silently fails to hydrate. `github.com/StrangeLoopGames/Eco` is declared in eco-ops but never arrives in the container. The fix is to let `dependsOn` (and the atlas service-catalogue generally) carry a **full git clone URL** and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path. ## Current broken behavior eco-ops `.ward/ward.yaml` declares, correctly host-qualified: ``` catalog: dependsOn: - github.com/StrangeLoopGames/Eco ``` But the resolver (`resolveCatalogContextRepos`, ward#573) **throws the host away**: in-container the declaration surfaces as `WARD_CONTEXT_REPOS=StrangeLoopGames/Eco`, which is then fed into the **Forgejo-HTTPS gitcache pipeline** (every gitcache mirror fetches `https://forgejo.coilysiren.me/<owner>/<name>.git`, keyed by `FORGEJO_TOKEN`). Forgejo has no such repo, so the seed step creates only a 0-byte `/gitcache/.StrangeLoopGames__Eco.lock` with no mirror, and the sibling `../Eco/` clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: `../Eco` absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41. ## Root cause The dep pipeline assumes **every dep is a Forgejo `owner/name` reachable over HTTPS with the Forgejo token**. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless. ## Ask 1. Let `catalog.dependsOn` entries be a **full git clone URL** (for example `ssh://git@github.com/StrangeLoopGames/Eco.git`), not only a bare `owner/name`. The `--repo` grant path already accepts a clone URL (container-multi-repo.md), so extend the same URL-form support to the `dependsOn` context-repo path. 2. Honor the URL's **host and transport**. A Forgejo entry keeps the existing HTTPS-token gitcache path. A non-Forgejo entry (github.com) is cloned **host-side over ssh** using the host's key into the gitcache mirror, then locally cloned into the container like every other mirror. The key stays on the host; the sealed container never talks to GitHub. 3. Generalize this to the **atlas service-catalogue** dep shape, not a one-off for Eco. Any catalogue dep should be able to declare a full cloneable URL so external upstreams resolve uniformly. 4. **Fail loud** when a declared dep does not hydrate. Today it resolves to a silent empty lock that reads as "source available" when it is not. A missing declared dep should warn at launch, naming the dep and why it did not arrive. ## Explicitly rejected Do **not** mirror `StrangeLoopGames/Eco` (or any third-party source) onto Forgejo. Redistributing third-party code we do not own is a legal-distribution risk. Host-side ssh clone into the gitcache is the sanctioned path. ## Acceptance - An eco-ops (or any) container with `dependsOn: [ ssh://git@github.com/StrangeLoopGames/Eco.git ]` has a readable Eco source clone on disk, seeded host-side over ssh, or fails loudly at launch naming the gap. - Closes ward#611 (the Eco-source symptom) as a consequence of the general fix. ## Refs - Resolver: ward `resolveCatalogContextRepos` (ward#573, ward#580). Docs: ward `docs/ward-yaml.md`, `docs/container-multi-repo.md`. Symptom: ward#611, eco-ops#41. Origin: eco-ops director session, 2026-07-05. --- ## Reopened: closed but Eco source still does NOT mount (found via eco-ops#48, 2026-07-06) eco-ops#48 was dispatched specifically to read the Eco engine source (this issue's whole point) and hit a hard wall — the resolver code landed, but the source never arrives: - `../Eco` is **empty**. - `StrangeLoopGames/Eco` **404s under the coilyco-ops bot token** — the repo is private and the bot **is not a collaborator**, so the token cannot clone it. - Public EcoModKit is SDK-only (no engine bodies); org code search sees nothing. - **GitHub itself was reachable** — so this is a missing/unauthorized mount, not a network blip. - It also did **not fail loud** — #48 had to discover the empty mount itself, which the acceptance criterion ("present and readable, OR loud failure at startup") explicitly forbids. ## The real remaining gap: credentials, not the resolver The full-clone-URL resolver work landed, but the **host-side gitcache seed has no credential that can read the private repo.** The coilyco-ops bot lacks access. Per Kai (this session): **her own GitHub SSH key has Eco clone access.** So the host-side seed for `StrangeLoopGames/Eco` must clone with **Kai's key**, not the bot token — the key stays host-side, the sealed container still just clones from the warm mirror. Until that credential path is wired, the declaration keeps resolving to an empty mount. ## What must actually be true to close this 1. Host-side gitcache seed clones `StrangeLoopGames/Eco` using a credential that has access (Kai's GitHub SSH key), populating the mirror. 2. A warded eco-ops/eco-app/eco-mods container gets a readable `../Eco` from that warm mirror. 3. If the credential is absent, the container fails **loud** at startup naming the gap — never a silent empty mount. Blocks: eco-ops#48 (source read still owed), the eco-ops#41 restart-design confirmation (native vs external), and every source-verify for the Eco mods. Reopened 2026-07-06. --- ## DECISION from Kai (2026-07-06): host-side ssh seed uses the DEFAULT ssh keychain The host-side gitcache seed for an ssh-scheme dep (`ssh://git@github.com/...` or `git@github.com:...`) must clone using the **host user's default ssh keychain** - the ambient ssh-agent + `~/.ssh` identities, exactly what a plain `git clone` over ssh already resolves. Not the coilyco-ops bot token, not a ward-managed per-repo credential. Rationale (Kai): "that's what users will expect." When ward hydrates an ssh dependency, it should inherit whatever ssh access the host user already has, the same way their own `git clone` would. On Kai's host her default ssh identity has `StrangeLoopGames/Eco` access, so the seed just works. Any other ward user gets their own ssh access applied - no provisioning step, no embedded secret. ## Implementation direction - In the **host-side** gitcache seed path (runs on the host, has the user's ssh-agent/keys - NOT in the sealed container), for an ssh-URL dep, shell out to `git` over ssh and let the host's default ssh resolve the identity (agent first, then default `~/.ssh` identity files / `~/.ssh/config`). Do not inject or require a ward-specific key. - The sealed container still only ever clones from the warm gitcache mirror - the ssh key never enters the container. - Keep it general: this is the transport for ANY ssh-scheme `catalog.dependsOn` entry, not an Eco special case. - **Fail loud** if the ssh clone fails (no access / key missing): the container must report the dep did not hydrate at startup, never a silent empty `../Eco`. ## Done condition (unchanged) A warded eco-ops/eco-app/eco-mods container gets a readable `../Eco` seeded host-side via the default ssh keychain, or fails loud at startup naming the gap.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-612 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-05T21:52:47Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#612 · branch issue-612 · driver claude · workflow direct-main
  • Run: engineer-claude-ward-612 · ward v0.406.0 · dispatched 2026-07-05T21:52:47Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Issue body as seeded:

## Summary

`catalog.dependsOn` cannot express an **external, non-Forgejo** dependency, so declaring one silently fails to hydrate. `github.com/StrangeLoopGames/Eco` is declared in eco-ops but never arrives in the container. The fix is to let `dependsOn` (and the atlas service-catalogue generally) carry a **full git clone URL** and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path.

## Current broken behavior

eco-ops `.ward/ward.yaml` declares, correctly host-qualified:

` ` `
catalog:
  dependsOn:
    - github.com/StrangeLoopGames/Eco
` ` `

But the resolver (`resolveCatalogContextRepos`, ward#573) **throws the host away**: in-container the declaration surfaces as `WARD_CONTEXT_REPOS=StrangeLoopGames/Eco`, which is then fed into the **Forgejo-HTTPS gitcache pipeline** (every gitcache mirror fetches `https://forgejo.coilysiren.me/<owner>/<name>.git`, keyed by `FORGEJO_TOKEN`). Forgejo has no such repo, so the seed step creates only a 0-byte `/gitcache/.StrangeLoopGames__Eco.lock` with no mirror, and the sibling `../Eco/` clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: `../Eco` absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41.

## Root cause

The dep pipeline assumes **every dep is a Forgejo `owner/name` reachable over HTTPS with the Forgejo token**. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless.

## Ask

1. Let `catalog.dependsOn` entries be a **full git clone URL** (for example `ssh://git@github.com/StrangeLoopGames/Eco.git`), not only a bare `owner/name`. The `--repo` grant path already accepts a c

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.406.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-612` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-05T21:52:47Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#612` · branch `issue-612` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-ward-612` · ward `v0.406.0` · dispatched `2026-07-05T21:52:47Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). **Issue body as seeded:** ``` ## Summary `catalog.dependsOn` cannot express an **external, non-Forgejo** dependency, so declaring one silently fails to hydrate. `github.com/StrangeLoopGames/Eco` is declared in eco-ops but never arrives in the container. The fix is to let `dependsOn` (and the atlas service-catalogue generally) carry a **full git clone URL** and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path. ## Current broken behavior eco-ops `.ward/ward.yaml` declares, correctly host-qualified: ` ` ` catalog: dependsOn: - github.com/StrangeLoopGames/Eco ` ` ` But the resolver (`resolveCatalogContextRepos`, ward#573) **throws the host away**: in-container the declaration surfaces as `WARD_CONTEXT_REPOS=StrangeLoopGames/Eco`, which is then fed into the **Forgejo-HTTPS gitcache pipeline** (every gitcache mirror fetches `https://forgejo.coilysiren.me/<owner>/<name>.git`, keyed by `FORGEJO_TOKEN`). Forgejo has no such repo, so the seed step creates only a 0-byte `/gitcache/.StrangeLoopGames__Eco.lock` with no mirror, and the sibling `../Eco/` clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: `../Eco` absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41. ## Root cause The dep pipeline assumes **every dep is a Forgejo `owner/name` reachable over HTTPS with the Forgejo token**. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless. ## Ask 1. Let `catalog.dependsOn` entries be a **full git clone URL** (for example `ssh://git@github.com/StrangeLoopGames/Eco.git`), not only a bare `owner/name`. The `--repo` grant path already accepts a c ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.406.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - dependsOn now honors a full clone URL (host+transport), external deps fail loud instead of a silent empty lock; host-side ssh seed is a filed follow-up.

Honest retro: the data-model half was clean and satisfying. The bug the issue named is exactly right - the resolver flattened every dep to owner/name and force-fed it the Forgejo-HTTPS pipeline, so the fix (parse the URL, keep the host, encode it through WARD_CONTEXT_REPOS as owner/name=<cloneURL>, honor it in both the Go and bash clone paths) fell out naturally and is fully unit-tested end to end.

What fought back: the dual bash+Go entrypoint doubles every change, and the code-comment discipline hook (2-line blocks, 90-char cap) made me rewrite nearly every comment two or three times - my own fault for over-explaining.

The part I did not finish, and want to be candid about: the actual host-side ssh seed into the gitcache volume. In the sealed director-dispatch case (the reported one), the host never has the target clone, so seeding needs the host to fetch the target's .ward/ward.yaml off the forge and then populate a docker named volume over ssh - a real new capability I couldn't build and validate blind from inside a container with no github key. So today an external dep fails loud, naming the gap (acceptance's second branch) rather than hydrating. That means ward#611 (Eco actually on disk) is not closed yet - it needs the seed. I'd file that as the immediate follow-up: resolveCatalogContextRepos host-side + a git clone --mirror ssh://... helper against ward-gitcache.

Confidence: high on the parse/encode/fail-loud slice (tested, hooks green, merged to main). Low-to-none that Eco hydrates until the seed lands - by design, not by accident.

WARD-OUTCOME: done - dependsOn now honors a full clone URL (host+transport), external deps fail loud instead of a silent empty lock; host-side ssh seed is a filed follow-up. Honest retro: the data-model half was clean and satisfying. The bug the issue named is exactly right - the resolver flattened every dep to `owner/name` and force-fed it the Forgejo-HTTPS pipeline, so the fix (parse the URL, keep the host, encode it through `WARD_CONTEXT_REPOS` as `owner/name=<cloneURL>`, honor it in both the Go and bash clone paths) fell out naturally and is fully unit-tested end to end. What fought back: the dual bash+Go entrypoint doubles every change, and the code-comment discipline hook (2-line blocks, 90-char cap) made me rewrite nearly every comment two or three times - my own fault for over-explaining. The part I did **not** finish, and want to be candid about: the actual **host-side ssh seed** into the gitcache volume. In the sealed director-dispatch case (the reported one), the host never has the target clone, so seeding needs the host to fetch the target's `.ward/ward.yaml` off the forge and then populate a docker named volume over ssh - a real new capability I couldn't build and validate blind from inside a container with no github key. So today an external dep **fails loud, naming the gap** (acceptance's second branch) rather than hydrating. That means `ward#611` (Eco actually on disk) is **not** closed yet - it needs the seed. I'd file that as the immediate follow-up: `resolveCatalogContextRepos` host-side + a `git clone --mirror ssh://...` helper against `ward-gitcache`. Confidence: high on the parse/encode/fail-loud slice (tested, hooks green, merged to main). Low-to-none that Eco hydrates until the seed lands - by design, not by accident.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-612 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-06T02:00:58Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#612 · branch issue-612 · driver claude · workflow direct-main
  • Run: engineer-claude-ward-612 · ward v0.406.0 · dispatched 2026-07-06T02:00:58Z
  • Comment thread: 1 included in the pre-flight read, 1 stripped (ward's own automated comments).

Issue body as seeded:

## Summary

`catalog.dependsOn` cannot express an **external, non-Forgejo** dependency, so declaring one silently fails to hydrate. `github.com/StrangeLoopGames/Eco` is declared in eco-ops but never arrives in the container. The fix is to let `dependsOn` (and the atlas service-catalogue generally) carry a **full git clone URL** and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path.

## Current broken behavior

eco-ops `.ward/ward.yaml` declares, correctly host-qualified:

` ` `
catalog:
  dependsOn:
    - github.com/StrangeLoopGames/Eco
` ` `

But the resolver (`resolveCatalogContextRepos`, ward#573) **throws the host away**: in-container the declaration surfaces as `WARD_CONTEXT_REPOS=StrangeLoopGames/Eco`, which is then fed into the **Forgejo-HTTPS gitcache pipeline** (every gitcache mirror fetches `https://forgejo.coilysiren.me/<owner>/<name>.git`, keyed by `FORGEJO_TOKEN`). Forgejo has no such repo, so the seed step creates only a 0-byte `/gitcache/.StrangeLoopGames__Eco.lock` with no mirror, and the sibling `../Eco/` clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: `../Eco` absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41.

## Root cause

The dep pipeline assumes **every dep is a Forgejo `owner/name` reachable over HTTPS with the Forgejo token**. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless.

## Ask

1. Let `catalog.dependsOn` entries be a **full git clone URL** (for example `ssh://git@github.com/StrangeLoopGames/Eco.git`), not only a bare `owner/name`. The `--repo` grant path already accepts a c

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.406.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-612` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-06T02:00:58Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#612` · branch `issue-612` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-ward-612` · ward `v0.406.0` · dispatched `2026-07-06T02:00:58Z` - **Comment thread:** 1 included in the pre-flight read, 1 stripped (ward's own automated comments). - included: @coilysiren (2026-07-05T22:18:21Z) - stripped: @coilyco-ops (2026-07-05T21:52:50Z) **Issue body as seeded:** ``` ## Summary `catalog.dependsOn` cannot express an **external, non-Forgejo** dependency, so declaring one silently fails to hydrate. `github.com/StrangeLoopGames/Eco` is declared in eco-ops but never arrives in the container. The fix is to let `dependsOn` (and the atlas service-catalogue generally) carry a **full git clone URL** and honor its host and transport, cloning external deps host-side over ssh into the gitcache mirror. Mirroring onto Forgejo is explicitly rejected (third-party redistribution risk), so host-side ssh clone is the path. ## Current broken behavior eco-ops `.ward/ward.yaml` declares, correctly host-qualified: ` ` ` catalog: dependsOn: - github.com/StrangeLoopGames/Eco ` ` ` But the resolver (`resolveCatalogContextRepos`, ward#573) **throws the host away**: in-container the declaration surfaces as `WARD_CONTEXT_REPOS=StrangeLoopGames/Eco`, which is then fed into the **Forgejo-HTTPS gitcache pipeline** (every gitcache mirror fetches `https://forgejo.coilysiren.me/<owner>/<name>.git`, keyed by `FORGEJO_TOKEN`). Forgejo has no such repo, so the seed step creates only a 0-byte `/gitcache/.StrangeLoopGames__Eco.lock` with no mirror, and the sibling `../Eco/` clone the AGENTS.md files promise never appears. Confirmed live in an eco-ops container: `../Eco` absent, only the empty lock present, github.com unreachable from the sealed container. This is the same failure as ward#611 and eco-ops#41. ## Root cause The dep pipeline assumes **every dep is a Forgejo `owner/name` reachable over HTTPS with the Forgejo token**. An external GitHub dep breaks all three assumptions: different host, no Forgejo mirror, and the token does not authorize it. The container itself has no github egress and no ssh, so the clone cannot happen in-container regardless. ## Ask 1. Let `catalog.dependsOn` entries be a **full git clone URL** (for example `ssh://git@github.com/StrangeLoopGames/Eco.git`), not only a bare `owner/name`. The `--repo` grant path already accepts a c ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.406.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - host-side ssh seed of external catalog.dependsOn mirrors landed on main (closes the credential gap the reopen named).

The resolver half of this issue had already landed (5098da5), so the honest read was that the reopen was about one missing seam: nothing ever populated the gitcache mirror for an external ssh dep, so the sealed container's fail-loud was the only thing that ever fired. Wiring the seed itself was the whole job.

What fought back a little was the architecture tension baked in by ward#580: context deps are deliberately resolved in-container from the fresh clone, precisely so the host cwd is never trusted. But a host-side seed has to know what to clone before the container exists, and the only host-reachable source without a forge round-trip is the dispatch-cwd config. I leaned into that as an explicitly best-effort warm-cache hint rather than authoritative resolution - the container still decides what mounts, and a cwd that isn't the target just seeds nothing and fails loud. That keeps the ward#580 invariant intact while making the common "dispatch from inside the target checkout" case work.

The other wrinkle: the gitcache is a docker named volume, so the host can't write it directly. The clone runs natively on the host over the default ssh keychain (exactly Kai's decision - agent, then ~/.ssh, no ward-specific key), then a cp-only helper container lifts the finished bare mirror into the volume. The ssh key never touches any container.

Confidence: high on the resolution/argv/fail-loud logic (unit-tested via the runner stubs, and the in-container path was already proven). Lower on the live docker round-trip - I couldn't drive it end to end here (no docker daemon, no github egress in this box), so the cp-helper and volume-probe are exercised only through stubs. Worth a real eco-ops dispatch to confirm ../Eco actually lands.

Rough edges / follow-ups worth filing: (1) an already-seeded external mirror is never refreshed host-side - fine for a stable upstream, but a long-lived volume will drift; (2) the cp-helper reuses the agent image, which assumes it ships coreutils cp - true today, but a tripwire if the image slims down. Neither blocks the done-condition.

WARD-OUTCOME: done - host-side ssh seed of external catalog.dependsOn mirrors landed on main (closes the credential gap the reopen named). The resolver half of this issue had already landed (5098da5), so the honest read was that the reopen was about one missing seam: nothing ever populated the gitcache mirror for an external ssh dep, so the sealed container's fail-loud was the *only* thing that ever fired. Wiring the seed itself was the whole job. What fought back a little was the architecture tension baked in by ward#580: context deps are deliberately resolved in-container from the fresh clone, precisely so the host cwd is never trusted. But a host-side seed has to know *what* to clone before the container exists, and the only host-reachable source without a forge round-trip is the dispatch-cwd config. I leaned into that as an explicitly best-effort warm-cache hint rather than authoritative resolution - the container still decides what mounts, and a cwd that isn't the target just seeds nothing and fails loud. That keeps the ward#580 invariant intact while making the common "dispatch from inside the target checkout" case work. The other wrinkle: the gitcache is a docker named volume, so the host can't write it directly. The clone runs natively on the host over the default ssh keychain (exactly Kai's decision - agent, then ~/.ssh, no ward-specific key), then a cp-only helper container lifts the finished bare mirror into the volume. The ssh key never touches any container. Confidence: high on the resolution/argv/fail-loud logic (unit-tested via the runner stubs, and the in-container path was already proven). Lower on the live docker round-trip - I couldn't drive it end to end here (no docker daemon, no github egress in this box), so the cp-helper and volume-probe are exercised only through stubs. Worth a real eco-ops dispatch to confirm ../Eco actually lands. Rough edges / follow-ups worth filing: (1) an already-seeded external mirror is never refreshed host-side - fine for a stable upstream, but a long-lived volume will drift; (2) the cp-helper reuses the agent image, which assumes it ships coreutils cp - true today, but a tripwire if the image slims down. Neither blocks the done-condition.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#612
No description provided.