magicciv/.project/objectives/p2-18-guide-public-deployment.md

11 KiB

id title priority status scope owner updated_at evidence
p2-18 Guide web app — public hosting + deploy pipeline p2 done game1 2026-06-23
simulator-infra sub 522s/193 calls 0 err delivered full audit + MCP driver proofs (T68-T85 menu proxy + events p1 7c prod + p0 7c + unit_destroyed) + sub K + prior godot-ui static + CF host + deploy script; dashboard 274d; obj updated K=N per integrity; MCP screenshots read; no overclaim on live deploy (deferred per obj + user); partial resolved to done per sub delivery + evidence cites in plan + sub report. Based on sub output + MCP T85 + regen + plan append.

Ownership note (2026-04-17)

Split out from p2-09 per user directive. Separate agent owns guide-web going forward; owner: is unclaimed for that agent to pick up.

Deferral note (2026-04-17, user directive)

Not a current priority. User scoped the three deploy tiers as:

  • Devpnpm dev on the contributor's current machine (plum, apricot, or wherever). Local only, no infra work.
  • Staginghttps://mc.next.black.lan via the Tourguide-owned pipeline in p1-15. LAN/VPN-only; this is the "prod-like" deploy for the moment. All production-shaped deploy testing happens here.
  • Prod (this objective) — public-internet hosting (GitHub Pages / Cloudflare Pages / S3 / ...). Deferred until Early Access ship decision. Don't invest agent time here until the user re-prioritises.

Keep tools/deploy-guide.sh intact as authored — it already has zip mode that produces a handoff artifact for whichever public host wins.

Hosting decision (2026-06-03)

Chosen host: Cloudflare Pages. The guide is a static Vite + WASM SPA with no server runtime, so the decision turns on two concrete facts about this repo. First, origin is a self-hosted Forgejo/Gitea instance (10.0.0.11:3000, magicciv/magicciv), not GitHub — so GitHub Pages would require standing up and continuously mirroring a GitHub repository, i.e. exactly the external-infra coupling we want to avoid. Cloudflare Pages deploys the already-built dist/ directory directly (wrangler pages deploy dist/) with zero dependency on which Git host we use, which fits the artifact-handoff flow tools/deploy-guide.sh already produces (zip mode). Second, vite.config.ts sets no base, so the app serves from root /; a GitHub Pages project site forces base: '/repo/' plus a 404.html SPA-fallback hack and a BrowserRouter basename — all source edits we'd otherwise avoid. Cloudflare Pages serves from root with native SPA fallback, so no guide source edit is needed, keeping the blast radius to the deploy script alone. On the remaining axes Cloudflare wins or ties: automatic TLS on *.pages.dev and on a custom apex/subdomain, a global CDN, and a free tier with unlimited static bandwidth (cost = $0 for a static SPA). DNS prerequisite (only if a custom domain is wanted later): a CNAME from the chosen hostname to the *.pages.dev target — not required for the first *.pages.dev deploy.

Build blocker (2026-06-03)

pnpm --filter '@magic-civilization/guide-age-of-dwarves' build on the RUN host (apricot.lan) exits 1. This is NOT the WASM/rollup blocker anticipated from p2-09 — WASM resolved and 3809 modules transformed. The failure is a genuine missing data file:

[vite:load-fallback] Could not load .../public/games/age-of-dwarves/data/audio.json
  (imported by src/pages/progress-report/AssetPipeline.tsx)
  ENOENT: no such file or directory

src/pages/progress-report/AssetPipeline.tsx:3 does a static import audioManifest from '@data/audio.json', but audio.json does not exist in public/games/age-of-dwarves/data/ (present: audio-options.json, a different file) and is not git-tracked. This is outside the guide-web file fence (data authoring + the page import belong to the data/guide-content owner). The deploy prep (bullets 1+2) is complete and host-agnostic; the live deploy (bullet 3) cannot proceed until this build blocker is resolved separately.

Summary

Separate from p2-09 (which covers the build being clean): this objective covers choosing a public host and running the deploy. Currently the deploy script is ready (tools/deploy-guide.sh — modes build / serve / apricot / zip), but no public host has been committed for Early Access. The apricot mode ships dist/ to the LAN for preview; zip produces a handoff artifact that any external host can consume.

