Files
galaxy-game/CLAUDE.md
T
Ilia Denisov 8565942392
Build · Site / build (push) Successful in 8s
Tests · Go / test (push) Successful in 2m22s
Tests · UI / test (push) Failing after 2m42s
feat(deploy): single-origin path-based deployment + project site
Serve the whole stack behind one host: site at /, game UI at /game/,
gateway REST at /api + /healthz, Connect at /rpc (prefix stripped by the
edge Caddy). The built artifact is domain-agnostic — the UI talks to the
gateway same-origin via relative URLs, so the same bundle runs under any
host with no rebuild and with CORS disabled.

- Rename the Connect proto service galaxy.gateway.v1.EdgeGateway ->
  edge.v1.Gateway; regenerate Go + TS; public path /rpc/edge.v1.Gateway.
- Move the game UI under base path /game (env BASE_PATH); make the
  manifest, service-worker scope, WASM loader, and all navigation
  base-aware via a withBase helper.
- Relative API + /rpc Connect prefix; Vite dev proxy mirrors the strip.
- Rewrite the edge Caddy (dev + prod) for path-based routing; empty CORS
  allow-lists (same-origin); single host.
- New VitePress project site (site/): i18n en/ru with switcher, LaTeX
  math, minimal monospace theme; built and served at /.
- dev-deploy compose/Makefile + CI (dev-deploy, prod-build, new
  site-build) build and seed the site; probes hit /, /game/, /healthz.
