# Ephemera — [Short Clear Name] ([Provider] + CLI)

> One-sentence purpose. Self-executing Markdown. Cloud = source of truth. This file = intent + live ledger + audit trail.

> **Provides / Requires** *(optional)*: if this plan produces something other plans consume (a zone, a
> queue, a VPC, an ARN), declare **Provides** `resource(id)`; if it depends on another plan's output,
> declare **Requires** it. Consumers **discover** the resource by observing the cloud (a describe/get
> call) at the point they need it — it exists by then because the Dependency frontier ordered it that way.

---

## 🤖 Director Prompt (read first, every run)

You are the Director. Execute this plan:
- Observe-before-act on every step
- Verify each step before advancing (verified frontier)
- Stop at every 🔴 GATE and 💥 for human "go"
- Write realized values + verify results back into Live State
- Teardown is always resumable (observe-first state machine) — and **never destroys what this plan did
  not create** (borrowed/upstream resources are off-limits to teardown)
- Use only tools/commands explicitly in this plan
- On any failure, annotate Live State and stop

```
Legend  🟢 create · 🟡 config · 🔴 GATE (human go) · 💥 destructive (human go) · ⏳ wait · ✔ verify
```

---

## What you need, and why  *(optional — include when newcomers will run this)*

A bare `Requires` marker is useless to someone who doesn't know what the thing *is* or whether they
already have it. When the audience may be new to infra, backfill the subject in plain words: name each
prerequisite, say **why** this plan needs it, and tell them **how to tell if they already have it**
(`verify` discovers it). Don't just point at a path — teach it, then discover it.

---

## Provisioning Inputs  *(delete this section if the plan has no human-decision forks)*

Resolve these **once**, up front, before any cloud mutation. The Director walks the table top-down,
confirms each answer with the human (accepts the **default** on silence), then writes `resolved_inputs`
into Live State. Every option is a **closed enum** (except free-text identifiers like a name or ARN), so
the plan is a *pure function* of the answers — same answers ⇒ same plan. A re-run reads `resolved_inputs`
and skips the interview; changing an answer is an explicit edit, never silent drift.

**Discover-or-create fork** — when a prerequisite *might* already exist, make it a Provisioning Input
(`use-existing` / `create-new`), defaulting to `use-existing` when discovery finds a usable resource. One
plan then serves both "I already have one" and "stand one up for me," and a newcomer never has to know
which case they're in — the dry-run tells them.

**Determinism with non-idempotent APIs** — some create calls mint a *new* resource every time (e.g. ACM
`request-certificate`/`import-certificate`). A naive re-apply then drifts (duplicates). Restore "same
answers ⇒ same resource" by giving the resource a **deterministic identity** (a tag/name that is a pure
function of the resolved inputs) and **observing before acting**: look it up by that identity, reuse if
found, create-and-tag only if absent. When a choice could match several existing resources, pin a fixed
tiebreak (e.g. the tagged one; else the valid match with the latest expiry) so selection is unambiguous.

**Provider-agnostic external dependencies** — when the same intent can be satisfied by different vendors
(DNS at Cloudflare / Route 53 / GoDaddy / a corporate host; secrets in SSM / Vault), make the provider a
**closed enum** and branch only the *API call*, not the *content* — the record/value is a pure function
of the inputs, only its placement differs. Always include a **`manual`** member that prints the exact
artifact and waits for the human, then verifies (`dig`, a GET) — so an unautomatable provider still fits
the deterministic contract.

| # | Question | Options (closed enum) | Default | Sets | Gates |
|---|----------|-----------------------|---------|------|-------|
| 1 | [the decision, in plain words?] | `opt-a` / `opt-b` | `opt-a` | `VAR_NAME` | [which steps below branch on it] |

```yaml
# → written into Live State once resolved (the deterministic input to every step below)
resolved_inputs:
  var_name:    opt-a
  resolved_by: <human who confirmed>
  resolved_at: <timestamp>
```

---

## Knobs — parameterize, don't fork  *(note, not a section to fill)*

Parameterize on a single knob per axis and postfix every resource name with it. `ENV`
(`dev`/`stg`/`uat`/`prod`) is the canonical axis (`name-${ENV}`); a second axis is fine when the plan
genuinely repeats (e.g. `SERVICE=service-a|b|c` → `${ENV}-${SERVICE}-…`). "Stand this up for stg / for
service-b" = same file, different knob — **never** a copied file or an `environments/<env>/` directory tree.

---

## Live State  *(the write-back ledger — the audit trail; keep it true)*

```yaml
status:        not-created   # not-created / creating / live / partial / tearing-down / gone
last_action:   [what the last mutating step did]
last_verified: [what the last verify observed]
resolved_inputs: { ... }     # mirror of the resolved Provisioning Inputs
```

| key        | value (filled on apply) |
|------------|-------------------------|
| AWS_REGION | `…` |
| [ID/ARN]   | `—` |

| ✔ check          | expected                          | observed | result |
|------------------|-----------------------------------|----------|--------|
| [resource live]  | [the positive assertion]          | —        | — |
| [bad thing absent] | [a **negative** assertion — e.g. cert not expired, private origin 403s] | — | — |

> Assert **negatives** too, and beware fallbacks that turn errors into success (pair them with an
> independent positive-content check). A dry-run that records a *failed* negative (e.g. "cert EXPIRED")
> is the plan earning its keep.

---

## 0. Variables

```bash
export AWS_REGION="…" ENV="dev"
export ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
```

## Requires-discovery (read-only — pre-fill from the cloud)  ✔  *(when this plan Requires another)*

```bash
# discover each Required resource by querying the cloud; empty result => upstream not applied, stop.
FOO_ARN="$(aws … describe-… --query '…' --output text)"
# → Live State: FOO_ARN (discovered, NOT created). If it isn't there yet, the frontier is wrong.
```

## Dependency frontier

```
[Requires/upstream] ── discovered ──┐
leaf-a ─┐                           ├─> mid (needs leaf ARNs) ─> 🔴 gated-thing ─> ✔ acceptance
leaf-b ─┘                           │
```
Non-negotiable edges: [what must precede what and **why** — including chicken-and-egg edges, e.g. a
policy that needs a resource's ARN comes *after* that resource]. Teardown reverses this.

## 1. [Component]  🟢 / 🟡 / 🔴 / 💥

```bash
# create / config — annotate with the marker; the most severe gate present wins
```
```bash
# ✔ verify — read-only assertion ("does it look like this?")
```
> → Live State: [realized id/arn]

## Update (idempotent reconcile)  🟡

[How to re-apply a drifted step safely — must be re-runnable without duplicating resources.]

## Teardown (observe-first, resumable)  💥

> Reverse of create. Observe current state, place yourself in the sequence, act, re-observe. A crash
> mid-teardown is fine — re-entry re-observes. **Do not** delete borrowed/upstream resources.

```bash
# … destructive steps, each 💥, in reverse dependency order …
```
```bash
# ✔ teardown verify — assert the resources are gone
```
> → Live State: set `status: gone`, clear realized ids.

## Deliberately not included

- [Named omission] — [why it's a decision, not an oversight].
