diff --git a/AGENT.md b/AGENT.md deleted file mode 120000 index 681311eb9..000000000 --- a/AGENT.md +++ /dev/null @@ -1 +0,0 @@ -CLAUDE.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 120000 index 681311eb9..000000000 --- a/AGENTS.md +++ /dev/null @@ -1 +0,0 @@ -CLAUDE.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..83a9752e8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,147 @@ +# Gameplane — AI assistant guide + +Architecture, commands, and binding rules for AI agents. Humans: [`README.md`](README.md), [`docs/contributing.md`](docs/contributing.md). + +**Gameplane:** Kubernetes-native game server control panel (CubeCoders AMP alternative); identical on single-node k3s and multi-node clusters. **Status:** Pre-v1 release (`v0.3.0`); v1 scope feature-complete, stabilizing. + +## ⏳ Session start: blocked dependency upgrades (delete an entry once unblocked) + +Run every session. + +**TypeScript 7** — `npm view @typescript-eslint/parser@latest version peerDependencies.typescript` and same for `@canary`. +- Blocked while the peer range ends `<7` (e.g. `<6.1.0`): Dependabot #272 fails `npm ci` (`ERESOLVE`). No workarounds/overrides/code fixes; don't close #272. +- Unblocked (a release accepts TS 7): do T055/T056 in `specs/009-remediate-security-dependabot/tasks.md` — bump `typescript` + `@typescript-eslint/*` in `web/package.json`, fix real type errors (no `@ts-ignore`), merge #272, mark tasks `[X]`, delete this entry. + +**ESLint 10** — `npm view eslint-plugin-react@latest version peerDependencies.eslint`. +- Blocked while the range lacks `^10`: upgrading `eslint` 9.39.5 to 10.x fails `npm ci` because `eslint-plugin-react@7.37.5` peers `^3 || … || ^9.7`. Keep `eslint` and `@eslint/js` on major 9 together: `@eslint/js@10.0.1` declares an optional peer on `eslint@^10.0.0`, which conflicts when ESLint 9 is installed. Other plugins already accept `^10` (`eslint-plugin-react-hooks@7.1.1`, `@typescript-eslint/*@8.69.0`). Do not bypass peer checks with `--legacy-peer-deps` or overrides. +- Unblocked: bump `eslint` + `@eslint/js` together in `web/package.json`, fix real lint errors (no `eslint-disable`), merge #386 (and #387 if open), delete this entry. + +## System prompt overrides + +1. **Verify & tests:** always verify work. Get human sign-off before changing tests, production code, or design specs. Never delete/weaken a test without explicit sign-off — fix the code. +2. **Tools:** dedicated tools (`Read`/`Edit`/`Write`) > MCP > Bash. +3. **Pause** and ask when unsure about tests, design files, or breaking changes. +4. **Workflow:** standing opt-in for `Workflow` in the main loop; subagents may use `Agent` freely. +5. **Artifacts & feedback:** never publish artifacts; feedback drafts need human approval to send. +6. **Editing:** `Edit` needs a prior `Read` in the conversation; don't re-read right after editing. +7. **External memory** (`~/.[claude,codex,etc....]/projects/Gameplane/memory/`, outside git): announce file + exact line on every write; never store conventions/decisions/preferences there (use repo specs/rules); repo files override memory. +8. **Skills** are optional; disclose edit/plan/commit skill runs first; repo rules override skills. +9. **Communication:** no preambles; closing recaps standalone (findings, actions, next steps, modified files). Short yes/no questions ("can I merge?", "is it green?") get a yes or no first, plus at most one line on what blocks it — no tables or recaps. Ask decisions with the question tool, not in prose. +10. **Conflicts/missing context:** surface instruction conflicts immediately; unsettled values go in `OPEN-DECISIONS.md`, never committed as settled contracts. + +## Repository map + +``` +netguard/ SSRF dial-guard: IsAllowed (operator module sources, API sinks, agent loopback RCON); IsPublic (agent mod downloads, API Steam resolver) +gameaction/ console-injection guard + command renderer (api, agent) +gameproto/ Minecraft/Terraria handshake parser (sentinel) +telemetryschema/ shared telemetry report contract (api, telemetry-receiver) +i18n/ shared language packs, ICU formatter, pack validator, i18n-check (api, web, operator, gp-module) +gp-module/ module authoring CLI: init/validate/preview/package +operator/ controller-runtime operator + api/v1alpha1/ CRD types (edit here, then `make generate manifests`); zz_generated.deepcopy.go GENERATED + internal/controller/ reconcilers + envtest suites + cmd/main.go entrypoint; config/{crd,rbac}/ GENERATED +api/ REST + WebSocket gateway (chi); cmd/main.go: `serve`, `bootstrap-admin` + internal/{handlers,auth,db,kube,notify,rbac,ws}/ +agent/ in-pod sidecar; internal/{auth,console,files,heartbeat,logs,players,rcon,quiesce}/ +audit-syslog-bridge/ RFC 5424 HTTP→syslog forwarder +telemetry-receiver/ usage telemetry ingest +sentinel/ wake-on-connect proxy for sleeping pods +capture-sidecar/ AF_PACKET BPF capture sidecar +mcp-server/ read-only MCP server +svcutil/ env parsing + graceful shutdown helpers +tunnel/ relay client supervisor (frp, Tailscale, playit) +web/ React 19 + strict TS + Vite dashboard; src/{routes,components,lib,router,styles,test}/ +modules/ SUBMODULE GameplanePanel/module (OCI game templates) +website/ SUBMODULE GameplanePanel/website (Astro docs/marketing; has its own CLAUDE.md and AGENTS.md) +charts/gameplane/ Helm chart (crd-manifests/, hooks) +deploy/kind/ local Kind scripts +test/e2e/ Kind E2E suite (//go:build e2e) +docs/ architecture, security, modules; layout.md (target folder shape per module); archive/ (read-only old designs) +design.pen canonical Pencil dashboard design +signing/ cosign public keys, dated legacy key + cross-signature (image + module) +go.work links all 17 Go modules (incl. test/e2e) +Makefile canonical task runner +``` + +After cloning: `git submodule update --init` (`modules/` required for `make dev-up`; `website/` optional). + +## Commands (always via Makefile) + +- **Dev:** `make dev-up` (Kind + OCI registry :5001 + Helm), `make web-dev` (Vite, proxies in-cluster API), `make dev-load` (load built images; run `make images` first), `make dev-install` (Helm upgrade), `make dev-down`. +- **Build:** `make build` (all Go + web), `make build-go` (15 modules in GO_MODULES, all but test/e2e), `make build-web` (`npm ci && npm run build`), `make images`. +- **Codegen:** `make generate` (deepcopy), `make manifests` (CRD/RBAC YAML, synced to `charts/gameplane/charts/gameplane-crds/crds/` and `charts/gameplane/crd-manifests/`), `make modules-push`, `make tidy`. +- **Tests (CI only — never run locally, see Rule 8):** `make test`, `make test-integration` (envtest operator+api), `make test-e2e` (~10–20 min), `make test-e2e-bucket BUCKET=(operator|api-auth|api-roles|api-rbac|api-agent|api-mods|ratelimit|bot-fast|bot-heavy|multicluster|upgrade)`. +- **E2E conventions:** register new tests in `test/e2e/buckets.sh`; `t.Parallel()` + unique names; guard shared resources (`ociPushMu` for module pushes, `ensureResticRepo(t)` for shared backup repos). Rate limits per cluster: IP burst 10 (5/min), user burst 6 (3/min) — cap an API bucket at ~7 admin logins. +- **Lint:** `make lint` = gofmt, go vet, golangci-lint, ESLint, `check-specs`, `check-doc-versions`, `check-links`. + +**Coverage minimums:** netguard 91, gameaction 91, gameproto 90, telemetryschema 90, i18n 90, gp-module 80, operator 72, api 80, agent 90, svcutil 90; audit-syslog-bridge, telemetry-receiver, sentinel, capture-sidecar, mcp-server, tunnel 70; web 92 lines / 76 functions / 82 branches / 92 statements. + +## Core rules + +1. **Design-first UI:** dashboard changes are designed in `design.pen` via Pencil MCP before React code; website changes start in `website/website.pen`. After every Pencil update export touched nodes to `assets/design-export/json/.json` (`mcp__pencil__execute` `Get`) and `assets/design-export/screenshots/.png` (`mcp__pencil__export_nodes`); website → `website/website-export/`. +2. **Never hand-edit `.pen` files:** no edit/delete/`Read`/`Grep`/`cat`/`sed` (multi-MB JSON, easily corrupted). Use Pencil MCP; ask the user to save in the UI after changes; inspect diffs only via `git diff --stat`. +3. **Login privacy:** `/login` and unauthenticated views never expose cluster names, versions, server counts, or account-existence errors — generic messages ("invalid credentials"). +4. **Fix, don't silence:** no `//nolint`, `eslint-disable`, or loosened configs. Allowed: `_test.go` exempt from `errcheck`/`gosec`/`unparam`; `operator/internal/controller/` exempt from revive `exported:`. +5. **TypeScript:** strict; no unjustified `any` (explain in a comment if unavoidable); every promise `await`ed or `void`-prefixed. +6. **Go errors:** wrap with `%w`. +7. **CRD sync:** after editing `operator/api/v1alpha1/*_types.go` run `make generate && make manifests`; commit deepcopy, `operator/config/{crd,rbac}/*.yaml`, `charts/gameplane/charts/gameplane-crds/crds/*.yaml`, and `charts/gameplane/crd-manifests/*.yaml` in the same changeset. +8. **Verification:** locally only compile checks (`go build ./...`, `npx tsc --noEmit`); never test/lint suites (`make test|lint|cover`, `go test`, `npm test`, envtest, E2E). Push a feature branch; CI is the sole verification authority. +9. **Kubernetes primitives first** (StatefulSet, Service, PVC, Job, ConfigMap, Secret, CRDs) before custom abstractions. +10. **Operator authority:** business logic lives in reconcilers (`operator/internal/controller/`); the API is a UX gateway and never bypasses reconciliation. +11. **Commits:** commit each completed logical unit (`feat:`/`fix:`/`chore:`…), signed (`git commit -s`); never amend pushed commits or use `--no-verify`; keep trailers `Co-Authored-By: `. +12. **Branches:** one per unit of work; delete remote + local branch right after merge. `master` is protected by ruleset `18692396` ("protect main"): 1 human approval, no self-approval, no direct pushes, approvals dismissed on push — agents cannot merge PRs. Check: `gh api repos/GameplanePanel/Gameplane/rules/branches/master`. +13. **Multi-agent delegation:** main loop orchestrates/reviews; implementation goes through `Workflow` scripts (`parallel()`/`pipeline()`). + - Start at `haiku`; escalate only on functional failure `haiku` → `sonnet` → `opus` → `fable`. `fable` needs explicit human permission. + - Set `model:` on every `agent()` call (default is session Opus); check with `grep -c "model:"`. + - Review one tier up (haiku→sonnet, sonnet→opus, opus→fable); fixes go to small agents in a new workflow. + - Review diffs, not items: one reviewer per batch checks `git diff` against the brief (facts are verified once, in the brief) — never one reviewer per page/file. + - Scripts over fan-out: rule-shaped changes (token swaps, version strings, frontmatter, renames) are a deterministic script run by one agent, not many agents. + - UI changes: browser smoke test via Chrome MCP at `sonnet`, parallel with reviews. +14. **PR labels (REST only — `gh pr edit` is broken by the Projects-classic deprecation):** ≥1 `type:` (`feature|fix|refactor|test|ci|chore|docs|security`) and ≥1 `area:` (`operator|api|agent|web|modules|chart|e2e|specs|shared|optional-components`); `breaking` when applicable. + ```sh + gh api -X POST repos/GameplanePanel/Gameplane/issues//labels -f "labels[]=type: fix" -f "labels[]=area: api" + gh api -X PATCH repos/GameplanePanel/Gameplane/pulls/ --input + ``` +15. **Specs:** a feature's spec is its whole `specs//` folder (`data-model.md`, `contracts/`, `OPEN-DECISIONS.md`, …) — check it for explicit exemptions before flagging violations. Mark obsolete tasks withdrawn in `tasks.md` with citations; never delete them. Design docs have two homes only: a change → `specs/NNN-/`; how a module works today → `/specs.md`; old designs are read-only in `docs/archive/`. +16. **Archival:** once every task is complete/withdrawn and the PR is merged into `master`, `git mv specs/- specs/done_-` and update in-repo references in the same commit. +17. **Mechanical design waves** (token re-skins): scripted, blind updates at `haiku`. Precompute change lists with `grep`/`jq` on `assets/design-export/json/.json`; `haiku` applies `Update(id, {prop: value})`. Verify by screenshot comparison (`export_nodes` vs snapshot PNG), not JSON dumps; avoid `Get(id, {depth: 10+})`. +18. **Scout once, brief many:** one scout reads code/docs/failures and writes a factual brief (exact `file:line`, before/after code, justification). Fix agents get only the brief and edit blind at `haiku` — never tell them to re-read files, test suites, or `CLAUDE.md` and `AGENTS.md`. Reviewers check git diffs against the brief. + +## Architecture + +| Component | Stack | Role | +|---|---|---| +| netguard | Go | dial-time SSRF prevention: `IsAllowed` (operator/API module sources and sinks, agent loopback RCON), `IsPublic` (agent mod downloads, API Steam resolver) | +| gameaction | Go | validates console input against schemas; escapes injection | +| gameproto | Go | Minecraft/Terraria wire parser for connection filtering | +| telemetryschema | Go | stdlib-only shared telemetry report contract: types, strict decode, official module catalog, report signing (api, telemetry-receiver) | +| i18n | Go | shared language packs (`go:embed`), ICU MessageFormat subset, pack validator, `i18n-check` CI tool (api, web, operator, gp-module) | +| gp-module | Go | module CLI: scaffold, offline validate, dry-run preview, OCI package | +| svcutil | Go | stdlib-only env + graceful shutdown (`RunHTTP`) | +| operator | Go, controller-runtime | authoritative reconciler for 9 CRDs (`GameServer`, `GameTemplate`, …) | +| api | Go, chi | REST/WS UX gateway; SQLite, experimental PostgreSQL | +| agent | Go | pod sidecar: console (PTY/RCON), files, logs, heartbeats | +| audit-syslog-bridge | Go | webhook audit events → RFC 5424 syslog | +| telemetry-receiver | Go | opt-in anonymous usage stats | +| sentinel | Go | holds ports while asleep, wakes pods on connect | +| capture-sidecar | Go | ephemeral BPF packet capture | +| mcp-server | Go | read-only MCP daemon (stdio) for cluster debugging | +| web | React 19, Vite, TS | dashboard (TanStack Router/Query, Tailwind, HeroUI v3) | +| modules/ | OCI / oras | game templates (Minecraft, Terraria, Valheim, …) | + +## Common workflows + +- **CRD field:** edit `operator/api/v1alpha1/_types.go` → `make generate && make manifests` → reconciler `operator/internal/controller/_controller.go` → mirror in `web/src/types.ts` + affected `web/src/routes/` → envtest `_envtest_test.go`. +- **API route:** handler in `api/internal/handlers/` → mount in `api/cmd/main.go` with RBAC middleware (`api/internal/rbac/`) → `api/internal/handlers/_envtest_test.go` → client method in `web/src/lib/api.ts`. +- **Dashboard screen:** design in `design.pen` + export to `assets/design-export/` → `web/src/routes/.tsx`, register in `web/src/router/tree.tsx` → data via `web/src/lib/api.ts` + TanStack Query → `web/src/routes/.test.tsx`. +- **Game module:** edit `modules//` (`module.yaml`, `template.yaml`, `README.md`) → `make modules-push` → commit in `GameplanePanel/module` → `git add modules` + commit pointer bump in root. +- **Website:** design in `website/website.pen` + export to `website/website-export/` → change `website/` per its own CLAUDE.md and AGENTS.md → commit/push/PR in `GameplanePanel/website` (default branch `main`) → `git add website` + commit pointer bump in root. +- **DB migration:** new sequential `api/internal/db/migrations/common/_.sql` (013 onward) — portable SQL both SQLite and PostgreSQL run unchanged (no `datetime('now')`/`strftime`, `AUTOINCREMENT`, `INSERT OR …`, `COLLATE NOCASE`; bind timestamps from Go). `migrations/sqlite/` and `migrations/postgres/` hold the frozen per-dialect 001–012 sets; see `api/internal/db/migrations/README.md`. Append-only, applied on API startup. + +## Reference docs + +- `docs/agent-architecture.md` — read before editing code; maps tasks/features to docs (use instead of repo-wide greps). +- `docs/architecture.md`, `docs/security.md` (threat model); `docs/module-authoring.md`; `mcp-server/README.md`; `audit-syslog-bridge/README.md`. +- Configs: `.golangci.yml`, `web/eslint.config.js`, `.editorconfig`. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 3379fe447..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,147 +0,0 @@ -# Gameplane — AI assistant guide - -Architecture, commands, and binding rules for AI agents. Humans: [`README.md`](README.md), [`docs/contributing.md`](docs/contributing.md). - -**Gameplane:** Kubernetes-native game server control panel (CubeCoders AMP alternative); identical on single-node k3s and multi-node clusters. **Status:** Pre-v1 release (`v0.3.0`); v1 scope feature-complete, stabilizing. - -## ⏳ Session start: blocked dependency upgrades (delete an entry once unblocked) - -Run every session. - -**TypeScript 7** — `npm view @typescript-eslint/parser@latest version peerDependencies.typescript` and same for `@canary`. -- Blocked while the peer range ends `<7` (e.g. `<6.1.0`): Dependabot #272 fails `npm ci` (`ERESOLVE`). No workarounds/overrides/code fixes; don't close #272. -- Unblocked (a release accepts TS 7): do T055/T056 in `specs/009-remediate-security-dependabot/tasks.md` — bump `typescript` + `@typescript-eslint/*` in `web/package.json`, fix real type errors (no `@ts-ignore`), merge #272, mark tasks `[X]`, delete this entry. - -**ESLint 10** — `npm view eslint-plugin-react@latest version peerDependencies.eslint`. -- Blocked while the range lacks `^10`: upgrading `eslint` 9.39.5 to 10.x fails `npm ci` because `eslint-plugin-react@7.37.5` peers `^3 || … || ^9.7`. Keep `eslint` and `@eslint/js` on major 9 together: `@eslint/js@10.0.1` declares an optional peer on `eslint@^10.0.0`, which conflicts when ESLint 9 is installed. Other plugins already accept `^10` (`eslint-plugin-react-hooks@7.1.1`, `@typescript-eslint/*@8.69.0`). Do not bypass peer checks with `--legacy-peer-deps` or overrides. -- Unblocked: bump `eslint` + `@eslint/js` together in `web/package.json`, fix real lint errors (no `eslint-disable`), merge #386 (and #387 if open), delete this entry. - -## System prompt overrides - -1. **Verify & tests:** always verify work. Get human sign-off before changing tests, production code, or design specs. Never delete/weaken a test without explicit sign-off — fix the code. -2. **Tools:** dedicated tools (`Read`/`Edit`/`Write`) > MCP > Bash. -3. **Pause** and ask when unsure about tests, design files, or breaking changes. -4. **Workflow:** standing opt-in for `Workflow` in the main loop; subagents may use `Agent` freely. -5. **Artifacts & feedback:** never publish artifacts; feedback drafts need human approval to send. -6. **Editing:** `Edit` needs a prior `Read` in the conversation; don't re-read right after editing. -7. **External memory** (`~/.[claude,codex,etc....]/projects/Gameplane/memory/`, outside git): announce file + exact line on every write; never store conventions/decisions/preferences there (use repo specs/rules); repo files override memory. -8. **Skills** are optional; disclose edit/plan/commit skill runs first; repo rules override skills. -9. **Communication:** no preambles; closing recaps standalone (findings, actions, next steps, modified files). Short yes/no questions ("can I merge?", "is it green?") get a yes or no first, plus at most one line on what blocks it — no tables or recaps. Ask decisions with the question tool, not in prose. -10. **Conflicts/missing context:** surface instruction conflicts immediately; unsettled values go in `OPEN-DECISIONS.md`, never committed as settled contracts. - -## Repository map - -``` -netguard/ SSRF dial-guard: IsAllowed (operator module sources, API sinks, agent loopback RCON); IsPublic (agent mod downloads, API Steam resolver) -gameaction/ console-injection guard + command renderer (api, agent) -gameproto/ Minecraft/Terraria handshake parser (sentinel) -telemetryschema/ shared telemetry report contract (api, telemetry-receiver) -i18n/ shared language packs, ICU formatter, pack validator, i18n-check (api, web, operator, gp-module) -gp-module/ module authoring CLI: init/validate/preview/package -operator/ controller-runtime operator - api/v1alpha1/ CRD types (edit here, then `make generate manifests`); zz_generated.deepcopy.go GENERATED - internal/controller/ reconcilers + envtest suites - cmd/main.go entrypoint; config/{crd,rbac}/ GENERATED -api/ REST + WebSocket gateway (chi); cmd/main.go: `serve`, `bootstrap-admin` - internal/{handlers,auth,db,kube,notify,rbac,ws}/ -agent/ in-pod sidecar; internal/{auth,console,files,heartbeat,logs,players,rcon,quiesce}/ -audit-syslog-bridge/ RFC 5424 HTTP→syslog forwarder -telemetry-receiver/ usage telemetry ingest -sentinel/ wake-on-connect proxy for sleeping pods -capture-sidecar/ AF_PACKET BPF capture sidecar -mcp-server/ read-only MCP server -svcutil/ env parsing + graceful shutdown helpers -tunnel/ relay client supervisor (frp, Tailscale, playit) -web/ React 19 + strict TS + Vite dashboard; src/{routes,components,lib,router,styles,test}/ -modules/ SUBMODULE GameplanePanel/module (OCI game templates) -website/ SUBMODULE GameplanePanel/website (Astro docs/marketing; has its own CLAUDE.md and AGENTS.md) -charts/gameplane/ Helm chart (crd-manifests/, hooks) -deploy/kind/ local Kind scripts -test/e2e/ Kind E2E suite (//go:build e2e) -docs/ architecture, security, modules -design.pen canonical Pencil dashboard design -signing/ cosign public keys, dated legacy key + cross-signature (image + module) -go.work links all 17 Go modules (incl. test/e2e) -Makefile canonical task runner -``` - -After cloning: `git submodule update --init` (`modules/` required for `make dev-up`; `website/` optional). - -## Commands (always via Makefile) - -- **Dev:** `make dev-up` (Kind + OCI registry :5001 + Helm), `make web-dev` (Vite, proxies in-cluster API), `make dev-load` (load built images; run `make images` first), `make dev-install` (Helm upgrade), `make dev-down`. -- **Build:** `make build` (all Go + web), `make build-go` (15 modules in GO_MODULES, all but test/e2e), `make build-web` (`npm ci && npm run build`), `make images`. -- **Codegen:** `make generate` (deepcopy), `make manifests` (CRD/RBAC YAML, synced to `charts/gameplane/charts/gameplane-crds/crds/` and `charts/gameplane/crd-manifests/`), `make modules-push`, `make tidy`. -- **Tests (CI only — never run locally, see Rule 8):** `make test`, `make test-integration` (envtest operator+api), `make test-e2e` (~10–20 min), `make test-e2e-bucket BUCKET=(operator|api-auth|api-roles|api-rbac|api-agent|api-mods|ratelimit|bot-fast|bot-heavy|multicluster|upgrade)`. -- **E2E conventions:** register new tests in `test/e2e/buckets.sh`; `t.Parallel()` + unique names; guard shared resources (`ociPushMu` for module pushes, `ensureResticRepo(t)` for shared backup repos). Rate limits per cluster: IP burst 10 (5/min), user burst 6 (3/min) — cap an API bucket at ~7 admin logins. -- **Lint:** `make lint` = gofmt, go vet, golangci-lint, ESLint, `check-specs`, `check-doc-versions`, `check-links`. - -**Coverage minimums:** netguard 91, gameaction 91, gameproto 90, telemetryschema 90, i18n 90, gp-module 80, operator 72, api 80, agent 90, svcutil 90; audit-syslog-bridge, telemetry-receiver, sentinel, capture-sidecar, mcp-server, tunnel 70; web 92 lines / 76 functions / 82 branches / 92 statements. - -## Core rules - -1. **Design-first UI:** dashboard changes are designed in `design.pen` via Pencil MCP before React code; website changes start in `website/website.pen`. After every Pencil update export touched nodes to `assets/design-export/json/.json` (`mcp__pencil__execute` `Get`) and `assets/design-export/screenshots/.png` (`mcp__pencil__export_nodes`); website → `website/website-export/`. -2. **Never hand-edit `.pen` files:** no edit/delete/`Read`/`Grep`/`cat`/`sed` (multi-MB JSON, easily corrupted). Use Pencil MCP; ask the user to save in the UI after changes; inspect diffs only via `git diff --stat`. -3. **Login privacy:** `/login` and unauthenticated views never expose cluster names, versions, server counts, or account-existence errors — generic messages ("invalid credentials"). -4. **Fix, don't silence:** no `//nolint`, `eslint-disable`, or loosened configs. Allowed: `_test.go` exempt from `errcheck`/`gosec`/`unparam`; `operator/internal/controller/` exempt from revive `exported:`. -5. **TypeScript:** strict; no unjustified `any` (explain in a comment if unavoidable); every promise `await`ed or `void`-prefixed. -6. **Go errors:** wrap with `%w`. -7. **CRD sync:** after editing `operator/api/v1alpha1/*_types.go` run `make generate && make manifests`; commit deepcopy, `operator/config/{crd,rbac}/*.yaml`, `charts/gameplane/charts/gameplane-crds/crds/*.yaml`, and `charts/gameplane/crd-manifests/*.yaml` in the same changeset. -8. **Verification:** locally only compile checks (`go build ./...`, `npx tsc --noEmit`); never test/lint suites (`make test|lint|cover`, `go test`, `npm test`, envtest, E2E). Push a feature branch; CI is the sole verification authority. -9. **Kubernetes primitives first** (StatefulSet, Service, PVC, Job, ConfigMap, Secret, CRDs) before custom abstractions. -10. **Operator authority:** business logic lives in reconcilers (`operator/internal/controller/`); the API is a UX gateway and never bypasses reconciliation. -11. **Commits:** commit each completed logical unit (`feat:`/`fix:`/`chore:`…), signed (`git commit -s`); never amend pushed commits or use `--no-verify`; keep trailers `Co-Authored-By: `. -12. **Branches:** one per unit of work; delete remote + local branch right after merge. `master` is protected by ruleset `18692396` ("protect main"): 1 human approval, no self-approval, no direct pushes, approvals dismissed on push — agents cannot merge PRs. Check: `gh api repos/GameplanePanel/Gameplane/rules/branches/master`. -13. **Multi-agent delegation:** main loop orchestrates/reviews; implementation goes through `Workflow` scripts (`parallel()`/`pipeline()`). - - Start at `haiku`; escalate only on functional failure `haiku` → `sonnet` → `opus` → `fable`. `fable` needs explicit human permission. - - Set `model:` on every `agent()` call (default is session Opus); check with `grep -c "model:"`. - - Review one tier up (haiku→sonnet, sonnet→opus, opus→fable); fixes go to small agents in a new workflow. - - Review diffs, not items: one reviewer per batch checks `git diff` against the brief (facts are verified once, in the brief) — never one reviewer per page/file. - - Scripts over fan-out: rule-shaped changes (token swaps, version strings, frontmatter, renames) are a deterministic script run by one agent, not many agents. - - UI changes: browser smoke test via Chrome MCP at `sonnet`, parallel with reviews. -14. **PR labels (REST only — `gh pr edit` is broken by the Projects-classic deprecation):** ≥1 `type:` (`feature|fix|refactor|test|ci|chore|docs|security`) and ≥1 `area:` (`operator|api|agent|web|modules|chart|e2e|specs|shared|optional-components`); `breaking` when applicable. - ```sh - gh api -X POST repos/GameplanePanel/Gameplane/issues//labels -f "labels[]=type: fix" -f "labels[]=area: api" - gh api -X PATCH repos/GameplanePanel/Gameplane/pulls/ --input - ``` -15. **Specs:** a feature's spec is its whole `specs//` folder (`data-model.md`, `contracts/`, `OPEN-DECISIONS.md`, …) — check it for explicit exemptions before flagging violations. Mark obsolete tasks withdrawn in `tasks.md` with citations; never delete them. -16. **Archival:** once every task is complete/withdrawn and the PR is merged into `master`, `git mv specs/- specs/done_-` and update in-repo references in the same commit. -17. **Mechanical design waves** (token re-skins): scripted, blind updates at `haiku`. Precompute change lists with `grep`/`jq` on `assets/design-export/json/.json`; `haiku` applies `Update(id, {prop: value})`. Verify by screenshot comparison (`export_nodes` vs snapshot PNG), not JSON dumps; avoid `Get(id, {depth: 10+})`. -18. **Scout once, brief many:** one scout reads code/docs/failures and writes a factual brief (exact `file:line`, before/after code, justification). Fix agents get only the brief and edit blind at `haiku` — never tell them to re-read files, test suites, or `CLAUDE.md` and `AGENTS.md`. Reviewers check git diffs against the brief. - -## Architecture - -| Component | Stack | Role | -|---|---|---| -| netguard | Go | dial-time SSRF prevention: `IsAllowed` (operator/API module sources and sinks, agent loopback RCON), `IsPublic` (agent mod downloads, API Steam resolver) | -| gameaction | Go | validates console input against schemas; escapes injection | -| gameproto | Go | Minecraft/Terraria wire parser for connection filtering | -| telemetryschema | Go | stdlib-only shared telemetry report contract: types, strict decode, official module catalog, report signing (api, telemetry-receiver) | -| i18n | Go | shared language packs (`go:embed`), ICU MessageFormat subset, pack validator, `i18n-check` CI tool (api, web, operator, gp-module) | -| gp-module | Go | module CLI: scaffold, offline validate, dry-run preview, OCI package | -| svcutil | Go | stdlib-only env + graceful shutdown (`RunHTTP`) | -| operator | Go, controller-runtime | authoritative reconciler for 9 CRDs (`GameServer`, `GameTemplate`, …) | -| api | Go, chi | REST/WS UX gateway; SQLite, experimental PostgreSQL | -| agent | Go | pod sidecar: console (PTY/RCON), files, logs, heartbeats | -| audit-syslog-bridge | Go | webhook audit events → RFC 5424 syslog | -| telemetry-receiver | Go | opt-in anonymous usage stats | -| sentinel | Go | holds ports while asleep, wakes pods on connect | -| capture-sidecar | Go | ephemeral BPF packet capture | -| mcp-server | Go | read-only MCP daemon (stdio) for cluster debugging | -| web | React 19, Vite, TS | dashboard (TanStack Router/Query, Tailwind, HeroUI v3) | -| modules/ | OCI / oras | game templates (Minecraft, Terraria, Valheim, …) | - -## Common workflows - -- **CRD field:** edit `operator/api/v1alpha1/_types.go` → `make generate && make manifests` → reconciler `operator/internal/controller/_controller.go` → mirror in `web/src/types.ts` + affected `web/src/routes/` → envtest `_envtest_test.go`. -- **API route:** handler in `api/internal/handlers/` → mount in `api/cmd/main.go` with RBAC middleware (`api/internal/rbac/`) → `api/internal/handlers/_envtest_test.go` → client method in `web/src/lib/api.ts`. -- **Dashboard screen:** design in `design.pen` + export to `assets/design-export/` → `web/src/routes/.tsx`, register in `web/src/router/tree.tsx` → data via `web/src/lib/api.ts` + TanStack Query → `web/src/routes/.test.tsx`. -- **Game module:** edit `modules//` (`module.yaml`, `template.yaml`, `README.md`) → `make modules-push` → commit in `GameplanePanel/module` → `git add modules` + commit pointer bump in root. -- **Website:** design in `website/website.pen` + export to `website/website-export/` → change `website/` per its own CLAUDE.md and AGENTS.md → commit/push/PR in `GameplanePanel/website` (default branch `main`) → `git add website` + commit pointer bump in root. -- **DB migration:** new sequential `api/internal/db/migrations/common/_.sql` (013 onward) — portable SQL both SQLite and PostgreSQL run unchanged (no `datetime('now')`/`strftime`, `AUTOINCREMENT`, `INSERT OR …`, `COLLATE NOCASE`; bind timestamps from Go). `migrations/sqlite/` and `migrations/postgres/` hold the frozen per-dialect 001–012 sets; see `api/internal/db/migrations/README.md`. Append-only, applied on API startup. - -## Reference docs - -- `docs/agent-architecture.md` — read before editing code; maps tasks/features to docs (use instead of repo-wide greps). -- `docs/architecture.md`, `docs/security.md` (threat model); `docs/module-authoring.md`; `mcp-server/README.md`; `audit-syslog-bridge/README.md`. -- Configs: `.golangci.yml`, `web/eslint.config.js`, `.editorconfig`. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index 07565ef67..ca1427ac4 100644 --- a/README.md +++ b/README.md @@ -300,8 +300,10 @@ bundle format and how to author additional modules. ## Contributing Design changes go through `design.pen` (Pencil) before any UI code is -written. See [`docs/contributing.md`](docs/contributing.md) for the -full guide: code style, test tiers, and the PR process. +written. New contributors start with +[Your first PR](docs/contributing.md#your-first-pr) (owning folder, local +check and CI time per change type); [`docs/contributing.md`](docs/contributing.md) +has the full guide: code style, test tiers, and the PR process. Translations are welcome. Translate in the browser on [Hosted Weblate](https://hosted.weblate.org/engage/gameplane/), or see diff --git a/docs/agent-architecture.md b/docs/agent-architecture.md index 3617b9cf6..72c93494d 100644 --- a/docs/agent-architecture.md +++ b/docs/agent-architecture.md @@ -48,8 +48,17 @@ This document is the **first stop** for AI agents modifying or extending Gamepla | --- | --- | | Translations, language packs, localized server or module messages (i18n) | `docs/translating.md`, `specs/019-localization-language-packs/contracts/` (`pack-format.md`, `server-messages.md`, `i18n-api.md`, `module-locales.md`, `check-tool.md`), and the Internationalization section of `web/specs.md` | | CI test selection: which jobs, packages and tests a PR runs (`hack/ci_scope.py`, `hack/check_changed_coverage.py`, `test/e2e/suite-components.json`, the `changes` job in `.github/workflows/ci.yaml`) | `docs/contributing.md` ("How CI picks tests"), `specs/023-change-based-test-selection/contracts/` (`ci-scope-cli.md`, `suite-components.md`, `changed-coverage.md`) | +| Add a CRD field | `operator/specs.md`, `AGENTS.md` (rule 7 and "Common workflows"), `docs/architecture.md` | +| Add an API route | `api/specs.md`, `docs/security.md`, `web/specs.md` (client-side code in `web/src/lib/api.ts`) | +| Add or change a dashboard screen | `web/specs.md`, `docs/layout.md` (web app), `assets/design-export/MANIFEST.md`, `docs/contributing.md` ("Design first for UI changes") | +| Add or change a game module | `docs/module-authoring.md`, `modules//specs.md` (module repo) | +| Change the website | `website/AGENTS.md` (submodule) | +| Add a DB migration | `api/specs.md`, `api/internal/db/migrations/README.md` | +| Change CI or a workflow | `docs/contributing.md` ("CI and workflows", "How CI picks tests") | +| Decide where new code goes | `docs/layout.md` | ## How to use this index 1. Identify the domain of your task (e.g., "Add an auth provider"). 2. Find the component handling this domain (e.g., "API Gateway"). -3. Read the documentation or resource at the path listed in the table (e.g., `api/specs.md`) to understand its architecture, boundaries, and specific rules before modifying code. \ No newline at end of file +3. Read the documentation or resource at the path listed in the table (e.g., `api/specs.md`) to understand its architecture, boundaries, and specific rules before modifying code. +4. Adding a file, package or screen? Check `docs/layout.md` for the target folder shape of that module. \ No newline at end of file diff --git a/docs/archive/README.md b/docs/archive/README.md new file mode 100644 index 000000000..3daae78a6 --- /dev/null +++ b/docs/archive/README.md @@ -0,0 +1,10 @@ +# Archive + +Read-only design documents from before the design-doc rule in +[`docs/contributing.md`](../contributing.md#where-design-docs-go). Nothing here +is current: the features have shipped and each file names the `specs.md` that +describes today's behavior. Do not add new files here; new changes go in +`specs/NNN-/`. + +- `superpowers/`: July 2026 designs and implementation plans for console + protocols, action stdin, multi-category modules and wake-on-connect. diff --git a/docs/superpowers/plans/2026-07-14-console-protocols.md b/docs/archive/superpowers/plans/2026-07-14-console-protocols.md similarity index 97% rename from docs/superpowers/plans/2026-07-14-console-protocols.md rename to docs/archive/superpowers/plans/2026-07-14-console-protocols.md index dd59db4a7..6ee44b514 100644 --- a/docs/superpowers/plans/2026-07-14-console-protocols.md +++ b/docs/archive/superpowers/plans/2026-07-14-console-protocols.md @@ -1,3 +1,5 @@ +> **Historical, superseded.** This July 2026 document is kept for reference only. The work it describes has shipped and the code may differ from it. Current behavior is described in `operator/specs.md` and `agent/specs.md`. See [docs/archive](../../README.md). + # Console Protocols Implementation Plan (Phase 2) > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax. @@ -10,7 +12,7 @@ ## Global Constraints -- **Phase 2 of** `docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read §1 first, including the 2026-07-14 correction about Satisfactory's `RunCommand`. +- **Phase 2 of** `docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read §1 first, including the 2026-07-14 correction about Satisfactory's `RunCommand`. - **Do NOT run tests or linters locally** (CLAUDE.md rule 8). CI is the source of truth. Compile checks ARE allowed and required. - **`go vet ./...` SKIPS files behind build tags.** After touching operator test files you MUST also run `cd operator && go vet -tags=envtest ./...`. A bad import in an `*_envtest_test.go` passes the plain check and reddens CI — this has already happened once on this feature. - **Sign commits**: `git -c commit.gpgsign=false commit -s -m "..."` (the `-c` MUST precede the subcommand). diff --git a/docs/superpowers/plans/2026-07-14-multi-category-modules.md b/docs/archive/superpowers/plans/2026-07-14-multi-category-modules.md similarity index 98% rename from docs/superpowers/plans/2026-07-14-multi-category-modules.md rename to docs/archive/superpowers/plans/2026-07-14-multi-category-modules.md index 0fe27178b..74ec20bcd 100644 --- a/docs/superpowers/plans/2026-07-14-multi-category-modules.md +++ b/docs/archive/superpowers/plans/2026-07-14-multi-category-modules.md @@ -1,3 +1,5 @@ +> **Historical, superseded.** This July 2026 document is kept for reference only. The work it describes has shipped and the code may differ from it. Current behavior is described in `operator/specs.md` and `gp-module/specs.md`. See [docs/archive](../../README.md). + # Multi-Category Modules Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. @@ -10,7 +12,7 @@ ## Global Constraints -- **Phase 1 of** `docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read the spec's §2 before starting. +- **Phase 1 of** `docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read the spec's §2 before starting. - **Do NOT run tests or linters locally.** Push to a branch; GitHub Actions is the source of truth (CLAUDE.md rule 8). A compile check (`go build ./...`, `go vet ./...`, `tsc --noEmit`) is fine and encouraged. Note `go build` skips test files — use `go vet ./...` to compile-check tests. - **After editing CRD Go types, run `make generate && make manifests`** and commit the regenerated `zz_generated.deepcopy.go`, `operator/config/crd/*.yaml`, `operator/config/rbac/*.yaml`, and `charts/gameplane/crds/*.yaml` in the SAME commit as the source change (CLAUDE.md rule 7). Codegen is not a test — running it locally is required. - **Sign every commit**: `git -c commit.gpgsign=false commit -s -m "..."` (the `-c` MUST precede the subcommand). Conventional-commit prefixes. @@ -974,7 +976,7 @@ git push -u origin feat/multi-category-modules ```bash gh pr create --title "feat: modules declare multiple categories" --body "$(cat <<'EOF' -Phase 1 of docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md. +Phase 1 of docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md. `category: string` becomes `categories: []string` across the CRDs, the bundle parser, the API catalog DTO, and the dashboard. A module may now belong to diff --git a/docs/superpowers/plans/2026-07-15-action-schema-stdin.md b/docs/archive/superpowers/plans/2026-07-15-action-schema-stdin.md similarity index 98% rename from docs/superpowers/plans/2026-07-15-action-schema-stdin.md rename to docs/archive/superpowers/plans/2026-07-15-action-schema-stdin.md index 7230e5d70..882318251 100644 --- a/docs/superpowers/plans/2026-07-15-action-schema-stdin.md +++ b/docs/archive/superpowers/plans/2026-07-15-action-schema-stdin.md @@ -1,3 +1,5 @@ +> **Historical, superseded.** This July 2026 document is kept for reference only. The work it describes has shipped and the code may differ from it. Current behavior is described in `operator/specs.md` and `agent/specs.md`. See [docs/archive](../../README.md). + # Action Schema & Stdin Transport Implementation Plan (Phase 3) > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`). @@ -10,7 +12,7 @@ ## Global Constraints -- **Phase 3 of** `docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read §3 first. +- **Phase 3 of** `docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md`. Read §3 first. - **Do NOT run tests/linters locally** (CLAUDE.md rule 8); CI is the source of truth. Compile checks (`go build`, `go vet`, `tsc --noEmit`) are allowed and required. - **`go vet ./...` SKIPS build-tagged files.** After operator test edits run `cd operator && go vet -tags=envtest ./...` too. - **After CRD type edits: `make generate && make manifests`**, commit the regenerated `zz_generated.deepcopy.go`, `operator/config/crd/*.yaml`, `operator/config/rbac/*.yaml`, `charts/gameplane/crds/*.yaml` in the SAME commit (rule 7). diff --git a/docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md b/docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md similarity index 98% rename from docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md rename to docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md index b2dad3712..5799343cb 100644 --- a/docs/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md +++ b/docs/archive/superpowers/specs/2026-07-14-console-protocols-categories-actions-design.md @@ -1,7 +1,9 @@ +> **Historical, superseded.** This July 2026 document is kept for reference only. The work it describes has shipped and the code may differ from it. Current behavior is described in `operator/specs.md`, `agent/specs.md` and `gp-module/specs.md`. See [docs/archive](../../README.md). + # Console protocols, multi-category modules, and richer quick actions **Date:** 2026-07-14 -**Status:** approved, not yet implemented +**Status:** shipped; historical (see the banner above) ## Problem diff --git a/docs/superpowers/specs/2026-07-24-wake-on-connect-design.md b/docs/archive/superpowers/specs/2026-07-24-wake-on-connect-design.md similarity index 97% rename from docs/superpowers/specs/2026-07-24-wake-on-connect-design.md rename to docs/archive/superpowers/specs/2026-07-24-wake-on-connect-design.md index 892617188..c8632d253 100644 --- a/docs/superpowers/specs/2026-07-24-wake-on-connect-design.md +++ b/docs/archive/superpowers/specs/2026-07-24-wake-on-connect-design.md @@ -1,7 +1,9 @@ +> **Historical, superseded.** This July 2026 document is kept for reference only. The work it describes has shipped and the code may differ from it. Current behavior is described in `sentinel/specs.md`. See [docs/archive](../../README.md). + # Wake-on-connect for idle auto-sleep **Date:** 2026-07-24 -**Status:** approved, not yet implemented +**Status:** shipped; historical (see the banner above) ## Problem diff --git a/docs/contributing.md b/docs/contributing.md index 10d112309..4e73738b6 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,5 +1,49 @@ # Contributing +This guide covers the dev loop, house rules, CI and the review process. New here? +Start with [Your first PR](#your-first-pr): it tells you which folder owns your +change, the one compile check to run, and how long CI takes. + +## Your first PR + +1. Fork the repo and create one branch for the change. +2. Find the owning folder for your change in the table below. For where new + code goes inside a module, see [`docs/layout.md`](layout.md). +3. Run the local check for that row only. Do not run test or lint suites + locally (`make test`, `make lint`, `go test`, `npm test`); CI runs them. +4. Commit with `git commit -s` and a conventional prefix (`feat:`, `fix:`, + `docs:`, `chore:`, …). +5. Open the PR and check that it has one `type:` label and one `area:` label + (see [Submitting a change](#submitting-a-change)). +6. Wait for CI (times below) and fix what it reports. + +| Change | Owning folder | Local check | CI wall clock (2026-10-20) | +| --- | --- | --- | --- | +| Docs only | `docs/`, `README.md`, any `*.md` | none | 1.8 min (run 2507) | +| Spec only | `specs/NNN-/` | none | 5.3 min (run 2525) | +| Web only | `web/` | `cd web && npx tsc --noEmit` | 18.3 min (run 2524) | +| One small Go service | `/` (e.g. `sentinel/`, `tunnel/`) | `go build ./...` in that module | same as any Go change: 29–75 min, median 37.5 min | +| Operator / CRD | `operator/` (types in `operator/api/v1alpha1/`) | `make generate && make manifests`, then `go build ./...` in `operator/` | same as any Go change | +| API route | `api/` (handlers in `api/internal/handlers/`) | `go build ./...` in `api/` | same as any Go change | +| Game module | `modules//`: PR in `GameplanePanel/module`, then a pointer bump here | `make module-validate MODULE=` | 2.7 min in `GameplanePanel/module` (run 79, 2026-10-20); the bump PR here runs the docs gates | +| Website | `website/`: PR in `GameplanePanel/website`, then a pointer bump here | `npm run build` in `website/` | 0.6 min in `GameplanePanel/website` (PR 25, 2026-10-22); the bump PR here runs the docs gates | + +Times are GitHub Actions wall clock for one PR run, measured 2026-10-20: runs +2507, 2525 and 2524, and 11 Go PR runs for the median. Change-based test +selection (`specs/023-change-based-test-selection/`) shortens Go runs as it +lands, so treat these as upper bounds. + +Gates a first PR trips on: + +- Unsigned commits: every commit needs `git commit -s`. +- Missing labels: one `type:` and one `area:` label on every PR. +- Local test or lint runs: only compile checks locally; CI is the verifier. +- UI without design: dashboard changes start in `design.pen` (Pencil), see + [Design first for UI changes](#design-first-for-ui-changes). Never hand-edit + `.pen` files. +- Submodule work in the wrong repo: `modules/` and `website/` changes go to + their own repos first, then a pointer bump PR here. + ## Dev loop ```sh @@ -195,15 +239,34 @@ which applies `deploy/kind/postgres.yaml` and installs the chart with login rate limiter. The frozen 001–012 upgrade path is covered separately by `TestPostgres_MigrateFromFrozenBaseline` in the `api (postgres)` job. +## Where design docs go + +Two homes, nothing else: + +- **A change** (feature, fix, cleanup) is written up in `specs/NNN-/` + through speckit (spec, plan, tasks, `OPEN-DECISIONS.md`). Once every task + is done and merged, the folder is renamed `specs/done_NNN-/`. +- **How a module works today** is described in its `/specs.md`, + updated in the same PR as the behavior (constitution Principle IV, + checked by `make check-specs`). + +No other folder holds designs or plans. Older designs from before this rule +are kept read-only in [`docs/archive/`](archive/README.md). + ## AI-assisted development Much of this codebase is developed with AI coding assistants (Claude Code). The project was started on Claude Opus 4.8 (`claude-opus-4-8`); since June 2026 development continues on Claude Fable 5 -(`claude-fable-5`). Agent-facing guidance lives in -[`CLAUDE.md`](../CLAUDE.md) — keep it current when project structure or -house rules change. All contributions, AI-assisted or not, go through -the same review, lint, and test gates below. +(`claude-fable-5`). Agent-facing guidance lives in one real file, +[`AGENTS.md`](../AGENTS.md); keep it current when project structure or +house rules change. `CLAUDE.md` is a symlink to `AGENTS.md` for tools +that read that name, and the `website/` submodule uses the same pair +(`AGENTS.md` real, `CLAUDE.md` link). Always edit `AGENTS.md`: with +`git config core.symlinks false` (the Windows default) the link checks +out as a one-line text file that only holds the name `AGENTS.md`. All +contributions, AI-assisted or not, go through the same review, lint, +and test gates below. ## Submitting a change diff --git a/docs/layout.md b/docs/layout.md new file mode 100644 index 000000000..bdf245b03 --- /dev/null +++ b/docs/layout.md @@ -0,0 +1,132 @@ +# Layout + +Target folder shape for every Go module and for `web/src`, and how far each one +is from it today. Spec 028 (`specs/028-split-big-packages/`) applies it with +behavior-preserving moves; until then, use it for new code and in review. Build +and test commands are in [`AGENTS.md`](../AGENTS.md) and +[`contributing.md`](contributing.md). + +## 1. Kinds + +Root files allowed in every Go module: `go.mod`, `go.sum`, `specs.md`, +`README.md`, `Dockerfile` (and `Dockerfile.*`), `.testcoverage.yml`. + +### Service + +A long-running binary (a Deployment, DaemonSet, sidecar or Job image). + +```text +/ +├── cmd/main.go thin wiring: flags and env (svcutil), construct, run +├── internal// all behavior, one package per concern +├── specs.md +├── README.md +└── Dockerfile +``` + +- `cmd/`: only wiring. A second binary gets `cmd//main.go`. +- `internal//`: the code, grouped by what it does (`auth`, `console`, `store`, …). +- Tests sit next to the file they test (`x.go` → `x_test.go`). +- No `.go` file at the module root. + +### Library + +A module other modules import, with no binary of its own. + +```text +/ +├── .go … one flat root package named after the module +├── testdata/ fixtures, if any +└── specs.md +``` + +- One package at the root. A subpackage only for a separately importable concern. +- Tests next to the file they test. + +### CLI tool + +A binary people run by hand. + +```text +/ +├── cmd//main.go commands and flag parsing +├── internal// behavior +├── pkg// only packages other modules import +└── specs.md +``` + +- Tests next to the file they test. + +### Web app (`web/src`) + +```text +web/src/ +├── pages/.tsx one file per screen +├── pages//
.tsx sections of a screen over 500 lines +├── routes/ route definitions only, no screen code +├── router/ assembles the route tree +├── components/ parts used by 2+ screens +├── components/ui/ primitives +├── lib/ API client, hooks, helpers, i18n +├── styles/ +├── test/ shared test setup +├── api/types.ts generated, never edited by hand +└── types.ts hand-written shared types +``` + +- `pages/.tsx` holds a screen. When it passes 500 lines it is split into + section files under `pages//` (e.g. `pages//Footer.tsx`, + `pages//UpperSection.tsx`) and `pages/.tsx` composes them. +- `routes/` only exposes pages: one route-definition file per page; `router/` + only assembles them (spec 028 OD-7 fixes the file names). +- A part moves to `components/` once two or more screens use it; until then it + stays with its screen. +- Tests sit next to the file they test (`X.test.tsx`, `X_.test.tsx`). + +## 2. Layout map + +Measured on `master` at `54d5819` (2026-10-22). "Gap today" is `matches` or the +move spec 028 makes. + +| Module | Kind | Target | Gap today | +| --- | --- | --- | --- | +| `agent` | Service | `cmd/main.go` + `internal//` | matches | +| `api` | Service | `cmd/main.go` + `internal//` | `cmd/` holds 7 non-test files (bootstrap, gateway, management, memlimit, metrics, panel key) → keep wiring in `cmd/`, move behavior to `internal//`; second binary `cmd/messages-export/` matches; `internal/handlers/` is one flat package of 146 files (spec 028 splits it) | +| `audit-syslog-bridge` | Service | `cmd/main.go` + `internal//` | 3 files in root `package main` (`main.go` 460 lines) → `cmd/main.go` + `internal//` | +| `capture-sidecar` | Service | `cmd/main.go` + `internal//` | matches | +| `mcp-server` | Service | `cmd/main.go` + `internal//` | root `package main` (`main.go`, `tools.go`, `fixadvice.go`) → `cmd/main.go` + `internal//`; `internal/kube/` matches | +| `operator` | Service | `cmd/main.go` + `internal//` | matches; `internal/controller/` is one flat package of 150 files (spec 028 splits it); `api/v1alpha1/` see §3 | +| `sentinel` | Service | `cmd/main.go` + `internal//` | `main.go` 1,017 lines at root → `cmd/main.go` + `internal//` | +| `telemetry-receiver` | Service | `cmd/main.go` + `internal//` | 12 source files in root `package main` → `cmd/main.go` + `internal//`; `web/` and `design-export/` assets stay | +| `tunnel` | Service | `cmd/main.go` + `internal//` | `main.go` 1,052 and `playit_reporter.go` 525 lines at root → `cmd/main.go` + `internal//`; `frpc/` (separate build module, not in `go.work`) stays | +| `gp-module` | CLI tool | `cmd/gp-module/` + `internal//` | matches; `pkg/` holds the 5 packages `api` and `test/e2e` import | +| `i18n` | Library + CLI | flat root package + `cmd/i18n-check/` | matches (see §3) | +| `gameaction` | Library | flat root package | matches | +| `gameproto` | Library | flat root package | matches | +| `netguard` | Library | flat root package | matches | +| `svcutil` | Library | flat root package | matches | +| `telemetryschema` | Library | flat root package | matches | +| `test/e2e` | Test suite | root package + `internal//` | matches (see §3) | +| `web` | Web app | `src/pages/` + route definitions in `src/routes/` | no `src/pages/` yet: screens live in `src/routes/` → move each to `src/pages/.tsx`; screens over 500 lines to split: `AdminSettings.tsx` 2,478, `CreateServer.tsx` 1,352, `tabs/Mods.tsx` 1,291, `tabs/settings/Networking.tsx` 1,045, `ThemeSettings.tsx` 886, `Users.tsx` 825, `Servers.tsx` 680, `tabs/Files.tsx` 670, `tabs/settings/ShareLinks.tsx` 658, `Backups.tsx` 642, `Dashboard.tsx` 596 | + +## 3. Exceptions + +- `test/e2e`: a test suite, not a service or library. Tests sit at the root + (`//go:build e2e`) with per-game packages under `internal//` and shared + helpers in `internal/fakeoidc/`, `internal/probe/` and `internal/protocol/`. +- `operator/api/v1alpha1`: CRD types follow the kubebuilder layout that + `make generate` and `make manifests` expect. +- `i18n`: a library (root package plus `catalog/`, `icu/`, `modlocale/`, `pack/`, + each imported on its own) that also ships the `i18n-check` CLI under + `cmd/i18n-check/`; it matches both kinds. + +## 4. Enforcement + +Spec 028 adds a folder-shape check to `make lint` once every module matches this +doc. Until then, review enforces it: new code follows the target shape even in a +module that does not match yet. + +## 5. Not decided here + +Where each large package splits, and the line limit for files other than web +screens, belong to spec 028 (`specs/028-split-big-packages/`). diff --git a/specs/027-contributor-docs-layout-map/research.md b/specs/027-contributor-docs-layout-map/research.md index c838c47df..cc36dbb46 100644 --- a/specs/027-contributor-docs-layout-map/research.md +++ b/specs/027-contributor-docs-layout-map/research.md @@ -2,6 +2,16 @@ All facts measured on `master` at `8a67f2e` (2026-10-20) unless stated. +## R0. Re-measurement before implementation (T002) + +Re-measured on `master` at `54d5819` (2026-10-22). No change to R1–R5 except: + +- R1: unchanged (`AGENT.md`, `AGENTS.md` mode `120000` → `CLAUDE.md`; `CLAUDE.md` regular file). No `AGENT.md` reference outside `specs/`. +- R2: the superpowers plans hold 4 internal `docs/superpowers/` path references (3 plans; `multi-category-modules.md` has 2), not 5. 19 `specs.md` outside `specs/` (unchanged). +- R3: `gp-module` also has `pkg/` (5 packages: archetypes, packager, preview, scaffold, validator) imported by `api/internal/handlers/modules_builder.go` and `test/e2e`; `api/cmd/` holds 7 non-test files plus `cmd/messages-export/main.go`; `mcp-server` root holds `main.go`, `tools.go`, `fixadvice.go`; `tunnel` root holds `main.go` (1,052 lines) and `playit_reporter.go` (525). `api/internal/handlers/` 146 and `operator/internal/controller/` 150 `.go` files (unchanged). +- R4: `web/src/routes/` non-test screens over 500 lines: `AdminSettings.tsx` 2,478, `CreateServer.tsx` 1,352, `tabs/Mods.tsx` 1,291, `tabs/settings/Networking.tsx` 1,045, `ThemeSettings.tsx` 886, `Users.tsx` 825, `Servers.tsx` 680, `tabs/Files.tsx` 670, `tabs/settings/ShareLinks.tsx` 658, `Backups.tsx` 642, `Dashboard.tsx` 596. No `web/src/pages/` yet. +- R6: `docs/contributing.md` has no opening paragraph (it starts with `# Contributing` then `## Dev loop`); T003 adds one. + ## R1. Agent instruction files - `git ls-files -s`: `AGENT.md` and `AGENTS.md` are mode `120000` (symlink) with target `CLAUDE.md`; `CLAUDE.md` is a regular file (15,993 bytes). Equal md5 sums come from the links, not from copies. diff --git a/specs/027-contributor-docs-layout-map/tasks.md b/specs/027-contributor-docs-layout-map/tasks.md index 0c3b77ec8..b026f9442 100644 --- a/specs/027-contributor-docs-layout-map/tasks.md +++ b/specs/027-contributor-docs-layout-map/tasks.md @@ -26,8 +26,8 @@ description: "Task list for spec 027: contributor docs and layout map" ## Phase 1: Setup -- [ ] T001 Create branch `claude/027-contributor-docs` in GP from latest `master` (after the spec docs PR merges) and `claude/027-contributing-pointer` in WEB from latest `main` -- [ ] T002 Re-measure on the new `master` the facts the docs will state (research R1–R4: symlink modes, `AGENT.md` references, `specs.md` count, module shapes, largest web files) and record the SHA and any drift at the top of `specs/027-contributor-docs-layout-map/research.md` +- [X] T001 Create branch `claude/027-contributor-docs` in GP from latest `master` (after the spec docs PR merges) and `claude/027-contributing-pointer` in WEB from latest `main` (done on `claude/project-thread-y09b3k` in both repos, the thread branch) +- [X] T002 Re-measure on the new `master` the facts the docs will state (research R1–R4: symlink modes, `AGENT.md` references, `specs.md` count, module shapes, largest web files) and record the SHA and any drift at the top of `specs/027-contributor-docs-layout-map/research.md` --- @@ -37,10 +37,10 @@ description: "Task list for spec 027: contributor docs and layout map" **Independent test**: three sample changes answered from the guide alone (SC-001). -- [ ] T003 [US1] Write the "Your first PR" section in `docs/contributing.md` directly after the opening paragraph per [contracts/first-pr-section.md](./contracts/first-pr-section.md): 6 steps, change-type table (8 rows) with CI times and source runs from research R5 dated 2026-10-20, gates list -- [ ] T004 [US1] Add a link to the new section in the opening paragraph of `docs/contributing.md` and in `README.md` "Contributing" (line ~303) -- [ ] T005 [P] [US1] WEB: add a short "Your first PR" pointer to `src/content/docs/contributing.mdx` linking to the GitHub `docs/contributing.md#your-first-pr` (absolute GitHub URL from `src/config.ts`), no copied table; local check `npm run build` -- [ ] T006 [US1] Commit GP (`docs: add first-PR guide to contributing`) and WEB (`docs: link first-PR guide`) +- [X] T003 [US1] Write the "Your first PR" section in `docs/contributing.md` directly after the opening paragraph per [contracts/first-pr-section.md](./contracts/first-pr-section.md): 6 steps, change-type table (8 rows) with CI times and source runs from research R5 dated 2026-10-20, gates list +- [X] T004 [US1] Add a link to the new section in the opening paragraph of `docs/contributing.md` and in `README.md` "Contributing" (line ~303) +- [X] T005 [P] [US1] WEB: add a short "Your first PR" pointer to `src/content/docs/contributing.mdx` linking to the GitHub `docs/contributing.md#your-first-pr` (absolute GitHub URL from `src/config.ts`), no copied table; local check `npm run build` +- [X] T006 [US1] Commit GP (`docs: add first-PR guide to contributing`) and WEB (`docs: link first-PR guide`) --- @@ -50,10 +50,10 @@ description: "Task list for spec 027: contributor docs and layout map" **Independent test**: `git ls-files -s AGENTS.md CLAUDE.md` shows `AGENTS.md` `100644`, `CLAUDE.md` `120000`, and `AGENT.md` is gone (SC-002). -- [ ] T007 [US2] Re-run the reference search for `AGENT.md` across GP (excluding `specs/done_*`); if any hit, stop and report -- [ ] T008 [US2] *(OD-1)* Swap the instruction files in one commit: `git rm AGENTS.md` (symlink), `git mv CLAUDE.md AGENTS.md`, `ln -s AGENTS.md CLAUDE.md && git add CLAUDE.md`, `git rm AGENT.md`; check with `git ls-files -s AGENTS.md CLAUDE.md` (`100644` and `120000`) and that the content of `AGENTS.md` is byte-identical to the old `CLAUDE.md` -- [ ] T009 [US2] *(OD-1)* Rewrite the "AI-assisted development" section of `docs/contributing.md`: `AGENTS.md` is the real file in both repos, `CLAUDE.md` is a link to it, plus the Windows `core.symlinks=false` note -- [ ] T009a [US2] Commit (`docs: make AGENTS.md the single agent instructions file`) +- [X] T007 [US2] Re-run the reference search for `AGENT.md` across GP (excluding `specs/done_*`); if any hit, stop and report +- [X] T008 [US2] *(OD-1)* Swap the instruction files in one commit: `git rm AGENTS.md` (symlink), `git mv CLAUDE.md AGENTS.md`, `ln -s AGENTS.md CLAUDE.md && git add CLAUDE.md`, `git rm AGENT.md`; check with `git ls-files -s AGENTS.md CLAUDE.md` (`100644` and `120000`) and that the content of `AGENTS.md` is byte-identical to the old `CLAUDE.md` +- [X] T009 [US2] *(OD-1)* Rewrite the "AI-assisted development" section of `docs/contributing.md`: `AGENTS.md` is the real file in both repos, `CLAUDE.md` is a link to it, plus the Windows `core.symlinks=false` note +- [X] T009a [US2] Commit (`docs: make AGENTS.md the single agent instructions file`) --- @@ -63,11 +63,11 @@ description: "Task list for spec 027: contributor docs and layout map" **Independent test**: no design/plan docs outside the named homes except `docs/archive/` (SC-003). -- [ ] T010 [US3] Add "Where design docs go" to `docs/contributing.md` (plan D2: change → `specs/NNN-/`; how a module works today → `/specs.md`; nothing else) and one matching line under rule 15 of `AGENTS.md` -- [ ] T011 [US3] *(OD-2)* `git mv docs/superpowers docs/archive/superpowers`; update the 5 internal `docs/superpowers/` references inside the moved plans to the new paths -- [ ] T012 [US3] *(OD-2)* Prepend the historical banner to each of the 5 moved files naming the `/specs.md` that now describes the shipped behavior (console/actions → `operator/specs.md` and `agent/specs.md`; multi-category modules → `operator/specs.md` and `gp-module/specs.md`; wake-on-connect → `sentinel/specs.md`); write `docs/archive/README.md` -- [ ] T013 [US3] Search GP for any remaining `docs/superpowers` path (excluding `specs/done_*`, which keep historical text) and fix it -- [ ] T014 [US3] Commit (`docs: write design-doc homes rule and archive superpowers docs`) +- [X] T010 [US3] Add "Where design docs go" to `docs/contributing.md` (plan D2: change → `specs/NNN-/`; how a module works today → `/specs.md`; nothing else) and one matching line under rule 15 of `AGENTS.md` +- [X] T011 [US3] *(OD-2)* `git mv docs/superpowers docs/archive/superpowers`; update the 4 internal (count corrected in research R0) `docs/superpowers/` references inside the moved plans to the new paths +- [X] T012 [US3] *(OD-2)* Prepend the historical banner to each of the 5 moved files naming the `/specs.md` that now describes the shipped behavior (console/actions → `operator/specs.md` and `agent/specs.md`; multi-category modules → `operator/specs.md` and `gp-module/specs.md`; wake-on-connect → `sentinel/specs.md`); write `docs/archive/README.md` +- [X] T013 [US3] Search GP for any remaining `docs/superpowers` path (excluding `specs/done_*`, which keep historical text) and fix it +- [X] T014 [US3] Commit (`docs: write design-doc homes rule and archive superpowers docs`) --- @@ -77,11 +77,11 @@ description: "Task list for spec 027: contributor docs and layout map" **Independent test**: every `go.work` module and `web` has a row with kind, target and gap (SC-004). -- [ ] T015 [US4] *(OD-3, OD-4)* Write `docs/layout.md` §1 Kinds per [contracts/layout-doc.md](./contracts/layout-doc.md) and plan D3 (service: `cmd/main.go` + `internal//`; library: flat root package; CLI: `cmd//`; web: `pages/.tsx`, split into `pages//
.tsx` over 500 lines, `routes/` only exposes pages, `components/` for parts used by 2+ screens) -- [ ] T016 [US4] Write §2 Layout map (18 rows) from the T002 re-measurement; the `web` row lists each screen in `web/src/routes/` over 500 lines as a gap, stating the `master` SHA; §3 Exceptions (`test/e2e`, `operator/api/v1alpha1`, `i18n`); §5 Not decided here -- [ ] T017 [US4] *(OD-5)* Write §4 Enforcement: spec 028 adds the folder-shape check to `make lint` once every module matches; until then review enforces the doc -- [ ] T018 [US4] Add `docs/layout.md` and `docs/archive/` to the repository map in `AGENTS.md` -- [ ] T019 [US4] Commit (`docs: add target layout map for Go modules and web`) +- [X] T015 [US4] *(OD-3, OD-4)* Write `docs/layout.md` §1 Kinds per [contracts/layout-doc.md](./contracts/layout-doc.md) and plan D3 (service: `cmd/main.go` + `internal//`; library: flat root package; CLI: `cmd//`; web: `pages/.tsx`, split into `pages//
.tsx` over 500 lines, `routes/` only exposes pages, `components/` for parts used by 2+ screens) +- [X] T016 [US4] Write §2 Layout map (18 rows) from the T002 re-measurement; the `web` row lists each screen in `web/src/routes/` over 500 lines as a gap, stating the `master` SHA; §3 Exceptions (`test/e2e`, `operator/api/v1alpha1`, `i18n`); §5 Not decided here +- [X] T017 [US4] *(OD-5)* Write §4 Enforcement: spec 028 adds the folder-shape check to `make lint` once every module matches; until then review enforces the doc +- [X] T018 [US4] Add `docs/layout.md` and `docs/archive/` to the repository map in `AGENTS.md` +- [X] T019 [US4] Commit (`docs: add target layout map for Go modules and web`) --- @@ -91,14 +91,14 @@ description: "Task list for spec 027: contributor docs and layout map" **Independent test**: every "Common workflows" entry has a row; links resolve (SC-005). -- [ ] T020 [US5] Add rows to "Task to doc map" in `docs/agent-architecture.md`: CRD field, API route, dashboard screen, game module, website, DB migration, CI change, where new code goes (→ `docs/layout.md`); keep the i18n row; add a `docs/layout.md` pointer under "How to use this index" -- [ ] T021 [US5] Commit (`docs: extend task-to-doc map`) +- [X] T020 [US5] Add rows to "Task to doc map" in `docs/agent-architecture.md`: CRD field, API route, dashboard screen, game module, website, DB migration, CI change, where new code goes (→ `docs/layout.md`); keep the i18n row; add a `docs/layout.md` pointer under "How to use this index" +- [X] T021 [US5] Commit (`docs: extend task-to-doc map`) --- ## Phase 7: Review and delivery -- [ ] T022 Tier-up review (`sonnet`) of the combined GP and WEB diffs against spec FR-001..FR-020 and this task list; fixes go to a new `haiku` fix wave +- [X] T022 Tier-up review (`sonnet`) of the combined GP and WEB diffs against spec FR-001..FR-020 and this task list; fixes go to a new `haiku` fix wave - [ ] T023 Push both branches; open WEB PR (`type: docs`) and GP PR (`type: docs`, `area: specs`, `area: shared`); check CI green and every CodeRabbit finding fixed or settled before calling either ready - [ ] T024 After the WEB PR merges: bump the `website` submodule pointer in GP (`chore: bump website submodule`) in its own PR - [ ] T025 After every PR merges: mark tasks `[X]`, `git mv specs/027-contributor-docs-layout-map specs/done_027-contributor-docs-layout-map` and update references in the same commit (rule 16); tell spec 028 that `docs/layout.md` is on `master`