Polyhydra Skills  /  Uncategorized

dogfood-staging

Take a late-development app from "code works locally" to a durable dev/QA deployment (e.g. dev.<app>.com) that the owner actually uses day-to-day, then track what dogfooding surfaces and decide when it's ready to graduate to production. Use when a project has a defined V1/stabilizing scope and the next step is standing up a semi-durable dev-tier environment, when the user says "dogfood", "staging site", "dev site", "dev.<app>.com", or asks whether a project is ready for a dev-tier cutover.

Uncategorized

Drop this in — save the block below as ~/.claude/skills/dogfood-staging/SKILL.md, or run:

mkdir -p ~/.claude/skills/dogfood-staging
cat > ~/.claude/skills/dogfood-staging/SKILL.md <<'EOF'
# (paste the full source block below into this file)
EOF

Full source

SKILL.md — copy everything inside
# Dogfood Staging

The move from "V1 is stabilizing" to "this app is real" is a durable dev-tier
deployment the owner actually lives in, not a demo. This skill sequences that
move and composes existing skills for the mechanics rather than duplicating
them — it does not replace `v1-delivery-shaping`, `mapped-frontier-development`,
`r620-deploy`/`stack-apply`, or `cloudflare-dns`; it's the checklist that
strings them together at this specific point in a project's life.

Terminology: this workspace calls the safe QA/rapid-test tier **`dev`**, not
"staging" — see [[feedback_dev_means_staging_tier]] and the `ubiquitous-language`
skill's Stage/Tier axis. The skill is still named `dogfood-staging` (the
*concept* of dogfooding a pre-production tier), but every hostname/stack/
branch this produces should say `dev`.

## When this applies

- The project already has a recorded V1 definition (`v1-delivery-shaping`) and
  is in or past stabilization — not still adding core feature scope.
- The goal is a **standing, durable** environment the owner dogfoods against
  repeatedly (days/weeks), not a one-off demo or a throwaway preview build.
- Distinct from the app's `debug.<app>.com` / `localhost` tiers if it has
  them (per whatever ADRs the app already has — check `docs/decisions/`
  first): `dev` here is the tier right before production, carries
  closer-to-real config, and is where the owner notices real gaps by
  actually using the thing. If a repo already has its own established
  `staging.` convention predating this glossary, don't silently rename it —
  flag the mismatch and ask, per [[feedback_dev_means_staging_tier]]'s "How
  to apply" note.

If the project is still adding V1 scope, or doesn't have a recorded V1
definition yet, run `v1-delivery-shaping` first — dogfooding an undefined
scope just produces noise about missing features that were never promised.

## Phase 1 — Scope freeze

1. Confirm the V1 definition (or current stabilization scope) is current —
   re-read the owning epic, don't assume last session's understanding still
   holds.
2. Explicitly list what does **not** need to work yet for the dev tier to be
   useful. Dogfooding surfaces real gaps fastest when the owner knows which
   gaps are already known-and-deferred versus genuinely new.
3. If standing up the dev tier is itself a material change (new hostname,
   new secret scope, new auth callback surface, a data-retention decision),
   run it through `mapped-frontier-development` Phase 0 rather than treating
   DNS + deploy as routine — this is exactly the "material deployment
   change" case that skill exists for.

## Phase 2 — Stand up the environment

1. **Naming**: `dev.<app-domain>` (e.g. `dev.channelcheevos.com`). Confirm
   the actual domain the app uses — check existing ADRs/Infisical config
   before assuming `polyhydragames.com`/`polyhydra.app`; some apps (e.g.
   channel-cheevos) use their own domain.
2. **DNS**: use the `cloudflare-dns` skill — create the record before
   deploying, not after ("DNS first, not as an afterthought" is called out
   explicitly in `r620-deploy` because skipping it was real, avoidable
   friction previously).
3. **Deploy**: use `r620-deploy` for a first-time stack, `stack-apply` to
   reconcile an existing one. The dev tier must be a tracked Portainer/Swarm
   stack sourced from the repo's `homelab-gitops` definition — not a rogue
   local `docker run`/build (see the repo's own no-rogue-builds policy if it
   has one). Verify with the same rigor as production: real TLS cert
   subject, clean startup logs, a real endpoint through the public path —
   the dev tier being "less important" than prod is not a reason to skip
   verification; it's the thing the owner is about to trust daily.
   - **From a sandboxed/background session**, standing up a brand-new
     stateful stack (a new DB, a new secret-bearing service) hits a real
     wall documented in `r620-remote-ops`: creating *new* Docker Swarm
     secrets can't be done invisibly (the value would have to be typed into
     a visible tool call to reach Portainer's API) — see that skill's
     "Creating NEW secrets on r620" section and its
     `scripts/create-swarm-secrets.sh` helper. Prepare everything else
     (compose files, network, non-secret file placement via the one-shot-
     stack trick), then hand the secret-creation step to the operator via
     `!` rather than attempting a workaround.
