## Essence

GameOS is a handheld game console shell: a full-panel picker (the shell) that hands off to one
of a fixed roster of procedural games, each rendered through an indexed 184x224 pipeline (a
256-entry RGB565 palette, upscaled 2x nearest to the panel's own 368x448) with no bitmap or
audio assets anywhere - everything on screen and in the mix is generated by code. This bundle
carries the shell and three of the donor's six games, plus the donor's own two harness apps:

- **The shell**: on the esp32-s3-touch-amoled-18 port, this is the donor's own real shell
  (`components/gos_shell/registry.c`/`apps.c`/`shell.c`, vendored byte-for-byte - see that
  port's `NOTICE.md`), the exact one Sylve's own physical board boots into: a light-gray,
  fine-striped field carrying a Wii-menu-style grid of white rounded "channel" tiles, one per
  game/app, each a cyan letter icon over its title, empty gray slots for open room, scrollable
  once the roster outgrows one screenful. A bottom bar holds a round SET button (opens a
  scrollable settings list: volume, brightness, aim mode, sensitivity, invert X/Y,
  recalibrate, factory reset), a "GAMEOS" label, and a battery percentage or, on a device with
  no battery HAL (this port's own case - see that port's `NOTICE.md`), "USB". A swipe down from
  a running game's top edge pauses it into an overlay (RESUME / RESTART / CALIBRATE / QUIT, over
  a frozen, dimmed view of the game); QUIT is what actually returns to the grid. A first-run
  tilt-calibration wizard ("HOLD THE DEVICE HOW YOU PLAY", tap to set center) precedes the grid
  on a device that has never been calibrated before. The rp2350 port instead ships a smaller,
  from-scratch two-card picker (that port's own `launcher.c`) filling the identical functional
  role - not a copy of this real shell, and not attempted as one (see that port's own README).
- **GUNSHIP**: an AC-130-style thermal gunner. A HUD reports mission stats (saved/lost
  civilians, wave, salvage) across the top; the rest of the panel is an orbiting top-down
  thermal-sensor view of a town, in scanlined blue-to-white shades, with a small reticle.
  Zombies, survivors, shells and impacts are filled circles, quads and soft glowing blobs in
  the thermal ramp - brighter reads as hotter or faster. A briefing screen (mission stats,
  control legend) precedes each wave.
- **LUCKY 7**: a full-bleed slot machine face: three reel windows over a chrome cabinet, a side
  lever, LED-digit BET/WIN/cash readouts, and a paytable card behind a tap. Reels spin with
  backward anticipation, motion-blur ghosting, and a detent-stepped landing; a win strobes the
  payline and fountains coins out of the tray.
- **GOLF**: a full-resolution (not the indexed pipeline the other two use - see Demands),
  procedurally-generated 18-hole course rendered top-down: fairway, rough, sand and water
  hazards, a wind indicator, a swing-power bar. A title/attract screen with a champion's plaque
  precedes solo or pass-and-play rounds; a scorecard, initials entry and a top-5 leaderboard
  bookend each round. Struck balls arc, bounce and roll with real physics (sand/rough friction,
  wind drift, in-flight curl by club); holing one triggers a drop animation and, on an ace,
  confetti.

- **AIM TEST**: the donor's own harness app (`components/gos_shell/apps.c`), a 30-trial
  reaction/aim benchmark. A red target circle appears at a random spot; holding the aim
  crosshair inside it for 500ms scores the trial and spawns the next one, up to 30, then shows
  a median time-to-acquire readout ("PASS < 1200 MS") with a tap-to-retry. Four live-tuning
  sliders (CUT/BETA/RNG/CRV, the aim filter's own cutoff/beta/range/curve constants) sit in the
  bottom strip, draggable mid-run.
- **DIAG**: the donor's own harness app (`components/gos_shell/apps.c`), a single-screen live
  readout of every HAL subsystem this build has: pitch/roll, raw gyro, resolved aim vector and
  shake, touch position and state, the button bitmask, battery (or "NONE (USB)" on a device with
  none), free heap, a spatial-grid smoke-test query count, uptime and frame counter, plus a
  256-colour palette strip and a touch crosshair. A tap in the lower two-thirds beeps.

GameOS's own sixth game, still in development upstream, remains out of scope (unfinished in the
donor repository as cloned). **GOLF, AIM TEST and DIAG are not part of every port**: see each
pack's own port README for exactly which of the five it ships (today, GOLF ships on the
esp32-s3-touch-amoled-18 port only - its font and its per-hole world-state memory needed
pack-specific answers the rp2350 sibling has not been given yet; AIM TEST and DIAG ship on the
esp32-s3-touch-amoled-18 port only, once that port started vendoring the donor's real shell
rather than a bespoke picker - see that port's own `NOTICE.md`).

## Interactions

**The shell:**

- A tap-and-release on a tile launches that game/app. (intent: launching discards whatever was
  on screen before it, so it fires on release, never on press - a press-edge shell firing on the
  very first stray touch is a real bug the donor's own history reports.) On the donor's real
  shell (esp32-s3-touch-amoled-18 port), a drag on the grid instead scrolls it, and a contact
  that scrolled never launches on release - the grid only scrolls once its own roster outgrows
  one screenful.
- A tap on the bottom bar's SET button opens a scrollable settings list (volume, brightness, aim
  mode, sensitivity, invert X/Y, recalibrate, factory reset); a tap on its own back arrow
  returns to the grid. (donor's real shell only.)
- On first use (or after a factory reset), a tap anywhere dismisses the tilt-calibration wizard
  and sets the device's neutral pose. (donor's real shell only.)

**AIM TEST, once opened:**

- Holding the aim crosshair inside the current target for 500ms scores that trial and spawns the
  next, 30 total, then shows a median time-to-acquire result with a tap-to-retry. (intent: the
  donor's own risk-gate benchmark, unchanged.)
- Dragging in the bottom strip adjusts whichever of the four tuning sliders (CUT/BETA/RNG/CRV)
  the drag started over.

**DIAG, once opened:**

- A tap in the lower two-thirds beeps (a live HAL smoke test, not a menu action). (intent: proof
  the touch/audio path is alive while every other readout updates continuously on its own.)

**GUNSHIP, once launched:**

- Continuous device tilt aims the reticle within the fixed view. (intent: holding the device IS
  the aim, with no button in between - the donor's own default aim mode, unchanged here.)
- A touch anywhere in the lower two-thirds of the panel fires, held for continuous fire.
  (intent: the top third stays a HUD/menu strip a touch must never fire through.)
- The same lower-two-thirds touch, read during the pre-wave briefing screen, starts the
  mission. (intent: the one control the player already has their thumb on is what commits to
  the wave, so there is only one thing to learn.)

**LUCKY 7, once launched:**

- Dragging down on the reel unit pulls the lever; the drag's own length sets how hard, which
  sets how fast the reels spin. (intent: a touch gesture standing in for the donor's own
  accelerometer "yank" - see Demands - with the same "harder pull, harder spin" shape the donor
  itself already offers as a fallback.)
- A tap on the bet +/- buttons raises or lowers the bet; a tap on the marquee opens the
  paytable, and any tap closes it again.

**GOLF, once launched:**

- A drag-and-release on the ball, read as a direction and length, arms a swing. Where a raw
  accelerometer sample stream is available (this bundle's own preferred input - see Demands),
  the physical swing motion that follows - a backswing then a forward stroke, the donor's own
  swing detector - sets the shot's power; the drag's own direction sets its aim. Where no raw
  stream is available, the drag's own length stands in for power directly (the donor's own
  touch fallback). (intent: the donor's own signature mechanic - a real golf swing's motion, not
  a fixed-power tap.)
- A tap on the club indicator cycles putter/chipper/driver.
- Ordinary taps drive the title screen (mode select, options, leaderboard-clear-hold), the
  scorecard ("tap to continue"), initials entry and the pass-and-play hand-off card - the
  donor's own menu flow, unchanged.

**All three games, and AIM TEST/DIAG:**

- Swiping down from the top edge (a press above the panel's own top strip, dragged down past a
  real threshold) pauses whatever is running. (intent: the donor's own always-available escape
  gesture, unconditionally, from anywhere - this bundle implements no other way to interrupt
  play, since none of the games' own EXIT chip is wired in this show-phase port.) On the rp2350
  port's own bespoke picker, this returns straight to the launcher, discarding all progress -
  that port never implements a pause state. On the donor's real shell (esp32-s3-touch-amoled-18
  port), it instead opens a pause overlay over a frozen, dimmed view of what was running, with
  four options: RESUME (continue), RESTART (quit then relaunch fresh), CALIBRATE (the same
  tilt-calibration wizard, returning here when done), and QUIT (the one that actually returns to
  the grid, discarding all progress - the direct equivalent of the rp2350 port's own single-step
  swipe-exit).

## Demands

Requires:

- **60 frames per second.** The donor measures 60.0fps on all six games on its own hardware
  (an Xtensa LX7 dual-core @ 240MHz, with a hardware palette-LUT DMA upscale doing the
  184x224-to-368x448 expansion off the CPU's own critical path). This bundle's Demand is the
  same number, not a lower one adopted for convenience: GUNSHIP's whole skill (leading a moving
  target through a camera that lags the true transform by design) and LUCKY 7's mechanical,
  deliberately-not-eased reel motion are both timed against a real frame rate, not a "some
  number of updates eventually" one.
- A colour panel with at least a 256-entry indexed palette, or true colour. The thermal ramp and
  the slot machine's chrome/gold/felt/cream palette are load-bearing - they are how speed,
  heat and material read at a glance, not decoration on top of a monochrome game.
- Single-point touch.
- A continuous gravity or tilt vector for GUNSHIP's aim (the donor's own default mode); a
  touch-drag aim fallback exists in the donor for a device with none, but is not implemented in
  this show-phase bundle - see the port README.
- At least 65536 bytes of scratch memory for one of GUNSHIP's or LUCKY 7's own state at a time
  (measured on this bundle's own port: GUNSHIP's state is the larger of the two, well under half
  that budget). GOLF's own per-hole procedural state is a different order of magnitude entirely
  (several megabytes, not tens of kilobytes) - a port carrying GOLF states its own answer to
  that separately; see that port's README.

Prefers:

- An 8-voice chip synth (or equivalent) for GUNSHIP's engine/gun/servo loops, LUCKY 7's full
  sound set (a ratchet, a lever thump, coin drops, three tiers of win fanfare), and GOLF's own
  club/swing/hole-out sounds. Genuinely **absent** from every port of this show-phase bundle -
  silent, not merely quiet by design - see each port README's Demands table for why that is a
  stated gap, not a silent one.
- A save slot for LUCKY 7's cash/bet/stats, GUNSHIP's salvage/upgrades, and GOLF's own top-5
  leaderboard. Also absent from every port; all three games already tolerate having no save data
  at all (every launch starts fresh), which is exactly the fallback each port relies on.
- A raw, wall-clock-stamped accelerometer sample stream at IMU rate (~200Hz), which is what
  LUCKY 7's own "yank" gesture detector and GOLF's own swing detector both integrate over. A
  device in this class typically publishes only a filtered gravity vector and a discrete shake
  event, never a raw stream. MET on the esp32-s3-touch-amoled-18 pack
  (`packs/esp32-s3-touch-amoled-18/docs/decisions/0003-...`), which is what makes GOLF's own
  swing mechanic possible there, unmodified from the donor - that port's own GUNSHIP tilt aim is
  also fused from this same stream. The rp2350 sibling does not declare one: LUCKY 7 there keeps
  its own touch-drag fallback, and GOLF is not part of that port at all.

The requirements above, in the form `bun run verdict` checks (see
[`docs/convention/app-bundle.md`](../../docs/convention/app-bundle.md)'s "Demands are also
machine-readable"). This is the strictest set in the repository and it is meant to be: the
indexed pipeline renders 184x224 and upscales it 2x, so 368x448 is the size the shell and all
three games were composed at, and `scalesTo` is the 1x pipeline with the upscale dropped, below
which there is no picture left to shrink. The tilt vector carries no fallback because this
show-phase bundle implements none: the donor's touch-drag aim exists upstream and is not in these
ports, so a device with no gravity vector is refused rather than promised something.

```json demands
{
  "convention": "0.1",
  "panel": {
    "minW": 368,
    "minH": 448,
    "scalesTo": { "minW": 184, "minH": 224 },
    "orientation": "either",
    "color": true
  },
  "buttons": [],
  "touch": { "points": 1 },
  "sensors": [
    { "kind": "vector", "why": "GUNSHIP aims the reticle by tilt, the donor's own default aim mode" }
  ],
  "memory": { "baseBytes": 65536 },
  "tick": { "needsMs": 16, "refuseUnderMs": 8 }
}
```
