Commit Graph

18 Commits

Author SHA1 Message Date
4426d3e339 feat(docs): phase 14 - game-neutral core docs, an Ultima Online group, and checkTerms
All checks were successful
PR checks / checks (pull_request) Successful in 2m10s
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
2026-10-22 08:54:37 -05:00
9938e71e07 feat(facts): phase 13 - per-game facts, Rust and RunicNPC checked, main green again
All checks were successful
PR checks / checks (pull_request) Successful in 8m25s
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
2026-10-22 08:09:22 -05:00
4e1ce2316a docs(site): the Asset Bridge — protocol 8, a screen for client files, and pictures in the screenshots (Phase 9c)
All checks were successful
PR checks / checks (pull_request) Successful in 1m41s
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
2026-09-15 05:45:38 -05:00
e91e76bfa9 docs(site): the Event System — the platform's facts, and two pages for it
All checks were successful
PR checks / checks (pull_request) Successful in 9m34s
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
2026-09-09 22:04:27 -05:00
d01fd55f43 Merge branch 'edge' into docs/platform-facts-protocol-5 2026-09-01 13:19:46 +00:00
391a4a131b chore(facts): protocol 5 and Module API 1.9.0 -- HOLD until the cutover
Engagement Phase 12b. **This branch is deliberately red and must not be merged until the
Phase 13 cutover has landed link and servuo-plugins on `main` and CI has republished the
bundle.** checkFacts.mjs fetches every value from the source repo's `main`, so it fails
today exactly as designed:

  FAIL  protocol (sidecar)   platform.json 5   link main 4
  FAIL  protocol (overlay)   platform.json 5   servuo-plugins main 4
  FAIL  moduleApi            platform.json 1.9.0   website main 1.6.0
  FAIL  protocol (bundle)    platform.json 5   installer bundles:current.json 4

Every other check is green on this branch: checkLinks 2503, checkReference 22, checkA11y,
checkCsp, checkSidebar, 42 tests.

moduleApi is **1.9.0**, not the 1.7.0 the plan named -- Phase 11 moved it twice after that
sentence was written (1.8.0 for the `admin` ceiling, 1.9.0 for the module seed API).

## Also: the one page whose whole subject is the protocol number was the one page that
## hardcoded it

platform.json's own header says no version number is ever hardcoded in prose, and
`architecture/protocol-versions.mdx` had `4` written out three times -- the headline
sentence and both declaration-site rows. It now imports platform.json like every other
page that quotes a fact, so it moves with the JSON and cannot say 4 while the JSON says 5.

Note the interpolation is OUTSIDE the code spans: MDX does not evaluate an expression
inside backticks, so `PROTOCOL_VERSION: u32 = {platform.protocol}` would have rendered the
braces literally. Verified in the built HTML -- "currently 5", and no `platform.protocol`
survives anywhere in the output.

The same page's "what a bump obliges" section said version 4 was the first bump to need a
store migration, which read as though every bump does. v5 needed none (it only widens
frames the store already keeps whole -- docs link/v5.md), so the sentence now says which
did and which did not, and v5.md joins v4.md under canonical documents.

## The fill-in step, at merge time

Three values are not knowable today because the artefact does not exist yet. After the
bundle republishes, run:

    GITEA_TOKEN=<token> node scripts/checkFacts.mjs

and copy what it prints in the "says" column into src/data/platform.json:

  * bundle.tag, bundle.sidecar, bundle.overlay -- from installer bundles:current.json
  * releases.Module-uo -- if the cutover cuts a new module release
  * verifiedOn -- the date you ran it

Then re-read `getting-started/connect-a-game-server` and `administration/the-shard-connection`,
which quote the bundle, before merging. The check is green when all 19 agree.

AI-assisted: written with Claude Code.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-01 08:14:09 -05:00
c8a293b8f6 docs(admin): the engagement rules screen, and the privacy inventory an engagement mailer changes
Engagement Phase 12a. The site had pages for where a message goes (Notifications and
email) and what it says (Message templates), and nothing at all for what makes one get
sent -- the four Engagement screens the workstream built.

