Files
SpaceWanderer/ORCHESTRATOR.md
T
2026-09-28 16:13:35 +00:00

11 KiB

ORCHESTRATOR.md — Operating Manual for Successor Agents

You are taking over the autonomous development loop on SpaceWanderer (browser game: dungeon-crawler-type, very replayable — turn-based space adventure where you build & expand your ship/base from found parts and resources, survive random encounters, manage a crew of flawed humans with good/bad traits; server-authoritative turn engine). Gameplay reference: Out There (Android), but more building-focused. Single player only, by design — no auth/accounts/multiplayer, ever, unless the user changes that decision. This document captures operational knowledge that is NOT written down in AGENTS.md. Read AGENTS.md first — it defines the mandatory change process and this file complements it.

Credentials are deliberately NOT stored in this file — see §1 for where they live. Never paste token values into any tracked file.

1. Project identity

  • Repo: Gitea at https://git.kkhost.pl → org OpenCode, repo SpaceWanderer (full name OpenCode/SpaceWanderer). Every Gitea API/MCP call uses owner=OpenCode, repo=SpaceWanderer.
  • Local workdir + prod deploy dir: /home/krzyw/projects/sandbox/space-wanderer (git checkout; docker compose up -d --build deploys from here).
  • Prod URL (planned, not deployed yet): https://space-wanderer.dev.kkhost.pl (Traefik → docker-compose stack on this host); first deploy happens after the initial scaffold. Once live, GET /api/version reports the running version.
  • Gitea API access for git.kkhost.pl: prefer the gitea MCP tools — they are already configured with a token. For raw REST/curl, read the current token from the local .env file at repo root (variable GITEA_TOKEN; .env is gitignored and must never be committed): source .env && curl -s -H "Authorization: token $GITEA_TOKEN" https://git.kkhost.pl/api/v1/user. (Incident note, Sept 2026: an early draft of the Flight-Ops doc committed a live token to the repo; that commit was purged from all refs and the token has since been rotated — never repeat this.)
  • Current user: kamilkrzywda (id 1).
  • Current state: initial scaffold (v0.1.0) is done on branch scaffold-base off master — base stack copied/adapted from ../flight, scificn-ui frontend integrated, CI/e2e pipeline in place; not merged/deployed yet (first deploy happens after the scaffold lands). From here the loop below applies as-is.

2. The loop (per issue)

  1. Small incremental change on a feature branch off master; connected issues may share one branch/PR with a single version bump.
  2. Bump package.json version + add CHANGELOG.md entry at top: ## v<version> — YYYY-MM-DD HH:MM then bullet points of what changed (docs-only changes get patch bumps too).
  3. Local verification ONLY (there are NO local e2e runs): npx tsc --noEmit, npx vitest run (the whole unit suite must pass — the test count grows over time, so compare against the previous green run, never a hardcoded number), npx playwright test --list (all specs compile and list).
  4. Push → CI (~7 min) → auto-merge on green (PRs have auto-merge configured; no manual merge needed — verify via GET /pulls/{n} that state=closed and merged_at is set). Note: if a PR was created via API rather than create-on-push, double-check auto-merge actually fires.
  5. Tag the version on master: git tag v<version> origin/master && git push origin v<version> (after git fetch origin master).
  6. Deploy prod in a PTY (long task): cd /home/krzyw/projects/sandbox/space-wanderer && git checkout master && git reset --hard origin/master && docker compose up -d --build; then verify curl https://space-wanderer.dev.kkhost.pl/api/version returns the new version and the root page is HTTP 200.
  7. Close each issue with a comment: what changed, version, PR number, merge commit SHA, deployed URL.
  8. Repeat for the next open issue: GET /issues?state=open&type=issues — note Gitea lists pull requests in this endpoint too; filter out entries with a pull_request key.

