Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ tasks:
- bash tests/smoke/caddy-mvp-s05-03-offline.sh
- bash tests/smoke/e8-s03-01.sh
- bash tests/meta/doc10-roadmap-status.yaml
- bash tests/meta/e1g-standing-policy.yaml
- bash tests/meta/release-provenance.yaml
- bash tests/meta/verify-fetch-depth.yaml
- bash tests/meta/batch2-smoke-wired.yaml
Expand Down Expand Up @@ -416,7 +417,7 @@ tasks:
- bash tests/smoke/e8b-offline.sh

e8b:up:
desc: "E8b — LIVE on-demand demo bring-up (COSTS MONEY; DECIDED-B on-demand, NOT standing). Composes the E1g GSK substrate, re-syncs the GitOps app-of-apps, waits healthy. Tear down with e8b:down after the demo."
desc: "E8b — LIVE on-demand demo bring-up (COSTS MONEY; DECIDED-B on-demand, ephemeral-by-default — a standing go-live substrate is a separate recorded and time-boxed carve-out, D-042/E1g-S07). Composes the E1g GSK substrate, re-syncs the GitOps app-of-apps, waits healthy. Tear down with e8b:down after the demo."
env:
KUBECONFIG: '{{.KUBECONFIG}}'
cmds:
Expand Down Expand Up @@ -448,7 +449,9 @@ tasks:
- |
set -euo pipefail
# The demo surfaces are GitOps-managed on the ephemeral GSK cluster, so
# destroying the gridscale substrate reclaims everything. No standing env.
# destroying the gridscale substrate reclaims everything. The demo is
# ephemeral-by-default; a standing go-live substrate is a separate
# recorded and time-boxed carve-out (D-042 / E1g-S07), not this cycle.
task e1g:down
echo "E8b demo torn down (gridscale substrate destroyed via e1g:down)."

Expand Down
17 changes: 13 additions & 4 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,19 @@ reviewer path.
**Runbook:** [gridscale-live-demo.md](runbooks/gridscale-live-demo.md)

DECIDED-B (operator-approved): E8b ships as a reproducible **on-demand** bring-up
(`task e8b:up` / `e8b:down`), proven ephemerally — **not a standing environment**
(the ruthless-teardown cost rule holds; the "interview window" is
operator-triggered). Offline-authored below; live bring-up/serve pending the live
cycle.
(`task e8b:up` / `e8b:down`), proven ephemerally — the ruthless-teardown cost rule
holds by default and the "interview window" is operator-triggered.

**Go-live carve-out (D-042, supersedes the dev-phase absolute):** the project has
entered go-live, where a standing live substrate is intentionally permitted — but
**only when it is recorded and time-boxed**: what is up and since-when are captured
in `evidence/live/e1g-gsk-2026-07-18.md`; the teardown-by date and owner (with the
~1–2 week go-live window) will be captured in decision **D-042** (operator-placed)
and surfaced by **E1g-S07** (`task e1g:status`, soft WARN).
This does **not** make "always-on" acceptable and does **not** weaken the
create→verify→destroy discipline for per-story live proofs — the ephemeral
`e8b:up`/`e8b:down` cycle stays ephemeral-by-default. Offline-authored below; live
bring-up/serve pending the live cycle.

| ID | Story | Status |
| --- | --- | --- |
Expand Down
5 changes: 5 additions & 0 deletions docs/development/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ task lint # markdown + shellcheck
Implementation lanes add `task test` (L0–L2), promtool, and cluster integration checks per
story — see [testing.md](testing.md) for the full matrix and the CI workflows that run it.

The go-live standing-substrate policy (a standing live env is permitted when **recorded and
time-boxed** — D-042, `tests/meta/e1g-standing-policy.yaml`, surfaced by E1g-S07's soft
`task e1g:status` WARN) is **cost-governance, not a blocker**: the doc-truth guard only reddens
on an unreconciled unqualified "no standing env" absolute, never on the substrate being up.

## Testing

Mandatory TDD — see [testing.md](testing.md) and ADR-0701. Summary:
Expand Down
11 changes: 11 additions & 0 deletions docs/runbooks/gridscale-day0.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,17 @@ object-storage anchor is destroyed last because it holds the remote state; if
you want to keep the anchor between tests (cheapest option) destroy only the
workload stacks and leave `object-storage` up.

**Go-live standing carve-out (D-042).** The dev-phase "tear it all down every
time" rule was an overspend guard for the build phase. In **go-live** a standing
live substrate is intentionally permitted — but **only when it is
recorded and time-boxed**: what is up and since-when are captured in
`evidence/live/e1g-gsk-2026-07-18.md`; the teardown-by date and owner (with the
~1–2 week go-live window) will be captured in decision **D-042** (operator-placed)
and surfaced by **E1g-S07** (`task e1g:status`, a soft WARN once the default ~14-day
window is exceeded). This is cost-governance, not a blocker, and it does
**not** relax the ruthless per-test teardown above — that stays the default; the
carve-out covers only the one sanctioned, recorded standing substrate.