New page: Engagement rules. Rules, Audiences, the trigger catalog and the send log on
one page, sitting between the two it joins up. Templates already has its own page and
Suppressions is in Troubleshooting, so neither is repeated here.

Two things it exists to state plainly:

  * Every rule ships disabled, including the ones a module brings. "Installed" is not
    "on", and an upgrade whose Team mail went quiet is the same fact.
  * The ceiling is a TREE, not a ladder. The tempting reading -- a staff-only event
    could obviously also go to one person -- is wrong, and the example is the argument:
    "one person" for cheat detection is the player it was detected on.

Troubleshooting gains the symptom that page answers ("nothing is sent for one
particular event"): the rule is off, the rule is dormant, its own cooldown held it, or
the audience is empty.

Privacy: two rows the engagement work makes necessary, and one sentence it made false.

  * app-content claimed "Nothing is cached for offline use". Phase 8 shipped a DataStore
    snapshot of the inbox, so it was untrue -- and that row feeds the generated Play Data
    Safety answers, which is a store-review matter rather than a doc nit. The snapshot now
    has its own row and its own Play mapping (Messages / Other in-app messages; not
    collected by us, stored on the device), and app-content's claim is narrowed to
    everything else.
  * deploy-engagement, for the deployment scope: an address is now used for more than
    getting into an account, there is a delivery log holding a one-way hash of it, and
    there is a suppression list. Its retention line says what is true rather than what a
    reader assumes -- none of these tables has a retention sweep.

PLAY_DATA_SAFETY.md regenerated from the inventory; legal.lastUpdated moved with the page
it dates.

Verified: the whole `verify` chain green -- checkSidebar (plannedSidebar moved with the
live tree), checkFacts 19/19, checkQuickstart 59, checkReference 22, checkLinks 2605,
checkA11y, checkCsp, playDataSafety --check, 42 + 7 tests. Read in a browser as well, in
the served build.

AI-assisted: written with Claude Code.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-01 08:11:10 -05:00
7709b055a4 docs(troubleshooting): suppression and bounces (engagement Phase 9)
ENGAGEMENT.md §6.0b assigns this repo the operator-facing half of Phase 9
(website#176 + docs#191). Two new sections, split along the line that actually
matters when somebody reports it.

"One person stopped receiving email" is the suppression case, and the three
things an operator gets wrong about it: they can still reset their password
(suppression scopes to engagement rules only, so that is the expected shape of
the problem rather than a contradiction); Not sent, Bounced and Failed in the
Send Log mean three different things and only one of them is about your
configuration; and no row at all means they were excluded before anything was
queued, by an opt-in or by the verification gate.

"Everyone stopped receiving email at once" exists to stop the wrong reflex. A
whole-deployment stop is never the suppression list — a wrong password never
suppresses anybody, only the receiving server naming a specific dead mailbox
does — and it says so before an operator starts clearing rows.

Also notes that lifting a suppression asks for the full address because
addresses are stored one way, so it reads as the privacy design rather than a
missing feature.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-31 10:49:26 -05:00
283814dbf2 docs(admin): on-site notifications, and the defaults page that was wrong
Engagement Phase 7 gives the platform a third notification channel — an inbox
on the site itself — so `notifications-and-email.mdx` gains a section for it:
what it is, that it is the one channel on by default, that its body is always
plain text, and that the nightly prune takes read items only.

Two corrections in the same file, both of which were already false before this
phase and would have become misleading with it:

- "Who receives what" said push was opt-OUT. Push stream subscriptions have
  always been opt-IN, and engagement Phase 3 made that explicit in the channel
  registry. Rewritten as three defaults plus the Team mute that overrides all
  three, and pointed at the preferences grid the same phase gave the web.
- `capabilities.mjs` claimed "Web, push and email … push arrives by default".
  The web channel did not exist until now and push has never arrived by
  default. Reworded to name the on-site inbox as the opt-out one.

The push section now says what a tickle raised by an engagement rule carries,
and that it is still only a pointer.

Code: RunicGateway/website#TBD · Docs: RunicGateway/docs#TBD

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-31 02:07:35 -05:00
e0d491d33b docs(admin): Team notification emails are engagement rules now
The Teams page gains the section engagement Phase 6 owes it. The operator-facing
fact is the one that has to land first: Team emails used to send with no
configuration and now arrive switched off, so nobody gets them until a rule is
turned on in Admin → Engagement → Rules.

Also says what each of the four seeded rules sends and why the two roster ones
ship off, that a rule decides whether the site sends at all while members still
choose per Team, why a digest is re-read at send time, and that unsubscribe links
in mail already sent still work — now stopping the emails they came with without
also silencing that Team's push.

Site: RunicGateway/website#TBD · Docs: RunicGateway/docs#TBD

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-29 20:12:27 -05:00
289b6b3a8d docs(admin): a page for the message-template editor
The operator half of engagement Phase 5b, which 6.0b of ENGAGEMENT.md assigns to
this repo: what the Templates screen is for, how a shipped default is edited in
place without an upgrade taking the edit back, why variables are clicked rather
than typed, the two halves of every message, draft vs published, the preview and
its dark-mode approximation, test sends, duplicating to make a new template, and
the send log.

Written for someone running a site, not someone reading the design document: it
explains what to do and why the refusals exist, and names no version numbers - the
facts check reads authority from each repo's `main`, and none of this is there yet.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-29 18:14:21 -05:00
2da014790e docs(admin): email is SMTP now, and the Gmail connect flow is gone
Engagement Phase 1's share of this repo (docs/website/ENGAGEMENT.md
§6.0b). Four pages described a delivery path that no longer exists — one
of them under the heading "There is no SMTP option", which is now the
opposite of true.

notifications-and-email.mdx: the Email section is rewritten around the
three postures the org lead settled on (§7.1 Q5), leading with a relay
and naming smtp.gmail.com:587 with an app password as the migration off
OAuth2. Two cautions carry the failures that produce no error at all —
Implicit TLS left on for port 587, which hangs, and an operator-typed
sender the relay will not accept, which is an SPF/DMARC rejection that
looks like nothing. Send test is what proves both.

troubleshooting.mdx gains those two, plus the enabled toggle, which now
gates every message rather than some of them.

configuration.mdx loses the "set up Google first" ordering constraint,
which is gone with the borrowed client. system-architecture.mdx's
encrypted-at-rest list is corrected: the Gmail refresh token is replaced
by the transport credentials, which are write-only like the sidecar
token.

This lands on `edge`, so nothing here is published while `main` still
carries the Gmail flow. platform.json and capabilities.mjs are untouched
— they belong to Phase 12.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 21:01:03 -05:00
c29ec94f46 feat(screens): phase 9 — real screenshots, from a real shard
All checks were successful
PR checks / checks (pull_request) Successful in 1m25s
D4 asked for screenshots of the review stack rather than placeholders. Seventeen
of them: eleven of the site in a browser, six of the app on a phone, all from one
demo deployment wired to a running ServUO shard over a real sidecar, captured on
one day (D42).

The deployment is branded "Runic Gateway Demo" rather than a real community (D43),
and the captures sit beside the claims they support — the homepage, /features/, and
five of the administration pages phase 7 could describe but not show (D44).

The rig is committed rather than remembered (D45):

  scripts/seedDemo.mjs        content, by driving the site's own API — never SQL,
                              because a row the product could not have produced is
                              a screenshot of a product that does not exist
  src/data/screens.mjs        every capture: route, viewport, scroll, alt, caption
  scripts/captureScreens.mjs  npm run screens:capture
  scripts/checkScreens.mjs    the ninth check script, in CI

Shard-side dressing is servuo-plugins' scaffolding (D46), never deployed.

The rig found five things nothing else had. One is fixed upstream — a fresh
module-uo install pinned wire protocol 3 against a sidecar speaking 4, released as
v1.0.2, which this repo's own facts check then caught in platform.json. Four are
raised as product observations and worked around in the rig: a renamed guild
member never reaches the site, a guild deleted while the shard is down is a ghost
row forever, "Houses in danger" cannot show a house that was already collapsing,
and the app's news list prints raw ISO timestamps.

Players online reads 0. Logging a character in needs a UO client driven by hand,
and that is where this stopped — PLAN.md §10 says exactly why, and how to retake
the two frames that would change.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-25 09:55:55 -05:00
d89ce06bb8 docs(builder): phase 8 — modules, architecture and reference
All checks were successful
PR checks / checks (pull_request) Successful in 1m13s
Twenty pages completing the tree section 10 planned: Modules (8), Architecture
(5) and Reference (7). Four decisions, D38-D41, recorded in PLAN.md section 10.

D39 is the one that shaped the phase. Section 1 forbids re-specifying a
contract, and a Reference section is exactly where that rule is most tempting to
break, so the line is drawn at names: every environment variable, config key,
installer command, visibility rung and canonical document is listed with one
terse line saying what it is FOR, while shapes, semantics and every "why" stay
in the canonical document.

That is only safe because the names are checked. checkReference.mjs compares six
enumerations against the repositories that own them, over the Gitea API, as set
comparisons in BOTH directions -- and the second direction is the one that earns
its keep, because a reference page does not usually rot by describing something
that vanished, it rots by quietly not mentioning what was added since.

The check went green on its first run, which is the least trustworthy possible
outcome, so it was verified by breaking it: seven mutations, all caught. The one
worth keeping is the visibility ladder REORDERED with its membership unchanged
-- it is a security boundary, and a set comparison alone would have passed it.

D41 turns plannedSidebar from a checklist into a checked invariant, and finding
out why was the phase's first defect: it had already drifted, because phase 7
added the Content page under D37 and never updated the list. Nothing failed,
because nothing read it. checkSidebar.mjs now asserts the two trees agree on
groups, labels and order -- order because the order of Getting started IS the
installation path.

Two more things the writing found. PLAN.md's page count was wrong and had been
since section 10 was written ("roughly 38, 37 planned" for a tree of forty).
And module.json's `mounts` and the SPA's paths are different mechanisms that no
single document stated plainly -- module-uo declares admin: ["/shard",
"/uo-link"] while its screen lives at /admin/uo/link, because API routes are
deliberately NOT namespaced while SPA routes are. That is precisely the
distinction the installer got wrong in v0.1.0, and it now has a named home.

D40: the docs link to /architecture/'s drawn diagrams rather than importing
them. Those components carry marketing chrome and depend on diagram.css, which
Starlight does not load; the docs use text diagrams, which paste into an issue.

npm run verify green: 40 pages across 5 groups agree with plannedSidebar, 2390
internal links resolve, 123 repository links point at a branch, 19 facts, 59
quickstart checks, 22 reference enumerations, astro check 0 errors, 36 tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 12:17:31 -05:00
e8cb6061fe fix(facts): installer v0.1.1 is released, so the note names a version
All checks were successful
PR checks / checks (pull_request) Successful in 9m22s
Recovering installer's orphan tag published v0.1.1, which moved the platform
under this branch and turned checkFacts red -- the check working exactly as it
should, since a version this site quotes had changed.

  FAIL  release installer
        platform.json says : v0.1.0
        installer releases/latest says : v0.1.1

The 500 that orphaned the tag was a race with the tag push one second earlier,
not a structural failure: re-running the workflow took the built-in orphan-tag
recovery path and published all four assets unchanged.

So the stale-path Aside stops saying "v0.1.0 is still the current download",
which is no longer true, and says the durable thing instead -- v0.1.0 prints
the old path, v0.1.1 prints the real one -- which stays correct however many
releases follow. platform.json and the PLAN.md version table move to v0.1.1,
and the phase 7 findings record the pipeline defect as a fourth finding.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 11:50:38 -05:00
a993b872ac fix(docs): clear the quickstart drift the upstream fixes caused
All checks were successful
PR checks / checks (pull_request) Successful in 1m1s
The three defects phase 7 found are fixed and merged: website#163
(SECRET_ENC_KEY missing from the root .env.example, plus BOT_INTERNAL_KEY in
the README's "set at least" list) and installer#22 + docs#174 (the handoff
printing /admin/shard).

website#163 turned checkQuickstart red here, which is precisely what the
declaration was built to do -- it fails the moment a declared key appears
upstream, so the note describing the omission cannot outlive the defect. The
SECRET_ENC_KEY entry is deleted and notInUpstreamEnvExample is now empty; the
export stays so the next divergence gets an entry rather than passing quietly.

The stale-path Aside on Connect a game server is pinned to v0.1.0 rather than
calling the installer permanently wrong, and now says WHY the old path is worse
than a 404: the SPA has no route for it, so it redirects to the dashboard and
the link looks like it worked.

v0.1.0 is still the current download, and not only because releases lag. The
release run for installer#22 built every artifact and pushed tag v0.1.1, then
took a 500 creating the release -- so the tag is orphaned and no binaries were
published. Raised on installer; nothing is worked around here.

This also recovers 084ee0b, which was pushed to feat/phase-7-docs after PR #10
had already merged f499f2b, and so never reached main.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 11:39:36 -05:00
f499f2b72b docs(journey): phase 7 — the installation path and administration
All checks were successful
PR checks / checks (pull_request) Successful in 9m25s
Twenty documentation pages: Getting started (7) and Administration (13), the
journey no existing document owns end to end because the repositories are
organised by component and an operator is not.

Four decisions of record, taken before anything was written (D34–D37, PLAN.md
§10 "How phase 7 built the documentation journey"):

- D34 one PR for all twenty pages.
- D35 the install page is SELF-CONTAINED: it prints a complete Compose file and
  a complete .env that an operator copies without visiting another repository.
  That is a copy of somebody else's file, so it is checked rather than trusted —
  scripts/checkQuickstart.mjs re-reads website main:docker-compose.yml and
  main:.env.example over the Gitea API and fails on any disagreement, in both
  directions: a value that drifts fails, and a service or variable that appears
  upstream fails until it is either included or recorded as deliberately omitted
  with a reason. Its first run found two stale entries.
- D36 every Administration screen was walked on a real deployment before it was
  described — the rig being the quickstart itself, against the published image,
  so one run proved the install page and produced the detail the admin pages
  needed.
- D37 a thirteenth Administration page, Content, so that every admin nav row has
  a home without organising the docs by the app's menu.

What the live deployment disproved, all three now documented:

- The documented Compose deploy does not boot. SECRET_ENC_KEY is required in
  production (utils/secretBox.js throws at require time) and is missing from
  website's ROOT .env.example — the file Compose reads. It is present in
  server/.env.example, which is why dev never hits it. The quickstart carries it,
  declared as an upstream omission so the check fails the day it is fixed.
- The installer points operators at a screen that no longer exists: it prints
  <site>/admin/shard, and INSTALL.md §5 repeats it, but since the module cutover
  the screen is /admin/uo/link. Both the binary and the guide are stale.
- The admin Restart button opens a window.confirm whose text is the honest
  warning that a deployment with no supervisor does not come back — which is why
  `restart: unless-stopped` is called out as load-bearing rather than left as
  boilerplate.

And the defect only a look found, three phases running: the .env block's prose
promised that every highlighted line must be changed, while `mark` given the
variable names highlighted the names alone and left the values unmarked. Every
check passed on a page that was wrong about its own highlighting.

verify green: 890 internal links, 52 branch links, 19 facts, 59 quickstart
checks, 0 astro-check errors.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-24 08:35:03 -05:00
66187dde5d feat(site): phase 1 — the foundation
Some checks failed
PR checks / checks (pull_request) Failing after 4m19s
Astro 7 with the Node adapter, Starlight mounted at /docs, the token file, both
self-hosted typefaces, the layout shell, and the two build-time checks from §12.

The palette's gold and cyan are sampled from runic-emblem.png rather than
guessed, per §11: 494,059 opaque pixels binned by hue, each value annotated with
its measured contrast against the ground, and restricted rather than brightened
where a ratio fails.

- checkTokens.mjs fails the build on any colour literal outside tokens.css,
  which is what keeps §7's "recolouring is a file copy" promise true.
- checkFacts.mjs re-reads all 14 externally-sourced facts from their authorities
  over the Gitea API and fails on disagreement. It also enforces D13: no email
  address in the source outside brand-default/brand.json.
- Both were negative-tested; neither has ever been allowed to pass by default.

§6 asks for output:'server' with per-page prerender=true. Astro 7 expresses the
same runtime shape as output:'static' with an adapter, opting individual routes
out — so the default is static rather than accidentally server-rendered.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 19:08:52 -05:00