Polyhydra Skills / Uncategorized
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.
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
# 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.