12 KiB
Session 0061.0 — Transcript
Date: 2026-06-01 Goal: launch-app Slice 3 — build
provision-vm(§6.7) +scaffold-gitea-repos(§6.8), then dogfood them against wiggle-snip to a verified pre-prod/api/health.Outcome: DONE. Both skills built; wiggle-snip dogfooded green end-to-end — live pre-prod at
https://wiggle-snip-ppe.wiggleverse.org/api/health(v0.1.0, deploys.id=2, all 9 phases ok), re-run idempotent. 5 skill bugs caught + fixed.Highlights: dropped SSH deploy keys for an HTTPS + Secret-Manager-token git credential helper (Cloudflare-proxied Gitea won't pass SSH :2222 to a GCE VM); switched the edge scheme
ppe.<name>.<base>→<name>-ppe.<base>(Cloudflare Universal SSL doesn't cover 3-label hosts); repos private-by-default (guide §5.5).
Plan (final)
- Read SPEC/ROADMAP/SESSION-PROTOCOL; claim session ID (0061).
- Map the deploy substrate (flotilla-core deploy.py phases 2–8).
- Reconcile flotilla-core reality vs the launch prompt (Arc 1).
- Build
provision-vm+scaffold-gitea-reposskills (Arc 2). - Dogfood: scaffold-gcp-project → VM → provision → deploy → green /api/health.
- Fix the 5 skill bugs the dogfood surfaced (Arcs 4, 6, 7).
- "private by default" convention (guide §5.5) per operator directive.
- Update SPEC §2/§5.1/§5.3 + flotilla-core derive_names + tests (edge scheme).
- Close Slice 3 in ROADMAP; commit + push branches; deliver PR compare URLs.
- Finalize + publish this transcript.
Pre-session state
- launch-app SPEC v0.0.2; Slices 1–2 closed. Skills:
scaffold-gcp-project(§6.5),define-deployment(§6.6).wiggle-snipbuilt + tested, NOT a git repo, with adeployment.tomlthat validated 5 PASS + 1 WARN (gcloud config absent). - gcloud active on-disk pointer
benstull-infra— left untouched all session (every gcloud call pinnedCLOUDSDK_ACTIVE_CONFIG_NAMEper-process).
Turn-by-turn arc
Arc 1 — flotilla-core reality vs the launch prompt
Launch prompt claimed "flotilla-core v0.3.0 on main, tagged, 371 tests" with the
scaffold/validate verbs. Reality (SPEC §4.1 read-the-world): main = 333e0ff
v0.2.0 (per-PR preview environments), no scaffold/validate. The Slice-2 work is
on branch slice2-schema-validate (worktree ~/projects/wiggleverse/flotilla-core-slice2),
rebased on the preview-env main, not merged, not tagged. This session used the
slice2 worktree's CLI and never touched the flotilla-core main checkout (the parallel
preview-environments line owns it).
Arc 2 — Build the two skills
Studied deploy.py to pin exactly what phases 2–8 expect on the VM. Built under
launch-app/skills/: provision-vm/ (SKILL.md + provision-runtime.sh +
read-deployment-params.py) and scaffold-gitea-repos/ (SKILL.md + init-and-push.sh).
Both shell assets bash -n clean; param reader round-trips the toml. (Initial
provision-runtime used an SSH deploy key for private clones — later abandoned, Arc 6.)
Arc 3 — GCP foundation for wiggle-snip
Operator chose full live bring-up, new billing account. Drove
scaffold-gcp-project: project wiggle-snip under org wiggleverse.org; dedicated
wiggle-snip profile --no-activate (active pointer verified unchanged); billing
linked (01789B-…); APIs enabled; ADC quota pinned to wiggle-snip. Validate flipped
to 6/6 PASS. (ADC quota-project is global — flagged: next OHM session must re-pin.)
Arc 4 — VM + firewall + runtime (bugs #1–#3)
Static IP 35.255.99.91; VM wiggle-snip-ppe (e2-micro, debian-12, shielded) RUNNING.
IAP-only firewall. Bug #1: default VPC default-allow-ssh (:22 ← 0.0.0.0/0)
defeats IAP-only (rules union) → delete default-allow-ssh/rdp. IAP SSH verified.
provision-runtime installed node 18.20.4 / npm 9.2.0 / python 3.11.2. Bug #2:
clone temp ${INSTALL_DIR}.clone under root-owned /opt not writable by the service
user → root pre-creates it user-owned. Bug #3: Gitea has an AAAA record, VM has
no IPv6 route → SSH clone "Network is unreachable" → forced IPv4 (later mooted by Arc 6).
Arc 5 — Gitea repo + the One-Name fork
No GITEA_TOKEN in env; org push-to-create disabled. Operator created the repo as
wiggleverse/wiggleverse-wiggle-snip-app (the <org>-<product>-app convention),
not wiggle-snip. One-Name divergence (§3.3). Operator chose: keep
deployment/project/VM = wiggle-snip, repoint [app].repo. Logged as Slice-5 backlog.
Pushed main + tag v0.1.0 over SSH (as ben.stull). Repo kept private (operator).
Arc 6 — Private path: SSH deploy keys don't work behind Cloudflare (bug #4)
First tried the private path with an on-VM SSH deploy key (operator registered the
public half). Clone failed: the VM cannot reach git.wiggleverse.org:2222 — it
resolves the Cloudflare IP, which only forwards HTTP/S, not SSH (the operator's laptop
reaches :2222 via a non-CF path). HTTPS :443 works fine. Pivot: clone + fetch over
HTTPS with a git credential helper that fetches the read token from Secret Manager
via the VM's metadata service account — no token ever through the session, no SSH,
no deploy key at all (one read token serves both flotilla's pin fetch and the VM
clone). Required: VM cloud-platform scope (stop/set-scopes/start) + SA
secretmanager.secretAccessor on the secret. Operator created the read-only Gitea
token → I created the empty SM secret + IAM, operator piped the value
(gcloud secrets versions add … --data-file=-, pbpaste). Also adopted "repos are
private by default" (guide §5.5 new) per operator directive — public is now an
explicit opt-out. ADC needed gcloud auth application-default login (operator) for the
laptop-side pin read. HTTPS clone then succeeded.
Arc 7 — Edge TLS: 3-label host has no Cloudflare cert (bug #5) + nginx :80/:443
Deploy #1 (deploys.id=1) reached phase 8 then failed: SSLV3_ALERT_HANDSHAKE_FAILURE.
Cause: ppe.wiggle-snip.wiggleverse.org is a 3-label host, and Cloudflare
Universal SSL only covers the apex + *.<zone> — no edge cert → handshake fails.
Fix (operator-confirmed): switch the scheme to <name>-ppe.<base> (single
label, covered by *.wiggleverse.org) across SPEC §2/§5.1/§5.3, flotilla-core
derive_names + tests (371 green), define-deployment, and wiggle-snip's record;
re-provisioned nginx; operator added the new DNS A record. Then a 301 loop:
Cloudflare was hitting origin :80 (Flexible/uncached) → my nginx :80→:443 redirect
looped. Fix: nginx now serves the app on both :80 and :443 (origin ports are
firewalled to Cloudflare only; CF handles the visitor http→https upgrade) — works in
Cloudflare Full or Flexible. Edge then returned {"status":"ok","version":"0.1.0"} 200.
Arc 8 — Green deploy + idempotency + closeout
flotilla-core deploy wiggle-snip: all 9 phases ok → deployed v0.1.0
(deploys.id=2), phase-8 verify 2.4s. Re-running provision is a no-op (service user
already exists / packages present / repo already cloned). Committed + pushed:
flotilla-core slice2-schema-validate (edge fix), engineering
slice3-provision-vm-scaffold-gitea (skills + SPEC + guide §5.5 + ROADMAP),
wiggle-snip main (toml). PRs left for the operator (no Gitea token in session).
Cut state (end of session)
| engineering | branch slice3-provision-vm-scaffold-gitea pushed (PR pending) — 2 skills, SPEC §2/§5.1/§5.3, guide §5.5, ROADMAP Slice 3 DONE |
| flotilla-core | branch slice2-schema-validate pushed (PR pending) — Slice-2 verbs + session-0061 edge-host fix; 371 tests green; NOT merged/tagged |
| wiggleverse-wiggle-snip-app | main @ 5745b09 + tag v0.1.0; private repo |
| wiggle-snip live | VM wiggle-snip-ppe (project wiggle-snip, IP 35.255.99.91), deployed v0.1.0, green at https://wiggle-snip-ppe.wiggleverse.org/api/health (deploys.id=2) |
| gcloud active pointer | benstull-infra — untouched all session |
| Slice 3 ledger | Status |
|---|---|
| provision-vm (§6.7) | ✅ built + dogfooded green |
| scaffold-gitea-repos (§6.8) | ✅ built (repo created by hand in UI — org push-create off) |
| Proof: VM up + /api/health + no-op re-run | ✅ met |
§19.2 / Slice-5 candidates surfaced
- One-Name vs the
<org>-<product>-apprepo convention (§3.3) — repo name and deployment/One-Name diverge; decide whether §3.3 blesses it (OHM-grandfathered shape) or enforces equality. - ADC quota-project is global — scaffold-gcp-project's
set-quota-projectre-points the shared ADC per app; next OHM session must re-pin. Needs a per-process mechanism or a documented re-pin. - Standard-stack run command not in the §5.1 schema — provision assumes
uvicorn app.main:app:8001; add optional[vm].entrypoint/portif a 2nd app diverges. - scaffold-gitea-repos needs a token to create org repos (org push-create disabled); the dogfood created the repo by hand.
APP_URLoverlay key emitted but unused by wiggle-snip (Slice-2 carry-over).
What lands on the operator's plate
- Open two PRs (no Gitea token in session):
- Optional cleanup: delete the now-unused read-only deploy key on
wiggleverse-wiggle-snip-app, and the old
ppe.wiggle-snipDNS record if one was added. - Re-pin ADC quota project before the next OHM secret operation
(
gcloud auth application-default set-quota-project wiggleverse-ohm). - wiggle-snip pre-prod is live + billable — tear down at Slice 5 or when done.
Prompt the operator can paste into the next Claude Code session
launch-app Slice 4 — orchestrator + design-phase skills (SPEC §3, §4, §6.0–§6.4).
Reserve your own session ID first (claim-session-id.sh).
State (after Slice 3 / session 0061):
- Slices 1–3 DONE. All four phase skills exist under
~/projects/wiggleverse/engineering/launch-app/skills/: scaffold-gcp-project (§6.5),
define-deployment (§6.6), provision-vm (§6.7), scaffold-gitea-repos (§6.8).
- flotilla-core Slice-2 work + the session-0061 edge-host fix is on branch
slice2-schema-validate (worktree ~/projects/wiggleverse/flotilla-core-slice2, 371
tests green, pushed to origin, NOT merged/tagged). Engineering Slice-3 work is on
branch slice3-provision-vm-scaffold-gitea (pushed, PR pending). Open both PRs.
- wiggle-snip is LIVE pre-prod: repo wiggleverse/wiggleverse-wiggle-snip-app (private),
VM wiggle-snip-ppe (project wiggle-snip, IP 35.255.99.91), deployed v0.1.0, green at
https://wiggle-snip-ppe.wiggleverse.org/api/health.
Task: build the launch-app orchestrator (state from on-disk artifacts §4.1, the §4.2
gates, §4.3 "teach the map", §6.0 intake) + the design-phase skills (scaffold-prototype,
write-spec, write-roadmap, scaffold-standard-stack), and extend scaffold-gcp-project to
confirm-not-assume the stack. Tests: orchestrator resumes from each partial state; gates
refuse out-of-order phases.
Conventions to honor (hardened in Slice 3):
- Repos PRIVATE by default (guide §5.5); VM clones over HTTPS with a read token from
Secret Manager via a git credential helper (NO SSH deploy keys — Cloudflare-proxied
Gitea won't pass SSH :2222 to a GCE VM).
- Edge host is <name>-ppe.<base> (single label — Cloudflare Universal SSL doesn't cover
3-label hosts). nginx serves :80+:443 (no hard redirect).
- gcloud isolation: CLOUDSDK_ACTIVE_CONFIG_NAME per-process; never touch the active
pointer. Hard secrets rule: pbpaste/stdin gestures only, never ask for bytes.
- Parallel preview-environments line owns flotilla-core main + feat/preview-environments;
only ever touch the slice2 worktree.
Slice-5 backlog: One-Name-vs-repo-convention (§3.3); global ADC quota-project re-pin;
[vm].entrypoint/port schema gap; scaffold-gitea-repos org-repo-create token; unused
APP_URL overlay key. wiggle-snip pre-prod is live + billable — tear down at Slice 5.
Bump SPEC to v0.0.3 when next touched (its §2/§5.1/§5.3 text + guide §5.5 changed).