Social / Chat surface: chat log, activity timeline, reputation graph (redacted, no restart) #63

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

Context

Net-new Social / Chat surface. The survey (#7) flags ChatSent as a full chat log and notes the social actions map to no existing ticket - they deserve their own. All already exported - no game restart.

No-reset spine

Consume the social 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.

Exporters

ChatSent (full chat log), Play and FirstLogin (activity / new arrivals), ReputationTransfer (who reps whom).

Redaction (load-bearing here)

Chat is player-authored content - treat it like the outside-in public MCP does: redact player names by default (hash/handle), and gate raw names/message bodies behind the same redaction posture the public MCP uses. Do not surface raw chat + real names on a public path. Default to counts/timelines and redacted samples; a names-shown mode is operator-gated, not public.

Scope

  • MCP tool get_eco_social (text + JSON + _meta.ui card): activity timeline (logins, playtime, new arrivals from FirstLogin), chat volume over time and by channel, a reputation graph (who reps whom), and redacted recent-chat samples.
  • /preview/social.json data plane (redacted).
  • SPA /social route - activity timeline, chat-volume chart, reputation graph, redacted chat feed. Live badge on the homepage. Read the dataviz skill before charts.

Acceptance

  • Redaction enforced by default; a test proves raw names/message bodies are not emitted on the public path.
  • MCP tool registered, ward exec smoke passes; thin/zero-data paths degrade gracefully.
  • respx-mocked unit tests for aggregation + redaction.
  • SPA route renders against /preview/social.json, vitest test, ward exec frontend-test/lint green.
  • ward exec test/lint green; docs/FEATURES.md updated. Survey: #7.
## Context Net-new **Social / Chat** surface. The survey ([#7](https://forgejo.coilysiren.me/coilyco-gaming/eco-app/issues/7)) flags `ChatSent` as a **full chat log** and notes the social actions map to no existing ticket - they deserve their own. All already exported - no game restart. ## No-reset spine Consume the social 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. ## Exporters `ChatSent` (full chat log), `Play` and `FirstLogin` (activity / new arrivals), `ReputationTransfer` (who reps whom). ## Redaction (load-bearing here) Chat is **player-authored content** - treat it like the outside-in public MCP does: **redact player names by default** (hash/handle), and gate raw names/message bodies behind the same redaction posture the public MCP uses. Do not surface raw chat + real names on a public path. Default to counts/timelines and redacted samples; a names-shown mode is operator-gated, not public. ## Scope * **MCP tool** `get_eco_social` (text + JSON + `_meta.ui` card): activity timeline (logins, playtime, new arrivals from `FirstLogin`), chat volume over time and by channel, a reputation graph (who reps whom), and redacted recent-chat samples. * **`/preview/social.json`** data plane (redacted). * **SPA `/social` route** - activity timeline, chat-volume chart, reputation graph, redacted chat feed. Live badge on the homepage. Read the `dataviz` skill before charts. ## Acceptance * Redaction enforced by default; a test proves raw names/message bodies are not emitted on the public path. * MCP tool registered, `ward exec smoke` passes; thin/zero-data paths degrade gracefully. * `respx`-mocked unit tests for aggregation + redaction. * SPA route renders against `/preview/social.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-63 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-05T04:40:31Z). 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-63` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-05T04:40:31Z). 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_social MCP tool + /preview/social.json + SPA /social route landed on main; public-default redaction enforced by test.

This one leaned hard on the trades/crafting spine, which is exactly why it went smoothly on the plumbing side - streamed CSV, the #5 column-corrector, the citizens join, Time-to-day were all already load-bearing and battle-tested, so wiring a fourth consumer was mostly shape work. The interesting part was redaction. I read it as "raw names and raw message bodies must not reach the public path," so handles are keyed on the resolved identity and the same handle is scrubbed into message bodies in place - that let me write one blunt test that serializes the whole public payload and asserts no real name appears anywhere, which is the guarantee I'd actually want to trust.

What fought back: I couldn't capture the real ChatSent / Play / FirstLogin / ReputationTransfer column headers - the exporter is admin-gated and no key is mounted in this container (ChatSent 401'd, the rest timed out). So every social field is picked from a candidate list of plausible header names and degrades to a warning rather than a crash if a column is missing. That's the one soft spot: the aggregation logic is solid, but the exact column names are educated guesses. Confidence is high on the redaction + wiring + graceful-degrade behavior, medium on the field mapping until someone runs it against a live server with a key.

Follow-up worth filing: confirm the four social action column shapes against a live capture and tighten the candidate lists (noted in docs/FEATURES.md). Also the reputation graph caps at the top 10 nodes by volume - fine for a card, but a busy late-cycle server might want a zoom/filter. Merge with origin/main picked up the new Ecoregion + Replay surfaces cleanly; only FEATURES.md and index.css needed hand-resolution.

WARD-OUTCOME: done - get_eco_social MCP tool + /preview/social.json + SPA /social route landed on main; public-default redaction enforced by test. This one leaned hard on the trades/crafting spine, which is exactly why it went smoothly on the plumbing side - streamed CSV, the #5 column-corrector, the citizens join, Time-to-day were all already load-bearing and battle-tested, so wiring a fourth consumer was mostly shape work. The interesting part was redaction. I read it as "raw names *and* raw message bodies must not reach the public path," so handles are keyed on the resolved identity and the same handle is scrubbed into message bodies in place - that let me write one blunt test that serializes the whole public payload and asserts no real name appears anywhere, which is the guarantee I'd actually want to trust. What fought back: I couldn't capture the real ChatSent / Play / FirstLogin / ReputationTransfer column headers - the exporter is admin-gated and no key is mounted in this container (ChatSent 401'd, the rest timed out). So every social field is picked from a candidate list of plausible header names and degrades to a warning rather than a crash if a column is missing. That's the one soft spot: the aggregation logic is solid, but the exact column names are educated guesses. Confidence is high on the redaction + wiring + graceful-degrade behavior, medium on the field mapping until someone runs it against a live server with a key. Follow-up worth filing: confirm the four social action column shapes against a live capture and tighten the candidate lists (noted in docs/FEATURES.md). Also the reputation graph caps at the top 10 nodes by volume - fine for a card, but a busy late-cycle server might want a zoom/filter. Merge with origin/main picked up the new Ecoregion + Replay surfaces cleanly; only FEATURES.md and index.css needed hand-resolution.
Sign in to join this conversation.
No description provided.