## Phase-1 ↔ phase-2 deltas

- **Substrate**: phase-1 kind cluster → phase-2 GSK managed cluster. Same
Expand Down
40 changes: 31 additions & 9 deletions docs/runbooks/gridscale-live-demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,38 @@ demo (e.g. an interview walkthrough), show the read-only demo surfaces, then
runbook ([gridscale-day0.md](gridscale-day0.md)) — read that first; this runbook
only adds the demo layer and the demo choreography.

## Framing — on-demand, NOT standing (DECIDED-B)
## Framing — on-demand by default (DECIDED-B, reconciled by D-042)

The E8b story was originally phrased as "keep the gridscale stack running through
the interview window". That contradicts the project's ruthless-teardown cost
rule (the gridscale GSK cluster + LBaaS cost real money every hour they are up).

**Reconciled, operator-approved definition:** E8b is a **reproducible on-demand
bring-up**, proven ephemerally — **not a standing environment**. The "interview
window" is an **operator-triggered** `task e8b:up` … demo … `task e8b:down`
cycle, run just before the demo and torn down immediately after. It is **not
always-on** infra. If you need it live for a 30-minute interview, bring it up 10
minutes before and tear it down the moment you are done.
bring-up**, proven ephemerally. The "interview window" is an
**operator-triggered** `task e8b:up` … demo … `task e8b:down` cycle, run just
before the demo and torn down immediately after — it is **not always-on** infra.
If you need it live for a 30-minute interview, bring it up 10 minutes before and
tear it down the moment you are done.

### Go-live standing carve-out (D-042, supersedes the dev-phase absolute)

The dev-phase DECIDED-B rule treated a standing environment as forbidden — an
overspend guard for the build phase, now superseded for go-live (see below).
The project has now entered **go-live**,
where a standing live substrate is intentional (planned ~1–2 weeks, decision
**D-042**). Under go-live a standing substrate is permitted, but **only when it
is recorded and time-boxed**:

- **recorded** — what is up and since-when are captured in
`evidence/live/e1g-gsk-2026-07-18.md`; the teardown-by date and owner will be
captured in decision **D-042** (operator-placed) and surfaced by **E1g-S07**; and
- **time-boxed** — an explicit teardown deadline, surfaced by **E1g-S07**
(`task e1g:status`, a soft WARN once the default ~14-day window is exceeded).

This carve-out is cost-governance, not a licence for "always-on": it does **not**
declare standing infra always fine, and it does **not** weaken the ephemeral
`e8b:up`/`e8b:down` demo cycle or the per-story create→verify→destroy live-proof
discipline, both of which stay ephemeral-by-default.

## TL;DR

Expand Down Expand Up @@ -133,9 +153,11 @@ pool keeps billing).

The demo runs on the same GSK node pool + LBaaS + public IPs as E1g (see the cost
table in [gridscale-day0.md](gridscale-day0.md) — the GSK node pool dominates).
Because E8b is **on-demand, not standing**, the cost is bounded to the minutes the
demo actually runs. **Always `task e8b:down` immediately after the demo.** Never
leave the demo up "just in case" — bring it up again on demand next time.
Because the E8b demo is **on-demand and ephemeral-by-default**, its cost is bounded
to the minutes the demo actually runs. **Always `task e8b:down` immediately after
the demo.** Never leave the demo up "just in case" — bring it up again on demand
next time. (A deliberately *standing* go-live substrate is a separate, recorded and
time-boxed carve-out — see the go-live carve-out above and D-042.)

## Read-only posture (why the demo is safe to expose)

