# Thunder Smith character-skin contract

Thunder Smith is SmolForge's small animated workshop familiar. Contributors
may create alternate character skins, but every skin must fit the same frame,
action, anchor, and compositing rules so SmolForge can place it into official
workshop scenes without per-skin exceptions.

New user skins follow the profile-aware
[character skin specification](../../character-skins.md): `skin.json` plus one
horizontal strip per action. The older `8 × 9` atlas remains a validated
runtime artifact and can be exported into that user-facing format.

## Kit contents

- [Official Thunder Smith spritesheet](./official/thunder-smith-spritesheet.webp)
- [Apron and horned-helmet vNext study](./official/thunder-smith-apron-helmet-study.webp)
- [Spritesheet contact sheet](./official/thunder-smith-contact-sheet.png)
- [Action reference board](./official/thunder-smith-action-board.webp)
- [Official SmolForge prop board](./official/smolforge-prop-board.webp)
- [Generated motion previews](./official/previews/)
- [Current product-pack contact sheet](./official/product-skin/source/qa/contact-sheet.webp)
- [Current product-pack motion previews](./official/product-skin/source/qa/previews/)
- [Current product-pack regeneration receipt](./official/product-skin/source/regeneration-receipt.json)
- [Skin-swap stage demonstration](./official/skin-swap-stage-demo.webp)
- [Upload demo screenshot](./official/upload-demo.png)
- [Example exported Sites composite](./official/example-sites-composite.png)
- [Validation report](./official/validation.json)
- [Machine-validatable v1 skin schema](../../skin.schema.json)
- [Legacy atlas metadata (not a `skin.json` schema)](./skin-contract.json)
- [Generation prompts](./GENERATION-PROMPT.md)
- [User-package exporter](./scripts/export-user-package.mjs)
- [Interactive upload and compositing demo](./demo/index.html)

The interactive demo accepts either a transparent standalone PNG/WebP or the
legacy `1536 × 1872` atlas. It validates atlas dimensions, required frames,
and transparent unused cells; lets the user animate or scrub each state; and
composites the character between fixed SmolForge background and foreground
prop layers. It can export the resulting stage as a `960 × 600` PNG.

Export the legacy canonical atlas as a valid compatibility `product` profile
package:

```bash
node docs/design/thunder-smith-kit/scripts/export-user-package.mjs \
  --output /tmp/thunder-smith-product
```

This exporter is intentionally crop-only: it never mirrors the readable `S`,
invents missing frames, resizes art, or hides game-profile gaps. It does not
recreate `official/product-skin/`, whose non-directional actions are curated
generated art. The crop-only export and the checked-in official product skin
are two distinct packages.

The GIFs under `official/previews/` are legacy-atlas visual-QA derivatives.
They remain useful for reviewing the compatibility source, but they do not
represent the current curated product strips unless explicitly regenerated.
The official product package is split into authoring [`source/`](./official/product-skin/source/)
and installable [`dist/`](./official/product-skin/dist/). Run
`npm run build:character-skin` from the repository root to validate the
canonical v0.4 manifest contract, file magic, decoded dimensions, alpha,
per-frame occupancy, safe bounds, and transparent-pixel RGB; rebuild the
installable package; emit lossless runtime WebPs; and regenerate the current
contact sheet, per-action previews, receipt, and TypeScript manifest.
`npm run check:character-skin` fails when any derived package, runtime, or QA
artifact is missing, stale, or unexpected.

The contact sheet uses the receipt's fixed nine-row order: idle, walk right,
walk left, wave, celebrate, fail, wait, work, and review. Its eight columns are
the maximum clip width; unused cells intentionally show only the checkerboard.
The preview GIFs loop for visual QA even when a runtime clip has different
playback semantics.

## The official Thunder Smith `S` badge

The vNext official Thunder Smith carries one unmistakable uppercase `S` on a
terracotta leather work apron:

- The `S` is embossed directly into the apron bib rather than mounted on a
  separate square body tile.
- It is part of the character layer and appears in every used frame.
- It remains on the same anatomical side and at a consistent apparent scale.
- The `S` must read normally, never backward. A mechanically mirrored frame
  must have its badge redrawn or corrected.
- It is the only letter, word, logo, or rune in the official character art.

The official product package uses the apron-and-horned-helmet Thunder Smith for
every product action, including independently generated left- and right-facing
walk strips. No strip is mirrored; the `S` badge, hammer hand, and tail side
remain consistent. Generated upstream videos are not committed, so the curated
package is pinned by its manifest, selected source pixels, output hashes, and
regeneration receipt rather than deterministically regenerated from the legacy
atlas. The exact committed source-art revision is recorded in `skin.json` and
`export-report.json`. The public contract Gist is a specification source, not
the provenance source for these pixels.