4. **Secrets**: the dev tier gets its own credential set, never copied
   production secrets. Use `infisical-lookup` to find where sibling secrets
   for this app already live, `secret-apply` to generate/land new ones.
   Check whether the app has its own dogfood/dev secret-boundary ADR first
   (e.g. channel-cheevos's `docs/decisions/0001-auth0-dogfood-boundary.md`
   pattern — a real precedent for "the dev tier must not become an implicit
   production/provider-migration change").
   - **Infisical folders must already exist before `secret-apply`/
     `infisical secrets set` can write into them** — confirmed 2026-08-17/18:
     writing to a brand-new `<env>/<path>` combination fails (the CLI's own
     error is swallowed by `secret-apply.sh`'s stderr redirect, surfacing
     only as a generic `FAILED`). Create the folder first:
     `infisical secrets folders create --name <last-path-segment> --path
     <parent-path> --env <env> --domain <infisical-domain> --projectId
     <id>`. A brand-new tier (e.g. a first-ever `dev` environment secret for
     an app whose Infisical config only had `prod` before) will hit this —
     don't assume the write failure means bad credentials or a typo'd path
     before checking whether the folder exists.
5. **Auth callbacks**: if the app has OIDC/OAuth login, the dev tier needs
   its own callback/logout/origin allowlist entries (see `keycloak-admin`
   skill or the app's own auth provider config) — don't reuse production
   callback URLs. Check the app's own provisioning tooling first (e.g. a
   `scripts/provision-*-client.py` in its repo) before hand-rolling client
   creation via raw Keycloak admin API calls — a canonical script may
   already encode conventions (realm, secret sourcing direction, staged
   enable, group mappers) that a hand-rolled version will miss or get
   backwards.

## Phase 3 — Dogfood tracking

1. Use the dev environment for real, ongoing use — not a single smoke test.
   The point is surfacing gaps only real use finds.
2. File every gap found as a GitHub issue on the app's repo, tagged so
   dogfood-sourced issues are distinguishable from originally-planned V1
   scope (e.g. a `dogfood` label). This keeps "what dev-tier use taught us"
   separate from "what we already knew we'd build."
3. Triage using the same severity judgment as `v1-delivery-shaping`'s P0–P2
   language: repair blockers before graduation, defer polish to tracked
   follow-ups.
4. If the `quest-log` skill or a similar dashboard is already in use for this
   repo, surface dogfood-tagged issues there rather than starting a separate
   tracking document.

## Phase 4 — Graduation criteria

The dev tier is ready to inform a production cutover decision when:

- No open P0/P1 dogfood-tagged issues remain.
- The owner has used it continuously (not just tested it) for a length of
  time they're comfortable calling representative — this is a judgment call
  for the owner, not a fixed duration this skill should assume.
- Deployment mechanics (DNS, secrets, auth callbacks, verification steps
  above) are proven repeatable, not just "worked once."

Graduating to production is its own decision requiring its own explicit
confirmation — this skill stands up and validates the dev tier; it does not
authorize a production cutover, merge, or DNS cutover to a production
hostname. Treat that as a separate, explicitly confirmed action per this
project's standing authorization rules for outward-facing changes.

## Related skills

- `ubiquitous-language` — the Stage/Tier axis definition (`dev` vs
  production) this skill's naming follows.
- `v1-delivery-shaping` — defines the scope the dev tier is meant to
  validate; run first if V1 isn't yet defined.
- `mapped-frontier-development` — Phase 0 packet for standing up the dev
  tier when it's a material change, not routine.
- `cloudflare-dns`, `r620-deploy`, `stack-apply` — the actual DNS/deploy
  mechanics this skill sequences but doesn't duplicate.
- `r620-remote-ops` — the sandboxed-session deploy path, including the
  new-secret-creation hard boundary noted above.
- `infisical-lookup`, `secret-apply` — the dev tier's own secret set,
  including the folder-must-exist-first gotcha noted above.
- `keycloak-admin` — dev-tier-specific OIDC callback config, if applicable.
- `quest-log` — surfacing dogfood-tagged issues if the repo already uses it.