Expand Down
2 changes: 1 addition & 1 deletion openspec/changes/e1g-gridscale-day0/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ later serialized step (`task e1g:up`). See docs/runbooks/gridscale-day0.md.
- [ ] E1g-S05g: Cloudflare DNS records → LBaaS IPs + LE staging→prod + live public serve verify — depends S05c/S05e/S05f
- [ ] E1g-S05h: **Security spike** — investigate + mitigate GSK worker-node public-IP exposure (node came up EXTERNAL-IP `185.241.34.168`; `gridscale_k8s ~>2.2` exposes no disable-public-IP arg — confirm provider/API limit + safe mitigation)
- Build order: **S05a → S05d → S05b → S05c → (S05e ∥ S05f) → S05g.** S05h is an independent security spike (no dep).
- [ ] E1g-S06: Reconcile the retired "no standing env" DECIDED-B prose → go-live recorded+time-boxed carve-out (INBOX/ROADMAP/runbooks/Taskfile e8b strings) + offline doc-truth guard `tests/meta/e1g-standing-policy.yaml`; forward-refs S07 + D-04x. Doc-truth/cost-governance, NOT a blocker. Closes audit WIP-D1 (doc half). Body in `agent-context/BACKLOG.md` § "Go-live cost governance".
- [x] E1g-S06: Reconcile the retired "no standing env" DECIDED-B prose → go-live recorded+time-boxed carve-out (ROADMAP/both gridscale runbooks/Taskfile e8b desc+comment strings; INBOX left to the edge session) + offline doc-truth guard `tests/meta/e1g-standing-policy.yaml` wired into `test:meta:ci`; forward-refs S07 + D-042 + `evidence/live/e1g-gsk-2026-07-18.md`. Doc-truth/cost-governance, NOT a blocker. Closes audit WIP-D1 (doc half). Body in `agent-context/BACKLOG.md` § "Go-live cost governance".
- [ ] E1g-S07: Cost-visibility standing marker + `task e1g:status` + SOFT time-box WARN guardrail (~14d default, always exit 0, absent=no-op) wired into `test:meta:ci`; `task verify` EXIT 0 even with a stale marker (softness proof). Closes audit WIP-D1 (TTL half). Depends S06.
- [x] Gate: `task test:smoke:e1g` (offline) — EXIT 0
- [ ] Live-proof: `task e1g:up` then re-sync app-of-apps (later serialized step, costs money)
65 changes: 65 additions & 0 deletions tests/meta/e1g-standing-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# E1g-S06 — go-live standing-substrate doc-truth guard (offline; WIP-D1).
#
# The dev-phase DECIDED-B rule ("on-demand, NOT standing / No standing env") was
# an overspend guard for the BUILD phase. The project is now in go-live (D-042),
# where a standing live substrate is intentional — permitted ONLY when it is
# RECORDED (what is up, since-when, teardown-by, owner) and TIME-BOXED (explicit
# teardown deadline), surfaced by E1g-S07's `task e1g:status` soft WARN.
#
# This guard keeps that reconciliation honest offline:
# (a) the go-live "recorded and time-boxed" carve-out phrasing is PRESENT in
# each governed durable home; and
# (b) no governed doc silently reintroduces an UNQUALIFIED standing-env
# absolute (an absolute with no go-live/recorded/time-boxed/carve-out
# qualifier nearby).
# It scans ONLY durable docs — never agent-context/ (coordination, not truth).
# Asserts on prose STRINGS, never line numbers (matches doc10-roadmap-status).
# Known limitation: the ABSOLUTE pattern matches only the specific removed
# phrasings ("no standing env" / "not a standing environment" / "NOT standing").
# A reworded regression (e.g. "no standing infrastructure") is out of scope by
# design — the guard closes the exact reconciled contradiction, not every synonym.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
ROADMAP="$ROOT/docs/ROADMAP.md"
LIVEDEMO="$ROOT/docs/runbooks/gridscale-live-demo.md"
DAY0="$ROOT/docs/runbooks/gridscale-day0.md"
TASKFILE="$ROOT/Taskfile.yml"

fail() { echo "FAIL: $*" >&2; exit 1; }

# Stable phrase the reconcile introduces everywhere the carve-out is stated.
CARVEOUT='recorded and time-boxed'
# The dev-phase absolutes that, UNQUALIFIED, contradict the go-live carve-out.
ABSOLUTE='no standing env|not a standing environment|NOT standing'
# Qualifiers that make a standing statement a legitimate carve-out, not a false
# absolute (go-live permits a recorded, time-boxed standing substrate).
QUALIFIER='go-live|recorded|time-boxed|carve-out|D-042'

# --- (a) carve-out phrasing PRESENT in each governed durable home ------------
for DOC in "$ROADMAP" "$LIVEDEMO" "$DAY0"; do
[ -f "$DOC" ] || fail "governed doc missing: $DOC"
grep -qiF "$CARVEOUT" "$DOC" \
|| fail "$DOC must state the go-live '$CARVEOUT' standing carve-out"
done

# The carve-out must forward-reference the enforcement story + the decision/record.
grep -qiF 'E1g-S07' "$LIVEDEMO" \
|| fail "gridscale-live-demo.md carve-out must forward-reference E1g-S07 (soft WARN time-box)"
grep -qiF 'D-042' "$LIVEDEMO" \
|| fail "gridscale-live-demo.md carve-out must cite the D-042 go-live decision"
grep -qiF 'e1g-gsk-2026-07-18' "$LIVEDEMO" \
|| fail "gridscale-live-demo.md carve-out must cite the evidence/live record"

# --- (b) no UNQUALIFIED standing-env absolute in any governed doc ------------
# A line carrying an absolute is only OK if it also carries a go-live qualifier.
for DOC in "$ROADMAP" "$LIVEDEMO" "$DAY0" "$TASKFILE"; do
[ -f "$DOC" ] || fail "governed doc missing: $DOC"
if grep -iE "$ABSOLUTE" "$DOC" | grep -qivE "$QUALIFIER"; then
grep -inE "$ABSOLUTE" "$DOC" | grep -ivE "$QUALIFIER" >&2 || true
fail "$DOC carries an UNQUALIFIED standing-env absolute (add a $QUALIFIER carve-out qualifier, or reconcile the line)"
fi
done

echo "OK: E1g-S06 go-live standing-substrate doc-truth (recorded + time-boxed carve-out)"
Loading