The checked-in manifest and license file currently declare `CC-BY-4.0`. That
assertion has repository history but no independent rights evidence in this
kit. The project owner must confirm that the source and generated media can be
distributed under those terms before enabling public registry distribution;
the build preserves the existing declaration and does not treat it as proof.

The current `fail.v01` source strip bows in disappointment and then returns to
an upright stance. Until replacement art provides a truly failed final hold,
the manifest truthfully uses `loop` with bowed frame 3 as its reduced-motion
poster. It must not use `hold` on the neutral final frame.

The current product-pack contact sheet passes the structural checks, but manual
visual review still shows faint baked floor/contact shadows in parts of the
directional walks and celebrate sequence. Those pixels predate the deterministic
packager and conflict with the clean character-layer ideal below. Keep them as
known source-art debt rather than deleting character pixels heuristically;
replacement strips should remove them before this art is treated as registry
gold.

Community skins do **not** need to wear the `S`. Their author may use a simple
personal emblem, a blank badge, or no badge at all. The upload contract is
about geometry and behavior, not ownership of the character's identity.
SmolForge remains recognizable through the fixed stage, prop, palette, and UI
layers around the uploaded character.

## Legacy atlas geometry

The app reads one fixed transparent atlas:

| Property | Required value |
| --- | ---: |
| Cell | `192 × 208 px` |
| Columns | `8` |
| Rows | `9` |
| Atlas | `1536 × 1872 px` |
| Row order | Fixed, as listed below |

Used cells must contain one complete character pose. Every cell after a row's
last used frame must be fully transparent.

| Row | State | Frames | Used columns | Timing |
| ---: | --- | ---: | --- | --- |
| 0 | `idle` | 6 | 0–5 | `280, 110, 110, 140, 140, 320 ms` |
| 1 | `running-right` | 8 | 0–7 | `120 ms` each; final frame `220 ms` |
| 2 | `running-left` | 8 | 0–7 | `120 ms` each; final frame `220 ms` |
| 3 | `waving` | 4 | 0–3 | `140 ms` each; final frame `280 ms` |
| 4 | `jumping` | 5 | 0–4 | `140 ms` each; final frame `280 ms` |
| 5 | `failed` | 8 | 0–7 | `140 ms` each; final frame `240 ms` |
| 6 | `waiting` | 6 | 0–5 | `150 ms` each; final frame `260 ms` |
| 7 | `running` | 6 | 0–5 | `120 ms` each; final frame `220 ms` |
| 8 | `review` | 6 | 0–5 | `150 ms` each; final frame `280 ms` |

`running` means actively working or processing. It is not locomotion.
Directional travel belongs only in `running-right` and `running-left`.

## Character layer versus SmolForge layers

A submitted skin contains the character only:

- body, clothing, optional personal badge, and identity-defining worn
  accessories;
- hard-edged, physically attached effects when a state truly needs them;
- transparent pixels everywhere else.

The character atlas must not contain scenery, floors, cast shadows, floating
marks, UI, labels, or task-specific objects. In particular, do not bake any of
the official SmolForge objects into a skin.

SmolForge separately owns and composites:

1. **Stage layer** — workshop floor, backdrop, lighting bed, and placement.
2. **Prop layer** — official product objects and contextual effects.
3. **Character layer** — the selected official or community skin.
4. **UI layer** — labels, controls, status text, and accessibility affordances.

This separation lets one skin work on Code, Actions, Sites, Transcripts, and
future surfaces without regenerating the character.

## Official action and object mapping

The app may pair a character state with an official object according to page
context. The object is never required inside the uploaded atlas.

| Product context | Official stage or prop | Typical character state |
| --- | --- | --- |
| SmolForge identity / repository | Smolnir hammer and rune chest | `idle`, `waving`, or `review` |
| Code navigation / moving work | Repository tile or chest parcel | `running-left` or `running-right` |
| Issues | Cracked standing stone with a terracotta repair seam | `waiting`, `running`, or `failed` |
| Pull requests | Interlocking forged rings | `review` or `running` |
| Actions | Pair of thunder drums | `running`, `waiting`, or `failed` |
| Sites / deploys | Longship carrying a repository parcel | `running`, `jumping`, or `review` |
| Agent transcripts | Black raven carrying a sealed scroll | `review` or `waiting` |
| Successful release | Tiny celebratory goat, used sparingly | `jumping` or `waving` |

