# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).

## [2.11.0] - 2026-07-17

### Added
- **Start screen shows the cartridge-detail view (#43).** The web player's start
  screen now renders the real `cart.Description` (with `[[ ]]` paragraph breaks)
  plus a metadata line (Activity / Author / Version) above the splash and Start
  button, matching the official app's pre-play screen — so copy, art, and layout
  of the first player-facing surface can be proofed in the sim. Falls back to the
  generic screen when no description is set.

### Changed
- **Tasks page now matches iOS ordering (#47).** `get_state` returns tasks sorted
  alphabetically by `Name` (the order the iOS app shows), instead of declaration
  order.

## [2.10.0] - 2026-07-17

### Added
- **Five new sim-parity lint rules for device-confirmed gotchas** the simulator
  cannot reproduce:
  - **#27** — warns on 2+ consecutive spaces in a `Choices` label (renders as a
    literal `&nbsp;` on the iOS picker).
  - **#44** — warns when a table value is used as a table key (`mem[POOL] = v`),
    which crashes the iOS app on save.
  - **#45** — warns when `OnStart` activates a zone with
    `activateZoneOrEnterIfInside(...)` at its top level and also raises a modal
    (the synchronous `OnEnter` replaces the intro message and orphans its
    Callback). Nesting-aware, so deferring the activation into a Callback is not
    flagged.
  - **#46** — warns when a multiple-choice prompt says tap/click/press (choices
    render as a picker, not buttons).
  - **#47** — warns when tasks are not declared in alphabetical order by `Name`
    (iOS sorts the Tasks page alphabetically).
- **Design guide "iOS Sim-Parity Gotchas" section** and matching API-reference
  notes (persisted-global keys, dense numeric tables, no concurrent modals,
  choices-as-picker, alphabetical task order).
- **`wheriflo init` agent notes** (`CLAUDE.md`/`AGENTS.md`) gain rules 11-15 for
  the above device findings, and the free-text rule now points at enumerated `==`.

### Fixed
- API reference no longer shows `task.Media = nil` in the Task example (it
  contradicted the "never set Media = nil" rule).

## [2.9.0] - 2026-07-14

