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

218 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
- [x] Read SPEC/ROADMAP/SESSION-PROTOCOL; claim session ID (0061).
- [x] Map the deploy substrate (flotilla-core deploy.py phases 28).
- [x] Reconcile flotilla-core reality vs the launch prompt (Arc 1).
- [x] Build `provision-vm` + `scaffold-gitea-repos` skills (Arc 2).
- [x] Dogfood: scaffold-gcp-project → VM → provision → deploy → green /api/health.
- [x] Fix the 5 skill bugs the dogfood surfaced (Arcs 4, 6, 7).
- [x] "private by default" convention (guide §5.5) per operator directive.
- [x] Update SPEC §2/§5.1/§5.3 + flotilla-core derive_names + tests (edge scheme).
- [x] Close Slice 3 in ROADMAP; commit + push branches; deliver PR compare URLs.
- [x] 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):
- engineering: https://git.wiggleverse.org/wiggleverse/engineering/compare/main...slice3-provision-vm-scaffold-gitea
- flotilla-core: https://git.wiggleverse.org/wiggleverse/flotilla-core/compare/main...slice2-schema-validate
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).
```