These are compositing defaults, not alternate meanings for the animation rows.
For example, `failed` must read as failure whether it appears beside an issue
stone or a thunder drum.

## Cell anchors, baseline, facing, and scale

Coordinates below are local to each `192 × 208` cell.

- **Safe bounds:** keep visible character pixels within `x = 18…174` and
  `y = 16…192`.
- **Ground anchor:** the midpoint between the feet or lowest body contact is
  anchored at `(96, 192)` in grounded poses.
- **Baseline:** `y = 192`. Do not draw a floor or shadow on it.
- **Canonical facing:** three-quarter front-right, with the face and `S` badge
  readable.
- **Directional facing:** `running-right` faces and travels toward image right;
  `running-left` faces and travels toward image left.
- **Non-directional rows:** retain the canonical three-quarter facing unless
  the motion needs a small temporary head or torso turn.
- **Scale:** establish one canonical body scale from the base character and
  hold it across the atlas. A typical grounded character is `150–168 px` tall,
  never wider than the safe bounds, with no frame-to-frame zooming.
- **Airborne motion:** move the character upward for `jumping`; do not shrink
  it. The conceptual landing point remains `(96, 192)`.
- **Wide poses:** waving limbs and running strides must remain inside the safe
  bounds and must not cross into a neighboring cell.

Small pose-driven bounding-box changes are expected. Apparent body scale,
camera angle, material, and head-to-body proportion must remain stable.

## State behavior

- `idle`: subtle breathing, blink, or tiny material sway; visibly animated but
  calm enough to sit beside code.
- `running-right`: clear rightward locomotion with an alternating gait.
- `running-left`: clear leftward locomotion with matching identity and cadence.
- `waving`: greeting shown through the limb pose, without motion marks.
- `jumping`: anticipation, lift, peak, descent, and settle using body position.
- `failed`: readable deflation, sadness, or error reaction without a floating
  red X.
- `waiting`: expectant, asking posture for approval, help, or user input.
- `running`: focused work, processing, scanning, or busy-hand motion without
  walking or directional travel.
- `review`: attentive inspection using lean, gaze, blink, or head tilt.

Do not use speed lines, dust, floor marks, glows, auras, loose sparkles,
floating punctuation, detached smoke, or separate tear droplets. Any approved
effect must touch the character, be opaque, and remain inside the frame.

## Upload validation

A user upload is accepted only when all of the following pass:

### File and grid

- PNG or WebP with alpha support.
- Exactly `1536 × 1872 px`.
- Exactly the required `8 × 9` cell layout and row order.
- Every required used cell is non-empty.
- Every unused cell is fully transparent.
- Fully transparent pixels contain no hidden RGB residue.

### Geometry

- No visible pixel crosses a cell boundary.
- Character pixels stay inside the `18 px` horizontal and `16 px` vertical safe
  margins, except that grounded poses may touch the `y = 192` baseline.
- Grounded poses align to the same baseline and horizontal anchor.
- Scale and camera remain stable; preview animation has no size popping.

### Identity and semantics

- The official Thunder Smith has one readable, correctly oriented `S` badge in
  every frame. Community skins consistently preserve whatever badge choice
  their author made: personal emblem, blank badge, or none.
- The same character identity, face, proportions, palette, material, and worn
  accessories persist across all rows.
- Directional rows face the correct way and show an alternating gait.
- Each row communicates its declared state; `running` is work, not foot-running.
- `idle` contains visible micro-motion rather than six identical frames.

### Clean compositing

- Background is transparent, with no chroma-key fringe.
- No scenery, floor, cast or drop shadow, UI, readable text beyond the `S`,
  official SmolForge prop, detached effect, guide mark, or neighboring pose is
  present.
- Edges are clean enough to remain readable when rendered at one cell or
  smaller.

Automated checks should reject structural failures. A contact sheet and
per-row motion preview must still receive visual review for identity drift,
backward badges, wrong facing, inert motion, and semantic mistakes.

## Contributor workflow

1. Design one canonical full-body character on a removable flat chroma
   background.
2. Lock the identity, camera, scale, palette, material, and any optional worn
   badge or emblem.
3. Generate one horizontal strip per row using the exact frame count.
4. Correct `running-left` rather than accepting a backward `S`.
5. Extract frames and compose the atlas deterministically; do not ask an image
   model to lay out the final `8 × 9` atlas.
6. Validate the atlas, then inspect a contact sheet and every row in motion.

See [GENERATION-PROMPT.md](./GENERATION-PROMPT.md) for a complete example and
a reusable one-row prompt.
