Crew-driven actions + NEXT TURN engine (v0.10.0) #44

Closed
opened 2026-10-01 14:50:59 +02:00 by kamilkrzywda · 1 comment
Owner

Spec: docs/superpowers/specs/2026-10-01-crew-driven-actions-design.md — §3, §4, §5 (duration modifiers), §7, §8, §9, §11

Every action becomes a queued task assigned to a crew team (lead + optional helpers). Nothing resolves instantly; time advances only via POST /api/turn.

Scope

  • tasks table (schema v7; backfill creates empty table for existing saves)
  • POST /api/turn tick
  • all action endpoints → queueing with lead+helpers
  • duration model (§4; power 1 for everyone until #45)
  • trait duration modifiers (§5)
  • UI: NEXT TURN button bottom-right + turn counter, crew picker with live duration, task strip
  • OpenAPI update
  • rework existing unit/e2e assertions from instant-resolution to the queue+tick model

Acceptance criteria

  • tasks table per spec §3 (id, saveId, type JUMP|EXPLORE|SHIP_UPGRADE|SHIP_EXPAND|SHIP_BUILD|RESEARCH, payload JSON, leadCrewId, helpers JSON array, duration, remaining, status ACTIVE|PENDING|DONE); DDL in lib/db/client.ts synced with lib/db/schema.ts; idempotent backfill for existing saves
  • POST /api/turn advances exactly 1 turn: decrement ACTIVE tasks → resolve completions (per-type resolvers reusing save.ts / ship / research logic) → grant events → free team → auto-start FIFO successors → mission-completion checks; returns full snapshot
  • All action endpoints queue instead of resolving (POST /api/jump, /api/explore, /api/ship/{upgrade,expand,build}, /api/research gain leadCrewId + helperCrewIds[]); no turn increment on queue; costs deducted at queue time (ship work & research)
  • Duration = max(1, ceil(work / totalPower) + mismatchPenalty + traitModifierSum); work points EXPLORE 4 · JUMP hops×3 · SHIP 6 · RESEARCH 9; power 1 per crew until #45; all constants in new lib/game/crew-costs.ts (§11)
  • Role mismatch: lead outside preferred role → +1 turn (JUMP→PILOT/NAVIGATOR · EXPLORE→SCIENTIST · SHIP work→ENGINEER/TECHNICIAN · RESEARCH→SCIENTIST); helpers never penalised; STEADY UNDER PRESSURE cancels it
  • Trait duration modifiers per §5 tables apply to the lead only (7 good + 8 bad)
  • Validation: unknown crew → 404; busy/committed crew → 409; in-transit or second jump → 409; unreachable target / unaffordable cost / build gate → existing error codes
  • In-transit rule per §3: no new queuing while a JUMP is ACTIVE; pending jumps do not block; only one JUMP at a time (active or pending); non-jump work continues through the jump and resolves against its target
  • Mission accept becomes instant/free; completion checks run on tick when the satisfying task completes (jump arrival → DELIVER; scan → EXPLORE/FIND)
  • Snapshots (world/system/crew responses) gain active+pending tasks array {id, type, targetLabel, leadName, helperNames[], remaining}; admin CRUD panel picks up new tables automatically
  • UI: NEXT TURN button fixed bottom-right + turn counter (empty queue = free skip); inline team builder (lead radio + helper checkboxes) with live-computed duration; busy/committed members disabled with reason; active-tasks strip bottom-left, pending FIFO tasks dimmed
  • OpenAPI spec updated in lib/openapi.ts
  • Unit tests per §9: duration matrix (work values × power bands × mismatch × traits), queue validation rules (busy 409, in-transit 409, second jump 409), tick resolution (completion effects, FIFO auto-start, mission checks), cost deduction at queue time
  • E2E reworked to the queue+tick model per §9 (CI-only)

Process (AGENTS.md loop)

One feature branch off master → version bump v0.10.0 + CHANGELOG entry at top → local verify (npx tsc --noEmit, npx vitest run, npx playwright test --list; no local e2e) → push, CI auto-merge on green (verify merged_at) → tag v0.10.0 on master → manual deploy (docker compose up -d --build) → close issue with comment.
Also: flip the spec doc status line to approved and correct its §10 table issue numbers (#44 engine, #45 skills, #43 traits) when committing it on this branch.

**Spec:** `docs/superpowers/specs/2026-10-01-crew-driven-actions-design.md` — §3, §4, §5 (duration modifiers), §7, §8, §9, §11 Every action becomes a queued task assigned to a crew team (lead + optional helpers). Nothing resolves instantly; time advances only via `POST /api/turn`. ## Scope - tasks table (schema v7; backfill creates empty table for existing saves) - POST /api/turn tick - all action endpoints → queueing with lead+helpers - duration model (§4; power 1 for everyone until #45) - trait duration modifiers (§5) - UI: NEXT TURN button bottom-right + turn counter, crew picker with live duration, task strip - OpenAPI update - rework existing unit/e2e assertions from instant-resolution to the queue+tick model ## Acceptance criteria - [ ] `tasks` table per spec §3 (id, saveId, type JUMP|EXPLORE|SHIP_UPGRADE|SHIP_EXPAND|SHIP_BUILD|RESEARCH, payload JSON, leadCrewId, helpers JSON array, duration, remaining, status ACTIVE|PENDING|DONE); DDL in lib/db/client.ts synced with lib/db/schema.ts; idempotent backfill for existing saves - [ ] `POST /api/turn` advances exactly 1 turn: decrement ACTIVE tasks → resolve completions (per-type resolvers reusing save.ts / ship / research logic) → grant events → free team → auto-start FIFO successors → mission-completion checks; returns full snapshot - [ ] All action endpoints queue instead of resolving (`POST /api/jump`, `/api/explore`, `/api/ship/{upgrade,expand,build}`, `/api/research` gain `leadCrewId` + `helperCrewIds[]`); no turn increment on queue; costs deducted at queue time (ship work & research) - [ ] Duration = max(1, ceil(work / totalPower) + mismatchPenalty + traitModifierSum); work points EXPLORE 4 · JUMP hops×3 · SHIP 6 · RESEARCH 9; power 1 per crew until #45; all constants in new lib/game/crew-costs.ts (§11) - [ ] Role mismatch: lead outside preferred role → +1 turn (JUMP→PILOT/NAVIGATOR · EXPLORE→SCIENTIST · SHIP work→ENGINEER/TECHNICIAN · RESEARCH→SCIENTIST); helpers never penalised; STEADY UNDER PRESSURE cancels it - [ ] Trait duration modifiers per §5 tables apply to the lead only (7 good + 8 bad) - [ ] Validation: unknown crew → 404; busy/committed crew → 409; in-transit or second jump → 409; unreachable target / unaffordable cost / build gate → existing error codes - [ ] In-transit rule per §3: no new queuing while a JUMP is ACTIVE; pending jumps do not block; only one JUMP at a time (active or pending); non-jump work continues through the jump and resolves against its target - [ ] Mission accept becomes instant/free; completion checks run on tick when the satisfying task completes (jump arrival → DELIVER; scan → EXPLORE/FIND) - [ ] Snapshots (world/system/crew responses) gain active+pending tasks array {id, type, targetLabel, leadName, helperNames[], remaining}; admin CRUD panel picks up new tables automatically - [ ] UI: NEXT TURN button fixed bottom-right + turn counter (empty queue = free skip); inline team builder (lead radio + helper checkboxes) with live-computed duration; busy/committed members disabled with reason; active-tasks strip bottom-left, pending FIFO tasks dimmed - [ ] OpenAPI spec updated in lib/openapi.ts - [ ] Unit tests per §9: duration matrix (work values × power bands × mismatch × traits), queue validation rules (busy 409, in-transit 409, second jump 409), tick resolution (completion effects, FIFO auto-start, mission checks), cost deduction at queue time - [ ] E2E reworked to the queue+tick model per §9 (CI-only) ## Process (AGENTS.md loop) One feature branch off master → version bump v0.10.0 + CHANGELOG entry at top → local verify (`npx tsc --noEmit`, `npx vitest run`, `npx playwright test --list`; no local e2e) → push, CI auto-merge on green (verify merged_at) → tag v0.10.0 on master → manual deploy (`docker compose up -d --build`) → close issue with comment. Also: flip the spec doc status line to approved and correct its §10 table issue numbers (#44 engine, #45 skills, #43 traits) when committing it on this branch.
Author
Owner

v0.10.1 merged to master — PR #47, merge commit 678cd48a.

Shipped:

  • Hydration-safe banner clock (f1633eb) — the React 19 hydration mismatch on every production load (the build-time clock in the prerendered banner) is fixed; CI-verified (no hydration errors in any job log).
  • World-map jump dispatch fix (e792c8a) — the ACTUAL cause of the three deterministically red e2e jump specs: TeamBuilder's blocked term was undefined (not null) whenever no jump was queued (Array.find semantics), so the roster rendered (truthiness check) while the DISPATCH button's blocked !== null guard read it as blocked — the JUMP panel's dispatch was permanently dead on an empty queue, i.e. in the state every one of those specs reaches (smoke 174/202/516, red on runs 396/399/400 with 300 s waitForResponse timeouts). The fix coalesces the queued-jump search to null so blocked is strictly QueueDto | null.

Verification: full e2e suite green on the merged head (run 402 / job 413: 26/26, including the three formerly red specs, 5.7–6.9 s each; restart-persistence spec green); unit suite green (166 tests).

Tags: v0.10.0 → 9020a77, v0.10.1 → 678cd48a.

Deploy: PENDING — the deploy guard requires a clean main checkout, but /home/krzyw/projects/sandbox/space-wanderer has untracked .opencode/ and docs/, so the first production deploy was skipped; https://space-wanderer.dev.kkhost.pl is still undeployed (404). To ship: reset the main checkout to origin/master (keeping the untracked files) and run docker compose up -d --build there, then verify /api/version = 0.10.1.

v0.10.1 merged to master — PR #47, merge commit 678cd48a. Shipped: - Hydration-safe banner clock (f1633eb) — the React 19 hydration mismatch on every production load (the build-time clock in the prerendered banner) is fixed; CI-verified (no hydration errors in any job log). - World-map jump dispatch fix (e792c8a) — the ACTUAL cause of the three deterministically red e2e jump specs: TeamBuilder's `blocked` term was `undefined` (not `null`) whenever no jump was queued (`Array.find` semantics), so the roster rendered (truthiness check) while the DISPATCH button's `blocked !== null` guard read it as blocked — the JUMP panel's dispatch was permanently dead on an empty queue, i.e. in the state every one of those specs reaches (smoke 174/202/516, red on runs 396/399/400 with 300 s `waitForResponse` timeouts). The fix coalesces the queued-jump search to null so `blocked` is strictly `QueueDto | null`. Verification: full e2e suite green on the merged head (run 402 / job 413: 26/26, including the three formerly red specs, 5.7–6.9 s each; restart-persistence spec green); unit suite green (166 tests). Tags: v0.10.0 → 9020a77, v0.10.1 → 678cd48a. Deploy: PENDING — the deploy guard requires a clean main checkout, but `/home/krzyw/projects/sandbox/space-wanderer` has untracked `.opencode/` and `docs/`, so the first production deploy was skipped; https://space-wanderer.dev.kkhost.pl is still undeployed (404). To ship: reset the main checkout to origin/master (keeping the untracked files) and run `docker compose up -d --build` there, then verify `/api/version` = 0.10.1.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/SpaceWanderer#44