chore: replace AI skill library with 16 verified mvp-* skills
Deploy to Staging / Build Images (push) Successful in 5m32s
Deploy to Staging / Deploy to Staging (push) Successful in 44s
Deploy to Staging / Verify Staging (push) Successful in 5s
Deploy to Staging / Notify Staging Ready (push) Successful in 4s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
Deploy to Staging / Build Images (push) Successful in 5m32s
Deploy to Staging / Deploy to Staging (push) Successful in 44s
Deploy to Staging / Verify Staging (push) Successful in 5s
Deploy to Staging / Notify Staging Ready (push) Successful in 4s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
Removes the old planner/decision-critic/role-agents/domain-agents system (step-injector scripts, stale scopes, sprint-era workflow) and replaces it with 16 ground-truth-verified skills under .claude/skills/: change control, debugging playbook, failure archaeology, architecture contract, domain reference, OCR/Gemini pipeline, config and secrets, build and env, run and operate, diagnostics (with tested scripts), validation and QA, docs and writing, launch readiness, deploy-safety campaign, proof and analysis toolkit, and research frontier. RULE 0/1/2, the temporal-contamination rule, and the decision stress-test protocol are carried forward into the new skills; the retired content remains in git history. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e729d425fd
commit
c239bb9347
@@ -0,0 +1,228 @@
|
||||
---
|
||||
name: mvp-launch-readiness
|
||||
description: >-
|
||||
Load when assessing whether MotoVaultPro is ready to launch, take paying users, or make
|
||||
a public claim ("is this production ready", "can we ship", "launch checklist", "is
|
||||
billing working", "is this secure enough", "what is blocking launch", "can we say X is
|
||||
supported"). Also load before writing any user-facing statement about supported
|
||||
features, security posture, or reliability, and when triaging the known launch blockers
|
||||
- Stripe upgrade dead-end, Resend webhook secret, backup emails, fuel-log cache bug,
|
||||
Traefik/Grafana default credentials, stale AUDIT.md.
|
||||
---
|
||||
|
||||
# MVP Launch Readiness
|
||||
|
||||
External positioning for a PRE-LAUNCH product heading toward paying users. This skill
|
||||
defines what must be PROVEN before claiming readiness or shipping to humans, and lists
|
||||
the launch blockers with evidence. Its purpose is to prevent claiming readiness by eye.
|
||||
|
||||
## When to use / When NOT to use
|
||||
|
||||
Use this skill when:
|
||||
- Deciding whether a feature can be called "supported" in any user-facing or public claim.
|
||||
- Building or reviewing a launch checklist, readiness report, or go/no-go decision.
|
||||
- Triaging any of the launch-blocking gaps listed below.
|
||||
|
||||
Do NOT use this skill for:
|
||||
- Fixing the CI gate hole itself — that is `mvp-deploy-safety-campaign` (the executable campaign).
|
||||
- How to deploy, roll back, or operate environments — `mvp-run-and-operate`.
|
||||
- What counts as test evidence and how to run suites — `mvp-validation-and-qa`.
|
||||
- Secrets/config mechanics (adding a secret, YAML config loader) — `mvp-config-and-secrets`.
|
||||
- Doc trust levels and doc-sync — `mvp-docs-and-writing`.
|
||||
- Issue/branch/PR mechanics for fixing any blocker — `mvp-change-control`.
|
||||
|
||||
## Verdict labels (use these exact words)
|
||||
|
||||
| Label | Meaning |
|
||||
|---|---|
|
||||
| PROVEN | Verified against code/config on 2026-07-07, with a test or a staging-verified procedure behind it |
|
||||
| UNPROVEN | Implemented in code but never verified end-to-end; no test, no recorded staging run |
|
||||
| BROKEN | Verified defective: the code path exists and does the wrong thing (or nothing) |
|
||||
|
||||
A claim without a label is not a claim. "It compiles" and "staging boots" are not evidence:
|
||||
CI gates nothing beyond build + boot (canonical statement: `mvp-validation-and-qa` section 1).
|
||||
|
||||
## 1. Launch-blocking gaps (each verified 2026-07-07 — re-verify before acting)
|
||||
|
||||
| # | Gap | Verdict | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 | Upgrade path from tier-gated features dead-ends: the Upgrade button in `UpgradeRequiredDialog` just closes the dialog (`// TODO: Navigate to upgrade page`) | BROKEN | `frontend/src/shared-minimal/components/UpgradeRequiredDialog.tsx:55-58`. Nuance: a working checkout UI exists at route `/garage/settings/subscription` (`frontend/src/App.tsx:1175`), so users who find Settings can upgrade — but the upsell moment (hitting a gated feature) converts to nothing. Fix is a navigate call, not a Stripe build-out. |
|
||||
| 2 | Resend inbound email webhook secret is unconfigured in every environment: `resend-webhook-secret` is absent from `secrets/app/`, from the backend secret mounts in `docker-compose.yml:133-141`, and from the 12-file list in `scripts/inject-secrets.sh:30-43` | BROKEN (feature), not open (security) | `POST /api/webhooks/resend/inbound` is a PUBLIC endpoint (no JWT — `backend/src/features/email-ingestion/api/email-ingestion.routes.ts:13`). It fails CLOSED: `verifyWebhookSignature` throws when `RESEND_WEBHOOK_SECRET` is unset (`resend-inbound.client.ts:39`) and the handler returns 400 (`email-ingestion.controller.ts:141-146`). So no unsigned request is accepted — but email ingestion cannot work at all until the secret is provisioned end-to-end (secret file + compose mount + inject-secrets + Gitea secret). Config schema already treats it as optional (`backend/src/core/config/config-loader.ts:121,185`). |
|
||||
| 3 | Backup failure emails are never sent — the notification path checks settings and the admin email, then only logs "Backup notification would be sent" | BROKEN | `backend/src/features/backup/domain/backup.service.ts:441` (`TODO: Integrate with email service`). The settings UI implies emails that do not exist. A paying user's backup can fail silently. |
|
||||
| 4 | Fuel-log delete invalidates only the imperial cache: `deleteFuelLog` hardcodes `'imperial'` and `invalidateCaches` deletes only that unit-system's two keys | BROKEN | `backend/src/features/fuel-logs/domain/fuel-logs.service.ts:223` and `:266-271`. The inline comment "simple sweep below" is false — there is no sweep. Metric-unit users see deleted logs for up to the cache TTL. |
|
||||
| 5 | Traefik dashboard on prod has placeholder auth: basicauth hash is `admin:$2y$10$foobar` (not a valid bcrypt digest), while `api.insecure: true` with port 8080 published in the base compose | BROKEN (relies on firewall only) | `docker-compose.prod.yml:23`; `config/traefik/traefik.yml:1-4`. Generate a real htpasswd bcrypt hash before launch. |
|
||||
| 6 | Grafana admin password defaults to `admin` unless the `GRAFANA_ADMIN_PASSWORD` Gitea secret is set | UNPROVEN (mitigated) | `docker-compose.yml:350` (`${GRAFANA_ADMIN_PASSWORD:-admin}`). Mitigation exists: `grafana-ipwhitelist` middleware allows only RFC1918 source ranges (`config/traefik/dynamic/grafana.yml:3-5`), so it is not internet-reachable — but confirm the Gitea secret is actually set on staging and prod before launch. |
|
||||
| 7 | CI gates nothing: no tests, no lint, no security scan on any PR; PR builds clobber `:latest` and the prod deploy input defaults to `latest`, so prod can ship an unmerged PR build | BROKEN (process) | `.gitea/workflows/staging.yaml` (PR-triggered, pushes `:latest`), `.gitea/workflows/production.yaml` (`image_tag` default `latest`). Do not fix here — route to `mvp-deploy-safety-campaign`. Launch gate: prod deploys by SHA, and at least one real test job on PR. |
|
||||
| 8 | No account-deletion / data-retention policy document, despite the feature being live: a 30-day grace period is implemented (`backend/src/features/user-profile/jobs/account-purge.job.ts`) and mentioned once in `docs/USER-GUIDE.md:613` | UNPROVEN (code exists, policy does not) | Paying users require a stated retention policy. `docs/AUDIT.md` flagged this MEDIUM in 2025-12 (AUDIT.md:303); still no doc. Write the policy doc; verify the purge job's behavior matches it before publishing. |
|
||||
| 9 | `docs/AUDIT.md` (dated 2025-12-13, verdict "CONDITIONALLY READY") was never updated after remediation, so the real open findings are unknowable from the doc | BROKEN (as a record) | Several findings are demonstrably fixed in code (see section 3) while the doc presents them as open. Do a remediation-status pass: add a Status column (FIXED with file:line / OPEN / SUPERSEDED) so a reader can trust it. Until then, treat AUDIT.md as an archive, never as the current posture. |
|
||||
|
||||
## 2. Claim discipline
|
||||
|
||||
Rule: nothing is "supported" until it has (a) a verified runbook in the repo and (b) a test
|
||||
or a staging-verified procedure. "The code exists" is UNPROVEN. When docs contradict code,
|
||||
code wins — say so in the claim.
|
||||
|
||||
### Billing claims specifically
|
||||
|
||||
Do not claim billing works until the FULL cycle has been exercised on staging with Stripe
|
||||
test mode: checkout -> webhook -> tier sync -> downgrade. Current status, checked 2026-07-07:
|
||||
|
||||
- Status: UNPROVEN. The subscriptions capsule (`backend/src/features/subscriptions/`) has
|
||||
ZERO test files (`find backend/src/features/subscriptions -name "*.test.ts"` returns
|
||||
nothing). Git history shows real-world debugging fixes on 2026-02-16 ("charge immediately
|
||||
on subscription", "attach payment method to customer" — commits `8a73352`, `72e5573`),
|
||||
which implies the flow was manually exercised around then, but there is no test and no
|
||||
recorded staging verification of the full cycle since. Honest label: implemented,
|
||||
manually poked in Feb 2026, never verified end-to-end as a procedure.
|
||||
|
||||
How to verify it (the moving parts):
|
||||
|
||||
1. Price IDs: four env vars map plan -> Stripe price and back:
|
||||
`STRIPE_PRO_MONTHLY_PRICE_ID`, `STRIPE_PRO_YEARLY_PRICE_ID`,
|
||||
`STRIPE_ENTERPRISE_MONTHLY_PRICE_ID`, `STRIPE_ENTERPRISE_YEARLY_PRICE_ID`.
|
||||
Sandbox defaults are baked into `docker-compose.yml:124-127` and `.env.example:12-15`;
|
||||
staging/prod override via `.env` generated from Gitea variables. Forward mapping in
|
||||
`getPriceId`, reverse tier inference in `determineTierFromStripeSubscription`
|
||||
(`backend/src/features/subscriptions/domain/subscriptions.service.ts:831-900`). An
|
||||
unknown price ID silently maps to tier `free` — a misconfigured price ID downgrades
|
||||
paying users without error.
|
||||
2. Secrets: `stripe-secret-key` and `stripe-webhook-secret` are file secrets read by
|
||||
`getStripeConfig()` (`backend/src/core/config/config-loader.ts:258-263`), mounted at
|
||||
`docker-compose.yml:140-141`, injected on deploy by `scripts/inject-secrets.sh`.
|
||||
3. Endpoints: `POST /api/subscriptions/checkout` (authenticated,
|
||||
`subscriptions.routes.ts:28-29`) and `POST /api/webhooks/stripe` (PUBLIC, signature-
|
||||
verified, `webhooks.controller.ts:24`). Tier lands in `user_profiles.subscription_tier`
|
||||
via `syncTierToUserProfile` and gates features through
|
||||
`backend/src/core/config/feature-tiers.ts` (limits and key catalog canonical in
|
||||
`mvp-config-and-secrets` section 3; unknown feature keys FAIL OPEN).
|
||||
4. Staging procedure: on staging with Stripe TEST-mode keys in the Gitea secrets, drive
|
||||
checkout from `/garage/settings/subscription` with card 4242 4242 4242 4242; confirm
|
||||
the webhook fired (`{container="mvp-backend-staging"} | json` in Grafana, and the
|
||||
`subscription_events` table); confirm `user_profiles.subscription_tier` flipped; then
|
||||
cancel/downgrade and confirm the tier reverts and the vehicle-selection flow triggers
|
||||
when over the free limit. The Stripe webhook endpoint for staging must be registered
|
||||
in the Stripe dashboard pointing at `https://staging.motovaultpro.com/api/webhooks/stripe`
|
||||
and its signing secret stored as the `STRIPE_WEBHOOK_SECRET` Gitea secret. Record the
|
||||
run (date, commit SHA, screenshots or log queries) in the issue that tracks the claim.
|
||||
|
||||
Remember owner non-negotiables while doing this: no destructive DB operations without a
|
||||
fresh backup, and never hand-edit files on staging/prod servers (rsync --delete reverts
|
||||
them; all changes flow through the repo and workflows).
|
||||
|
||||
## 3. Security posture — the honest list (verified 2026-07-07)
|
||||
|
||||
What IS solid (PROVEN in code):
|
||||
|
||||
| Item | Evidence |
|
||||
|---|---|
|
||||
| Auth0 JWT validation: JWKS-based verification with issuer and audience pinning | `backend/src/core/plugins/auth.plugin.ts:105-108` (`allowedIss`, `allowedAud`) |
|
||||
| Network isolation: database network is `internal: true`; prod removes postgres/redis host ports | `docker-compose.yml:405-407`; `docker-compose.prod.yml` sets `ports: []` |
|
||||
| File-based secrets, never env, never committed (only `.example` siblings; `google-wif-config.json` is deliberately committed WIF config) | `backend/src/core/config/config-loader.ts:175-188`; `scripts/inject-secrets.sh` |
|
||||
| Upload magic-byte validation: content vs claimed MIME type checked before accepting documents | `backend/src/features/documents/api/documents.controller.ts:253-285` |
|
||||
| Storage keys from `crypto.randomBytes(32)` (the AUDIT.md CRITICAL, since fixed) | `backend/src/features/documents/api/documents.controller.ts:587` |
|
||||
| Stripe and Resend webhooks verify signatures and fail closed | `webhooks.controller.ts:36-46`; `resend-inbound.client.ts:37-52` |
|
||||
| Audit-log capsule and backend ESLint exist (both were AUDIT.md gaps) | `backend/src/features/audit-log/`; `backend/eslint.config.js` |
|
||||
|
||||
Open or unknown (do not claim these):
|
||||
|
||||
| Item | Status | Truth |
|
||||
|---|---|---|
|
||||
| Content-Security-Policy on the served frontend | OPEN | `frontend/nginx.conf` contains zero `add_header` directives — the SPA is served with no CSP. Backend registers `@fastify/helmet` with defaults (`backend/src/app.ts:49`), which covers API responses only. AUDIT.md finding still open. |
|
||||
| Google Maps API key exposure | OPEN (by design, unmitigated) | The key is injected at container startup into `window.CONFIG` served to every browser (`frontend/scripts/load-config.sh` -> `/usr/share/nginx/html/config.js`). Browser-side Maps keys are normal ONLY if referrer/API restrictions are set on the key in Google Cloud Console — that restriction is not verifiable from this repo. UNVERIFIED until checked in the console. AUDIT.md also flags key leakage via photo URLs. |
|
||||
| PostgreSQL connection encryption | RESOLVED CONTRADICTION — connections are UNENCRYPTED | `docs/ARCHITECTURE-OVERVIEW.md:511` claims "PostgreSQL SSL/TLS"; `docs/SECURITY.md:30` says unencrypted, network-isolated. Code wins: no `ssl` option anywhere in `backend/src/core/config/database.ts` or the `database:` block of `config/app/production.yml:12-18`. SECURITY.md is correct. Acceptable for a single-host internal-network deployment; never claim encrypted-in-transit DB. |
|
||||
| Tier guard fail-open | OPEN (deliberate, document it) | `canAccessFeature` returns true for unknown feature keys (`backend/src/core/config/feature-tiers.ts`). A typo in a feature key silently un-gates a paid feature. |
|
||||
| Admin guard typing in backup routes | OPEN | `backend/src/features/backup/api/backup.routes.ts` uses `(fastify as any).requireAdmin` 16 times — a typo compiles clean and would ship an unguarded admin endpoint. |
|
||||
|
||||
## 4. Reproducibility standard for launch
|
||||
|
||||
Launch bar: a new machine must be able to stand up the product from the repo plus Gitea
|
||||
secrets alone. No snowflake state on any server.
|
||||
|
||||
- Docs to trust: `docs/BUILD-SERVER-SETUP.md` (VPS + act_runner provisioning) and
|
||||
`docs/CICD-DEPLOY.md` (deploy pipeline — the most code-accurate infra doc). Known
|
||||
defects in both, verified 2026-07-07:
|
||||
- BUILD-SERVER-SETUP.md installs act_runner v0.2.13 (line 84) but its "Update Runner"
|
||||
section downloads v0.2.12 (line 247) — following the update procedure downgrades.
|
||||
- CICD-DEPLOY.md says runner labels `mvp-build`/`mvp-prod` (lines 34, 59-60); actual
|
||||
labels are `stage`/`prod` (`.gitea/workflows/staging.yaml:29`, `production.yaml:34`).
|
||||
- Ansible caveat (the prune-cron gap): the staging runner's daily Docker prune cron was
|
||||
installed out-of-band and is deliberately NOT in the repo; worse,
|
||||
`ansible/deploy-staging-runner.yml:317-324` REMOVES a legacy "Docker cleanup" cron.
|
||||
Rebuilding the staging box from ansible silently drops disk hygiene, and the 29G root
|
||||
disk then fills from per-commit image builds. This is the one standing exception to
|
||||
"everything flows through the repo" — a rebuilt box needs the prune cron reinstalled
|
||||
manually until it is codified.
|
||||
- Everything on-server is disposable by design: `scripts/inject-secrets.sh` regenerates
|
||||
all 12 secret files from Gitea Actions secrets on every deploy, and rsync `--delete`
|
||||
replaces `config/` and `scripts/`. If standing up a new box requires copying anything
|
||||
from an old box other than data volumes, that is a launch-blocking reproducibility bug.
|
||||
- Trap: bind-mounting a missing secret file makes Docker create a DIRECTORY at that path;
|
||||
a new machine hitting "secret is a directory" errors is this failure mode (canonical
|
||||
description and fix: `mvp-config-and-secrets` section 2).
|
||||
|
||||
## 5. Ranked pre-launch checklist
|
||||
|
||||
Run every evidence command from the repo root. A row is done when the command output
|
||||
contradicts the gap, not when a PR merged.
|
||||
|
||||
### Blocking (no paying users until green)
|
||||
|
||||
| Item | Evidence command |
|
||||
|---|---|
|
||||
| Wire UpgradeRequiredDialog to `/garage/settings/subscription` | `grep -n "TODO: Navigate" frontend/src/shared-minimal/components/UpgradeRequiredDialog.tsx` (must return nothing) |
|
||||
| Full billing cycle verified on staging, Stripe test mode, recorded in an issue | See section 2 procedure; `find backend/src/features/subscriptions -name "*.test.ts"` should also stop returning empty |
|
||||
| Real bcrypt hash on prod Traefik dashboard; kill `api.insecure` or unpublish 8080 | `grep -n "foobar" docker-compose.prod.yml` (nothing); `grep -n "insecure" config/traefik/traefik.yml` |
|
||||
| Backup failure emails actually send | `grep -n "would be sent" backend/src/features/backup/domain/backup.service.ts` (nothing) |
|
||||
| CI runs at least one real test job; prod deploys pinned by SHA (route to `mvp-deploy-safety-campaign`) | `grep -n "npm test\|jest\|pytest" .gitea/workflows/*.yaml` (currently nothing); `grep -n "default: 'latest'" .gitea/workflows/production.yaml` |
|
||||
| Account-deletion / data-retention policy doc exists and matches `account-purge.job.ts` behavior | `ls docs/ \| grep -i -E "retention\|deletion"` (currently nothing) |
|
||||
| GRAFANA_ADMIN_PASSWORD Gitea secret set for staging and prod | Check Gitea repo secrets; on server: `docker exec mvp-grafana env \| grep GF_SECURITY` must not show `admin` |
|
||||
|
||||
### Important (fix before public claims, not necessarily before first user)
|
||||
|
||||
| Item | Evidence command |
|
||||
|---|---|
|
||||
| Fix fuel-log delete cache invalidation for both unit systems | `grep -n "'imperial'" backend/src/features/fuel-logs/domain/fuel-logs.service.ts` (line 223 hardcode gone) |
|
||||
| Provision `resend-webhook-secret` end-to-end or disable/hide email ingestion | `grep -n "resend-webhook-secret" scripts/inject-secrets.sh docker-compose.yml` (currently no hits in either) |
|
||||
| Remediation-status pass on `docs/AUDIT.md` | `grep -n "Status" docs/AUDIT.md` shows a per-finding FIXED/OPEN column |
|
||||
| CSP header on the frontend (nginx or Traefik middleware) | `grep -n "add_header" frontend/nginx.conf` or `grep -rn "Content-Security" config/traefik/` |
|
||||
| Verify Google Maps key has referrer restrictions | Google Cloud Console (not verifiable from repo — record the check in an issue) |
|
||||
| Type the backup routes' admin guard | `grep -c "as any).requireAdmin" backend/src/features/backup/api/backup.routes.ts` returns 0 |
|
||||
| Codify the staging-runner prune cron in ansible | `grep -n "prune" ansible/deploy-staging-runner.yml` shows an install task, not just removal of the legacy cron |
|
||||
|
||||
### Nice-to-have
|
||||
|
||||
| Item | Evidence command |
|
||||
|---|---|
|
||||
| Tier guard fail-closed (or an explicit allowlist test) | `grep -n "fail open\|return true" backend/src/core/config/feature-tiers.ts` |
|
||||
| Fix BUILD-SERVER-SETUP.md v0.2.12/v0.2.13 and CICD-DEPLOY.md label drift | `grep -n "0.2.12" docs/BUILD-SERVER-SETUP.md; grep -n "mvp-build" docs/CICD-DEPLOY.md` |
|
||||
| Silent-failure TODOs: fuel-log delete error toast, stations pull-to-refresh no-op | `grep -rn "TODO" frontend/src/features/fuel-logs/components/FuelLogsList.tsx frontend/src/features/stations/mobile/StationsMobileScreen.tsx` |
|
||||
| PostgreSQL in-transit encryption (only if topology ever spans hosts) | `grep -n "ssl" backend/src/core/config/database.ts config/app/production.yml` |
|
||||
|
||||
## Provenance and maintenance
|
||||
|
||||
Authored 2026-07-07 from direct repo inspection at commit `e729d42`-era main. Every
|
||||
file:line above was verified on that date. Volatile facts and their re-verification
|
||||
commands:
|
||||
|
||||
- UpgradeRequiredDialog dead-end: `grep -n "TODO: Navigate" frontend/src/shared-minimal/components/UpgradeRequiredDialog.tsx`
|
||||
- Resend webhook secret absent from deploy path: `grep -n "resend-webhook-secret" scripts/inject-secrets.sh docker-compose.yml secrets/app/ -r`
|
||||
- Backup email log-only: `grep -n "would be sent" backend/src/features/backup/domain/backup.service.ts`
|
||||
- Fuel-log cache hardcode: `grep -n "invalidateCaches" backend/src/features/fuel-logs/domain/fuel-logs.service.ts`
|
||||
- Traefik placeholder auth: `grep -n "foobar" docker-compose.prod.yml && grep -n "insecure" config/traefik/traefik.yml`
|
||||
- Grafana default password: `grep -n "GRAFANA_ADMIN_PASSWORD" docker-compose.yml`
|
||||
- CI runs no tests: `grep -n "npm test\|jest\|pytest" .gitea/workflows/*.yaml` (expect no hits until fixed)
|
||||
- Prod deploy default tag: `grep -n "default: 'latest'" .gitea/workflows/production.yaml`
|
||||
- Subscriptions capsule untested: `find backend/src/features/subscriptions -name "*.test.ts"`
|
||||
- Stripe price-ID mapping: `grep -n "PRICE_ID" docker-compose.yml backend/src/features/subscriptions/domain/subscriptions.service.ts`
|
||||
- Tier limits and fail-open: `grep -n "VEHICLE_LIMITS\|return true" backend/src/core/config/feature-tiers.ts`
|
||||
- pg unencrypted: `grep -n "ssl" backend/src/core/config/database.ts config/app/production.yml` (expect no hits)
|
||||
- No frontend CSP: `grep -n "add_header" frontend/nginx.conf` (expect no hits)
|
||||
- 30-day grace period: `grep -rn "grace" backend/src/features/user-profile/jobs/account-purge.job.ts docs/USER-GUIDE.md`
|
||||
- AUDIT.md staleness: `grep -n "CONDITIONALLY READY" docs/AUDIT.md`
|
||||
- Ansible prune-cron gap: `grep -n "cron" ansible/deploy-staging-runner.yml`
|
||||
- Runner label drift: `grep -n "runs-on" .gitea/workflows/staging.yaml .gitea/workflows/production.yaml && grep -n "mvp-build" docs/CICD-DEPLOY.md`
|
||||
|
||||
Not verifiable from the repo (recheck externally): Google Maps key referrer restrictions
|
||||
(Google Cloud Console); whether the GRAFANA_ADMIN_PASSWORD and Stripe/Resend Gitea secrets
|
||||
are actually set (Gitea repo settings); whether a Stripe test-mode webhook endpoint is
|
||||
registered for staging (Stripe dashboard).
|
||||
Reference in New Issue
Block a user