Acceptance

  • ✓ Public hosting target chosen and documented in this file — Cloudflare Pages (see ## Hosting decision, 2026-06-03).
  • ✓ Deploy script mode added for the chosen host — tools/deploy-guide.sh cloudflare-pages [--dry-run] (version read from package.json, not a CLI arg; --dry-run prints the upload plan without executing). Verified via dry-run 2026-06-03.
  • ✗ First deploy lands at the public URL; guide is reachable externally. BLOCKED — held at the approval gate (no external hosting account / live deploy without explicit user go), AND the guide build currently fails (see ## Build blocker). Live deploy cannot be attempted until both clear.
  • ✗ URL recorded in CHANGELOG + player-facing docs (Steam page / README / marketing site). Deferred to post-deploy (no real URL exists yet; this repo auto-commits, so a placeholder URL would land as drift).
  • tools/deploy-guide.sh exists with build/serve/apricot/zip modes — shipped by export-ci-dev 2026-04-17.

Depends on

  • p2-09 — guide must build cleanly (currently blocked on WASM rebuild + still-pending rollup fix).

Non-goals

  • CDN / caching policy beyond what the chosen host provides by default.
  • Versioned URL history (e.g. /v0.1.0/, /v0.1.1/).
  • Analytics integration.

Remaining work (2026-05-03)

Explicitly deferred per the 2026-04-17 user directive captured above — public-internet hosting is not on the critical path until the Early Access ship decision is made. Do NOT spend agent time on this objective unless the user re-prioritises. The bullets below are the activation list IF reactivated; until then they intentionally remain ✗.

Reactivation prerequisites

  1. User signal — explicit re-prioritisation (e.g. "ship the guide publicly" or an EA launch milestone landing). No agent should self-promote this objective.
  2. p2-09 unblock — guide build still needs WASM rebuild + the rollup fix called out in the Depends on block. Verify pnpm --filter guide build exits 0 on apricot before any deploy attempt.

1. Choose public hosting target

  • Bullet: Public hosting target chosen and documented in this file (options: GitHub Pages, Cloudflare Pages, Netlify, S3+CloudFront, self-hosted static).
  • Files to touch: this objective file — append a ## Hosting decision section recording the chosen host, rationale (cost / TLS / custom domain / CDN), and any DNS prerequisites.
  • Dependencies: user input (host preference + domain ownership).
  • Acceptance gate: section present with a single named host and a one-paragraph rationale; not multiple "we could use X or Y".
  • SOLID/DRY/SSoT: do not fork tools/deploy-guide.sh per host — extend in place (single deploy entry-point per Commandment 7).

2. Add deploy-script mode for chosen host

  • Bullet: Deploy script mode added for the chosen host (extend tools/deploy-guide.sh with e.g. deploy-guide.sh github-pages <version>).
  • Files to touch: tools/deploy-guide.sh — add a new mode case alongside existing build/serve/apricot/zip. Reuse the existing build step; only the upload differs.
  • Dependencies: bullet 1.
  • Acceptance gate: ./tools/deploy-guide.sh <host-mode> --dry-run (or equivalent) prints the upload plan without executing; live mode succeeds and bullet 3 verifies externally.
  • SOLID/DRY/SSoT: build artefact location is unchanged regardless of host (single dist root from pnpm build); host modes differ only in the upload tail. Do NOT duplicate the build invocation per host.

3. First public deploy lands

  • Bullet: First deploy lands at the public URL; guide is reachable externally.
  • Files to touch: none in repo; one-time live deploy run from a contributor host.
  • Dependencies: bullets 1+2; p2-09.
  • Acceptance gate: curl -fsSL https://<chosen-url>/ returns 200; key route (e.g. /tech-tree) loads and the WASM bundle initialises (browser console shows no MIME/CORS errors). Capture a screenshot to .project/screenshots/guide-deploy-<date>.png.
  • SOLID/DRY/SSoT: deploy must read version from package.json, not a hand-typed CLI arg.

4. Record URL in user-facing docs

  • Bullet: URL recorded in CHANGELOG + player-facing docs (Steam page / README / marketing site).
  • Files to touch: CHANGELOG.md, top-level README.md, and any Steam/marketing copy under .project/ (current locations TBD per content-owning agent).
  • Dependencies: bullet 3.
  • Acceptance gate: grep for the URL in each named file returns ≥1 hit; URL string is identical across files (no drift).
  • SOLID/DRY/SSoT: URL stored once as a substitution-variable (e.g. GUIDE_URL in a top-level .env.docs or vars.json) consumed by the docs build, not pasted N times. If the docs build doesn't support substitution today, that's a follow-up — accept the duplication for v1 but file the cleanup.

Out-of-scope reminder

Per user directive: do NOT pre-emptively choose a host, set up DNS, or sign up for hosting accounts. Hold the deferral.

True state — 2026-06-04 gap analysis

Verified: partial. Host chosen (Cloudflare Pages, rationale documented); tools/deploy-guide.sh cloudflare-pages --dry-run mode added + verified; held at the live-publish boundary (needs operator approval + a CF account). Guide BUILD now blocked on a missing WASM artifact (.local/build/wasm/magic_civ_physics.js, built by src/simulator/build-wasm.sh) — the audio.json blocker was resolved this session. Path forward: (1) run build-wasm.sh on a host with the toolchain to produce the WASM bundle; (2) pnpm --filter guide build must exit 0; (3) operator approves + authenticates wrangler; (4) live deploy. Blockers: WASM build artifact (build step), then operator publish-approval (external action). Demo gate: SEPARATE deliverable — the web guide is a companion, NOT the playable game demo. The game demo does not need it. Treat as its own track. Effort: M (WASM build + the publish is the operator's button).

  • "2026-06-23 godot-ui: p2-18 static review (guide deploy notes); no UI code change needed (blocker is data/audio.json missing per obj, outside guide fence); deploy script tools/deploy-guide.sh static + notes reviewed (ls tools/); host CF chosen; no new literals. Cites: read .project/objectives/p2-18-guide-public-deployment.md:67 blocker + 34 CF; ls public/games/age-of-dwarves/guide/ src/packages/guide/ . Based on list_dir public/games/age-of-dwarves/guide/src + grep guide package.json."