Skip to content

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-manager selects compose service prod (not dev/Vite) for app_type portfolio.
  • Container nginx listens on 8080; Traefik file route and labels must target :8080.
  • staging_deploy checks out ticket.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):

  1. Agree initiative with repo rafael.gonzalez.albes/portfolio.
  2. Dispatch or Work T1 — worker creates branch; open PR when ready.
  3. Wait for CI green (or click through when ci_url appears on the ticket).
  4. Deploy to staging — pick free slot, wipe+claim+deploy.
  5. Open preview_url → complete DoD → Approve human test.
  6. Merge PR on Forgejo → confirm prod CI green.
  7. 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)

  1. Upsert ticket state (infra VPS Ship DB):
bash local/upsert-ship-INFRA-101.sh
  1. Open https://ship.rafaelgonzalezalbes.com — initiative shows live (not demo).
  2. Open preview → run DoD checklist on the diagram page.
  3. Mark checklist items done → Approve human test.
  4. 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-deploy after 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-bot SSH

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:

  1. On infra VPS, install key + token: bash local/install-ship-apps-ssh-key.sh
  2. Ship compose mounts /run/ship-secrets and sets APPS_SSH_HOST
  3. 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.