- Sync docs (ARCHITECTURE, gateway README/openapi, dev-deploy &
  local-dev READMEs, CLAUDE.md, ui/PLAN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:19:07 +02:00

9.9 KiB

Galaxy Game — Project Conventions

This repository hosts the Galaxy Game project.

Sources of truth

  • docs/ARCHITECTURE.md — global architecture, security model, cross-service contracts, and project-wide rules.
  • docs/FUNCTIONAL.md — per-domain user stories that describe what each user-visible operation does, with the exact gateway and backend logic for it. Starting point for any change request that touches behaviour.
  • docs/FUNCTIONAL_ru.md — Russian translation of docs/FUNCTIONAL.md, maintained as a convenience for the project owner. Not a source of truth — when the two files disagree, the English version wins. Every point edit applied to docs/FUNCTIONAL.md must also be mirrored into docs/FUNCTIONAL_ru.md in the same patch (translate the changed paragraphs only, do not re-translate the whole file). A full re-translation only happens on explicit owner request.
  • docs/TESTING.md — testing layers (unit / integration), the integration runbook, and the principles every test must follow (no-op observability for testcontainers, t.Fatal on infrastructure breakages, label-driven preclean). Read before adding tests or modifying the integration harness.
  • galaxy/<service>/README.md — service conventions, layout, configuration, and operations for an implemented or planned service.
  • galaxy/<service>/openapi.yaml and *.proto files — exact wire contracts for REST and gRPC surfaces.

Planning of service implementation and Implementing Plan

  • galaxy/<service>/PLAN.md — staged implementation plan for the service. May be already complete and resides for historical reasons.
  • galaxy/<service>/docs/ — live topic-based documentation that's deeper than what fits in README.md (per-feature design notes, protocol specs, runbooks). Not stage-by-stage history.

Branching and CI flow

Branches:

  • main — production-track. Direct pushes are disallowed; the only way in is a PR merge from development. A merge fires prod-build.yaml which packages the artifacts; production rollout is manual through deploy-prod.yaml.
  • development — long-lived dev integration branch. Every merge into it auto-deploys to the dev environment via dev-deploy.yaml (single origin https://galaxy.lan: site at /, game at /game/, gateway REST at /api).
  • feature/* — short-lived branches off development. Merged back via PR; only then do they reach the dev environment automatically.

Workflows in .gitea/workflows/:

File Trigger What it does
go-unit.yaml push + PR matching Go paths Fast Go unit tests.
ui-test.yaml push + PR matching ui/** Vitest + Playwright.
integration.yaml PR to development/main; push to development testcontainers integration suite.
dev-deploy.yaml push to development; workflow_dispatch on any ref Build images + (re)deploy to tools/dev-deploy/.
prod-build.yaml push to main Build prod images and docker save into artifacts.
deploy-prod.yaml workflow_dispatch Manual rollout (placeholder until prod host exists).

Deployment cadence

The long-lived dev environment (tools/dev-deploy/) is single-tenant: one live deployment, redeployed on every merge into development. While a PR is open the dev environment stays on whatever was last merged — pushes to feature/* only fire the test workflows (go-unit, ui-test, integration), not dev-deploy.yaml.

To preview an unmerged feature branch on the shared dev environment, trigger dev-deploy.yaml manually from the Gitea UI (Actions → Deploy · Dev → Run workflow) and pick the feature ref. The deploy is idempotent: the next merge into development simply overwrites whatever the manual dispatch left behind.

Per-stage CI gate

Every completed stage from any PLAN.md (per-service or ui/PLAN.md) must be exercised on gitea.lan before being declared done. The short version:

  1. Commit the stage changes on the feature branch.
  2. git push gitea … to publish the branch.
  3. Poll the latest run in the Gitea UI (or the API) until it leaves running. Inspect the log on failure.
  4. Only after every workflow that fired is success may the stage be marked done in the corresponding PLAN.md.

Decisions during stage implementation

Stages from PLAN.md produce decisions. Those decisions never live in a separate per-decision history file. Instead, every non-obvious decision is baked back into the live state in three places:

  1. The plan itself. Update the relevant stage's text, acceptance criteria, or targeted tests so it reflects what was decided. If earlier already-implemented stages need to follow the new agreement, correct their code, tests, and live docs in the same patch.
  2. Later, not-yet-implemented stages. When a decision affects later stages — scope, dependencies, deliverables, or tests — update those stages now, do not leave the future to re-derive them.
  3. Live documentation. Module README.md, project docs/ARCHITECTURE.md, docs/FUNCTIONAL.md (with its docs/FUNCTIONAL_ru.md mirror), the affected service openapi.yaml or *.proto, and any topic doc under galaxy/<service>/docs/ that the decision touches. README.md and ARCHITECTURE.md always describe current state, not the history of how it was reached.

Scope of PLAN.md changes

The existing codebase of galaxy/<service> may be modified or extended when a plan stage requires it. All such changes must be covered by new or updated tests and reflected in documentation when they affect documented behavior.

Migrations

Schema changes for backend go into a new 0000N_*.sql file under backend/internal/postgres/migrations/ with a monotonically increasing prefix. 00001_init.sql is the historical baseline and stays immutable; every subsequent change is its own additive migration with matching Up/Down sides. pressly/goose/v3 (embedded into the backend binary) applies pending migrations on startup, so the long-lived dev environment picks up schema deltas without a manual reset.

Before the first production deployment the migration chain may be squashed back into a single fresh 00001_init.sql for a clean slate; plan that work as an explicit task when it lands. See backend/internal/postgres/migrations/README.md for the local authoring conventions (file naming, transactional vs. non-transactional sections, backward-compatible deletes, rollback expectations).

Documentation discipline

  • Code and docs are kept in sync. If an implementation changes behavior described in a .md or .yaml file, update that file in the same patch.
  • If existing docs are incomplete or wrong for behavior you are already touching, fix them in the same patch.
  • Do not silently remove commitments from galaxy/<service>/README.md or galaxy/<service>/docs/*.md. When a rule changes, either update it in place with the new agreement, or move the section to a more appropriate doc with a reference kept.
  • Cross-module impact: if a new agreement requires changes in already-implemented modules, make those changes — code, tests, docs — in the same patch, and record the new rule in docs/ARCHITECTURE.md.

Documentation synchronisation

The same behaviour is described in several parallel sources: code, docs/ARCHITECTURE.md, docs/FUNCTIONAL.md (with its Russian mirror docs/FUNCTIONAL_ru.md), the affected service README.md, the relevant openapi.yaml or *.proto, and the topic-based docs under galaxy/<service>/docs/. They must never disagree.

  • Any patch that changes user-visible behaviour, an API contract, or a cross-service flow updates every affected source in the same change set — never one source in this patch and another later.
  • Before declaring a change complete, read the relevant sections of docs/ARCHITECTURE.md, docs/FUNCTIONAL.md, the affected service README, the relevant openapi.yaml or *.proto, and the implementing code; confirm they describe the same behaviour.
  • When two sources disagree about existing behaviour, do not pick one silently. Decide which one is authoritative, fix the contradiction in the same patch, and call out the change in the response. If the resolution is non-obvious, escalate to the user before proceeding.
  • When touching code, also re-read inline package and Go Doc Comments in the affected packages and update them when they no longer match the code.
  • When docs/FUNCTIONAL.md changes, mirror the same change into docs/FUNCTIONAL_ru.md (translate only the touched paragraphs). Skipping the mirror is treated as an incomplete patch.

Code compactness

  • Prefer compact code over speculative universality. Three similar occurrences are not yet a pattern — wait for the third real caller before extracting an abstraction.
  • Do not add seams, hooks, or configuration knobs for hypothetical future requirements. If the next stage of PLAN.md will need something, the next stage will add it.
  • A bug fix does not need surrounding cleanup; a one-shot operation does not need a helper function; a single concrete value does not need a parameter.
  • When the plan can be satisfied by reusing an existing function or type, do that instead of introducing a new one.
  • This rule is about scope, not laziness — well-named identifiers, precise types, and full test coverage stay non-negotiable.

Dependencies

  • Before adding a new module, check its upstream repository for the latest stable version and use that.
  • When a well-maintained library clearly outperforms stdlib for a concrete need, do not adopt it silently — propose a short list of 1+ candidates for the user to pick. Default remains stdlib.

Language

  • All code, comments, identifiers, commit messages, docs, and filenames are written in English.
  • User-facing chat responses follow the Russian-translation rule from the user-level CLAUDE.md.