Phase 7d-A-6 close — Clone-on-submit for target_repo
Date: 2026-07-06
Status: Accepted
Plan: docs/plans/2026-07-06-phase-7d-a-6-clone-on-submit.md
Summary
Phase 7d-A-6 removed the target_repo blocker: ralph can now clone a
remote GitHub URL into its own filesystem on submit, so hosted /ralph
can execute against any repo the shared PAT can read. Before: every
submit needed an absolute path that only existed on the operator's
laptop. After: caller sends target_repo_url, ralph resolves it into
/data/repos/<sha256(url)[:16]>/, runs the pipeline.
What shipped
| Sub-phase | Artefact |
|---|---|
| 7d-A-6-1 | rocky-hq/contracts#7 — RunConfigSchema gains target_repo_url + target_ref; target_repo optional; .refine() one-of. Version bump 0.3.0 → 0.4.0, tag v0.4.0 published to GitHub Packages. |
| 7d-A-6-2 | rocky-hq/ralph#8 — src/rocky/serve/repo_cache.py (resolve_from_url, RepoAuthMissing, RepoCloneError) + SubmitRunRequest pydantic mirror + registry.submit prefers URL over path + 8 new tests (6 cache-unit + 2 route). |
| 7d-A-6-2-bump | rocky-hq/rocky#104 — parent submodule bump. |
| 7d-A-6-3 | rocky-hq/ralph#9 — fly.toml [mounts] block + RALPH_REPO_CACHE_ROOT=/data/repos. Fly volume rocky_ralph_data (3 GB, iad, encrypted) created out-of-band. App scaled to 1 machine. |
Verification
/data/repos/dabe951c06bc3d3f/on the Fly worker holds a clean checkout ofrocky-hq/ralph@main(verified viaflyctl ssh console -C 'ls /data/repos/…').POST /runs {target_repo_url: "https://github.com/rocky-hq/ralph", target_ref: "main", …}→200 {"run_id":"…","status":"running"}.- Run traversed the full pipeline (clone → loader → runner → KAHN outcome) and terminated with
outcome: stuck— expected, as the worker has noANTHROPIC_API_KEYfor prompt execution. Wire-level plumbing is proven; prompt-execution readiness is a separate followup. GITHUB_TOKEN,RALPH_REPO_CACHE_ROOT, and volume mount all confirmed present in the Fly machine env.
Decisions locked
- One-of
target_repo | target_repo_url. URL wins on tie. Local dev unaffected. - GitHub-only auth in this phase. Non-github URLs pass through un-rewritten (test-friendly with
file://remotes). Other hosts fail cleanly if they require auth. - Fine-grained PAT with
contents:readscope, saved to Fly as theGITHUB_TOKENsecret. Rotation by the operator; no automated flow yet. - No cache eviction in 7d-A-6. 3 GB volume is enough for solo-tier probing.
- No auto-push. Ralph commits stay local until per-workspace creds land in Phase 7d-C.
- Single-machine app. Removed the auto-provisioned second machine so one volume suffices; auto-stop still handles idle cost.
Known limitations
- Prompt execution is not wired in prod. Runs terminate with
outcome: stuckuntilANTHROPIC_API_KEY(or a Claude SDK equivalent) is set as a Fly secret. Filed as7d-A-6-followup-anthropic-key. - Console UI still emits
target_repoonly. Submitting from the browser requires the 7d-A-6-4 slice (form field + submodule bump). Until then, hosted submits are curl-only. - Cache is unbounded. Filed as
7d-A-6-followup-eviction. - Bearer is still the shared static HMAC. Per-workspace JWT is a Phase 7d-C prereq.
- No auto-push. Filed as
7d-A-6-followup-autopush(needs per-workspace creds — waits for 7d-C).
Followups queued
| Slice | Blocked by | Description |
|---|---|---|
| 7d-A-6-4 | — | Console submit form: add "Repo URL" + "Ref" fields; bump @rocky-hq/contracts to 0.4.0. |
| 7d-A-6-followup-anthropic-key | — | Set ANTHROPIC_API_KEY as a Fly secret + document in ralph/CLAUDE.md. |
| 7d-A-6-followup-eviction | — | LRU / TTL on /data/repos before the volume fills. |
| 7d-A-6-followup-autopush | 7d-C | Ralph pushes commits back to origin using per-workspace creds. |
Related decisions
2026-07-06-phase-7d-a-close.md— parent phase (hosted RALPH runtime on Fly.io).2026-05-02-rename-fleet-to-hearth— HEARTH continues to be the driver surface for 7d-C per-workspace pods.