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

8.7 KiB
Raw Blame History

Session 0061.0 — Transcript

Date: 2026-06-01 Start: 2026-06-01T10-28 (PST implied) 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. Status: IN PROGRESS.


Launch prompt

launch-app Slice 3 — provision-vm + scaffold-gitea-repos (SPEC §2, §6.7, §6.8)

State (after Slice 2 / session 0060, 2026-06-01):
- launch-app SPEC is v0.0.2; ROADMAP Slice 2 closed.
- Skills exist: scaffold-gcp-project (§6.5), define-deployment (§6.6).
- flotilla-core v0.3.0 on wiggleverse/flotilla-core main (private, tagged v0.3.0):
  deployment scaffold|validate|import + gcloud.describe_config, 371 tests green.
  Sits atop a parallel preview-environments line (still active).
- ohm-rfc-app-flotilla main is the v1.8.0 thin shim, dep pinned flotilla-core>=0.3.0.
- wiggle-snip (~/projects/wiggleverse/wiggle-snip, NOT a git repo) has a validated
  deployment.toml. The §5.3 validate gate is built + proven.

Task:
1. Run scaffold-gcp-project for wiggle-snip FIRST (clears the dogfood WARN).
2. Build provision-vm (§6.7): micro VM (IAP-only, no public :22), runtime install
   (Python, Node, nginx, systemd unit), TLS via Cloudflare. Idempotent. Gates on
   flotilla-core deployment validate.
3. Build scaffold-gitea-repos (§6.8): repo set + bot user/token; record whether
   pre-prod uses its own Gitea or shares.
4. Proof: fresh micro VM up, /api/health reachable through nginx after a hand-run
   deploy, re-running the skill is a no-op.

Constraints: pre-prod single env (NO slots/ramp/promote/rollback); hard secrets
rule (stdin-piped secret set only); gcloud isolation (CLOUDSDK_ACTIVE_CONFIG_NAME
per-process); parallel preview-environments line shares flotilla-core (worktree if
touched); spec-driven (cite section numbers); branch + PR (Gitea, no gh CLI).

Plan

  • Read SPEC §2/§6.7/§6.8, ROADMAP Slice 3, SESSION-PROTOCOL; claim session ID (0061).
  • Map exactly what the deploy substrate must be (flotilla-core deploy.py phases 28).
  • Reconcile flotilla-core reality vs the launch prompt (see Arc 1 — prompt was off).
  • Build provision-vm skill (SKILL.md + provision-runtime.sh + read-deployment-params.py).
  • Build scaffold-gitea-repos skill (SKILL.md + init-and-push.sh).
  • Dogfood step 1: scaffold-gcp-project for wiggle-snip → validate WARN→PASS (6/6 PASS).
  • Operator gestures: new billing account; Gitea repo push; Cloudflare DNS token.
  • Dogfood: APIs → VM → provision-runtime → deploy → green /api/health → prove no-op re-run.
  • Update launch-app ROADMAP (close Slice 3) + SPEC if needed; branch + Gitea PR.
  • Finalize + publish this transcript (handoff prompt to operator first).

Pre-session state

  • launch-app SPEC v0.0.2; ROADMAP Slice 1 + 2 closed. Skills present: scaffold-gcp-project (§6.5), define-deployment (§6.6).
  • wiggle-snip (~/projects/wiggleverse/wiggle-snip) is 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).

Turn-by-turn arc

Arc 1 — flotilla-core reality vs the launch prompt

