The build job runs docker on the runner host's daemon, so every run left
two tagged images behind for good. Remove this run's tags once they are
pushed and cap BuildKit's cache at 5 GB, always, never failing the job.
The deploy drops the dangling image its pull replaced on the rgcom host.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
platform.json's androidApk.serviceable goes to true. It was false because
v0.5.0's build did not work. v0.7.0's released APK (SHA256 matches
SHA256SUMS) was installed on an Android 16 emulator, connected to the
public demo and read its Rust servers, so /app/ links the download again.
The comment beside the flag now says when it was last checked and to set
it back to false when `asset` moves to a release nobody has run. PLAN.md
D29 gets a dated note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Android-app v0.7.0 released 2026-10-30 (M8's cutover). platform.json
quotes it and its APK; /app/ gains the two things it added for a member
(report/block/hide on chat lines, and deleting the account), and the
Moderation docs name the app's long press beside the site's menu.
androidApk.serviceable is left false: it records a person having run
the build, and nobody has run v0.7.0's release APK on a phone yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
check:facts went red on seven facts after the day's releases: Module API
1.21.0, Module-Rust v0.7.0 (coreApi ^1.21.0), Module-uo v1.4.1,
Rust-Plugins v0.3.1, and the Rust bundle 2026.10.30 (plugin v0.3.1).
platform.json now quotes them, verified 2026-10-30.
Re-reading the pages that quote them: Module API 1.21.0 is report, block
and hide, so the Moderation page's Reports section described a queue
that no longer exists (forum-only, and "handling a report never touches
the content"). It now covers chat-line reports, the report snapshot,
Hide for everyone from the queue, and what members do for themselves
(Hide for me, Block, Account -> Blocked). The Rust docs' Feed row names
the chat-line menu. The app's menu is not mentioned: the app release
that carries it (v0.7.0) has not shipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
The app now sends reports, blocks and hides (USER_SAFETY.md step 4,
Android-app#64). A new app row, app-safety, declares them: App activity ->
Other user-generated content (the report's note) and Other actions (the
report, block and hide themselves), collected, not shared, required. Other
actions moves from "not collected" to collected in PLAY_DATA_SAFETY.md.
The deployment row for posted content now says a report keeps a copy of
what it reported, and that blocks and hides are kept on the account and
deleted with it (user_blocks and user_hidden_items cascade).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
check:facts was red on main after account deletion's core and module
steps merged: core's MODULE_API is 1.20.0, Module-uo declares coreApi
^1.20.0, and Module-uo released v1.4.0. Every page reads these from
platform.json, and none needed rewording. Folded into this PR by the org
lead's choice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Step 5 of docs/website/ACCOUNT_DELETION.md (§4.5). The page explains how
to delete an account on a community's site and in the app, what goes,
what stays and for how long, and who to ask when you cannot sign in. It
cannot delete anything, because we hold no account.
- The facts are one deployment row in collection.mjs (deletion.goes,
.stays with a keep each, .staff), in the website's own words. /privacy
section 3 renders it as prose, the page renders its lists, and
PLAY_DATA_SAFETY.md prints them under the console's deletion questions.
- The app rows that a deletion touches (session, trust token, push,
inbox snapshot) say so. The Data Safety notes now answer "yes" to
account deletion and name the URL. lastUpdated is 2026-10-30.
- D85: linked from the footer's legal bar, /privacy sections 2 and 3,
and /app/'s "Two ways to get it".
- D86: someone who cannot sign in is sent to their community's operator,
never to us.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Play counts data that leaves the device as collected, whoever runs the
server it lands on. The generated Data Safety notes answered "not
collected" because we run no server, which is true of /privacy and the
wrong answer to the form (org lead, 2026-10-29, Android PLAN.md M8).
An audit of the app's edge (every body, query and header in data/api/)
found more leaving the phone than section 2 listed. New rows: the
User-Agent, game-account links and creation, the Contact form, staff
posts and moderation, notification read state and preferences, search
text. The sign-in and trusted-device rows now say what is sent (the
credentials, the device's make and model).
- PlayMapping: a list of console types, plus required and purposes,
checked against PLAY_DATA_TYPES / PLAY_PURPOSES (the first version
filed "Other user-generated content" under Messages).
- The generator answers per data type, lists every type not collected,
and answers the form's security questions.
- The premise test now asserts what is still true: nothing is shared.
- legal.lastUpdated moves to 2026-10-29 with /privacy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
check:facts was red on main: platform.json said Module-Rust v0.6.0 and
releases/latest says v0.6.3. v0.6.1 and v0.6.3 are demo-only fixes (DM26,
DM27; there is no v0.6.2) and still require MODULE_API ^1.19.0, so no
page's prose changes. Every page that shows the version interpolates it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
The public demo (demo.runicgateway.com) is live, so the site's demo slot
can be switched on. Two decisions of record, PLAN.md §15:
- D82: /beta reads the demo's GET /api/v1/public/settings demo.logins on
the server, caches it for a minute, and lists the published sign-ins
with the next wipe. If the demo does not answer, /beta links to its
sign-in page instead. Gate two reads as in place.
- D83: a deep link into a game module's pages carries data-demo-game, and
applyBrand.mjs fills it only for games in the new mounted demoGames
list. The Rust features gain links into /rust and /rust/servers/demo.
UO's stay hidden on a Rust demo.
The public-demo not-built entry is removed, because its resolvedBy was met.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
checkFacts turned this PR red on drift from the demo-mode merges, not on
the Node change: website main is at MODULE_API 1.19.0, Module-Rust main
declares coreApi ^1.19.0, and Module-Rust released v0.6.0. Every page
reads the three values from platform.json; verifiedOn moves to today.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
CVE-2026-111497 ("Node2Shell") is an out-of-bounds write in Node's
bundled Brotli decoder, fixed on 2026-10-21 in 22.23.4. The image used
the floating node:22-bookworm-slim tag, so a cached or stale base could
still carry an older 22.x. Pin the patched version in both stages and in
the PR checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Both were released on 2026-10-23. Android-app v0.6.0 is the cutover that
ships the Rust screens and the app's core wording (M20), and its APK asset
is runic-gateway-0.6.0.apk. Module-Rust v0.5.1 brings the weapon names in
the feed. verifiedOn moves to 2026-10-23, and checkFacts agrees on all 41
facts.
androidApk.serviceable stays false. It records a person's confirmation
that the published APK works, and nobody has run v0.6.0 against a real
site yet. A smoke test of the signed APK on the emulator covered install,
launch, the HTTPS rule and one TLS round trip, but no Runic Gateway site
serves HTTPS locally.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
rust-feed (web) and app-rust-server (phone) showed the prefab names the
module printed before Module-Rust#60 ("with ak47u entity"). Both were
retaken from the Rust demo running that fix, after re-running
mockRustDemo.mjs so the feed's times are fresh. They now read "with
Assault Rifle", "with Thompson", "with MP5A4".
The phone shot was taken with the emulator in Pacific/Honolulu so that no
row crosses midnight and picks up a date; the timezone was restored
afterwards. The real Oxide rig has nobody on, so the server reads 0 of
40 online, and app-rust-server's alt now says "how many players are
online" rather than "six".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Nine captures tagged rust (D65): the server list, feed, leaderboard,
live map, permissions and the RunicNPC profile editor on the web, and a
server's feed, its map and the player's Rust account in the app, from
an Android-app edge debug build.
D80: every name in a capture is a role (admin, moderator, player...),
from this phase on. D81: the Rust demo's players are mock data written
by scripts/mockRustDemo.mjs; the server, its status, map, permission
groups and RunicNPC sync state are the real Oxide test server's. The
captions and /modules/rust/ and /app/ say which part is a demo cast.
seedDemo.mjs gains --game rust (server row with a weekly wipe, public
presence for the demo, two NPC profiles, a VIP group). captureScreens.mjs
gains a click list per screen. The homepage shows the Rust live map beside
the UO capture (D76); three Rust docs pages carry their screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16 phase 17, built on the org lead's answers of 2026-10-22,
recorded as D75-D79 (count of record 79).
- D75: core marketing pages describe what a game module adds in core's
words. "Game intelligence" became "What a game module adds"; each
game's own list moved to src/data/games/ and renders only on the new
/modules/uo/ and /modules/rust/. /modules/ is a card per game.
- The page-side capability check is per game (assertGameCoverage), the
item phase 13 moved here.
- D76: the homepage keeps its game screenshot, labelled. screens.mjs
entries may carry `game` (D67); Screenshot.astro and the app grid
print the game's name and mark the figure data-game.
- D77: /app/ describes the Rust screens (released the same day).
- D78: /runicnpc/, the standalone page, is in the header and footer;
the narrow header was re-measured so five links keep two rows.
- D79: checkTerms.mjs --built reads every built page's visible text
after the build; game routes and data-game elements allow their
game's words. Proven against ten plants. Still the twelfth check.
- Re-read fixes: /architecture/'s "two machines is also the maximum",
Discord "guild" on /integrations/, and collection.mjs against what
Steam sign-in and the Rust module keep (PLAY_DATA_SAFETY.md
regenerated; the Play answers are unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16 phase 16, with the org lead's D72-D74 (count of record 74).
- Eight pages nested in the Rust group at /docs/rust/runicnpc/ (D72): the
overview, Install RunicNPC, Commands, NPC profiles, NPC placements, Cost and
caps, Profile fields (the one field reference the editor, rnpc.profile set,
the data file and the API all share) and The API.
- The API page carries every call, hook and data shape (D62), in the site's
own words, with no decision numbers and no per-call version history (D74).
Signatures print from src/data/runicnpc.mjs through Signature.astro.
- checkReference.mjs gains two sections. Against docs main:runicnpc/API.md:
the API version, 24 call and 10 hook signatures, and the field names of the
profile, RunicNpc_List rows, placements, routes and owners (D73), plus
exactly one <Signature> per call on api.mdx. Against runicnpc-rust
main:plugin/RunicNPC.cs: console verbs, permissions, place options and
config keys. Six planted drifts all failed before it was trusted.
- checkSidebar and checkTerms read nested sidebar groups via flatLabels().
- The Rust overview, Events zones and rewards, and Canonical documents link
the new pages; the canonical documents gain the four runicnpc/ docs.
Both NPC admin screens were walked on the walk site first (D36); nothing was
saved. npm run verify is green.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16 recorded three phase-15 findings as reported rather than
fixed. The org lead asked for them fixed the same day: Module-Rust #59
(24 decision numbers in admin copy, and "1 player holds one") and
Rust-Plugins #45 (EventsEnabled's comment). The record now says so.
Reading every EventsEnabled check for that comment also showed the
switch applies at once when saved from the site's config editor, while
the bridge's other settings wait for its next reload. RunicGateway.json
now says that, and adds paying coins to what the switch stops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16 phase 15. A Rust group of fifteen pages after Ultima Online
(D70): the module, installing the bridge (installer, Pterodactyl egg, by
hand), connecting the site, verifying the bridge, linking Steam accounts,
permissions and groups, mod config, who sees who is online, events, zones
and rewards, optional plugins, the live map, troubleshooting, and three
reference pages - rust-link's sidecar.toml, the plugin's
RunicGateway.json, and the event kinds.
checkReference.mjs gains three Rust sections rather than a new check:
sidecar.toml keys and RUSTLINK_* overrides from Rust-Link config.rs,
RunicGateway.json from the plugin's PluginConfig, and the event kinds
in three classes against Module-Rust server/catalogue.js (D71). Every
admin screen was walked on the walk site before it was described (D36).
Core pages now hand off to the Rust group instead of linking out to the
docs repository. Three defects the re-read found here are fixed: the UO
/health sample's hard-coded protocol 4 (both games' samples now render
platform.json through <Code>), Authentication's missing Steam tab, and
two pages saying the site cannot reach EventsEnabled - false on Rust.
Installer v0.8.1 moved checkFacts red mid-phase; platform.json follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16, D63/D64, plus D67-D69 taken 2026-10-22.
- Core docs use core's words: a Team, a game server, the sidecar, the bridge
plugin, the game module. Detail that belonged to Ultima Online moved into
its pages rather than being dropped.
- New "Ultima Online" sidebar group (nine pages): an overview, Connect a
ServUO shard, Verify the bridge, The shard connection, Client files,
Troubleshooting, Bridge.cfg, sidecar.toml, Shard event catalog. Five moved
URLs answer a real 301 (src/config/redirects.mjs, asserted by the served
test).
- Getting started is game-neutral. "Connect your game server" keeps the old
URL and hands off to each game; Rust links docs main:rust-link/INSTALL.md
until phase 15. "Verify the whole stack" stays, neutral (D69).
- D68: GameExample.astro labels a game's real example on a core page with the
game's name; checkTerms allows that game's words inside it.
- scripts/checkTerms.mjs, the twelfth check: 16 game words, docs pages and
both sidebar trees, one allowlisted phrase (Discord's "Guild (server) ID").
Wired into verify and pr-checks.yml.
- canonicalDocs grouped by game; the Rust group carries four documents.
- D67: the screenshot rule from #32 is adopted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
platform.json drops the single protocol/bundle/capability list for a
`games` object (uo, rust) plus a `runicnpc` block. checkFacts.mjs loops
the protocol, plugin-protocol, capability and bundle checks over both
games, reads the schema-2 bundles at v2/<game>/current.json, checks each
module's coreApi, and reads RunicNPC's plugin.toml. 41 facts, up from 19;
clears the two that were red on main (Module API 1.17.0, installer
v0.8.0).
D66: the hero, footer and structured data show the Module API only; each
game's protocol and bundle go on that game's pages, and the docs landing
table splits into shared versions plus one column per game.
The re-read section 1 requires found, and this fixes:
- three hard-coded "1.6.0" Module API versions in the module docs
- module-lifecycle saying modules load alphabetically (the loader now
refuses a second module, W19), and the install pages never mentioning
the one-module rule
- "there is one module" in five places, now two modules both built
inside this project; the Integration Kit keeps its draft banner
- the homepage diagram's ServUO/uo-link sub-labels (D63 wording now)
notBuilt drops `second-module` (resolved by Module-Rust's release) and
rewords `multi-module`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Adds PLAN.md section 16 with the org lead's six decisions of 2026-10-22
(D60-D65) and phases 13-18:
- D60 per-game sidebar groups (Ultima Online, Rust); core docs go
game-neutral, moved pages keep redirects
- D61 RunicNPC as a subgroup inside Rust, plus a standalone /runicnpc/
- D62 the RunicNPC API carried in full, with a drift check against
docs/runicnpc/API.md (a deliberate exception to section 1)
- D63 game names allowed outside game sections, game vocabulary not
- D64 checkTerms.mjs, a twelfth check, amending D53
- D65 Rust screenshots last, captured from the rigs
Also records what moved since the site's last fact check: Module API
1.17.0 and installer v0.8.0 (main is already red on both), Module-Rust
v0.5.0, the v2/rust bundle at protocol 14, and RunicNPC v1.0.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
The last leg of the Asset Bridge (docs/link/v8.md §16, phase 9c). The cutover put
protocol 8 on `main` in every declaration site, which turned this repo red by
construction: `checkFacts.mjs` reads the protocol from `link`'s `main`.
Two checks were failing, and only one of them was expected.
**checkFacts (10)** — protocol 7 → 8 in all three reads (sidecar, overlay, bundle),
the bundle triple to 2026.09.15 / sidecar v2.3.0 / overlay v1.3.0, and the releases
to link v2.3.0, installer v0.2.0, Module-uo v1.3.0. `moduleApi` and the capability
list did not move: core's whole share of eight phases was a deletion.
**checkReference (9)** — nobody had planned for this one. `Bridge.cfg` grew nine keys
across phases 5, 6 and 7 and the reference page had gone on not mentioning them,
which is the failure mode that check exists for. They are listed as two new groups,
`Client assets` and `Spawn files`, because they are two separate consents.
## The prose the bridge changed
- **New page, Administration → Client files.** `Admin → Client files` is a screen an
operator has to press a button on and the site had no page for it. It carries the
rule nothing else states: **nothing here happens on a restart**, so a patched client
keeps serving the old artwork until somebody says so.
- **Protocol versions** said the most recent bump touched five repositories and named
`website`. Protocol 8 touched four and did not reach core — its absence is the
interesting half, and the page now says why. The store-migration paragraph gains 8,
which changed no line of the sidecar's store despite moving megabytes.
- **The bridge** said "two ways in". There are three: bulk reads go over the
request/reply path in pages, one in flight at a time, and the reason they must not
ride the event stream is structural rather than about speed.
- **Requirements** gains `libgdiplus` for Linux shard hosts, and the note that no game
client has to be installed for any of this — a ServUO shard cannot boot without one.
- **Troubleshooting** gains the failure mode that did not exist before: a bestiary with
no pictures, or items reading as numbers.
- **Maintenance** gains the button to press after patching a client, and **Verify the
whole stack** the `doctor` check that landed with it.
- The spawn atlas and the marketplace both carry artwork now, so both capability
descriptions say so, and `canonicalDocs` points at `link/v8.md`.
## Screenshots, retaken against the real thing
The atlas and marketplace shots predate the asset plane, so both were retaken on the
demo deployment against a real shard and a real sidecar, plus the new admin screen:
1,095 portraits imported in 2.1 s, 67,496 names in 1.4 s, 313 item pictures in 0.8 s.
Nothing here is a mock-up. The atlas shot moved 36px down its page because portraits
made the rows taller.
## One defect found on the way
`seedDemo.mjs` pinned the demo deployment at **protocol 4** with a comment explaining
a `module-uo` debt that has since been paid. Its default now comes from
`platform.json`, so the rig cannot drift two protocols behind the platform again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Phase 16c of the events plan: `runicgateway.com`'s half of the workstream, now
that `main` carries the engine, the module, the app and the bundle.
The checks were already red and named their own answers:
* `checkFacts` — nine values had moved. Protocol 5 → 7 in all three declaration
sites, `moduleApi` 1.9.0 → 1.10.0, the bundle to 2026.09.10 with sidecar
v2.2.0 and overlay v1.2.0, `link` v2.2.0, `Module-uo` v1.2.2.
* `checkReference` — twenty-seven `Bridge.cfg` keys the site listed nowhere: the
events switch and its sweep, the ten caps, the oracle NPC, the two lease keys
and the seven participation keys. They are five new groups rather than an
appendix to an existing one, because `EventsEnabled` is a second consent
switch and belongs beside its own ceilings.
Two pages, matching the treatment Teams has:
* **Scheduled events** (Administration) — where it is and who sees it, authoring
and immutable versions, the switchboard that arrives off, caps as a condition
on an `UPDATE` rather than a role check, the dry run, the run console, what an
event owns versus what it borrows, generated cleanup, the shard's own switches,
and what a player sees.
* **Events architecture** — the two sentences it turns on, what is a table and
what deliberately is not, budgets in SQL, the ledger's two rules, at-most-once
on a wire that can lose an answer, the three layers, and the four omissions.
And the rest of the surface:
* `/privacy` gains **`deploy-events`** — the participation ledger is personal
data and no row named it. Scores and ranks against a module-opaque member key,
linked to an account where one is linked; the diagnostic log swept after 90
days on terminal runs only; the run, its steps and its participants not swept
at all, because they are the record of what was done to a shared world.
`deploy-game-data`'s citation moves from `link/v4.md` to `v7.md`.
* **Protocol versions** — the most recent bump touched *five* repositories, and
the `website` row is the interesting one: core is normally out of a protocol
bump's reach and this one reached it, because what changed was not a game noun
but the shape of a thing core owns the ledger for. The store-migration
paragraph now says four bumps' worth rather than two.
* Two capability entries, so `/`, `/features/` and `/modules/` stop omitting the
subsystem — an Administration item, and an **Event calendar** under Community
with `/site/events` as its deep link. Deliberately *not* `needsModule`: a bare
core can author and run an event, and only the world verbs need a module.
* **`reference/event-catalog` is retitled "Shard event catalog"** and says what
it is not. Two things in the docs were called an event catalog; the route is
unchanged, so nothing outside this repository breaks.
* `canonicalDocs` gains `website/EVENTS.md` and moves `link/v4.md` → `v7.md`.
No screenshots. Capturing the events surfaces means standing the whole rig back
up — game server, sidecar, core, module, a published event with a live run — for
two or three images that no check requires, and the engagement workstream's own
site leg added none either.
`npm run verify` green end to end, including `checkReference` against the
protocol spec that only reached `docs` `main` in RunicGateway/docs#232 — the
seventh cutover step, which 16b had left on `edge`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
ENGAGEMENT.md Phase 14. The deploy-engagement entry in the collection
inventory said "Kept until the operator removes them; nothing here expires
on its own" and, in its detail, "the delivery log, the suppression list and
the per-person rate limits have no retention sweep, so they are as long as
the site is old". That was the true answer until Phase 14 landed a sweep,
and it is the kind of sentence a Play reviewer reads.
It now describes what the code does: a nightly sweep with an
operator-settable horizon per table (180 days for the delivery log, 30 for
the queue and the rate limits), and the two things that deliberately do NOT
expire — an item still waiting to be sent, because it is a message the site
still intends to deliver, and the suppression list, because ageing an entry
out would mean mailing an address that already bounced.
PLAY_DATA_SAFETY.md is unchanged and check:datasafety stays green:
deploy-engagement is deployment-scoped, and the Play answers are generated
from the app-scoped entries, because Play asks what the app collects rather
than what a self-hosted deployment keeps.
All ten checks green, 42 tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>