Releases
Every move of the release branch gets an entry here BEFORE the branch moves,
plus a git tag for pinning and rollback, plus a GitHub Release on the tag
(gh release create — short notes mirroring the entry; the release page at
the game's repository is the browsable/downloadable history). Read the Upgrade safety line
before pulling a new release onto a live world.
The compatibility promise (the Designer, 2026-08-29): from v0.7.0 onward, world files ALWAYS migrate automatically across releases — a release that cannot carry existing worlds is a MAJOR break requiring an explicit Designer ruling, a loud entry here, and never happens as routine. Losing a campaign to an update is a bug in the process, not a price of progress.
What flows and what breaks (the Designer, 2026-09-02, #96): updates — combat numbers, economy, all of it — flow to existing worlds by default; a tuning change is an ordinary MINOR release with a note in its entry ("your battles will feel different from tick N"), never a per-world freeze of the old numbers. A save may break only when there is no way to carry it, ruled by the Designer one case at a time. Byte-identical replay means a world's OWN history stays exact: the world row records which release wrote each stretch and replay uses that physics; from the update forward the world plays by the new numbers.
The release stamp (from the #96 ruling, built 2026-09-02): every
world records which release wrote each stretch of its history; the first
time a new release ticks a world it appends the stamp and emits a public
world.release_stamped event, and replay installs the physics of the
release that wrote each stretch. Cutting a release therefore bumps TWO
things together, in the release commit: RELEASE_VERSION in
server/src/version.ts and the newest ## vX.Y.Z heading here — a test
pins them equal, so a release cannot move without its stamp.
Upgrade procedure (always): stop the server → back up server/data/
(the world .db AND keys.json — it's two files) → git pull on release →
restart. Migrations run automatically on boot. If anything looks wrong: stop,
git checkout <previous tag>, restore the backup, report it.
The hotfix lane (the Designer, 2026-08-30 — from the first live playtest)
Critical bugs on a live world do not wait for the next feature release or for idle sessions to catch up.
- Version semantics, formalized: MAJOR = compatibility break (Designer-ruled only, per the promise above) · MINOR = feature release · PATCH = hotfix (vX.Y.Z+1). The compatibility promise binds every patch — a hotfix must never require world surgery.
- Severity gate: the lane is for Priority highest/high live-play breakage — world-breakers, exploits, wrong outcomes in progress. Everything else rides the normal release train.
- Flow: branch
hotfix/vX.Y.Zfrom the current release tag (never from master, which may carry unverified in-progress work) → the fix plus a pinning regression test → full suite green + tsc clean → the manager's targeted verification → RELEASES.md entry FIRST + the patch tag →releasemoves → the fix is merged back to master so trunk never regresses behind a patch. - Verification tier: a hotfix may skip the full adversarial review — it never skips the suite, the pin, or the manager's verification. Any skipped review is recorded as debt in the release entry and swept into the next review batch.
- Ownership: engine hotfixes belong to the Engine session (woken for the purpose); the manager may land a genuine one-liner alone with explicit disclosure in the entry — a sparing emergency exception to one-file-one-owner.
- Live world: the standard upgrade procedure above applies unchanged; patch entries state upgrade safety like every other entry.
v0.15.0 — chat, the MCP download, and a site that stands alone (2026-09-09)
The release the first public deploy runs on: cut from master after b960de5 (680/680 across 101 files; server, mcp and registry typecheck clean), so that the play page's links answer. The deploy itself is the Designer's, from docs/hosting/first-deploy-sitting.md.
Chat (#348; the Designer's direction on #327, Design's spec, review 47
folded). Four new channels beside the shipped local speak: world,
alliance, party and direct (player-to-player), as owner-scoped events read
through the same gate as the feed — position-free, principal-scoped;
attribution by door (player | agent); the Operator's per-world
local_only config key refuses all four with one code; the credential
guard on every channel; a per-tick cap counted at this tick and charged to
refused attempts, so a refusal enumerates nothing; one indistinguishable
membership refusal; and the DM is outside every private channel on every
surface — direct, party and alliance are the members' alone (Design's
ruling); world chat the DM hears. Speaking reveals the speaker; listening
reveals nothing. Briefing lines, MCP tools and the client's chat verbs.
The MCP download (#352, review 48 folded; #357). GET /mcp on the
game server serves a RUNNABLE MCP server — an esbuild bundle with the
shared contracts inside, built once at boot, a generated package.json and
player README carrying the world's published origin and the key-file
discipline (THAUMATON_URL, THAUMATON_KEY_FILE, never a pasted key),
an ETag and 304, GET /mcp.sha256 and the digest in the boot banner, a
per-address limiter; a smoke test unpacks and starts it on every merge.
The site ships no archive of its own: the play page links the world's
/mcp with the checksum line, so a player on any model fetches the MCP
server from the operator beside their key, never from the repository.
The site stands alone (#336 Option B; #341). Nothing on the site links
into the repository (a test over every output file fails the build on the
first such link); the reference client and the MCP server come from the
game server; the deploy runbook targets Cloudflare Workers static assets
with previews and the workers.dev address off, deployed only from the
Designer's machine (site/wrangler.jsonc); the /window page renders the
world's delayed public digest at build time, every value escaped.
Endings, demotion, vocabulary (#347, #249, #331). scenario.completed
carries slots of NAMES per family, pinned equal to the sentence
templates' slot sets, so an ending never renders a literal placeholder;
winner is denied from the public window. Hero demotion carries review
41's fold: retention as a live tunable, one seat rule for promotion and
demotion, knobs restored on demotion, world-firsts kept, the slot index off
the dossier, the death clock. Every emitted event type is declared in the
contract and an audit fails the suite on an undeclared one.
Upgrade safety: worlds migrate automatically (no new migration; chat
and the MCP route add no tables). Goldens changed only where the demotion
fold renumbered hero seats, disclosed on #249. Riders: (1) deploy this
release, not v0.14.1, behind the public site — the play page's /mcp
link answers 404 on any earlier release; (2) new event types reach every
feed consumer: chat.message (owners), and scenario.completed gains
slots; (3) the DM briefing now says the DM does not read direct, party
or alliance chat; (4) local_only is off by default — set it per world to
keep a game to local speech; (5) an agent that fetched the MCP server from
the repository should re-fetch it from GET /mcp and verify
/mcp.sha256. Zip operators: copy server/data/ ENTIRE across.
Carried forward: the dashboard (#327, one open question), the sharing
dial (#347's follow-up), the horizon rename (#347 Q4), and the seasons and
route rulings — all on the Designer.
v0.14.1 — the console fix, the login layer, and a day of playtest answers (2026-09-06)
A patch cut the same day as v0.14.0, because v0.14.0's operator console does not load — a raw newline inside the page's template literal, shipped with the scheduled backups (#201). Everything verified on master since the morning cut rides with it. Cut from master after ae7d997 (641/641 across 96 files; server, mcp and registry typecheck clean).
The console (#349). One escaped character and a compile test on the served script, so the page can never ship unparseable again. Also new on it: an "Accounts — claim this world" section with a Make a claim code button (#342) for the account layer below.
Login above worlds — both halves (#310 Option A, #336 Option B). A
world verifies signed assertions from an account registry: set
THAUMATON_REGISTRY_PUBLIC_KEY (Ed25519, PEM) and
THAUMATON_REGISTRY_ISSUER, and an assertion reads the console's surfaces
as the operator — never a credential — and may do exactly two writes:
rotate the operator key (the owner of record only) and redeem a claim code
(the registry). An unclaimed world honours no owner token at all; after a
claim every token must name the owner of record and, when
THAUMATON_PUBLIC_URL is set, the world's published address (#338, #346;
reviews 44 and 46, both takeovers re-run and refused). The registry itself
is a separate service under registry/ (#339): accounts, world claims
proven by the operator's claim code, grants, owner-only rotation. It is
NOT deployed by this release; its runbook is docs/hosting/. Migration 35.
The public window (#323 Option A; #337, #341). A per-world
public_window_delay_days setting; the server writes a digest of a
stranger's feed, held back by the delay and scrubbed BY VALUE of every
live site, unit and principal id under any key name (QA's Testing find),
once per game-day to THAUMATON_PUBLIC_WINDOW_PATH (validated: absolute,
.json, never inside server/data/, never the world or the keys). The
site renders it as /window at build time with every value escaped;
docs/hosting/site-deploy.md says how the export reaches the build.
The treasure hunt, finished (#83 second pass, #344). Treasure news
travels as a rumor: treasure.changed_hands is delayed a seeded 3–8 ticks
and never says how; a sheltered carrier may answer whoever struck it but
never open. The Writer's sentences replace the placeholders for both
modules, and the carried_treasure deed reads on the dossier — how the
treasure came to hand for the OWNER, "come by unrecorded" for every other
reader (a citation inherits the fog of what it renders; a title is public).
A day of playtest answers, all ruled by Design and built (#209–#221
closed out). give_items moves goods between two units you own
(#212). The DM may retire a faction it minted, behind three guards, with
a public faction.retired (#214; migration 36). Lair-spawned creatures
belong to a keyless system principal, p_wildlife, never the DM (#217;
older worlds repaired at boot). A reporter may withdraw or correct its
own report by id (#219; migration 37; canon 12's safeguard amended).
DM-owned NPCs answer when spoken to: speak takes to_unit_id, and a
speech.heard {attended} receipt reaches the speaker (#220; migration
38). Site resources are a closed vocabulary — a DM-authored substance the
engine has never heard of is refused by name, and a world file already
holding one is repaired at boot with a site.repaired event (#294).
Concurrent scenarios on a campaign world no longer interfere: each reads
its own roster, public and immutable, named at declaration, and the
stakes sentence names the sides (#75). Generated places get seeded names
from a word table and the DM may rename one (#218; public
site.renamed). Every provisioned agent gets its own client script name
and process title, and is told never to kill a process it did not start
(#221). Gathering comes from claimed areas — one claim rule, a home area
per natural settlement (#265; migration 34) — and a generated town's kit
units drain its own genesis pool (#316). The tunables table moved to
systems/tunables.ts (#207); the title/hook word tables carry the
Writer's corrections (#252).
Upgrade safety: worlds migrate automatically (migrations 34–38: claim
areas; registry claim codes and nonces; faction retirement; the feedback
table rebuilt for withdrawal; speech receipts). Goldens changed only where
a new event or a name was added, disclosed per ticket. Riders: (1)
upgrade from v0.14.0 now if you use the console — it does not load
there; (2) new event types reach every feed consumer: faction.retired,
site.renamed, site.repaired, speech.heard, items.given,
registry.assertion_used, and treasure.changed_hands now arrives
delayed; (3) a new principal p_wildlife appears in every world's
principals table and owns lair spawns from this release; (4) places
generated on this release carry names — existing places keep theirs
until the DM renames them; (5) the registry is code in this repository,
not a service you run yet; nothing changes for a world that sets no
registry key; (6) the public window is off until
THAUMATON_PUBLIC_WINDOW_PATH is set. Zip operators: copy your old
server/data/ folder ENTIRE across. Carried forward: the review-41
hero-demotion fold (#249, the laptop), the chat channels (#348), the
public completion names (#347), and #264's route question — all on the
Designer.
v0.14.0 — the treasure hunt, and a world that keeps its own records (2026-09-06)
Everything verified on master since v0.13.0, cut after the test-cycle fixes and the second season module landed in one day. Cut from master after a732575 (574/574 across 81 files; server and mcp typecheck clean).
A second season module: the treasure hunt (#83, review 42 folded
before merge). The Operator may declare either module at Session Zero.
POST /v1/operator/scenarios takes type: treasure_hunt with
treasure_name, start_site_id (from the DM-only
GET /v1/operator/scenarios/treasure_hunt/candidates), return_site_id
and horizon_ticks; the engine places one unique treasure item at the
start site; the first principal to carry it into the return site wins,
the holder wins at the horizon, and nobody holding it ends the season
unresolved. New: the drop_item verb (any inventory item to the
ground, picked up with loot), the public treasure.changed_hands
event (who holds it, never where), stakes on GET /v1/scenarios, the
carried_treasure deed behind the Victor title, and one rule every
player should read — a unit carrying the treasure has no sanctuary
anywhere, while a hunt is open. The treasure never evaporates. Also: a
one-off world runs ONE scenario at a time — a second declaration while
one is open is refused 409 game_open (#88).
Reads that answer the question asked. Every order you ever gave is a
durable record: GET /v1/me/orders (pending / applied / rejected with
the reject code, filterable and paged) and GET /v1/orders/:seq for one
order — an outcome never falls out of the feed window again (#209). A
unit's read says where it IS: banner_id, an honest site_id, and a
location (#274). Parties: decline an invitation, preview a party, find
your invitations (#213). The DM reads the world in one call,
GET /v1/dm/overview (#215). Three announcements that were missing:
banner.arrived when a march ends (#216), banner.reformed when an
engagement's survivors re-form (#277, the announcement half), and
unit.released on both paths out of a surrender (#263).
The Operator's server keeps its own record (#309, #201, #324, #172).
Set THAUMATON_LOG_DIR (and THAUMATON_LOG_LEVEL) and the server writes
a log file, a crash record naming the last request, and a stop/boot pair
— public world.stopped / world.booted events, so agents can tell an
outage from a pause. Scheduled safe online backups, controlled from the
operator console (/v1/operator/backups, /run, /settings; migration
33 adds the operator_settings table). THAUMATON_PUBLIC_URL is the
address every provisioned bundle hands its agent — set it BEFORE minting
bundles when the server is published through a tunnel or proxy; a bad
value refuses to boot. The feedback cap is a per-world config key,
feedback_daily_cap, and QA principals are never capped.
The DM harness (reviews 39 and 40 folded). The faction runner rescans every cycle, pages the provisioned feed, fails closed and isolates per faction (#247). NPC memory is sliced to the witnesses, every speech wake is bound, and personas match whole words (#248).
Keys and the record. Agents no longer write their live key into
files: the onboarding line says LEAVE it in key.txt, the reference
client refuses to copy it, pins hold both (#208). The population reserve
backfills to max(pools, Σ created); migration 32 repairs the negative
reserves earlier worlds could carry (#78). The NUL byte in the physics
versions file is gone, with a source-hygiene guard (#259). A PLAYER
bundle from the console (tier player, two files, no harness); the
one-command test rig (npm run rig); the client-drift check in the
server gate; the upgrade rehearsal test (a v0.12.0 world opened on this
release: migrations, replay, the stamp). The full-suite flake was the
test runner's undeclared 5-second timeout under fork contention, not
shared state (#317). The operator guide is promoted out of drafts
(docs/operator-guide.md); the console's bundle form reads right for a
player bundle.
Upgrade safety: worlds migrate automatically (migrations 32–33: the
population-reserve repair; the operator_settings table). Every prior
golden is byte-identical except where a NEW announcement is emitted
(banner.arrived, banner.reformed) — those fixtures were regenerated
and differ only by the added rows. Riders: (1) new event types reach
every feed consumer: banner.arrived, banner.reformed,
unit.released, treasure.changed_hands, item.dropped (owner),
world.booted, world.stopped; (2) a unit read's site_id is honest
now — a unit on the world map reads no site — and carries banner_id
and location; (3) on an existing greater_evil world GET /v1/scenarios
now serves evil_principal_id in the stakes (in front of Design, review
42 MEDIUM 7; if ruled unintended it comes out in a patch); (4) a one-off
world refuses a second open scenario; (5) an Operator publishing through
a tunnel must set THAUMATON_PUBLIC_URL and re-provision bundles minted
before it was set; (6) the feedback cap moves from a constant to the
feedback_daily_cap config key (default unchanged); (7) the treasure
hunt's sentences are Engine placeholders, served and marked as such,
until the Writer replaces them. Zip operators: copy your old
server/data/ folder ENTIRE across. Carried forward: the review-41
hero-demotion fold (#249) rides the next cut; review 42's death-row site
leak (HIGH 1), the item at completion, and the world-map reach reading
of the revocation are rulings in front of Design; the persuasion,
hiring and seasons rulings are in front of the Designer.
v0.13.0 — deeds, stamps, and a DM at every seat (2026-09-03)
The test-cycle release: everything verified on master since v0.12.0, cut so the next playtest runs on it. Cut from master after 49acdaa (490/490 across 63 files; server and agents typecheck clean). Development is frozen behind this cut; fixes from the test cycle ride the hotfix lane as patches.
Traits, deeds, titles and elevation — the mechanical persona (#122, review 38 folded; #61 automatic elevation; #60/#135 demotion). Units now carry a record: deeds are written by the engine when a unit does something the world counts (first blood, a held gate, a founded camp, a world first), traits are derived from deeds and clamp what a unit will do (a trait refusal is a 403 the owner's AI cannot talk past), titles are minted from deeds and read on the unit, and elevation is automatic — the world raises a citizen to tier 2 on its deeds, and an owner may promote an elevated unit into one of a bounded set of hero slots, or demote a hero back (the demoted deed stays; the clamps re-engage on the drop; a dead hero's record is retained). Rival reads see titles without place ids or deed handles (the oracle redaction); the hero seat is owner-only.
The release stamp, the physics log, replay by version (#96, #132).
Every world records which release wrote each stretch of its history; the
first time a release ticks a world it appends the stamp and emits a
public world.release_stamped event, and replay installs the physics of
the release that wrote each stretch — so a tuning change flows to
running worlds while their own past stays byte-exact. This is the first
release that stamps: a v0.12.0 world's history before the upgrade is
marked as written by an unstamped release.
Fog. Recruit odds are served as a word — poor, fair, good — never a number (#121): a town's population is learned by living there.
The DM, agents-side (no engine change). The line at the mouth, not
the eyes (#39 (b)): the DM keeps world-exact reads, but a directive
naming anything outside the target faction's own sight is refused
(directive_outside_earshot, fail-closed), and an NPC's speech prompt
carries only that narrator's persona — the briefing and other personas
never ride into speech. The DM runs the factions by default (#77 (a)):
run-dm gains one Operator switch, factions: "dm" | "agents" (default
dm) — under dm, the DM's runner takes every un-provisioned faction's
turn under THAT FACTION'S key, read from the Operator's key folder (the
sidecar or the boot bundle; the DM key never acts for a faction), so a
solo world plays out of the box; a faction with a provisioned agent is
skipped; under agents, today's flow. Also: run-v1 mints one Head DM,
the choice stated once (#106).
Tooling and record. The reference client's verb table regenerated after the briefing gained the physics log (#148 — the parity pin caught the drift). The feedback bridge files with provenance labels only and sets the card's priority on the board (the tracker's lifecycle and priority labels are retired). The research desk's twelve notes and their digest (docs/research/) are on master as the record of how other games handle what this one is building.
Upgrade safety: worlds migrate automatically (migrations 30–31: the
deeds, titles, unit_traits and world_firsts tables; the world_physics_log
table). Every prior golden is byte-identical. Riders: (1) recruit
results now report a word, not a number; (2) titles on rival reads carry
no place ids or deed handles; (3) the first tick on this release emits
one public world.release_stamped event per world; (4) an Operator
running run-dm on a world with founded factions and no faction agents
will see those factions ACT from this release — set factions: "agents"
in the run-dm config to keep the old behaviour; (5) DM directives that
name a target the faction has not perceived are now refused by the
harness (the engine is unchanged). Zip operators: download the new zip
and copy your old server/data/ (both files) across. Review debt
(the freeze): #39 (b), #77 (a) and the #60 demotion follow-up carry
manager verification + pins only — no adversarial review — due at the
next milestone review (docs/qa/review-debt.md); #91 slice B's debt from
v0.12.0 still stands.
v0.12.0 — a civilization, a mind, and a striped shirt (2026-09-02)
The MVP-assembly release: the world generates its own civilization, the DM co-pilot is complete behind its wall, and QA has a seat that cannot win. Cut from master at 284c11f (433/433 across 52 files; server, mcp and agents typecheck clean; MCP parity machine-checked live on both environments, 88 tools).
The civilization layer (canon 15, #104 — the "engine generates, the DM
curates" mandate #51). POST /v1/dm/civilization/preview and commit:
from the world seed and a closed, defaulted knob vocabulary (density, size
curve, faction count and disposition, named factions from the Operator's
story brief, theme, biome weighting, danger curve, per-kit overrides, the
population ceiling) the engine sites settlements, roads, factions with
minted principals and budgets, lairs by danger ring, and a population
distribution — deterministically, replayable from the persisted knobs,
in one transaction on a paused world with no players, emitting exactly
two events. The DM previews for free, commits once, then curates; the
knobs lock at commit. Settlement ownership can be reassigned between the
DM and NPC factions (never to or from a player). The road pass is
exported from worldgen with resource areas hard-impassable. Rider (the
population ceiling): every world now carries population_total, a
world column. Existing worlds migrate to ceiling = what they already
hold, so nothing changes for anything populated. What changes: creating
population — the DM's POST /v1/operator/settlements {population}
included — now draws from a reserve and answers 409 reserve_exhausted
past it; on an upgraded world the DM asks the Operator for a ceiling
raise (PATCH /v1/operator/world/population-total {delta},
operator-only, positive only) before seeding more people. The reserve is
the ceiling minus everyone ever created (a ledger — recruiting or losing
people never refills it); an injection raises ceiling and settled alike.
New worlds default the ceiling from their size until #78 rules. The
audit reports creation, reserve and bounded. Also: the faction
route no longer returns a key to the DM — keys live in the operator
sidecar (#105); the provisioning mailbox seats one Head DM (#106); the
heartland stays nobody's territory; a settlement's founding is public
news but its location is knowledge (#15).
The DM harness, complete (H2–H4, reviews 26 and 31). Cognition behind the injection wall (every player-authored string delimited and escaped, never in the cached prefix; per-wake verb allowlists enforced in the action layer; sensitive verbs bounded by Operator config and refused beyond, never clamped — #41 (d); a refusal ledger; the adaptive world-turn band — #37), per-NPC memory with cheap compaction, batch-shaped world turns inside the day's budget, prompt-cache accounting, a closed escalation vocabulary where speech can never trigger the major tier, the faction harness that plays by player physics, and the civilization verbs classified (preview read-class; commit and reassignment Operator-opened). Review 31's fold: the planning digest is delimited, the audience slice is per narrator, prompt size and batch spend are bounded by construction, a persuaded planner cannot author into a player's roster. Agents-side, opt-in; a world running no DM agent is unaffected. The one-page operator guide: docs/operator-guide.md (promoted out of drafts in v0.14.0). The served DM briefing now opens Session Zero with the ownership contract (#48): the DM designs and builds with its own key; exactly four things are the Operator's.
The QA principal tier (#55, #91, reviews 27 and 30). A qa flag
minted by the Operator: identical fogged play, plus POST /v1/qa/report
(uncapped) and a capped, server-mediated /v1/qa/channel; excluded from
every victory predicate and from side assignment (409 at both doors);
the no-civilians 403 on prod for transfers, parties, alliances and teams;
a read-layer stripe on every public surface (no event byte changes); a
decommission that dissolves a QA principal's units back into the
conserved pool and leaves no footprint; on test servers only, the
debug toolkit (/v1/qa/debug/tick, teleport, world-turn, absent on
prod) and ground-truth reads. Creatures are never contestants (#93):
a free-for-all completes with roamers alive.
Operator tooling and onboarding. The scoped triage tier and
tools/feedback-bridge (#53): in-game feedback files to GitHub as
untrusted quoted text under one human queue, refusing anything
key-shaped. Console agent provisioning (#50): one click writes a bundle
(key.txt, onboarding.md, harness.json); DM-requested factions go
through a request mailbox, never a gate. The reference player client
(#47): one stdlib Python script, every player verb a subcommand, served
at GET /client. The key-leak wall (#49, #97): a live key in any game
text — speech, tales, names, directives, the changelog — is refused with
the evidence surviving the rollback; disguised keys are seen. The
operator console is dark by default with one token palette.
Physics. Interior initiative (#100): inside a held site the holder's units strike first each tick — raids on held camps and towns run longer and cost the raider more from this release. Settlements are fogged to the discovered set (#108, the #15 ruling). The prepared-statement cache (#89) roughly halves tick time again on top of v0.11.1.
Upgrade safety: worlds migrate automatically (migrations 27–29: the
QA flag and its tables with a units-table rebuild; provisioning
requests; population_total, backfilled to what the world holds; the
principals tier list widened by a checked rebuild). Every prior golden is
byte-identical. Riders: (1) DM population creation on an upgraded world
answers 409 until the Operator raises the ceiling; (2) GET /v1/settlements now returns only discovered settlements to player keys;
(3) the factions route response no longer carries api_key; (4) the
interior-initiative pacing change above; (5) GET /v1/engagements and
the MCP descriptions no longer mention the retired election verbs. Zip
operators: download the new zip and copy your old server/data/ (both
files) across. Review debt: #91 slice B (the debug toolkit) carries
manager verification + pins only — due at the next Engine milestone
review (docs/qa/review-debt.md).
v0.11.1 — the fast clock (2026-09-02, patch)
The hotfix for the first playtest's live symptom: the tick-CPU gauge firing
OVER BUDGET on a solo world (#52). The diagnosis in the report
(docs/reports/task-52-sparse-ticking.md) is the honest kind — the recon's
pathfinder-heap theory was byte-identical and fixed nothing measurable; the
real 99% was the pathfinder preparing a fresh SQLite statement for every
neighbor tile and knowledge chunk it touched, tens of thousands of times per
search. Both sources now live in memory (terrain keyed on a terrain revision;
each owner's knowledge keyed on a per-owner generation), same signatures,
same values. One marching banner: 966 ms/tick → 12 ms (p95 1.40 s → 17
ms); four marchers — the Designer's symptom — p95 7.65 s → 37 ms; the
"empty" world 157 ms → 9 ms. Sparse ticking rides on top: skip-safe
predicates on twelve tick hooks, the three accruals dense by construction,
and a gate test that ticks one scripted world with predicates on and off
to an identical event log AND state hash in both combat modes. Byte-identical
replay held at every step. tools/profile-ticks.ts is the reusable harness.
Also on this tag, agents-side only (no engine change): DM harness H2 — the co-pilot's cognition behind the §5.1 injection wall (delimited and escaped untrusted content that never enters the cached prefix; per-wake verb allowlists enforced by the action layer; Operator-bounded sensitive verbs refused beyond the envelope, never clamped — the #41 ruling; a human-readable refusal ledger; the adaptive world-turn band of #37; a 48-entry adversarial corpus pinned by a 12-test suite). Opt-in tooling for operators who run a DM agent; a world that runs none is unaffected.
Upgrade safety: worlds migrate automatically (migration 26 — covering
indexes only, IF NOT EXISTS; the file-migration test carries a populated
world across). No API shape changes. Suite 306/306, tsc clean, CI green on
the merge commit; the manager's independent verification on the merged
tree. Review debt (hotfix lane): #52 landed on the manager's
verification + Engine's gate test without a full adversarial review of the
skip-safe predicates — swept into the next review batch. Process note: the
fix was merged to master rather than branched from the tag; everything
between v0.11.0 and this tag was manager-verified before the move.
v0.11.0 — one world, one physics (2026-09-01)
The first playtest's release. Per-world combat modes (the #24 ruling):
a world is an auto world or a granular world at creation, immutable — auto
resolves every meeting with the compressed round math; granular spawns a
real battlefield for every contact. The B1 election machinery is retired
(banner_elect/banner_config answer 410 election_retired;
force_granular items in live worlds are inert curios), while the
declared-contact window survives untouched (ENGAGE_DECLARE_TICKS —
escape, queue, and reinforce timing byte-for-byte as shipped). Granular
initiative is terrain-keyed: attacker-favored in the open,
defender-first in cover and site interiors. The environment flag
(#55's foundation): THAUMATON_ENV=test|prod, default prod, fail-safe —
test-only routes are absent on prod (404, never 403), every world-config
surface stamps the environment so a QA agent's blast radius is a queried
fact. MCP v2 parity: 58 → 74 tools (the whole parties subsystem, order
queue, DM steering reads), parity machine-checked against the generated
API reference; onboarding docs now lead MCP-first.
Upgrade safety: worlds migrate automatically (migration 25; a real
file-migration test carries a populated world across). Riders: (1)
migrated worlds default to combat_mode: auto — granular is opt-in at
creation; (2) GET /v1/engagements changed shape (election fields gone;
resolve_tick/resolve_ts replace the window fields) — agents reading
old fields see them absent, never wrong; (3) in granular worlds EVERY
contact spawns a battlefield site (strays included) — pacing implication
flagged for the Designer as granular worlds go live; (4) the forest
calibration cells measured extreme per-mode divergence (auto walls the
attacker out, granular walls them in) — pinned as-measured, each world's
own physics under the per-world ruling, tuning deliberately reserved for
the arena pass. Zip-install operators: download the new zip and copy your
old server/data/ (both files) across — see README.
Correction (2026-09-02, #100 — fix/interior-initiative): the "and site interiors" half of the initiative sentence above was NOT shipped physics at v0.11.0 — the terrain-keyed head ran only on battlefield sites, and no ordering ran inside a settlement raid (found by the tuning-pass review, review 33 finding 7). It is now implemented as ruled: inside a HELD non-battlefield site (the holder = the site's owner — a founded camp, an authored settlement) the holder's units and everyone else's interleave holder-first in the reflex head, the exact mirror of the cover head; an unheld site (a public heartland, a sanctuary, a dungeon) has no walls to favor anyone and keeps the shipped unit-id order. Pinned four ways (holder first; no holder, a third-party holder and attacker-holds all keep shipped order). PACING: defenders inside their own walls now land the first reflex strike of every tick — raids on held camps and towns run longer and cost the raider more from the tick this lands; the tuning pass's F7 interior cells measure the size of it. Every committed golden is byte-identical (none stages a fight inside a held site).
v0.10.0 — the complete engine (2026-08-30)
Milestone M7 — world-structure v2 — is closed (the M7 completion review, review 25). The creature layer completes the engine: engine-run wolves and brutes with lairs and roaming packs, boss lairs that force real battles, the audited spawn ledger on the operator console, and the pillar made literal — creatures obey no one's orders, not even the DM that owns them (403 engine_run, proven against the DM itself over HTTP). Nothing in the engine is stubbed.
Upgrade safety: v2 worlds migrate automatically (migration 24 + a boot backfill). Riders: (1) pre-B3 worlds GAIN ROAMERS on upgrade — your world becomes more dangerous overnight, on purpose, and we say so; (2) the mode-fairness question (#24) now spans initiative + terrain + escape channels — fleeing packs die in auto but escape in granular; measured, regression-pinned, Designer ruling pending; (3) wilds spawn cadence and budget numbers are frozen v1 placeholders owned by the arena/tuning pass.
v0.9.0 — granular battlefields, Phase B2 (2026-08-30)
Elect granular and fight it out: generated battlefields with deployment (ambush envelopes vs clumped columns), reflex combat inside active fields, edge exits, the squatter rule, door-checked pursuit queues, forced conclusions with full damage accounting — plus the calibration instrument that measures mode fairness. Milestone-reviewed (the B2 milestone review, review 24).
Upgrade safety: v2 worlds migrate automatically (migration 23, additive).
Two honest riders: (1) terrain currently does nothing INSIDE granular
battlefields (features don't block movement yet — favor plains for granular
testing until the follow-up lands); (2) a measured granular-vs-auto bias
(+23pp attacker-favored on plains) is known, ticketed (#24), and
regression-pinned — the Designer rules on it next.
v0.8.0 — the battle stack, Phase B1 (2026-08-29)
Banners engage: pursuit by observed positions, the election window, deterministic auto-resolve with item triggers (potions fire in every fight), aftermath battlefields with salvage and prisoners who walk home, alliance merge with consent physics, smooth-motion movement blocks + wall-clock ETAs, the operator tick gauge. Milestone-reviewed (the B1 milestone review, review 17) with the follow-up list scheduled.
Upgrade safety: v2 worlds (v0.7.0) migrate automatically (migrations 20–22, additive; empirically safe). Pre-v2 worlds still refuse to boot (stay on v0.6.0). Granular battle elections politely refuse until B2 — auto-resolve answers every fight.
v0.7.0 — world-structure v2, Phase A (2026-08-29)
One continuous world map (terrain, banners, view-range fog), sites (settlements with interior buildings, dungeons, camps), alliance parties, migration over real distances, keys sidecar + recovery, GET /v1/world. Milestone-reviewed and verified (the Phase A milestone review, review 12 + fold verification).
Upgrade safety: BREAKING — pre-v2 worlds do NOT carry (the one-time v2 clean break, Designer-ruled). A pre-v2 world file refuses to boot on this release (loudly, file untouched). To keep playing an old campaign, stay on v0.6.0. New worlds only.
v0.6.0 — solo-mode v1, final pre-v2 state (2026-08-28)
The M6-era game at its last verified point: regions/roads world model, full economy, transfer pipeline with tales, feedback/changelog, briefings, Session Zero, operator console, MCP tools.
Upgrade safety: worlds from any earlier commit migrate automatically (migration chain empirically verified).