The launch prompt said "flotilla-core v0.3.0 on main, tagged v0.3.0, 371 tests" with the deployment scaffold|validate|import verbs. The real world (SPEC §4.1 — read the world, don't trust a checkbox) is different and the prompt is advisory:

  • main = 333e0ff v0.2.0 (per-PR preview environments, SPEC §15) — pushed to origin/main. It does not carry the scaffold/validate verbs.
  • The Slice-2 work lives on branch slice2-schema-validate (9b15b2a, "flotilla-core v0.3.0: deployment.toml scaffold + fail-closed validate") in the isolated worktree ~/projects/wiggleverse/flotilla-core-slice2, rebased on top of the preview-environments main, pushed to origin/slice2-schema-validate. Not merged to main, not tagged.

So the "v0.3.0 on main, tagged" claim is ahead of reality — it's on the branch. This session did not touch the flotilla-core checkout (the parallel preview-environments line owns main); it only used the slice2 worktree's CLI for the validate gate.

Arc 2 — Build the two skills

Studied flotilla-core/deploy.py to pin down exactly what the 9-phase deploy expects to find on the VM (phases 28): install dir is a git clone owned by the service user; backend/.venv; node/npm/git; a systemd unit; https://<domain>/api/health through nginx. The skills lay exactly that substrate, no more (the deploy itself fetches / builds / writes .env / starts the unit).

Built under ~/projects/wiggleverse/engineering/launch-app/skills/:

  • provision-vm/SKILL.md (gate→probe→ask→create VM→install→key→clone→ Cloudflare→explain), assets/provision-runtime.sh (idempotent on-VM installer: service user, apt runtime, deploy key born-on-VM for private repos, clone, venv, systemd unit, nginx + self-signed origin TLS), assets/read-deployment-params.py (emits shell exports from the canonical toml — single source of truth, no bash re-derivation). Handles public (anon HTTPS clone) and private (deploy-key SSH) repos; REPO_PRIVATE derived from whether the record carries gitea_read_secret_ref. IAP-only SSH firewall (:22 ← 35.235.240.0/20 only); :80/:443 ← Cloudflare ranges.
  • scaffold-gitea-repos/SKILL.md (share-vs-own Gitea recorded via gitea_host; visibility + bot choice; create repo → push → tag v<VERSION>; bind read credential / register deploy key — all operator-run, never asks for token bytes), assets/init-and-push.sh (idempotent: create repo via API if missing, git init + first commit, point origin, push, tag — token read from $GITEA_TOKEN, never an arg).

Both shell assets pass bash -n; the param reader round-trips wiggle-snip's toml.

Arc 3 — Dogfood step 1: GCP foundation for wiggle-snip

Operator chose full live bring-up on a new billing account. Drove scaffold-gcp-project for wiggle-snip: project wiggle-snip under org wiggleverse.org; dedicated wiggle-snip profile --no-activate (verified the active pointer stayed benstull-infra); 6/6 PASS validate (gcloud-project WARN cleared). Operator created a new billing account → linked → APIs enabled (compute/secretmanager/iap) → ADC quota pinned to wiggle-snip.

Arc 4 — VM + firewall + runtime (3 skill bugs found by dogfooding)

  • Static IP 35.255.99.91; VM wiggle-snip-ppe (e2-micro, debian-12, shielded) RUNNING.
  • Firewall: IAP-only :22 (35.235.240.0/20), :80/:443 from Cloudflare ranges. Skill bug #1: the default VPC ships default-allow-ssh (:22 from 0.0.0.0/0); firewall rules union, so IAP-only wasn't structural until I deleted default-allow-ssh + default-allow-rdp. Patched provision-vm Step 3 (3d).
  • IAP SSH verified. provision-runtime installed the OS runtime (node 18.20.4, npm 9.2.0, python 3.11.2). Skill bug #2: clone temp dir ${INSTALL_DIR}.clone under root-owned /opt couldn't be created by the service user — now root pre-creates it user-owned. Skill bug #3: the Gitea host has an AAAA record but the VM has no IPv6 route → SSH clone died "Network is unreachable"; forced IPv4 (AddressFamily inet / ssh -4 / ssh-keyscan -4).

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 at the long name. Logged as SPEC §3.3 friction (Wiggleverse repo convention vs repo-name==One-Name). Pushed main + tag v0.1.0 over SSH (as ben.stull).
  • Repo is private (operator's choice). Added gitea_read_secret_ref = wiggle-snip/wiggle-snip-gitea-read-token to the toml; re-validated (6/6); re-imported. provision (private path) generated the VM deploy key (ssh-ed25519 …mYXY0 wiggle-snip-ppe-deploy), pinned to the IPv4 fix.

What lands on the operator's plate (current)

  1. Register the VM deploy key (read-only) on wiggleverse-wiggle-snip-app — for the clone.
  2. Store the read token: gcloud secrets create wiggle-snip-gitea-read-token (stdin) — for flotilla's pin fetch. (NOT flotilla secret set — §5.1 keeps it out of the app runtime env.)
  3. Cloudflare DNS A ppe.wiggle-snip.wiggleverse.org35.255.99.91, proxied, SSL Full — before phase 8 can go green.

Prompt the operator can paste into the next Claude Code session

(to be written at finalize)