From 0944ef66f0e5d8b98ed7fb15ea487aa7147ba3d6 Mon Sep 17 00:00:00 2001 From: Kamil Krzywda Date: Mon, 28 Sep 2026 14:08:49 +0000 Subject: [PATCH] docs: initial workflow rules (AGENTS.md) and successor-agent manual (ORCHESTRATOR.md) --- AGENTS.md | 21 ++++++++++++++++ ORCHESTRATOR.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 AGENTS.md create mode 100644 ORCHESTRATOR.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..951bd15 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,21 @@ +# SpaceWanderer — Workflow Rules + +## Change process (mandatory) +1. **Small, incremental changes** — work is done one change at a time, in smaller parts; each part must be tested thoroughly with unit and e2e tests covering it before moving on. Connected issues/tasks that reference the same part of code MAY be done together on one branch/PR with a single version bump (e.g. two UI tweaks to the same panel). +2. **Feature branch + PR** — every change lands on a feature branch off `master` (no direct pushes to master). The branch carries the commits below; open a pull request to `master`. +3. **Changelog entry** — every such change is listed in `CHANGELOG.md` with date, time and version (format: `## v — YYYY-MM-DD HH:MM`, then bullet points of what changed). +4. **Version bump** — the version is updated in `package.json`. +5. **CI must pass** — enforced by branch protection on `master` (required status check `CI / build + unit + e2e (pull request)`): the PR cannot be merged until CI is green. Merge via auto-merge (`POST /repos/{owner}/{repo}/pulls/{n}/merge` with body `{"do":"merge","merge_style":"merge","merge_when_checks_succeed":true}`) so it merges automatically once CI passes. CI never deploys to production; its e2e runs against an isolated stack inside dind. +6. **Tag + manual deploy after merge** — once merged, tag the version on `master`. Production (`https://space-wanderer.dev.kkhost.pl`) is updated manually afterwards: `docker compose up -d --build` on the host. Not deployed yet — the first deploy happens after the initial scaffold. + +## E2E tests (mandatory) +1. **CI-only** — e2e runs ONLY in CI, against an isolated docker stack inside dind built from `docker-compose.ci.yml` (project name `space-wanderer-ci`, app container on a static IP; see `.gitea/workflows/ci.yml`). There are NO local e2e runs at all: locally you verify with `npx tsc --noEmit` + `npx vitest run` + `npx playwright test --list` (typecheck, unit suite, specs compile and list), and the full e2e suite is verified by CI on every PR. **CI never deploys to or modifies production.** +2. **CI env vars** (`playwright.config.ts`, all optional) — `E2E_BASE_URL` (app under test; CI sets it to the in-network app host, so the config's default of the live Traefik instance at `https://space-wanderer.dev.kkhost.pl` is never used), `E2E_DEPLOY_CMD` (runs as Playwright webServer: deploys the stack and waits for readiness before tests start; CI uses build + `up -d` + `logs -f`), `PW_REMOTE_BROWSER=0` (CI runs local chromium because the shared remote browser server at 192.168.1.4 cannot reach a stack inside dind — when unset, the config defaults to that remote server and to the live instance as baseURL), and `E2E_COMPOSE_ARGS`, read by `e2e/state.spec.ts` for its mid-run `docker compose restart` (CI points it at `-f docker-compose.ci.yml -p space-wanderer-ci`). + +## API +1. **Live base URL** — `https://space-wanderer.dev.kkhost.pl` (the docker-compose build behind Traefik; not deployed yet). +2. **Machine-readable spec** — OpenAPI 3.x at `/api/openapi`; current app version at `/api/version`. +3. **Spec lives in code** — generated from `lib/openapi.ts`, served by `app/api/openapi/route.ts`; must be updated whenever an endpoint changes. + +## Other +1. For longer terminal tasks you should use pty_* tools. diff --git a/ORCHESTRATOR.md b/ORCHESTRATOR.md new file mode 100644 index 0000000..49613ef --- /dev/null +++ b/ORCHESTRATOR.md @@ -0,0 +1,64 @@ +# ORCHESTRATOR.md — Operating Manual for Successor Agents + +You are taking over the autonomous development loop on **SpaceWanderer** (browser game: turn-based space adventure — 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. 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:** as of writing, the repo is empty (fresh `git init`, no commits). Step zero for any successor: scaffold from `../flight` — copy architecture + `.gitea/workflows/ci.yml` + `docker-compose*.yml` patterns and adapt names — then this manual's loop applies. + +## 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 — 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 origin/master && git push origin v` (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://:3000` (exact IP TBD when scaffolding — copy `../flight/.gitea/workflows/ci.yml` + `docker-compose.ci.yml` and adapt); `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 +- **TARGET ARCHITECTURE — code not written yet; scaffold from `../flight`.** +- Next.js 16 App Router + React 19 + TypeScript strict, bun package manager (bun.lock), Node 22 in CI/Docker. +- 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. +- 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; session via stored id + custom header. +- 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).