Progression / Skills history surface: skill trajectories, enrich /jobs with history (no restart) #64

Closed
opened 2026-07-05 04:40:48 +00:00 by coilysiren · 2 comments
Owner

Context

Net-new Progression / Skills history surface. The survey (#7) notes the progression exporters overlap the jobs mod's view and can enrich /jobs with history - today /jobs shows current skills, not how citizens got there. All already exported - no game restart.

No-reset spine

Consume the progression action exporters already live on the server, via the shared streamed-CSV plumbing (crafting.py aggregation, #5 column-corrector, id-to-name join, Time-to-day). No new C# mod. Cross-reference the jobs mod's /api/v1/skills current-state surface.

Exporters

GainProfession, GainSpecialty, LoseSpecialty, SpecialtyLevelUp, CharacterLevelUp, CompleteClass, EnrollAction, plus the 3 progression daily series.

Scope

  • MCP tool get_eco_progression (text + JSON + _meta.ui card): per-citizen skill trajectories (when professions/specialties were gained, level-up cadence), server-wide progression trends, class completion, and enrollment. Names via the citizens join.
  • /preview/progression.json data plane.
  • Enrich /jobs - add a history lane to the existing jobs UI (how each citizen reached their current skills) consuming this surface, alongside or as part of a /progression view. Cross-link with /crafting (skill provenance ties who-can-craft to what-got-crafted).
  • Read the dataviz skill before charts.

Acceptance

  • MCP tool registered, ward exec smoke passes; thin/zero-data paths degrade gracefully.
  • respx-mocked unit tests for the aggregation + citizen join.
  • SPA history lane renders against /preview/progression.json, vitest test, ward exec frontend-test/lint green.
  • ward exec test/lint green; docs/FEATURES.md updated. Survey: #7.
## Context Net-new **Progression / Skills history** surface. The survey ([#7](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/7)) notes the progression exporters overlap the jobs mod's view and can enrich `/jobs` with **history** - today `/jobs` shows current skills, not how citizens got there. All already exported - no game restart. ## No-reset spine Consume the progression action exporters already live on the server, via the shared streamed-CSV plumbing (`crafting.py` aggregation, `#5` column-corrector, id-to-name join, Time-to-day). No new C# mod. Cross-reference the jobs mod's `/api/v1/skills` current-state surface. ## Exporters `GainProfession`, `GainSpecialty`, `LoseSpecialty`, `SpecialtyLevelUp`, `CharacterLevelUp`, `CompleteClass`, `EnrollAction`, plus the 3 progression daily series. ## Scope * **MCP tool** `get_eco_progression` (text + JSON + `_meta.ui` card): per-citizen skill trajectories (when professions/specialties were gained, level-up cadence), server-wide progression trends, class completion, and enrollment. Names via the citizens join. * **`/preview/progression.json`** data plane. * **Enrich `/jobs`** - add a history lane to the existing jobs UI (how each citizen reached their current skills) consuming this surface, alongside or as part of a `/progression` view. Cross-link with `/crafting` (skill provenance ties who-can-craft to what-got-crafted). * Read the `dataviz` skill before charts. ## Acceptance * MCP tool registered, `ward exec smoke` passes; thin/zero-data paths degrade gracefully. * `respx`-mocked unit tests for the aggregation + citizen join. * SPA history lane renders against `/preview/progression.json`, vitest test, `ward exec frontend-test`/`lint` green. * `ward exec test`/`lint` green; `docs/FEATURES.md` updated. Survey: [#7](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/7).
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-eco-app-64 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-05T04:40:58Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-eco-app-64` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-05T04:40:58Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - get_eco_progression tool + /preview/progression.json + /progression SPA page + /jobs skill-history lane landed on main (closes #64).

Honest retrospective:

The spine work went fast because the crafting atlas (#5) and trades ledger (#6) had already paved every hard part: streamed CSV, the _corrected_index realign, the citizen id→name join, the cache-per-(base,key) pattern. Progression is genuinely "another consumer of the same plumbing," so most of the module wrote itself by analogy. The dataviz call that felt right was small-multiples: one single-hue sparkline per event kind sidesteps the multi-series categorical-color trap entirely and reads as one system in the existing --leaf/--meteor palette.

What fought back: I couldn't reach the game server (public host timed out, no admin key mounted), so I never captured the real progression exporter headers. The skill/profession/level column names are therefore best-effort candidates keyed defensively off the header — same posture trades.py took with the undecoded BoughtOrSold enum. The citizen id, time, and per-action counts anchor on universal columns and stay correct regardless, and everything degrades to "blank skill" rather than a wrong row, but a live capture should still confirm the names and the three progression daily-series names (I fall back to conservative flatlist keyword discovery for those). That's the main follow-up worth filing.

The other friction was pure timing: three sibling containers (issue-20 ecoregion, issue-63 social, issue-62 world) landed on main while I was finishing, so I integrated and resolved conflicts three times. All were additive "both added a tool/route/nav-link/FEATURES-bullet at the same spot" tangles — tedious but low-risk, and the full suite (440 py / 74 vitest / lint / mypy / smoke / precommit) is green after each.

Confidence: high on the aggregation logic, the graceful-degradation paths, and the SPA (all respx/vitest-tested end to end, and I drove /preview/progression.json against a dead exporter to confirm the clean empty+warnings response). Medium only on whether the live column names match my candidates — that's the one thing I genuinely couldn't verify from in here.

WARD-OUTCOME: done - get_eco_progression tool + /preview/progression.json + /progression SPA page + /jobs skill-history lane landed on main (closes #64). Honest retrospective: The spine work went fast because the crafting atlas (#5) and trades ledger (#6) had already paved every hard part: streamed CSV, the `_corrected_index` realign, the citizen id→name join, the cache-per-(base,key) pattern. Progression is genuinely "another consumer of the same plumbing," so most of the module wrote itself by analogy. The dataviz call that felt right was small-multiples: one single-hue sparkline per event kind sidesteps the multi-series categorical-color trap entirely and reads as one system in the existing --leaf/--meteor palette. What fought back: I couldn't reach the game server (public host timed out, no admin key mounted), so I never captured the real progression exporter headers. The skill/profession/level column *names* are therefore best-effort candidates keyed defensively off the header — same posture trades.py took with the undecoded BoughtOrSold enum. The citizen id, time, and per-action counts anchor on universal columns and stay correct regardless, and everything degrades to "blank skill" rather than a wrong row, but a live capture should still confirm the names and the three progression daily-series names (I fall back to conservative flatlist keyword discovery for those). That's the main follow-up worth filing. The other friction was pure timing: three sibling containers (issue-20 ecoregion, issue-63 social, issue-62 world) landed on main while I was finishing, so I integrated and resolved conflicts three times. All were additive "both added a tool/route/nav-link/FEATURES-bullet at the same spot" tangles — tedious but low-risk, and the full suite (440 py / 74 vitest / lint / mypy / smoke / precommit) is green after each. Confidence: high on the aggregation logic, the graceful-degradation paths, and the SPA (all respx/vitest-tested end to end, and I drove /preview/progression.json against a dead exporter to confirm the clean empty+warnings response). Medium only on whether the live column names match my candidates — that's the one thing I genuinely couldn't verify from in here.
Sign in to join this conversation.
No description provided.