# {{CART_NAME}} — Wherigo Cartridge (Agent Notes)

<!-- Generated by `wheriflo init` as the starter CLAUDE.md / AGENTS.md for this
     project. Both files hold identical content: Claude Code reads CLAUDE.md,
     Codex reads AGENTS.md. Keep this file updated as the project evolves —
     treat it as the living handoff for the next assistant or collaborator.
     Fill in the "Project-specific notes" section at the bottom and delete any
     guidance that does not apply. -->

This project (**{{CART_NAME}}**) is a Wherigo cartridge built with the
`wheriflo` toolkit. Cartridges are GPS-based interactive stories written in
Lua 5.1, played with the official Wherigo apps on iOS and Android and hosted on
wherigo.com.

- Main cartridge: `{{LUA_FILENAME}}`
- Toolchain: `uvx wheriflo <command>` (CLI) or the wheriflo MCP server tools
  (`wheriflo_lint`, `wheriflo_build`, `wheriflo_teleport`, `wheriflo_respond`,
  `wheriflo_get_state`, `wheriflo_reset`, ...)

## Workflow

`init` (scaffold) → edit the `.lua` → `lint` → `build` (compiles + starts the
web player) → play-test every changed path (`teleport` / `respond` /
`get_state`) → `edit` (review all player-visible copy) → `compile` (validate
with the official Groundspeak compiler — the definitive compatibility check;
a passing local build is NOT sufficient) → `publish`.

## Testing guidance

- **Scope verification to what changed.** Test the changed path plus one
  consumer of any shared helper you touched. Full playthroughs are for
  flow-wide changes or on request — they burn a lot of interaction steps.
- To reach late-game states without replaying the whole cartridge, temporarily
  set a config variable (starting money, skip flags), test, and **revert
  before finishing**.
- `wheriflo_reset` fully wipes state and re-runs OnStart — faster than
  restarting the server between scenarios.

## Platform rules (violations ship bugs to real players)

1. **Apostrophes crash the official Groundspeak compiler in Lua COMMENTS
   only. Strings are safe.** Write natural dialogue with contractions; never
   over-sanitize player-facing copy to dodge this rule.
2. **Line breaks:** `\n` in quoted strings does not render on iOS — use
   `[[ ]]` long strings. For runtime-built multi-line text, concatenate with a
   `NL` constant defined as a long string containing a real newline:
   ```lua
   NL = [[
   ]]
   local text = table.concat(lines, NL)
   ```
3. **Teleports/simulator moves never fire `OnExit`.** Never gate a repeatable
   zone behind a flag that only `OnExit` clears — it permanently blocks
   re-entry. Gate one-shot content with state flags (`visited`, `completed`)
   and leave zones freely re-enterable.
4. iOS may re-fire `OnEnter` on foregrounding. Make repeated entry harmless
   (menus with a Leave option, retry loops) rather than guarding it away.
5. Omit `Media`/`Icon` properties entirely; setting them to `nil` crashes iOS.
6. Multiple choice: full answer strings go in `input.Choices` (native
   buttons); never enumerate options inside the question text.
7. When activating a zone the player may already be standing in, use the
   scaffold's `activateZoneOrEnterIfInside()` (activate first, then check
   containment) — iOS does not fire `OnEnter` on activation-while-inside.
8. **Free-text input on real iOS:** never forward `answer` out of `OnGetInput`
   and never call a string function on `answer` (`string.upper(answer)`,
   `string.gsub(answer, ...)` misbehave on device). For exact-match tokens,
   enumerate `==` against variants built from your own constants (include the
   iOS auto-capitalized first-letter form). Always give wrong answers a retry
   path. Smoke-test all text inputs on a real device before publishing.
9. Item commands: use the canonical named-command shape
   (`item.Commands.cmdX = Wherigo.ZCommand{...}` plus
   `function item:OncmdX(target)`). `cmd.OnClick` does not fire on real iOS,
   and setting `ZCommand.Enabled` breaks the official compiler.
10. End of game: set `cart.Complete = true` and show `Player.CompletionCode`
    (the scaffold's `showCompletion()` does both).
11. **Persisted globals: keys must be strings or numbers.** A table used as a
    key (`mem[someTable] = v`) crashes the iOS app on the next save (and can
    corrupt the save). The failure surfaces far from its cause.
12. **Keep persisted numeric tables dense.** iOS per-screen serialization can
    truncate a sparse numeric table at the first `nil` (`t = {}; t[4] = true`
    can come back empty). Fill from index 1, or key by string.
13. **No concurrent modals.** On iOS a new `MessageBox`/`GetInput` replaces the
    one on screen and the replaced box's `Callback` never fires. Don't activate
    a zone the player may be inside (synchronous `OnEnter`) while an intro
    message is mid-flight — move the activation into the message's `Callback`.
14. **Choices are pickers, not buttons** (iOS scroll-wheel, Android dropdown).
    Write "select"/"choose", not "tap"/"click"; keep labels short and
    single-spaced (2+ spaces render as a literal `&nbsp;` on the iOS picker).
15. **Task order is alphabetical by `Name` on iOS**, not declaration order.
    Prefix names with numbers (`"1. ..."`) if order matters.

Rules 11-15 are device-confirmed sim-parity gotchas — the wheriflo simulator
does not reproduce them, but `wheriflo lint` warns about each.

## Protecting the cartridge from decompilers

A published `.gwc` can be decompiled and scanned for the final coordinates.
Keep coordinates out of message text (reveal them via an image), prefer a
`Player.CompletionCode` unlock, and for a final zone build with
`wheriflo build --obfuscate-final-coords` (mark the zone with a `--@final-zone`
comment). See the wheriflo design guide's "Protecting Your Cartridge from
Decompilers".

## Real-world sync checklist (geocache-style cartridges)

Anything the cartridge asks players to read or open in the physical world must
stay in sync with the source:

- On-location passwords / verification answers ↔ the physical sign or landmark
- Combination codes ↔ the physical lock ↔ every piece of dialogue that hints
  at them
- Final coordinates ↔ the actual container placement
- Never put passwords, codes, or final coordinates in public docs (README,
  cache listing); they belong only in cartridge source and in player-facing
  copy that gameplay reveals.

## Project-specific notes

<!-- Zone layout, characters, puzzle logic, tuning decisions, coordinates
     status, upstream issue links, user preferences. Add lessons here as you
     learn them. -->
