8565942392
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>
203 lines
9.9 KiB
Markdown
203 lines
9.9 KiB
Markdown
# 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`.
|