Ship golden path (runbook)¶
Manual dry-run path for H12 — prove Idea → Done before auto workers.
Full diagram + human gate checklist: ship-golden-path-diagram.md.
Product rules: idea-to-done.md.
Expected path¶
agree → Plane → dispatch → PR → CI → staging_deploy → human_test → merge → authorize_ship → soak → done
| Stage | Who | Output |
|---|---|---|
| Agree | You | Review pack confirmed; tickets get app_mounted from repo (portfolio / wearlab / null) |
| Plane | Ship (W1) | Issue id (e.g. PORT-4, INFRA-101) |
| Dispatch | You / worker | Context pack; branch ship/<init>/<ticket>-<slug> |
| PR + CI | Worker / Woodpecker | Green pipeline; Woodpecker notify-ship-ci sets ci_url, clears waiting_on: ci |
| Staging deploy | Ship Deploy to staging (later worker-test-deploy) |
https://testingN.<domain>/ · nginx prod on 8080 |
| Human test | You on Ship | DoD checklist → approve_test |
| Merge | You (Forgejo) | PR merged to main · prod CI deploy |
| Authorize ship | Ship board | Soak watch begins (H9c probes live URL ~3 min) |
| Done | Soak pass or you | mark_done · Plane close (manual today) |
Push to main updates production for that repo. It does not redeploy staging bays — staging needs Ship Deploy to staging (claim + deploy on Apps VPS).
Golden paths by repo¶
| Path | Repo | Slot app | Preview example | Live |
|---|---|---|---|---|
| Docs (H12) | infra/devops |
docs |
testing1 → MkDocs page |
docs.* |
| Portfolio | rafael.gonzalez.albes/portfolio |
portfolio → compose prod |
testing3 → full site |
rafaelgonzalezalbes.com |
| WearLab (next) | alex/wearlab-main |
wearlab → compose prod |
testingN |
wearlab.* |
Docs path — INFRA-101 (first dry-run)¶
As deploy on Apps VPS (after bootstrap owns /opt/apps):
bash /opt/apps/scripts/slot-manager.sh claim testing1 INFRA-101 docs
bash /opt/apps/scripts/slot-manager.sh deploy testing1 \
https://git.rafaelgonzalezalbes.com/infra/devops.git main
# Preview: https://testing1.rafaelgonzalezalbes.com/...
bash /opt/apps/scripts/slot-manager.sh wipe testing1 # after human OK / release
From a laptop with the deploy key:
bash local/redeploy-testing1-via-deploy.sh
| Field | Value |
|---|---|
| Preview | https://testing1.rafaelgonzalezalbes.com/architecture/ship-golden-path-diagram/ |
| Live | https://docs.rafaelgonzalezalbes.com/architecture/ship-golden-path-diagram/ |
| Slot | testing1 · app docs |
| DoD | Mermaid renders · labels match · human gate section · steps accurate |
Portfolio path — PORT-GOLDEN-PATH-T1 (validated)¶
End-to-end reference for rafael.gonzalez.albes/portfolio before WearLab or more automation.
Agree (portfolio initiative)
→ Plane PORT-N (W1)
→ Dispatch / Work T1 (worker branch ship/init-…/PORT-…-slug)
→ Forgejo PR + Woodpecker CI on feature branch
→ notify-ship-ci → Ship: ci_url + waiting_on cleared
→ Deploy to staging → slot-manager: app_type portfolio, service prod, Traefik :8080
→ human_test on https://testingN.rafaelgonzalezalbes.com/
→ Merge PR → main CI deploys production
→ Authorize ship → H9c soak on https://rafaelgonzalezalbes.com/
→ mark_done
| Field | Value |
|---|---|
| Initiative repo | rafael.gonzalez.albes/portfolio |
app_mounted |
portfolio (set at agree from repo slug; used by staging_deploy) |
| Branch | ship/<initiative-id>/<ticket-id>-<task-slug> |
| Staging | https://testing3.rafaelgonzalezalbes.com/ (any free testingN) |
| Live | https://rafaelgonzalezalbes.com/ |
| CI | Woodpecker on portfolio repo; PR + non-main push notify Ship |
| Example PR | portfolio PR #5 (golden path, merged) |
Staging details (portfolio):
slot-managerselects compose serviceprod(notdev/Vite) forapp_typeportfolio.- Container nginx listens on 8080; Traefik file route and labels must target
:8080. staging_deploychecks outticket.branch_name, builds image, runs post-deploy health probe.- Free slots show a whoami placeholder (
slot-manager warm) so HTTPS never 404s on an empty bay.
Ship board flow (portfolio ticket):
- Agree initiative with repo
rafael.gonzalez.albes/portfolio. - Dispatch or Work T1 — worker creates branch; open PR when ready.
- Wait for CI green (or click through when
ci_urlappears on the ticket). - Deploy to staging — pick free slot, wipe+claim+deploy.
- Open
preview_url→ complete DoD → Approve human test. - Merge PR on Forgejo → confirm prod CI green.
- Authorize ship → soak watch → Mark live done when soak passes.
Laptop helpers (optional):
bash local/sync-slot-manager-apps.sh # after slot-manager changes on main
bash local/cleanup-staging-slots-remote.sh # wipe stale testing1/testing3 claims on Apps
bash local/cleanup-ship-slot-claims-remote.sh # release Ship DB claims for done tickets
Ship board (docs ticket — INFRA-101)¶
- Upsert ticket state (infra VPS Ship DB):
bash local/upsert-ship-INFRA-101.sh
- Open https://ship.rafaelgonzalezalbes.com — initiative shows live (not demo).
- Open preview → run DoD checklist on the diagram page.
- Mark checklist items done → Approve human test.
- After prod verify → Authorize ship / Mark live done as appropriate.
Live initiatives require checklist complete before approve_test, and a PASS receipt before authorize_ship. Demo tickets bypass those gates for UI practice.
What is not automatic yet¶
- Full Plane create from review pack (
worker-plane) — W1 shipped: agree creates Plane work items via REST when key installed - Full Cursor IDE / Agent SDK session (OpenRouter coding worker covers no-paste execute; IDE spawn still later)
- Auto git push / PR open from worker (writes workspace only unless you enable write tokens later)
- Plane auto-close when Ship
mark_done(close manually in Plane today) worker-test-deployafter CI green (today: Deploy to staging on the board)- Discord gate pings unless webhook installed (
local/install-ship-discord-webhook.sh) — optional - Forced-command
ship-botSSH
Shipped on portfolio path: W1 Plane on agree · CI→Ship webhook (ci_url) · H9c soak + auto-Done · staging prod on 8080 · app_mounted from repo at agree + deploy.
Ship → slot-manager bridge (H2 start)¶
Ship can claim/deploy/wipe staging bays when configured:
- On infra VPS, install key + token:
bash local/install-ship-apps-ssh-key.sh - Ship compose mounts
/run/ship-secretsand setsAPPS_SSH_HOST - On the board: Deploy to staging / Wipe staging slot
Under the hood: ship/bin/remote-slot.sh → SSH deploy@apps → /opt/apps/scripts/slot-manager.sh.
Board controls shipped (H1–H15 spine)¶
Truthful status also lives in idea-to-done.md hardening table (Shipped / Partial / Locked).
| Item | Board / API |
|---|---|
| H3 DoD | Apply DoD template loads docs/ux/infra/secrets checklist; human items gate approve_test |
| H6 receipts | Actions write artifact packs (slot, URLs, retries); detail pane shows timeline |
| H4 stuck | Red stuck strip when waiting_on exceeds thresholds; optional Discord webhook |
| H7 retries | Record retry increments budget (default max 3); exhausted → waiting_on: budget |
| H10 gates | Discord (optional) only on human_test / human_merge / budget / resources — not every log |
| H5 rollback | previous_revision saved on authorize/Done; Request rollback writes ROLLBACK receipt; H9c auto-soak probes live URL for 3 min post-ship, auto-rollbacks on 2 consecutive 5xx |
| H14 freeze | Header Freeze / Unfreeze — blocks staging deploy, ship, retries, dispatch (423) |
| H8 context | Context pack / Shadow plan / Work T1 — fixed briefing (goal, paths, DoD, forbidden) |
| W4 dispatch | Header Dispatch scan — finds unlocked queued/execute tickets and writes H8 packs |
| H9 control | Pause / Resume / Cancel (cancel wipes staging slot when claimed) |
| H8b rebase | Rebase plan — writes H8b steps receipt (worker does git rebase) |
| H8c warm-up | Context pack includes Graphify commands; laptop: node scripts/ship-work-t1.mjs T1 |
| H15 conflict | Dispatch blocked on overlapping paths (waiting_on: conflict); override allowed |
| D19b worker | ship-worker poller + OpenRouter coder (scripts/lib/openrouter-coder.mjs); key: bash local/install-ship-openrouter-key.sh |
| D25 secrets | needs_secrets refs; inject/wipe/reveal via slot-manager tmpfs /run/testing-slots/<slot>.env |
| H9b preflight | staging_deploy runs Apps preflight (MemAvailable) first |
| H2 warm bays | Free testingN keep whoami + Traefik TLS (slot-manager warm); wipe no longer leaves dead HTTPS |
| H21 away | Header Away — suppresses needs-you strip until until-time |
| H28 evidence | Upload evidence (ux/app @ human_test) → receipt + /evidence/... image |
| H31 wallet | Strip shows real OpenRouter spend this month / €30 soft · remaining · catalog date |
Stuck/gate Discord: bash local/install-ship-discord-webhook.sh (writes /run/ship-secrets/discord_infra_webhook).
Laptop / CI (optional — docker poller usually runs first):
# Install Plane API key on infra (from SOPS on VPS — never commit):
bash local/install-ship-plane-key.sh
# Install worker OpenRouter key on infra (from local/.env — never commit):
bash local/install-ship-openrouter-key.sh
SHIP_BASIC_AUTH='user:pass' SHIP_WORKER_MODE=code node scripts/ship-worker-runner.mjs
Apps sync: bash local/sync-slot-manager-apps.sh. H2c cron: bash local/install-apps-reclaim-cron.sh.
Next (portfolio stable): ops agents (pulse/digest/steward) · Plane auto-close on done · worker-test-deploy · worker opens PR.
Ops runbook: ops-agents.md.