### Changed
- **Free-text answer guidance now recommends enumerated `==`, not a `string.find`
  hedge (#36).** Follow-up device testing showed the v2.6.0 hedge
  (`string.find(string.upper(answer), TARGET, 1, true)`) is wrong for exact-match
  tokens: it is a substring test (a gate keyed to `7596` accepted `75960`) and it
  applies string operations to `answer`, which are unreliable on iOS (a
  `string.gsub`-based normalizer collapsed every input to the same value, so the
  gate accepted anything). The design guide and API reference now recommend, for
  passwords/codes/combos, enumerating `==` against variants built from your own
  constants (including the iOS auto-capitalized form) and never calling a string
  function on `answer`.

### Added
- **Lint now also flags `string.*(answer, ...)` in free-text `OnGetInput`** (e.g.
  `string.upper(answer)`, `string.gsub(answer, ...)`), not just forwarding the
  answer to a helper — catching the exact regression reported on #36.

## [2.8.0] - 2026-07-13

### Changed
- **Corrected the `NL = [[<newline>]]` lint/docs (#27).** Compiling a probe
  cartridge through the real Groundspeak compiler and reading the resulting
  bytecode showed that Groundspeak does **not** strip the first `[[` newline —
  it encodes `[[ ]]` newlines as a `<BR>` tag (`[[<newline>]]` compiles to
  `"<BR>\n"`, not `""`). Only the local wheriflo build (conformant Lua 5.1)
  strips it to an empty string. The v2.7.0 lint/API-reference wording that
  claimed the string is "empty on iOS/Groundspeak … stripped at compile time"
  was therefore inaccurate. The warning is kept (the idiom is genuinely fragile:
  empty in the local build, and field-tested as an unreliable line break on
  iOS) but now describes the behavior correctly and still recommends
  `NL = string.char(10)`.

## [2.7.0] - 2026-07-13

### Added
- **Lint + docs for the empty `NL = [[<newline>]]` idiom (#27).** A long string
  whose only content is a leading newline compiles to an empty string on a
  conformant Lua 5.1 compiler (which is what Groundspeak uses), so a newline
  constant written that way produces **no line break on device** even though the
  simulator may show one. `wheriflo lint` now warns about it and recommends
  `NL = string.char(10)`; the API reference documents the gotcha. (Verified: the
  `bottom-of-the-9th` repro uses `[[<newline>]]` as a list separator that is
  empty on device — now caught at build time.)

### Notes
- The related request to make the simulator itself strip the first `[[` newline
  (so it reproduces the device behavior) is deferred: it would flip the
  first-newline handling validated against iOS screenshots in v1.0.3 and needs a
  device re-verification. The lint catches the concrete failure at build time in
  the meantime.

## [2.6.0] - 2026-07-13

### Added
- **iOS-safe free-text input guidance + lint (#36).** Device testing showed
  that forwarding a free-text `answer` out of its `OnGetInput` handler (through
  a helper or extra callback layer) silently rejects correct answers on real
  iOS. The design guide and API reference now document the reliable pattern:
  validate the answer **inline** in the input's own `OnGetInput`, one dedicated
  `ZInput` per prompt, using a comparison **hedge** (match your own clean
  constants first, `string.find` as a fallback for the invisible trailing
  characters iOS can append). A new lint rule warns when a **free-text** input
  (`InputType = "Text"`) forwards its answer to another function; multiple-choice
  inputs are not flagged.

## [2.5.0] - 2026-07-13

### Added
- **Web player shows the cartridge splash (`cart.Media`) on the start screen
  (#43).** If the cartridge sets `cart.Media`, the load-screen art now renders
  above the title on the web player's start screen (served via `/media/`), the
  way the real Wherigo apps show the splash — closing the last part of the
  authoring loop the simulator could not preview. Falls back to the generic
  start screen when no media is set.

## [2.4.0] - 2026-07-13

### Added
- **`wheriflo init` now scaffolds `CLAUDE.md` and `AGENTS.md` (#41).** Every new
  project gets an agent/collaborator notes file (identical content in both, so
  Claude Code and Codex each read their own) preloaded with the platform rules,
  workflow, testing guidance, decompiler-protection tips, and a real-world sync
  checklist — plus an open "Project-specific notes" section to grow with the
  project. This carries the hard-won gotchas (apostrophes-in-comments-only,
  iOS line breaks, repeatable-zone/`OnExit` trap, free-text input caveats,
  scoped testing) to plain-CLI and CI users and to any AI client even before the
  MCP server is connected.

## [2.3.0] - 2026-07-13

### Added
- **Decompiler-protection guidance (#42).** Public Wherigo decompilers scan a
  published `.gwc` for the final coordinates. wheriflo now helps authors defend
  against the one-click extraction workflow:
  - **Lint warnings** for the two patterns these tools rank highest: a
    coordinate in message/description text (decimal-degrees or
    degrees-decimal-minutes), and literal coordinates on a zone whose name
    suggests it is the final/cache/victory zone. When the same coordinate
    appears in both, the warning escalates (the "Extremely High confidence"
    case). Warnings are suppressible with `-- wheriflo:ignore`.
  - **Design-guide section** "Protecting Your Cartridge from Decompilers":
    reveal coordinates via an embedded image, prefer a `Player.CompletionCode`
    unlock (Reverse Wherigo), and avoid literal final-zone points.
  - **Starter guidance** — the scaffold points authors at the protection
    guidance, and the generated Geocache final-cache zone documents the
    decompiler trade-off inline.
- **`wheriflo build --obfuscate-final-coords` (#42).** Opt-in build-time
  obfuscation of the final zone's coordinates. wheriflo finds the final zone
  (tagged with a `--@final-zone` comment, or named `final`/`cache`/`victory`)
  and rewrites its literal `ZonePoint` coordinates into an XOR-encoded blob that
  is decoded at load time — so neither the compiled `.gwc` nor the `.gwz`
  uploaded to wherigo.com contains a literal coordinate. Decoy points are added
  to dilute automated confidence ranking. The author's `.lua` is left untouched;
  the transform runs only on the in-memory build. Also exposed as the
  `obfuscate_coords` parameter on the `wheriflo_build` MCP tool.

## [2.2.0] - 2026-07-06

### Added
- **`wheriflo setup`** — one-command MCP configuration for any AI client.
  Detects installed clients (Claude Desktop, Claude Code, VS Code/Copilot,
  Cursor, OpenAI Codex) and writes the wheriflo server into each config:
  merges without clobbering other servers, backs up files before modifying,
  refuses to touch configs that fail to parse, and re-runs are idempotent.
  Flags: `--yes`, `--dry-run`, `--client KEY`. Resolves an absolute uvx
  path so GUI clients launched without the shell PATH still find the
  server.
- **Install one-liners** — `scripts/install.sh` (Mac/Linux) and
  `scripts/install.ps1` (Windows) install uv if needed, install wheriflo,
  and run `wheriflo setup`, taking a new user from nothing to a configured
  AI client with one pasted command.
- **Dispatchable releases** — the publish workflow can now be triggered
  manually (workflow_dispatch): it creates the version tag and GitHub
  Release from pyproject.toml, then publishes to PyPI in the same run.

### Fixed
- `wheriflo --version` now reads the version from package metadata instead
  of a second hardcoded string, so it can no longer disagree with
  pyproject.toml (2.1.6 shipped reporting itself as 2.1.5).

## [2.1.6] - 2026-07-06

First release published to PyPI (previously TestPyPI only). `pip install wheriflo`
and `uvx wheriflo` now work against the real index.

### Changed
- **Starter template rebuilt on battle-tested patterns** — the `wheriflo init`
  scaffold now uses native-Choices multiple choice with `answerIndex()`,
  `activateZoneOrEnterIfInside()`, OnEnter re-entry guards, a wrong-answer
  retry loop, and `[[ ]]` long strings throughout. The scaffold lints clean
  for both Geocache and WherigoGame types, and the Geocache scaffold now
  routes the player to the final cache zone via an `advanceToFinale()`
  override (previously the final cache zone was never activated).
- **Lint precision** — zones activated through helper functions are detected
  automatically (no `-- wheriflo:ignore` needed for the documented pattern);
  the `.. [[` warning fires only when a single newline follows the bracket
  (the canonical completion-code pattern no longer warns); repeated warnings
  are grouped with line lists so one pattern cannot bury the report.
- **MCP server instructions** now teach connected agents the full workflow
  plus ten critical platform rules (iOS line breaks, native choice buttons,
  zone activation, retry loops, completion codes, and more) without
  requiring a resource fetch.

### Added
- **Contextual cartridge error reporting** — Lua runtime errors now surface
  as `CARTRIDGE BUG` messages naming the failing handler (e.g. "OnEnter of
  zone 'Park'") and the real source file and line, instead of raw lupa
  tracebacks. The web player shows a dismissible error banner instead of
  dropping the connection.
- **Guard tests** — the scaffold must lint clean, build, and play to
  completion for both activity types; plus lint-precision and
  error-context regression tests (127 tests total).
- **PyPI Trusted Publishing workflow** — releases publish via OIDC on `v*`
  tag push, gated by the test suite.

### Fixed
- Docs drift: README timer limitation corrected (timers do run in the
  simulator), design guide and API reference multiple-choice patterns
  replaced with the iOS-safe native-buttons pattern, publishing flow
  corrected to `.gwz` everywhere, completion-code placeholder wording
  clarified.

## [2.1.0] - 2026-05-28

### Security
- **Hardened Lua sandbox** — cartridge code can no longer escape into Python or
  the host OS. The lupa runtime is now created with `register_eval=False` and
  `register_builtins=False` (removes the `python.eval`/`python.exec`/`python.builtins`
  bridge), and an `attribute_filter` blocks Lua from reading any private/dunder
  (`_`-prefixed) attribute of Python objects passed into Lua (previously a
  malicious cartridge could walk the internal `_py` bridge to reach `os`/`builtins`).
- **Removed additional escape vectors** — `loadstring`, `load`, and
  `package.loadlib`/`package.cpath`/`package.loaders` are now nilled before
  cartridge code runs, on top of the existing removal of `io`, `os`, `debug`,
  `loadfile`, and `dofile`. `require "Wherigo"` continues to work via the
  pre-populated `package.loaded` cache.
- **Added sandbox regression tests** covering nine escape vectors plus a check
  that legitimate cartridges still load and run.

### Docs
- Updated `SECURITY.md` sandbox description and corrected the stale supported-
  versions table (`0.1.x` → `2.0.x`).
- Added a sandbox security note to the README.

## [2.0.1] - 2026-05-27

### Fixed
- **Screen detection accuracy** — Screens mode now uses `is_screen` flag based on proximity to `Wherigo.MessageBox` and `Wherigo.GetInput` calls, plus function-scope analysis. Reduced false positives from 125 to 94 screens on BottomOfThe9Th (actual player screens: ~85). Local text, variant text, and fragment entries are correctly excluded.

## [2.0.0] - 2026-05-27

### Added
- **Screen-oriented copy editor** — new 📱 Screens mode renders every player-visible screen (MessageBox + GetInput) as an iOS phone frame preview with inline editing. Choices render as editable buttons. Text is directly editable around protected variable chips. Addresses #28.
- **Variables browser** — new 🔤 Variables mode lists all dynamic variables used in the cartridge, sorted by usage count. Clicking a variable shows its derivation, all possible values as editable rows with source line links, and which screens reference it.
- **Three-mode sidebar** — 📱 Screens | 🔤 Vars | 📝 Edit toggle. Screens is the default. Edit mode preserves the existing category/section/Gameplay/Source view unchanged.
- **Inline variable expand** — in Screens mode, clicking a chip opens an inline expand panel below the phone preview showing all possible values as editable rows (Option C from design review). Capped at 5 visible rows with "Show all N values" disclosure.
- **Value+line pairs** — `lookup_variable()` and all helper functions now return possible values as `{text, line, raw}` dicts instead of flat strings, enabling direct source-line editing.

## [1.4.1] - 2026-05-27

### Added
- **Lint: completion code flow check** — warns when a cartridge never sets `cart.Complete = true` AND never references `Player.CompletionCode`, indicating the cartridge isn't wired for code-based unlocking. Addresses #32.
- **Lint: placeholder code detection** — warns when a literal `"XXX"` or similar placeholder appears near completion-code-related text. Addresses #32.
- **Starter template: `showCompletion()` helper** — new standalone function with the canonical completion code pattern: `cart.Complete = true`, `Player.CompletionCode` with `or` fallback, 15-char `string.sub` truncated version, `[[ ]]` long strings for iOS line breaks. Addresses #32.
- **Design guide: expanded Completion Code section** — per-player/per-download explanation, 15-char fallback pattern, testing-flow gotcha (local .gwc = placeholder), inventory item persistence tip. Addresses #32.
- **API reference: updated Completion Code section** — 15-char fallback, `cart.Complete` ordering, simulator placeholder note. Addresses #32.

## [1.4.0] - 2026-05-27

### Added
- **Single-quote string support** — Lua single-quoted strings (`'text'`) are now normalized to double-quoted before extraction. Captures 21 player celebration messages from the `playerFlavors` table in BottomOfThe9Th and any other cartridge using single quotes. Addresses #17.
- **Function-call argument extraction** — strings passed as arguments to helper functions (e.g., `handleRunnerOut("text")`, `handleGameLoss(msg .. [[text]])`) are now captured. Addresses #17.
- **Dynamic local variable expressions** — `local var = expr .. [[text]]` multi-line concatenation assignments are now fully captured using `_collect_full_expression`. Previously only single-line `local var = "text"` was handled. Addresses #17.
- **`return [[text]]` extraction** — long-string return statements are now captured (e.g., scouting report hints). Addresses #17.
- **`obj.Text = pick({...})` passthrough** — when a Wherigo object's `.Text` is assigned from `pick({...})`, the line now correctly falls through to the pick handler instead of being consumed by the `obj.Text` pattern. Captures Moose Magic and scouting report prompt variants. Addresses #17.
- **Catch-all for uncaptured strings** — Phase 4.5 scans for quoted strings ≥ 20 chars on indented lines that weren't matched by any specific pattern. Captures strings inside nested table constructors, callback bodies, and other uncommon patterns.

### Improved
- **Entry count**: BottomOfThe9Th.lua went from 189 to 246 entries. RedHeadedLeague.lua went from 140 to 146 entries.

## [1.3.2] - 2026-05-27

### Improved
- **Variable inspector: values-first popover** — possible values are now the headline section of the chip popover. Source line, derivation chain, and function name are collapsed into a "Dev info" disclosure. Addresses #25 Problem A.
- **Variable inspector: `pick({...})` variants enumerated** — clicking a chip whose value comes from `pick({...})` now lists all variant strings as possible values. Previously showed "Randomized" with no values. Addresses #25 Problem B1.
- **Variable inspector: template patterns** — computed concatenations like `"Count: " .. balls .. "-" .. strikes` now show as a template: `Count: {balls}-{strikes}  |  Outs: {outs}` with `{var}` placeholders rendered as mini-chips. Addresses #25 Problem B2.
- **Variable inspector: long-string values trimmed** — possible values are stripped of leading/trailing whitespace and truncated to 80 chars with … to prevent empty green bars. Addresses #25 Problem B3.
- **Variable inspector: dead initializer filtered** — empty-string initializers (e.g., `local baseLabel = ""`) are removed from possible values when followed by exhaustive reassignment. Addresses #25 Problem B4.

## [1.3.1] - 2026-05-27

### Fixed
- **`--no-watch` help text clarified** — now reads "Disable browser auto-refresh; server still reports file changes via /api/poll" to clarify that the server-side generation counter is unaffected. Closes #26.

## [1.3.0] - 2026-05-26

### Added
- **Automatic zone OnEnter on activation** — when a zone becomes active while the player is already inside it, `OnEnter` fires automatically. Previously, `OnEnter` only fired via the explicit "Go Here" button. Now, after every game action (dismiss, answer, enter\_zone, start), the engine scans all zones and triggers `OnEnter` for any newly-active zone that contains the player's current position. Addresses #31.
- **Zone entry tracking** — new `_entered_zones` set tracks which zones have had `OnEnter` fired. Zones are removed from the set when deactivated, so reactivation triggers `OnEnter` again.
- **Post-action zone check** — `_check_zone_activations()` runs after `start()`, `dismiss()`, `answer()`, `answer_text()`, and `enter_zone()` to catch zone activations from callback chains.

## [1.2.2] - 2026-05-26

### Fixed
- **Map overlay z-index** — message and input panels triggered by "Go Here" on the map screen now render above the Leaflet map instead of behind it. Fixed by containing Leaflet's z-indices within a stacking context (`position: relative; z-index: 0` on `#map-container`). Addresses #30.
- **Map-to-message transition** — `enterZone()` now switches away from the map screen before processing the zone enter, so the resulting message/input panel appears against the menu screen rather than competing with the map.

## [1.2.1] - 2026-05-26

### Fixed
- **Function context misattribution for anonymous functions** — single-line `if...then...end` statements were decrementing block depth without a matching opener, causing enclosing scopes like `zoneSecondBase.OnEnter = function()` to pop immediately. Entries inside zone `OnEnter` handlers were being attributed to the most recently closed named function (e.g., `awardFirstBaseItem` instead of `OnEnter`). Fixed by subtracting the paired `end` from closers when a single-line if is detected. Addresses #29.
- **Standalone `do...end` block tracking** — bare `do` blocks (not part of `for`/`while`) now count as block openers, preventing their `end` from causing depth underflow in subsequent scopes.

## [1.2.0] - 2026-05-25

### Added
- **Clickable variable inspector** — clicking a code chip in the copy editor now shows a rich popover with the variable's source, type classification, derivation chain, and possible values. Addresses #25.
- **Value resolution strategies** — four static-analysis strategies for inferring possible values:
  - **Function return tracing**: traces `getShortName(currentBatter)` → `playerDB[name].short` → 7 player names
  - **Finite string enumeration**: scans all assignments and comparisons (e.g., `dest = "first"`, `dest == "second"`) to build the value set
  - **Numeric detection**: classifies `balls`, `strikes`, and arithmetic expressions
  - **`pick()` detection**: labels randomized text variables
- **Value type badges** in the popover: Derived value (purple), Finite values (green), Numeric (blue), Randomized (orange), Computed text (gray)
- **Derivation chain** display: shows how a value resolves through function calls and table lookups (e.g., `getShortName(currentBatter) → returns .short`)
- **Possible values** displayed as chips in the popover
- **Helper functions**: `_find_all_string_values()`, `_resolve_function_returns()`, `_collect_table_field_values()` for reusable static analysis

## [1.1.1] - 2026-05-25

### Fixed
- **Multi-line `[[ ]]` long strings in dynamic expressions** — expressions like `inputSwing.Text = countText .. [[\n...\nWhat is your approach?]]` now capture the full long string content. Previously, the continuation-line scanner stopped at the first line inside `[[ ]]` (which doesn't start with `..`), producing empty entries. New `_collect_full_expression()` helper tracks `[[`/`]]` balance to correctly span multi-line long strings.
- **Dynamic `.Text` reassignments no longer removed** — runtime `obj.Text = var .. [[...]]` assignments inside functions are now typed as `message` (story content) instead of `input_text`, so they survive the input\_group cleanup step added in v1.1.0.
- **Variant set titles for `obj.Text = pick({...})`** — when `pick()` appears in an `obj.Text = pick({...})` assignment, the variant set title now uses the object name (e.g., "inputUseItem text") instead of the generic "Text". For `Text = pick({...})` inside `MessageBox`, the enclosing function name is used.
- **Whitespace handling in `_parse_dynamic_text`** — newlines between segments in multi-line expressions are now skipped correctly.

## [1.1.0] - 2026-05-25

### Added
- **Selection options visible alongside questions** — the copy editor now shows response choices next to every ZInput prompt. Previously, only questions were visible without their answer options. Addresses #3.
- **Variable-based choices resolved** — choices assigned from a variable (e.g., `obj.Choices = playerNames`) are resolved by finding the variable's table literal and extracting its values. The source variable name is shown as "from `playerNames`".
- **Runtime choice reassignments gathered** — choices reassigned inside function bodies (e.g., conditional alternatives like "Call for a squeeze play") are collected and deduplicated alongside the initial choices.
- **`table.insert` choices detected** — dynamically built choice lists using `table.insert(obj.Choices, value)` are detected with best-effort resolution of the inserted value.
- **Short choices no longer filtered** — previously, short choice strings like "No", "A", "B", "C", "D" were filtered out. All choices on ZInput objects are now shown regardless of length.
- **Dynamic prompt grouping** — `_group_input_prompts` now scans all entries in the same section (not just adjacent ones), so choices from runtime reassignments are properly grouped with their prompt.
- **ZInput Text type consistency** — ZInput `.Text` assignments using long strings (`[[...]]`) or concatenation expressions now produce `input_text` entries (not `message`), enabling proper grouping with choices.
- **Unresolved dynamic indicator** — when choices come from a runtime variable that can't be statically resolved, the copy editor shows an italicized "(from varName)" placeholder.

## [1.0.5] - 2026-05-25

### Added
- **Copy editor watch/live-reload** — the copy editor now watches the `.lua` file for external changes and auto-refreshes the browser when the source is modified outside the editor. Enabled by default; use `--no-watch` to disable. Addresses #26.
- **`/api/poll` endpoint** on copy editor server — returns `{generation: N}` for browser polling, same pattern as the web player.
- **Browser auto-polls every 2 seconds** and shows a "Source file changed — refreshing..." toast when the file is modified externally.
- **Copy editor saves bump generation** — saving text in the editor also increments the generation counter, so a co-running `wheriflo play --watch` will reliably detect the change.
- **Cache-Control headers** on HTML responses to prevent stale browser caching.

## [1.0.4] - 2026-05-25

### Fixed
- **Copy editor matches iOS rendering for `\n`** — `_unescape_quoted` now converts `\n` and `\t` to spaces (matching iOS) instead of newline/tab characters. The copy editor textarea now shows text exactly as iOS renders it. Previously, `\n` in quoted strings showed as line breaks in the editor but not on iOS.

## [1.0.3] - 2026-05-25

### Fixed
- **iOS rendering: `\n` replaced with space, not removed** — iOS renders `\n` in quoted strings as a space (not nothing). Updated `_strip_quoted_newlines` to replace `\n` with a single space, collapsing adjacent `\n\n` to one space. Validated against 12-test iOS screenshots.
- **iOS rendering: `[[ ]]` first-newline preserved** — iOS/Groundspeak does NOT strip the first newline after `[[` like standard Lua does. Updated pre-processing to add an extra newline at `[[ ]]` boundaries so Lua’s stripping leaves the original newline intact. All 12 test cases now match iOS screenshots exactly.

## [1.0.2] - 2026-05-25

### Fixed
- **Simulator now matches iOS text rendering** — `\n` escape sequences in quoted Lua strings are stripped before execution, matching iOS behavior where `\n` in quoted strings produces no visible line break. Long string `[[ ]]` newlines are preserved (they work on all platforms). This is a source-level pre-processing step in the engine — the simulator now faithfully represents what iOS will display. Addresses #27.
- **Enhanced lint warning for `\n` in display strings** — warning message now explicitly states the text will "run together with no line break" on iOS, and suggests `[[ ]]` long strings or separate MessageBox calls.

### Added
- **iOS text rendering test cartridge** — `tests/ios_text_test.lua` with 12 test cases covering quoted `\n`, long string newlines, mixed concatenation, dynamic variables, paragraph spacing, and numbered lists. Use to verify simulator matches iOS app.

## [1.0.1] - 2026-05-22

### Fixed
- **Trivia grouping on BottomOfThe9Th** — data-table `q`/`answer` fields now correctly lowercase their type, fixing trivia_group merging. 10 trivia questions now properly group with their 4 choices and answer in the repro cartridge. Fixes #20.
- **Inline choices scan overflow** — `choices = {"A","B","C","D"}` on a single line no longer bleeds to the next line, preventing `answer` field values from being double-extracted as choices.
- **Field ordering in gameplay mode** — name entries sort before descriptions within the same section, matching the reader's expectation (title first, then detail). Fixes #23.
- **Variant card textarea styling** — variant textareas now have identical padding, font, border-radius, and focus states as regular message card textareas. Orange focus ring matches the variant accent color. Fixes #19.

## [1.0.0] - 2026-05-21

### Added
- **Runtime coverage tracking** — the flow map now records which nodes and edges you visit during playtesting. Visited nodes show a ✓ in the sidebar and a blue glow in the graph. Visited edges render in blue; unvisited remain gray. Coverage counter in the header shows "X/Y visited (Z%)". All tracked in `.wheriflo-flowmap.json` sidecar. Sprint 4 of #21.
- **`wheriflo_flow` MCP tool** — returns a text summary of the cartridge’s flow graph (nodes, edges, in/out counts) for AI review. Complements the browser flow map.
- **`wheriflo flow` in README** — documented in quick start and MCP tools table.
- **Sidecar files gitignored** — `.wheriflo-flowmap.json` and `.wheriflo-snapshots.json` added to `.gitignore`.

### Changed
- Version bump to 1.0.0 — all four sprints of the flow map feature (#21) are complete: static graph, designer editing, jump-to-section, and runtime coverage. All open issues resolved.

## [0.8.2] - 2026-05-21

### Added
- **Jump-to-section in flow map** — click ▶ Jump Here on any node to instantly load the engine, replay to that game state, and show the live event in the detail panel. Displays the current message/input with Dismiss and Answer controls, plus active zones and open tasks. Sprint 3 of #21.
- **Integrated engine** — flow map server lazy-loads the Lua engine on first jump. Each jump resets and replays from OnStart through the path to the target node.
- **`/api/jump`**, **`/api/dismiss`**, **`/api/answer`** endpoints for live game interaction from the flow map.
## [0.8.0] - 2026-05-21

### Added
- **Cartridge flow map** — `wheriflo flow <file.lua>` launches an interactive flow graph in the browser showing the cartridge’s gameplay structure as a DAG. Auto-discovers zones, scenes, inputs, tasks, start/end nodes and the transitions between them. Three-panel layout: sidebar node list, center graph canvas with color-coded nodes and directed edges, right detail panel with transition lists. Clicking nodes shows outgoing/incoming edges. Sprint 1 of #21.
- **`flow_analyzer.py`** — static Lua source analyzer that builds a node/edge graph from Wherigo object declarations, function scopes, zone activations, function calls, input triggers, and task completions.
- **`wheriflo flow` CLI command** — new command alongside `wheriflo edit` and `wheriflo play`.

## [0.7.3] - 2026-05-20

### Added
- **Clickable variable inspector** — clicking a code chip (purple variable token) in dynamic text now shows a dark popover with the variable’s source assignment, enclosing function, and value expression. Powered by a `/api/variable-info` endpoint that traces variable declarations backward from the usage line. Addresses #18 and #25.
- **`/api/variable-info` endpoint** — POST with `{variable, line}` returns the variable’s declaration source, assignment expression, and enclosing function name.

### Fixed
- **Variant card styling** — variant textareas now have the same full-width layout, padding, and spacing as regular message cards. Addresses #19.
- **Chips are now clickable** — removed `pointer-events: none` from code chips; they now have hover states and cursor pointer.

## [0.7.2] - 2026-05-20

### Added
- **ZInput prompt + choices grouping** — `input_text` entries (ZInput `.Text` prompts like "Select your answer:") are now merged into their adjacent trivia question cards as an editable inline prompt field. For cartridges where `inputX.Text` has the actual question and `inputX.Choices` has meaningful choices, they are grouped as "Prompt & Choices" cards. Addresses #3 and #20.
- **`input_group` type** — new entry type for ZInput prompt+choices units, rendered as a card with the prompt on top and lettered response options below.
- MCP `wheriflo_edit` output shows `input_group` entries with prompt and lettered choices.

## [0.7.1] - 2026-05-20

### Fixed
- **Section ordering by gameplay flow** — sections within a category (e.g. Story) are now sorted by their minimum `gameplay_order`, so the section containing `OnStart` appears first. Previously only entries within a section were sorted, but sections themselves stayed in source order. Fixes #23.
- **Sidebar sub-item order matches main content** — sidebar function sub-items now use the same first-occurrence order as the main panel instead of alphabetical sort. Fixes #24.
- **Lua method-syntax scope tracking** — `function obj:Method()` patterns are now detected for function scope tracking (fixes `OnStart` not being recognized in method-style cartridges).

## [0.7.0] - 2026-05-20

### Added
- **Gameplay order sort mode** — Story entries can now be sorted by gameplay flow instead of source line order. `OnStart` content appears first, followed by functions in declaration order (which matches gameplay flow in well-structured cartridges). Toggle between ▶ Gameplay and # Source via the header button. Default is gameplay order. Addresses #23.
- **Lua method-syntax scope tracking** — `function obj:Method()` patterns (e.g. `function cartRHL:OnStart()`) are now properly tracked as function scopes.

## [0.6.9] - 2026-05-20

### Added
- **Function-context subgrouping in copy editor** — large Story sections (15+ entries) are now broken into navigable sub-groups by the Lua function they appear in. The sidebar shows expandable sub-items (e.g. `resolveHomePlateTake (42)`) and the main panel shows purple function dividers. Clicking a function in the sidebar scrolls directly to that group. Addresses #19.
- **`pick({...})` variant set extraction** — randomized text tables using `pick({...})` are now detected and grouped as collapsible "Variants" cards showing all possible player-facing strings with lettered options (A/B/C/...). Variants with dynamic concatenation show code references as purple chips. Addresses #19.
- **Wheriflo logo lockup** — the copy editor header now displays the wheriflo brand mark (gradient location pin + wordmark) alongside "Copy Editor".

### Changed
- **Proper function scope tracking** — function boundaries are now tracked with `end` matching (not just nearest preceding function), giving accurate `function_context` on every entry.
- **Variant deduplication** — entries on lines already consumed by a `variant_set` are excluded from the flat entry list, preventing duplicates.

## [0.6.8] - 2026-05-19

### Added
- **Grouped question cards in copy editor** — questions with answer choices are now displayed as a single card with lettered options (A/B/C/D) instead of separate entries. For cartridges with explicit `answer` fields (e.g. `{q = "...", choices = {...}, answer = "..."}` tables), the correct answer is highlighted with a green indicator. Addresses #20.
- **Deduplication pass** — entries that were extracted twice at the same line are now deduplicated, reducing noise.

### Fixed
- **`obj.Choices` scan overflow** — inline choices like `inputX.Choices = {"A", "B", "C", "D"}` no longer bleed past the closing brace to capture unrelated strings from later lines.
- **ZInput names excluded** — internal input names (e.g. `ActIDeduction`) are no longer shown in the copy editor since they are not player-visible.

## [0.6.7] - 2026-05-19

### Added
- **`obj.Text = dynamic_expr` extraction** — the copy editor now extracts dynamic concatenation expressions on Wherigo object `.Text` properties (e.g. `inputSwing.Text = countText .. [[ swings remaining. ]] .. situationText`). Handles longstrings, quoted strings, and multiline continuations. Addresses #17.

## [0.6.2] - 2026-05-18

### Added
- **Dynamic message editing in copy editor** — messages with `..` concatenation (e.g. completion codes, variable interpolation) are now editable. Static text segments appear as editable textareas, code references appear as locked purple chips. Resolves #1.
- **`/api/save-dynamic` endpoint** — saves segment-based edits by reassembling the full Lua expression.

## [0.6.1] - 2026-05-18

### Added
- **Context labels in copy editor** (#2) -- messages, questions, and choices now show which function they appear in (e.g. "In: startScene1" or "In: actICorrectResponse"), making it easy to understand when each piece of text is shown to the player.
- **Bytecode delta in build output** (#8) -- build output now shows the change from the last build (e.g. "Bytecode: 126,487 bytes (-20 from last build)"). Previous size stored in `.wheriflo-build.json`.
- **Lint: `\n` in display strings** (#9) -- warns that `\n` in quoted strings doesn't render on iOS; suggests `[[ ]]` long strings instead.
- **Lint: `.. [[` concatenation warning** (#9) -- warns that Lua strips the first newline after `[[`, which causes silent text loss in concatenation patterns.

### Changed
- **Groundspeak compile error diagnostics** (#5) -- HTTP 500 errors now include GWZ size, retry suggestion, and `wheriflo lint` (not `wheriflo_lint`) in error messages.

## [0.6.0] - 2026-05-13

### Added
- **`wheriflo edit`** — browser-based copy editor for reviewing and editing all player-visible text without touching Lua code. Extracts names, descriptions, dialogue, questions, and choices from the .lua source and presents them in a clean web UI organized by category (Cartridge, Locations, Characters, Items, Tasks, Questions & Choices, Story). Edits save directly back to the .lua file.
- **MCP `wheriflo_edit` tool** — returns a text summary of all copy in the cartridge for AI review.
- **Copy editor change tracking** — when edits are saved in the copy editor, a `.wheriflo-edits.json` sidecar file records before/after for each change. MCP tools (`wheriflo_build`, `wheriflo_lint`, `wheriflo_compile`) automatically report pending copy editor changes in their output, then clear the log.
- **Lua decimal escape decoding** — `\DDD` byte sequences in Lua strings (e.g. em dashes `\226\128\148`) are decoded to proper Unicode for display and re-encoded on save.
- **Port fallback for copy editor** — if port 8080 is unavailable, tries the next 19 ports.

## [0.5.2] - 2026-05-12

### Added
- **Activity type selection** — `wheriflo init` now accepts `--activity` to set the cartridge type: `Geocache` (default, tied to a physical geocache), `WherigoGame` (standalone), or `Puzzle` (play-anywhere). MCP `wheriflo_init` has a matching `activity_type` parameter.
- **Final cache zone for Geocache type** — `wheriflo init --find-location LAT,LON` generates a `zoneFinalCache` zone at the physical cache coordinates with an `OnEnter` that shows the completion code. MCP `wheriflo_init` accepts `find_latitude`/`find_longitude`.
- **Missing find-location warning** — when using Geocache type without providing cache coordinates, the output warns and suggests either adding coordinates or switching to WherigoGame.
- **Activity property in API reference** — `ZCartridge.Activity` documented with all three valid values.

## [0.5.1] - 2026-05-12

### Changed
- **MCP prompts updated** — `design-cartridge` and `review-cartridge` prompts now include `wheriflo_publish` as the final step in the guided workflow (build → compile → publish).

## [0.5.0] - 2026-05-12

### Added
- **`wheriflo publish`** — opens wherigo.com to upload or update a cartridge. First run opens the upload page and copies the .gwz path to clipboard. After uploading, `--set-guid` saves the cartridge GUID in `.wheriflo.json`. Subsequent runs open the edit page for that cartridge directly. Also available as the `wheriflo_publish` MCP tool.
- **Favicon from cartridge icon** — the web player (`wheriflo play`) now serves the cartridge's icon image as the browser favicon, making the player tab identifiable when multiple tabs are open.

## [0.4.5] - 2026-05-12

### Added
- **Auto-reload web player** — `wheriflo play --watch` monitors the `.lua` file for changes and automatically reloads the engine + refreshes the browser. MCP `wheriflo_play` enables watching by default. Eliminates the manual restart cycle during iterative editing.
- **`/api/poll` endpoint** — browser JS polls every 2 seconds for a generation counter change, triggering auto-refresh.

## [0.4.4] - 2026-05-12

### Added
- **`[[ ]]` first-newline stripping documentation** — expanded Line Breaks on iOS section with concatenation examples showing double/triple newline rules and dynamic text with variables pattern.
- **iOS Zone Re-Entry guard pattern** — new section in design guide documenting the iOS background/foreground OnEnter re-fire behavior and the guard variable workaround.

### Changed
- **Apostrophe scope clarified** — docs now explicitly state that apostrophes in strings are safe (use contractions freely); only `-- comment` lines crash the Groundspeak compiler. Updated in both API reference and design guide.

## [0.4.3] - 2026-05-12

### Added
- **"Managing Cartridge Size" section** in design guide — string deduplication, code deduplication, what doesn't help (comments), what costs most (string literals), known compiler bugs vs size limits.
- **Groundspeak limits testing** — verified no hard size limits: 1MB source, 5MB GWZ, 2000 functions, 200 zones, 5000 strings all compiled successfully.

### Changed
- **Improved "invalid GWZ" error message** — lists specific known causes (apostrophes in comments, large functions, encoding artifacts, ZMedia ordering). Explicitly notes no hard size limit was found in testing.
- **Comments don't affect bytecode** — added note to API reference so developers don't waste time on cosmetic cleanup when optimizing size.

## [0.4.2] - 2026-05-11

### Changed
- **Reverted `<BR>` tags** — `<BR>` in quoted strings renders as literal text on iOS, not as line breaks. Reverted all `<BR>` changes from v0.4.1.
- **Accurate iOS line break documentation** — rewrote "Line Breaks on iOS" section with platform compatibility table. Only `[[ ]]` long strings get line breaks on iOS; `\n` in quoted strings works on Android + web player only.

## [0.4.1] - 2026-05-11

### Changed
- *(Reverted in 0.4.2)* Attempted `<BR>` tags for iOS line breaks — showed as literal text.

## [0.4.0] - 2026-05-11

### Added
- **Image/media support** — cartridges can now embed images (JPG, PNG, GIF, BMP) and audio (MP3, WAV, OGG) files. The build command auto-discovers `ZMedia` declarations in Lua, resolves files from the cartridge directory, and embeds them in both GWC and GWZ.
- **Cartridge icon and splash screen** — `cart.Icon` and `cart.Media` assignments are detected and mapped to the correct GWC header object IDs. Icons display in the Wherigo app's cartridge listing.
- **Web player image rendering** — MessageBox images are displayed inline in the browser-based play-testing UI via the `/media/<filename>` endpoint.
- **ZMedia section in API reference** — documents properties, Resources table, usage patterns, format recommendations, and the critical "declare after ZCartridge" rule.
- **Image best practices in design guide** — icon design rules (256x256 PNG, bold shapes), content sizing (640x480), PNG vs JPG trade-offs, Groundspeak reprocessing artifacts.
- **Publish instructions after compile** — `wheriflo_compile` now tells users to upload their .gwz to wherigo.com to list publicly.
- **Image guidance in MCP prompts** — `design-cartridge` asks about images, `review-cartridge` checks for media issues.

### Changed
- **Validator handles multi-object GWC** — validates media objects (valid byte, type code, length, bounds) in addition to the Lua bytecode.
- **Compile includes media in GWZ** — both CLI and MCP compile paths now discover and include media files in the GWZ sent to Groundspeak, fixing silent media omission.

### Fixed
- **ZMedia must be declared after ZCartridge** — discovered and documented that Groundspeak silently ignores media declared before the cartridge variable exists. API reference and design guide now warn about this.
- **Compile GWZ missing media** — `wheriflo compile` and `wheriflo_compile` were building GWZ files with only the Lua source. Media files are now included.

## [0.3.2] - 2026-05-10

### Added
- **Completion code support** — `Player.CompletionCode` is now displayed to the player at game end in both the starter template and the Red-Headed League cartridge. This is the Wherigo equivalent of signing a geocache logbook — players need it to log their find on wherigo.com.
- **Player section in API reference** — documents `Player.CompletionCode`, `Player.ObjectLocation`, and `Player.Name` with usage examples and ordering rules.
- **Completion Code section in design guide** — explains what completion codes are, why they matter, and the key rule: show the code *before* setting `cart.Complete = true`.
- **Test completion code in runtime** — `wherigo_shim.lua` now provides `TEST-CODE-12345` during play-testing so the completion code display can be verified.
- **Auto-play after build** — `wheriflo_build` now automatically loads the cartridge into the web player and returns the URL, so users can start play-testing immediately without a separate `wheriflo_play` call.

## [0.3.1] - 2026-05-10

### Added
- **Sandbox documentation** — full availability table in API reference showing which Lua standard libraries are available (`math`, `table`, `string`) and which are removed (`os`, `io`, `debug`). Includes `math.randomseed` workaround for `os.time()` unavailability.
- **Small-area cartridge guidance** — new section in design guide for compact spaces (baseball diamonds, escape rooms, small parks) with zone sizing, GPS drift, and sequential activation advice.
- **`-- wheriflo:ignore` lint suppression** — add to any line to suppress warnings. For dynamically activated zones, add to the `Wherigo.Zone()` constructor line.

### Changed
- **`showChoices` helper supports 5+ options** — letters A through H with numeric fallback beyond 8. Updated in starter template, API reference, and `generate_rhl.py`.
- **Dynamic zone activation lint message** — now suggests `-- wheriflo:ignore` instead of a generic "may be unreachable" warning when the zone variable is referenced elsewhere.

### Verified
- **Template `Media = nil` bug** — confirmed the current starter template does NOT set `Media = nil` or `Icon = nil`. The linter catches these if an AI or user adds them during editing.

## [0.3.0] - 2026-05-08

### Added
- **System keyring credential storage** — `wheriflo credentials set/clear/check` CLI commands. Stores Groundspeak compiler credentials in the OS-native encrypted keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service) instead of plain-text environment variables.
- **Runtime credential parameters** — `wheriflo_compile` now accepts optional `username`/`password` parameters, allowing AI assistants to collect credentials conversationally and pass them directly.
- **Security explanation before credential collection** — both the CLI (`wheriflo credentials set`) and MCP prompts now explain how credentials are stored and used before asking for them.
- **`keyring` optional dependency** — `pip install wheriflo[keyring]` installs keyring support.

### Changed
- **Credential resolution order** — runtime parameters → environment variables → system keyring. All three methods work; keyring is recommended for interactive use.
- **Build success message** — now guides AI to offer official Groundspeak compilation and collect credentials conversationally if needed.
- **`design-cartridge` and `review-cartridge` prompts** — instruct AI to collect credentials in the chat conversation (not in a terminal) and explain security before asking.
- **`wheriflo doctor`** — now shows credential source (environment / keyring / not configured) and suggests `wheriflo credentials set` when keyring is available.

### Security
- Removed hardcoded Groundspeak credentials from `generate_rhl.py`, `fix_distance.py`, and `.instructions.md`.
- All credential collection paths now use the keyring → env var → runtime parameter resolution chain.

## [0.2.9] - 2026-05-08

### Added
- **`wheriflo_get_resource` tool** — read API reference, design guide, or starter template directly via MCP, solving resource accessibility issues in clients that don't support MCP resource reading.
- **`wheriflo_leave_zone` tool** — trigger OnExit for a zone during play-testing. Complements `wheriflo_teleport` (which only triggers OnEnter).
- **`wheriflo_set_position` tool** — set the player's GPS coordinates to arbitrary lat/lon for testing proximity-based triggers and distance calculations.
- **Quick Start for AI Agents** section in README with the typical MCP workflow.
- **Automatic update checker** — CLI now checks PyPI for newer versions after each command (cached 24h, skipped for `mcp`). Prompts user to install if update available.

### Changed
- **`wheriflo_reset` description** — now clearly states it's a full state wipe (zones, characters, items, tasks), not a soft restart.
- **`engine.exit_zone()`** — now returns consistent status strings (`"ok"`, `"not_found"`, `"not_loaded"`, `"no_callback"`) matching `enter_zone()`.

### Fixed
- **Version mismatch note** added to install docs explaining pip environment precedence.

## [0.2.8] - 2026-05-07

### Fixed
- **Autoplay step counter** — reported "Completed X of Y steps" where X > Y because sub-lines (like "→ next: ...") were counted as steps. Now uses a dedicated counter for top-level steps only.
- **Inactive zone teleport** — teleporting to an inactive zone now returns a clear error ("Zone exists but is not active") instead of silently doing nothing. Same for missing zones.

### Added
- **`MoveTo()` documentation** — added Methods section to Item and Character in API reference. This is the most common missing doc for new users.
- **`MoveObject` lint rule** — linter now detects `:MoveObject()` calls (common pattern from other builders) and suggests `item:MoveTo(target)` instead.
- **`get_input` autoplay step** — new `{"get_input": true, "text": "value"}` step type for scripting text input retry scenarios.
- **Text input retry pattern** — added retry loop section to design guide with `GetInput` re-prompt example.

### Changed
- **Apostrophe lint message** — now suggests fix ("remove or rephrase") instead of just reporting the problem.
- **Credential error message** — now mentions restarting MCP server after adding env vars to config.
- **Simpler init variable names** — `wheriflo_init` now uses `cart` as the variable name instead of verbose names like `cartTheClockmakerSSecret`.

## [0.1.4] - 2026-05-03

### Changed
- **Multiple choice UX improvements** in design guide, API reference, and starter template:
  - Options now shown directly on the `GetInput` picker screen (not a separate `MessageBox`)
  - Double newlines (`\n\n`) between options for readability on iOS
  - Guidance on retry vs. proceed design for wrong answers

## [0.1.0] - 2026-04-29

### Added
- **MCP server** with 12 tools, 3 resources, and 2 prompts for AI-assisted cartridge development
  - Tools: `wheriflo_init`, `wheriflo_build`, `wheriflo_compile`, `wheriflo_play`, `wheriflo_validate`, `wheriflo_lint`, `wheriflo_get_state`, `wheriflo_teleport`, `wheriflo_respond`, `wheriflo_autoplay`, `wheriflo_reset`, `wheriflo_stop`
  - Resources: API reference, design guide, starter Lua template
  - Prompts: `design-cartridge` (guided creation), `review-cartridge` (automated play-test)
- **Groundspeak compiler integration** — `wheriflo_compile` tool and `wheriflo compile` CLI command compile cartridges via the official wherigo.com SOAP API for definitive compatibility testing. Credentials handled securely via environment variables only.
- **Tool annotations** — `readOnlyHint`, `idempotentHint`, `destructiveHint` on all tools
- **Server icon** — compass icon for MCP client UIs
- **Progress reporting** — `wheriflo_build` reports progress via MCP progress notifications
- **Linter** — `wheriflo_lint` tool and `wheriflo lint` CLI command check for common mistakes (Media=nil, wrong enums, missing callbacks, unreachable zones, comment apostrophes)
- **CLI** with `init`, `build`, `play`, `validate`, `lint`, `mcp` commands and `--version` flag
- **Lua 5.1 compiler** — compiles source to bytecode, converts 64-bit to 32-bit size_t
- **GWC packager** — builds valid .gwc cartridge binaries with correct header format
- **GWZ packager** — builds .gwz source archives
- **GWC validator** — validates binary structure (signature, header, objects, bytecode)
- **Runtime engine** — lupa-based Lua executor with full Wherigo API shim
- **Lua sandbox** — `io`, `os`, `debug`, `loadfile`, `dofile` removed before cartridge code runs
- **Web player** — browser-based play-testing UI served on localhost
- **iOS compatibility** — never sets `Media = nil` or `Icon = nil` (crashes iOS app)
- **Docker support** — Dockerfile for containerized MCP server deployment
- **GitHub Actions CI** — tests on Python 3.10–3.13 × Ubuntu + Windows
- Setup instructions for VS Code, Claude Desktop, Claude Code, Cursor, and Docker
- 53 tests covering compiler, packager, validator, engine, CLI, sandbox, lint, and iOS compatibility
