# Working On Plugins

> Game plugin and adapter section design for Savecraft. Use when creating new plugins, designing GameState sections, adding sections to existing parsers, or working on section output in plugins/ or worker/src/adapters/. Triggers on section layout, progressive disclosure, GameState output, plugin sections, adapter sections, section sizing, or overview design.

- Skill: `joshsymonds/working-on-plugins` (Agent Skill)
- Install (CLI): `npx skillmds@latest add joshsymonds/working-on-plugins`
- Raw SKILL.md: https://api.skillmd.com/api/skills/joshsymonds/working-on-plugins/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: joshsymonds (https://skillmd.com/u/joshsymonds)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/joshsymonds/working-on-plugins

---


# Working on Plugins: Section Design

Both WASM plugins (`plugins/*/parser/`) and API adapters (`worker/src/adapters/`) produce the same `GameState` shape: `identity`, `summary`, and `sections`. This skill covers how to design sections so they work well with the MCP tools and AI consumers.

For the ndjson contract, WASM runtime, and plugin build system, see `docs/plugins.md`. For API adapter architecture, see `docs/adapters.md`.

## Overview Section (Required)

`get_save` embeds one section's data as the overview in its response. It picks the first match from this list:

```
character_overview → player_summary → overview → summary
```

**Every plugin MUST produce a section matching one of these names.** If none match, `get_save` falls back to the first section alphabetically — which may be a 200KB deck list or item dump.

The overview section is the AI's first look at the save. It should contain enough context to answer "what is this character/save?" and guide the AI to the right detailed sections.

### What belongs in the overview

- Identity: name, class, level, rank — whatever defines "who"
- Key stats: currency, progression milestones, win/loss record
- **Index of available sections** with pointers — deck names (not card lists), character names (not inventories), match list (not turn-by-turn logs)
- Anything that helps the AI decide which section to fetch next

### What does NOT belong in the overview

- Full item lists, card lists, or inventories
- Turn-by-turn logs, match replays, or event histories
- Large nested objects that the AI won't use to route

**Target: overview section < 15KB.** A typical overview is 2-10KB.

## Section Size Limits

| Limit | Value | Enforced by |
|-------|-------|-------------|
| Hard max per section | 80KB | `SECTION_SIZE_LIMIT` in `worker/src/mcp/tools.ts` — `get_section` rejects sections over this |
| Overview target | < 15KB | Convention — keeps `get_save` response usable |
| Individual item section | < 10KB typical | Convention — per-deck, per-character sections |

If a section could exceed 80KB with realistic data, it MUST be split.

## Progressive Disclosure Pattern

When a game has a large collection (decks, characters, items, matches), split into per-item sections with an index in the overview.

### Pattern

```
overview section:
  decks: [{name: "Slivers", format: "Brawl", section: "deck:Slivers"}, ...]

deck:Slivers section:    (full card list, ~5KB)
deck:Control section:    (full card list, ~3KB)
```

The AI sees the index in the overview and fetches individual items as needed. This is how D2R shared stash (`overview` + `tab1`, `tab2`, ...) and MTGA (`player_summary` + `deck:*`, `game:*`) work.

### When to split

Split when **any** of these are true:
- Collection has variable size controlled by the player (decks, blueprints, characters)
- A single collection item can be > 5KB (full card lists, turn-by-turn logs)
- Total collection could exceed 80KB (80 decks × 5KB = 400KB)

### Section naming for per-item sections

Use `prefix:human_readable_name` — the AI requests sections by name, so readability matters.

```
deck:[HB] Slivers          (MTGA deck)
game:7a0be838-0033-4e16    (MTGA game log by matchId)
tab1, tab2, tab3            (D2R stash tabs)
character:Warrior            (per-character sections)
```

### Stale section cleanup

The daemon sends `allSectionNames` in every `PushSave` message. The worker deletes any sections NOT in this list. Dynamic section names (deck:*, game:*) work correctly — the parser includes them all in the output, and the worker cleans up sections that no longer exist.

## Section Descriptions

Section descriptions are the AI's guide for when to fetch a section. Write them as directives, not documentation.

**Good** (tells AI when/why to fetch):
```
"Deck list for [HB] Slivers (Brawl) — main deck, sideboard, and command zone cards"
"Turn-by-turn game log for match abc123 — use to analyze play sequencing and identify misplays"
"Aggregated character stats: resistances, magic find, FCR/FHR breakpoints — use to evaluate gear upgrades"
```

**Bad** (explains what it is, not when to use it):
```
"Contains the player's deck data"
"Game log information"
"Character statistics"
```

## Existing Section Patterns by Game

| Game | Overview Section | Per-Item Sections | Reference |
|------|-----------------|-------------------|-----------|
| D2R Character | `character_overview` | None (sections are small) | `plugins/d2r/parser/main.go` |
| D2R Stash | `overview` | `tab1`, `tab2`, ... | `plugins/d2r/parser/main.go:545` |
| MTGA | `player_summary` | `deck:*`, `game:*` | `plugins/magic/parser/main.go` |
| Clair Obscur | `overview` | `character:*` | `plugins/clair-obscur/parser/` |
| WoW | `character_overview` | None (API data is bounded) | `worker/src/adapters/wow/` |
| SDV | `player_summary` | None (data is naturally bounded) | `plugins/sdv/parser/main.go` |

## Attribution (Required)

Every plugin must declare an `[attribution]` section in `plugin.toml` listing the third-party IP sources whose data it uses. The build pipeline reads this to embed legal disclaimers in views.

```toml
[attribution]
sources = ["wotc", "scryfall", "17lands"]
```

Valid source keys are defined in `views/src/attributions.ts`: `wotc`, `scryfall`, `17lands`, `blizzard`, `raiderio`, `ludeon`, `concernedape`, `kepler`. To add a new source, add it to the `SOURCES` record in that file first.

The build fails if `[attribution]` is missing or uses an unknown key.

## Checklist for New Plugins

- [ ] Overview section name matches `OVERVIEW_SECTION_NAMES`
- [ ] Overview section < 15KB with realistic data
- [ ] All sections < 80KB with realistic data
- [ ] Large collections use per-item sections with index in overview
- [ ] Section descriptions are AI-directive (when/why to fetch)
- [ ] Section data is always a JSON object (not array, string, or scalar)
- [ ] `[attribution]` declared in `plugin.toml` with valid source keys
- [ ] Tested with a real save file — measure actual section sizes

