171 lines
8.7 KiB
Markdown
171 lines
8.7 KiB
Markdown
# 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
|
||
|
||
- [x] Read SPEC §2/§6.7/§6.8, ROADMAP Slice 3, SESSION-PROTOCOL; claim session ID (0061).
|
||
- [x] Map exactly what the deploy substrate must be (flotilla-core deploy.py phases 2–8).
|
||
- [x] Reconcile flotilla-core reality vs the launch prompt (see Arc 1 — prompt was off).
|
||
- [x] Build `provision-vm` skill (SKILL.md + provision-runtime.sh + read-deployment-params.py).
|
||
- [x] Build `scaffold-gitea-repos` skill (SKILL.md + init-and-push.sh).
|
||
- [x] 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 2–8): 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.org` → `35.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)
|
||
```
|