feat(admin): online dictionary update — upload archive, preview word diff, install & activate #53

Merged
developer merged 1 commits from feature/dict-admin-online-upload into development 2026-06-13 21:41:01 +00:00
Owner

What

Replaces the dictionary hot-reload with an online dictionary update in the GM console (/_gm/dictionary):

  1. Upload the scrabble-dawg-vX.Y.Z.tar.gz release archive (downloaded from the scrabble-dictionary repo).
  2. Preview, per variant, the words added/removed vs the active dictionary (counts + first ~200 + a large-removal warning). Version is parsed from the filename and editable.
  3. Confirm → the version is written to BACKEND_DICT_DIR/<version>/, loaded, and made the active version. Versions are immutable; in-progress games keep their pinned version.

Why

The old flow required dropping .dawg files onto the host by hand — impossible in the contour (read-only baked dict, nonroot). It also had three disagreeing version values. This makes the console the single safe path and unifies versioning on the release tag.

Key decisions (owner interview)

  • Upload a file (no backend egress / SSRF). Named volume at the dict dir, seeded from the image (nonroot-owned) so versions persist across redeploys. Write + load + activate in one step. Active version in the DB (dictionary_state singleton, restored on boot). Counts + ~200 sample in the preview. Seed stays v1.0.0 (owner uploads v1.1.0 to validate live). Immutable versions.

Notes for the contour

  • Schema change (dictionary_state in the baseline) → the test contour DB needs a wipe (DROP SCHEMA backend CASCADE + restart; goose re-migrates) per the usual pre-release practice.
  • New dawg-data volume seeds from the image on first deploy.

Tests

  • engine + dictadmin unit (diff, archive validation, hardened extraction, immutability).
  • integration: upload → preview → install → activate → restart-persistence → new-game pin / old-game intact → immutability → CSRF.
  • Container permissions / volume seeding / cross-restart persistence are verified live on the contour (not reproducible from the shell).
## What Replaces the dictionary **hot-reload** with an **online dictionary update** in the GM console (`/_gm/dictionary`): 1. Upload the `scrabble-dawg-vX.Y.Z.tar.gz` release archive (downloaded from the scrabble-dictionary repo). 2. Preview, per variant, the words **added/removed** vs the active dictionary (counts + first ~200 + a large-removal warning). Version is parsed from the filename and editable. 3. Confirm → the version is written to `BACKEND_DICT_DIR/<version>/`, loaded, and made the **active** version. Versions are immutable; in-progress games keep their pinned version. ## Why The old flow required dropping `.dawg` files onto the host by hand — impossible in the contour (read-only baked dict, nonroot). It also had three disagreeing version values. This makes the console the single safe path and unifies versioning on the release tag. ## Key decisions (owner interview) - **Upload a file** (no backend egress / SSRF). **Named volume** at the dict dir, seeded from the image (nonroot-owned) so versions persist across redeploys. **Write + load + activate** in one step. **Active version in the DB** (`dictionary_state` singleton, restored on boot). **Counts + ~200 sample** in the preview. **Seed stays `v1.0.0`** (owner uploads `v1.1.0` to validate live). **Immutable** versions. ## Notes for the contour - Schema change (`dictionary_state` in the baseline) → the test contour DB needs a wipe (`DROP SCHEMA backend CASCADE` + restart; goose re-migrates) per the usual pre-release practice. - New `dawg-data` volume seeds from the image on first deploy. ## Tests - engine + dictadmin unit (diff, archive validation, hardened extraction, immutability). - integration: upload → preview → install → activate → restart-persistence → new-game pin / old-game intact → immutability → CSRF. - Container permissions / volume seeding / cross-restart persistence are verified live on the contour (not reproducible from the shell).
developer added 1 commit 2026-06-13 21:29:30 +00:00
feat(admin): online dictionary update — upload archive, preview word diff, install & activate
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 23s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m5s
0f3671f42d
Replace the dictionary hot-reload with an online update flow in the GM console
(/_gm/dictionary): the operator uploads scrabble-dawg-vX.Y.Z.tar.gz, previews the
per-variant words added/removed against the active dictionary, and confirms to
install + activate it. Versions are immutable; in-progress games keep their pinned
version while new games use the new one.

- engine: DiffWords (enumerate both DAWGs, decode only the differences), OpenFinder,
  Registry.Finder, DictFiles; OpenWithVersions skips the .staging area.
- dictadmin: hardened release-archive validation + extraction (path-traversal,
  symlink, oversize, entry-count rejection) and staging -> install (atomic rename).
- game: active dictionary version persisted in the dictionary_state singleton
  (single source of truth, restored on boot), concurrency-safe accessor.
- storage: BACKEND_DICT_DIR is a named volume seeded from the image (nonroot-owned),
  so uploaded versions persist across redeploys; the build's DICT_VERSION labels the
  seed and equals the resident tag (BACKEND_DICT_VERSION).
- docs: ARCHITECTURE §5, FUNCTIONAL (+ru), backend README, TESTING, PRERELEASE.

Tests: engine + dictadmin unit; integration upload->preview->install->activate->
restart->pin->immutability->CSRF.
owner approved these changes 2026-06-13 21:40:21 +00:00
developer merged commit 315bcf75ae into development 2026-06-13 21:41:01 +00:00
developer deleted branch feature/dict-admin-online-upload 2026-06-13 21:41:01 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#53