3. Gitea API + MCP quirks (learned the hard way)

  • The gitea MCP tools work for issues/PRs/files/tags, and Actions endpoints work IF you pass owner=OpenCode, repo=SpaceWanderer. A wrong owner/repo makes EVERY call return {"message":"not found","url":"https://git.kkhost.pl/api/swagger"} — that swagger URL inside a 404 is the signature of "wrong path", not "Gitea is down".
  • MCP merge endpoint 405s → don't bother; auto-merge handles merging. Manual fallback if ever needed: POST /repos/OpenCode/SpaceWanderer/pulls/{n}/merge with body {"do":"merge","merge_style":"merge"}.
  • Gitea REST shapes (NOT GitHub's): list runs → {workflow_runs:[...]}; run jobs → /actions/runs/{id}/jobs returns {"jobs":[...]}; single job log via MCP get_job_log_preview (param tail_lines) or download_job_log to a file, then grep.
  • Failure anatomy in CI logs: the Playwright summary at the end lists "N failed" + test titles; each failure block has a code frame with line numbers plus an "Error Context:" path. The e2e step is the only one that can fail flakily; unit failures are real regressions.
  • CI watcher pattern that works: run_background_process (notify=true) running a loop that polls GET /actions/runs/{id} every 60 s and exits when status=completed, printing status+conclusion each tick. Don't poll in the foreground; don't sleep-loop on pty_read.

4. CI facts (.gitea/workflows/ci.yml + docker-compose.ci.yml)

  • Mirrors Flight-Ops: single job "build + unit + e2e" on runner gitea-runner (docker-in-docker). Fresh DB per run (volume removed); compose project space-wanderer-ci; app container on a static IP → E2E_BASE_URL=http://172.39.0.10:3000 (network space-wanderer-ci-e2e, subnet 172.39.0.0/24 — same mechanism as Flight-Ops, different network name so both can coexist on the shared daemon); PW_REMOTE_BROWSER=0 (CI runs local chromium — the shared remote browser server at 192.168.1.4 cannot reach a stack inside dind); E2E_COMPOSE_ARGS="-f docker-compose.ci.yml -p space-wanderer-ci" is read by e2e/state.spec.ts for its mid-run docker compose restart.
  • Flaky uplink → retry loops in ci.yml; an occasional "flaky" test (failed once, passed on retry) does NOT fail the run.
  • Branch protection requires status check CI / build + unit + e2e (pull request) — PRs cannot merge until green; CI never deploys to production.

5. E2E conventions (e2e/*.spec.ts) — read before touching tests

  • workers=1, serial in playwright.config.ts.
  • expect.poll callbacks must be total — a throw inside fails the test instantly instead of retrying. Guard/filter everything you dereference.
  • Deterministic dev hooks: window.__* exposed in dev builds + /api/test/* endpoints behind an env flag deliberately NOT set in prod.
  • Turn-based game → actions resolve synchronously server-side, so tests are inherently more deterministic than Flight-Ops's real-time sim; no pause-sim pattern needed.

6. Game-specific gotchas

  • None recorded yet — this is where hard-won knowledge accumulates as it is discovered. (Flight-Ops's equivalent sections documented Playwright evaluate serialization dropping prototype getters and MapLibre tile quantization — expect similar "the tool lies to you" surprises with whatever rendering approach lands.)

7. App architecture quick map

  • Scaffolded (v0.1.0) — stack proof only, no game systems yet; the rest of this section is what's built + what lands next.
  • Next.js 16 App Router + React 19 + TypeScript strict, bun package manager (bun.lock), Node 22 in CI/Docker.
  • Frontend component layer: scificn-ui v0.1.0 (https://scificn.dev) — copy-paste model on Radix + Tailwind v4; components live in components/ui/ (Button, Panel, Badge, Card, Alert, Tabs, StatCard, Terminal, StatusGrid, ProgressRing, BarChart, LineChart), theme tokens in app/globals.css. The app's interface base is the scificn-ui Mission Control showcase layout (https://www.scificn.dev/showcase/mission-control) rebuilt as components/mission-control.tsx and rendered from app/page.tsx, with placeholder ship content — no game logic yet. Deliberate deviation from upstream: --text-muted brightened #4A4A40 → #6B6B5C (+12pt lightness) for readability on the near-black background. Upstream repo has no LICENSE file — user-approved risk, keep components minimal and re-verify licensing before any redistribution of this codebase.
  • SQLite via Drizzle ORM + sql.js (WASM, kept external in next.config.ts) at db/space-wanderer.db volume-mounted; schema designed to switch to Postgres later. Schema is EMPTY at scaffold stage — first game tables land with the turn engine.
  • Vitest unit tests colocated (**/*.test.ts); Playwright e2e in e2e/, CI-only.
  • Server-authoritative TURN engine in lib/ — unlike Flight-Ops there is NO tick loop or sim clock: state advances only when a player action resolves a turn, and every action endpoint returns the full post-action snapshot which the client applies immediately (polling optional/later for multiplayer).
  • Plain React hooks for client state, no external store. NO auth/accounts/multiplayer by design (single player only) — there is no users/sessions table and no session headers; a "session" means the local single-player save slot in the DB, not an identity system. Do not copy Flight-Ops's lib/auth.ts / SignInScreen / admin patterns.
  • Dev/e2e hooks: window.__sw* in dev builds + /api/test/* behind env flag not set in prod.
  • Core game systems to build: turn engine, random encounter resolution, ship module grid (build/expand from found parts), resource management, crew with good/bad traits.

8. Finding current state (this file keeps no snapshots)

This manual is meant to stay valid long-term, so it deliberately contains NO version or issue snapshots — they rot. To orient yourself when you take over: git log --oneline -10 + CHANGELOG.md for what has shipped; GET /issues?state=open&type=issues (filter out entries with a pull_request key) for what is left to do; PLAN.md's "Later" section for the roadmap backlog. Note: PLAN.md does not exist yet — it will be the game design doc.

9. Standing user instruction & etiquette

  • The user's standing order for this project: work through the issue list autonomously — implement → PR → CI green → merge → tag → deploy prod → close with comment → next — until told to stop.
  • Small incremental changes; each part fully tested (unit + e2e) before moving on.
  • Long terminal tasks → pty_* tools (deploys, builds). Background watchers → run_background_process with notify=true.
  • Never push directly to master; never deploy from a feature branch; CI never touches prod — production deploys are manual only (docker compose up -d --build after tagging).
  • Never commit credentials. Tokens/secrets live in the MCP server config or with the user, never in tracked files (see incident note in §1).