Files
session-history/ohm/0061/SESSION-0061.0-TRANSCRIPT-2026-06-01T10-28--2026-06-01T22-25.md
T

12 KiB
Raw Blame History

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 28).
  • Reconcile flotilla-core reality vs the launch prompt (Arc 1).
  • Build provision-vm + scaffold-gitea-repos skills (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 12 closed. Skills: scaffold-gcp-project (§6.5), define-deployment (§6.6). wiggle-snip built + tested, NOT a git repo, with a deployment.toml that validated 5 PASS + 1 WARN (gcloud config absent).
  • gcloud active on-disk pointer benstull-infra — left untouched all session (every gcloud call pinned CLOUDSDK_ACTIVE_CONFIG_NAME per-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 28 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

  1. One-Name vs the <org>-<product>-app repo convention (§3.3) — repo name and deployment/One-Name diverge; decide whether §3.3 blesses it (OHM-grandfathered shape) or enforces equality.
  2. ADC quota-project is global — scaffold-gcp-project's set-quota-project re-points the shared ADC per app; next OHM session must re-pin. Needs a per-process mechanism or a documented re-pin.
  3. Standard-stack run command not in the §5.1 schema — provision assumes uvicorn app.main:app :8001; add optional [vm].entrypoint/port if a 2nd app diverges.
  4. scaffold-gitea-repos needs a token to create org repos (org push-create disabled); the dogfood created the repo by hand.
  5. APP_URL overlay key emitted but unused by wiggle-snip (Slice-2 carry-over).

What lands on the operator's plate

  1. Open two PRs (no Gitea token in session):
  2. Optional cleanup: delete the now-unused read-only deploy key on wiggleverse-wiggle-snip-app, and the old ppe.wiggle-snip DNS record if one was added.
  3. Re-pin ADC quota project before the next OHM secret operation (gcloud auth application-default set-quota-project wiggleverse-ohm).
  4. 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 13 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).