4e2c2dc4391f4c7eab5a156d85cff49e3500d0df
20 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 4e2c2dc439 |
docs(modules): mirror the rest of the Oxide ecosystem, and add a machine-readable set
Completes the uMod mirror beyond the Rust hook table, and adds agent/ — the same facts in TSV/JSONL at ~46% of the tokens. New prose: - OXIDE_API.md: the 19 developer pages under umod.org/documentation/api/ — plugin structure, hooks, commands, IPlayer, permissions, config, data files, database, localization, timers, web requests, dependencies, integration, preprocessor directives, security, style guide, CI, review. This is the framework our plugin is a guest in, where HOOKS.md is what the game says. - OPERATING.md: the 6 operator pages — installing Oxide on a server, then installing, configuring and permissioning plugins. New machine-readable set (agent/): - hooks.tsv 477 rows, ~31% of HOOKS.md - items.tsv 678 rows, ~80% of DEFINITIONS.md's item table - skins.tsv 104 rows covering 2,590 skins, ~77% - api.jsonl 150 code examples, ~47% of the two prose docs Generated in the same pass as the markdown, so the two cannot drift. HOOKS.md gains the universal-hook split: 34 of the 477 are uMod's own Covalence hooks, raised identically on every game uMod supports. Verified against /documentation/games/universal in the same capture - all 34 are in the Rust set and the Rust page adds none of its own, so the overlap is exact. The distinction is architectural: a universal hook is the portable part of the surface. Two things worth recording from building it: - The first skins.tsv was one row per skin and came out 11% LARGER than the markdown it replaces. Grouping it one row per item is what made it a saving. The token win is real for prose (3.2x on hooks) and small for tables that were already dense - agent/README.md says so plainly rather than claiming a flat number. - Signature extraction by brace depth silently captured body lines (a nested '}' in OnUserConnected's example ended the block early). It now matches on the hook's own name; all 477 rows verified to carry a real signature. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 8d31e497d2 |
docs(modules): add the Rust Oxide/uMod API reference for module-rust
Mirrors uMod's Rust game API into docs/modules/rust/ so the upcoming module-rust can be designed against it without a round trip upstream. - HOOKS.md: all 477 hooks in 20 categories, scraped verbatim from umod.org/documentation/games/rust. Each entry keeps its description, its return contract, its tags and every C# overload uMod publishes. Opens with a category table and a full alphabetical index (name -> category -> return contract). - DEFINITIONS.md: 678 items (short name, item id, display name) and 2,590 workshop skin ids across 104 items. - README.md: why the reference exists, how an Oxide hook actually binds (by name and arity, by reflection - a typo is silently never called), the four return contracts, and how the bridge invariants restate against Oxide. Records provenance and the exact refresh procedure: the hooks JSON endpoint is not Cloudflare-gated even though the HTML page is, and the definitions page has to come out of a browser. The mirror is reference only - uMod stays upstream and normative, and nothing here is a Runic Gateway contract. Rust wipes monthly, so both files carry their capture date (2026-09-15) and a re-capture note. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| fc2d4872f6 |
docs(link): the panel, and the two deletions it could see and no test could (Phase 8)
v8.md section 12.2 records the admin surface as built: one page, Admin -> Client
Files, over the creature catalogue, item and land art, and the cliloc table --
one page because they come off one client install and change on one event. The
four decisions are written down, including the one that kept the phase to a
single repo: section 14's "activity view" is the last import's own summary
rather than a filtered feed, which would have needed a core activity filter, a
`ctx.activity.list` helper and a MODULE_API bump to re-read rows the panel can
keep itself.
The part worth reading twice is what the walk found. Two of the three defects
were DELETIONS, both predate phase 8, and neither was reachable by a test that
did not already suspect them:
* the body import diffing its manifest against every family's rows, so 313
item pictures were staged for deletion under a sentence that was wrong about
what had happened;
* an approved vanish unlinking the sprite and keeping the row, so the key came
back for review forever and the count never moved.
The rule they share is now stated: after phase 5, `shard_assets` is three
collections in one table, and every read of it that feeds a decision has to say
which one it means. Two of the three that did not were deletions.
CLILOCS.md and SPAWN_ATLAS.md now name the button that exists (Admin -> Client
Files) instead of the one they promised, and modules/uo/API.md records that a
`needsReview` reply carries each vanished key's picture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| b84a73d0e7 |
docs(link): the phase the measurement cancelled, and the 452 wrong pictures it found on the way (Phase 6)
§16 planned deep animation keys and a bulk-fill switch. Measuring first changed the phase: a complete one-direction animation set is 174,453 frames / 281.5 MB (not the ~119,000 / ~117 MB §11 estimated), every frame carries its own centre and 23,818 of 26,274 actions have frames of differing size — and the site displays still pictures. So none of it was built (§11.2, org lead 2026-09-11); `body/<id>/a<n>/f<k>` stays named in §5 and refused in practice. §4.10 is what the walk found instead. A body's actions are a contiguous band and the next body's begin immediately after, so one action past the ceiling is a real record of another creature: 643 of 795 legacy bodies pass every check there and 452 are byte-identical to body+1's action 0. `Animations.GetAnimLength` is not the ceiling — it disagrees with the index arithmetic on one body of this client, by nine actions. What shipped: the 73 bodies with no art at action 0 (a horse at `body/820/a23`), the catalogue key carrying its action, the atlas join that reads it instead of hardcoding `a0`, and the ceiling that makes the fallback walk safe. Catalogue 1,022 -> 1,095; EXTRACTOR_VERSION 2 -> 3; protocol stays 8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| a4b63d87d2 |
docs(link): the hue belongs where the files are, and the cache poisons it (Phase 5)
§11.1 is new and carries what phase 5 measured: 49,152 addressable static ids (not the 81,884 `artidx.mul` declares -- `FileIndex` sizes its table from its length ARGUMENT), 39,189 with art, 4,244 land tiles, 9,963 + 12,140 empty index slots, and the whole set at 81 MB decoding in 34 s. That last number reopens the bulk question and the answer is still no: 108 MB of base64 through a 512 KB single-slot channel to store 43,433 pictures a shard displays a few hundred of. Two traps, both §4.5's failure mode -- a confident, plausible, wrong picture: - `Art.GetStatic` hands back the SAME cached Bitmap and `Hue.ApplyTo` repaints in place, so hueing edits the library's own copy: the plain key comes back hued from then on, and the next hue stacks. `Files.CacheData` off process-wide fixes it and also stops a game server retaining 74 MB of Bitmap. Copying instead does not solve the retention, and `new Bitmap(src)` throws on ARGB1555 anyway. - `PartialHue` (13,259 of 65,536 ids) decides whether a hue repaints every pixel or only the grey ones, from a file only the shard has. Item 597 is a wooden screen with painted flowers; one mode reddens the flowers, the other the whole screen, and both decode. Hence land takes no hue segment and `h0` is not a key. Plus the namespace trap that compiled: unqualified `TileData` binds to ServUO's own `Server.TileData`, because the enclosing namespace beats `using Ultima;`. §14 records what the wire gained -- the `static` and `land` families, `families` on `assets.sources`, and `assets.fetch` becoming shared plumbing whose family is DERIVED from the keys (§5 made the key the address; a request naming its family too would have two places to be wrong and one of them silent). Additive, so the protocol stays 8 and EXTRACTOR_VERSION stays 2. §15 records that `link` needed nothing in phases 4 or 5: it forwards verbatim in both directions. §17.10 is the four org-lead decisions. §12 and modules/uo/SCHEMA.md carry the website side: `uploads/items/`, per-row `catalog` staleness, and why a key with no art writes no row at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 6ef4b06c76 |
docs(link): two of the eight player bodies existed, and 233 nobody asked about (Phase 4)
Phase 4 built 4.3's UOP animation reader. What it found first changed what the phase
was worth, so the plan is corrected rather than merely annotated.
- New 4.9: what phase 4 measured. Of the EIGHT player bodies 4.8 assigned this
phase, two are in the client at all -- gargoyles 666/667, in AnimationFrame3.uop.
The six ghost bodies are in no package, and that is established rather than
unfound: the five packages hold 10,724 entries and the
build/animationlegacyframe/%06d/%02d.bin scheme claims every one, leaving no room
for another naming. Also the format as read (AMOU, a per-frame ARGB1555 palette,
direction as a slice of the frame table), the nine bodies whose frame count is not
a multiple of five, the validate-as-we-go bounds and the measurement that says
they refuse nothing real, and the live rig.
- 5.2 rewritten: the player-body set is the LIVING pair per race, six ids not
twelve. Ghost ids left it because no client has art for any of them. Still asked
of the shard, never hardcoded -- only the question changed. And with phase 4 in,
all six have art for the first time.
- 4.3 rewritten against what was measured, including why searching five UOP packages
for one body is NOT the never-sweep rule being broken: a legacy index is addressed
by position, a UOP entry by the hash of a name carrying the body id, which the
payload then declares again.
- 11 sizing: the catalogue is 1,022, not 787. The mix is recorded because "add every
body" sounds like it changes what a catalogue is, and it does not -- the legacy
787 was already 366 equipment bodies.
- 14: manifest and fetch rows carry `source` (legacy/uop). Additive, so protocol
stays 8; EXTRACTOR_VERSION 1 -> 2 is the change consumers actually see.
- 16 phase 4 marked DONE; 17.9 records the four org-lead decisions (fallback applies
to every body; ghost ids leave the set; own PNG encoder; NO_IMAGING stays flat).
- 4.8 and 8.1 keep their numbers as the record of what those phases measured, with a
pointer to where the answer landed.
Two consumer docs repeated the ghost claim as fact and are corrected:
website/SPAWN_ATLAS.md (787 -> 1,022, and "two thirds of the playable ghost and
gargoyle bodies have no art" -> about half the addressable body range) and
modules/uo/API.md (same sentence).
Code: servuo-plugins#31.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| d07772a3d8 |
docs(modules): the cliloc conversion step IS avoidable now (phase 2 debt)
SCHEMA.md's cliloc section still described the pre-protocol-8 world, two phases after phase 2 changed it. Three sentences said the same false thing, so fixing only the flagged one would have left the section arguing with itself: - "Sourced from files the operator supplies" -- the BASE comes from the shard on any install with uo-link configured; only the overlays are the filesystem's, and that asymmetry has a reason worth stating (ServUO has no server-side notion of a custom cliloc, so there is nothing to ask for). - "A base (the converted client table)" -- not converted any more. - "The conversion step is not avoidable ... so the shard cannot supply names on our behalf" -- it does supply them. Phase 2 ported UOFiddler's Mythic decompressor into the overlay precisely so nobody converts anything. Rewritten to say what is true and why the file path still exists (deprecated, not removed, so an existing install keeps working), plus the two things a reader of this table actually needs: `shard_cliloc_meta.payload` keeps the base's fingerprint under `base` SEPARATELY from the overlay hashes -- because on the bridge the old `clilocs.plain` label is supposed to disappear and one flat hash map would read that upgrade as a vanished source -- and boot does not import on the bridge path at all. Each claim checked against the code rather than from memory: shardClilocs.model.js:307 (`base: fingerprint`), :438-440 (refreshOnBoot returns `skipped` on the bridge), clilocSource.js:333 (`missingOverlays`). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 1a7481e9f4 |
docs(link): the catalogue is real, and UOFiddler's last job is gone (Phase 3)
Phase 3 is built and walked on a live shard. What the walk measured, and the
two places the design of record needed correcting:
§8.1, new: the catalogue is 787 exactly as §4.8 predicted, and the whole scan
of bodies 1-2047 takes 734 ms cold -- so the wall-clock paging §11 designed
never fires on this client. Every §4.8/§5.2 prediction held when the bytes were
rendered and LOOKED at: 320, 607, 608 and 666 come back absent rather than as
another creature's picture, and the direction split is 783 at index 1 against 4
at index 0 -- four player bodies, not six.
44 of the 787 hashes are shared by two or three bodies, which is the exact
signature of the wrong-picture bug, so it was chased rather than assumed. It is
the client's own Body.def aliasing (83 {1}, 84 {1}, 106 {12, 59}), and the check
that settles it is at the source: Translate(ref body, ref hue) rewrites `body`
only when bit 31 is set, unlike the one-argument overload -- and ResolveAnimation
calls that same two-argument overload, so validator and decoder resolve the
identical record.
§12.1, new: **§12 is right about the outcome and wrong about the mechanism.**
`shard_spawn_creatures` is emptied and refilled by every atlas refresh, and a
refresh runs on every boot -- so an imported filename written to that row is
destroyed by an ordinary re-parse of the ServUO tree, and the next Update finds
the client files unchanged and never restores it. Three tables outside that
blast radius, and the atlas import re-derives `art` on the way past.
§14: **§16 listed phase 3 as servuo-plugins + module-uo and that was wrong.**
web.rs routes every command explicitly, so `link` is in the phase. Corrected in
both places.
UOFIDDLER.md is DELETED, two phases earlier than §9.1 predicted -- creature art
was the only thing still on it. SPAWN_ATLAS.md §Artwork is rewritten around the
import, keeping the operator's own map as the thing that wins; the module's
SCHEMA.md gains the three tables and API.md the two admin routes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| bbd69a8e2e |
docs(link): clilocs come over the bridge now, and UOFiddler's first job is gone
Phase 2 of the Asset Bridge is built, so the documentation stops telling an operator to install a GUI tool. `v8.md` gains §9.1 and §9.2 — what the port cost, what it measured, and where the base table comes from now. The measurement worth keeping: **67,496 rows in 290 ms**, which is exactly what UOFiddler's own `Ultima.dll` produced from this same client through the converter this phase deletes. An independent implementation agreeing to the row is the strongest check available that a format decoder is correct, and it is not something a subtly-wrong one produces. §17 records the four shapes the org lead settled before any of it was written. Two departed from the recommendation: **the bridge always wins** (no source setting — there is no version of that question an operator benefits from answering) and **import is admin-triggered** (boot does not call the shard at all). `CLILOCS.md` is rewritten around that: where the table comes from, what arrives and in how many pieces, the refusals — including the two the file pipeline had no equivalent of (a client patched mid-import, and the base's exemption from the vanished-source rule, which exists so an upgraded install is not asked to approve a change the upgrade itself made). `UOFIDDLER.md` loses Part 1 entirely rather than having it rewritten. What is left is creature art, which phase 5 takes, after which the page goes away. `v3.md` §8.6 keeps its reasoning with a note saying what superseded it, because the argument for why the manual step existed is still the argument for why this was worth building. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| cd0d22a225 |
docs(engagement): Phase 11b's live walk — three decisions and six defects
The walk is the last piece of 11b and it was not a formality. It found six
defects, four of them in code shipped by earlier phases, and none of the six was
visible in a unit test: each is a disagreement between two things that agree with
each other in a fixture.
Three of the fixes were decisions rather than repairs, all settled by the org
lead before any code:
11 `uo.house.idoc_warning` ships delay_seconds: 900 and nothing could cancel
it -> add `uo.house.refreshed`, a 26th trigger with a body and a rule
12 a rule with a cooldown delivered on exactly ONE of its channels
-> `channel` joins the cooldown key; a cooldown is per DELIVERY
13 `uo.vendor.expiring` could not fire, because the market sweep does not
diff fees -> widen BridgeMarket.Signature() with exempt +
periodsRemaining
Files:
website/ENGAGEMENT.md the three decisions, the four repairs, and what the
walk proved rung by rung; the 11b bullet and the §8.6
family table now read 26 triggers / 34 bodies
website/BACKEND_DESIGN.md engagement_cooldowns gains `channel` in its PRIMARY
KEY, with the migration's information_schema guard and
why MariaDB forces one
link/v5.md the sweep has to DIFF the fees or the frame never
comes -- stated as the general rule for the next
enrichment, since it is emit cadence and not shape
modules/uo/API.md §5.7a the cancel-shaped trigger and the Ageless-vs-
LikeNew ServUO fact; §5.7b every link comes from
config/clientPaths.js, and the two mistakes that made
every call-to-action a dead link
Pairs with website#<core>, Module-uo#<uo> and servuo-plugins#<plugin>.
Co-Authored-By: Claude <noreply@anthropic.com>
|
|||
| 9d50e5cacf |
docs(engagement): Phase 11b — the seed API, the in-universe voice, the governor's letter
Four decisions settled by the org lead before any 11b code, plus MODULE_API
1.9.0 and what building it found.
DECISION 7 is the one 11a could not have discovered, because 11a seeded nothing.
The plan's own sentence — "24 rules, all enabled = 0; bespoke template bodies" —
described work with NO MECHANISM to land in: templateSeeds.js and coreRules.js
are core files with core arrays, and registries.js has no registerTemplates.
Adds api.registerEngagementSeeds({ templates, ruleGroups }). Templates re-ensure
every boot; rule groups are one-shot per named group, which is 11a's seed-key
finding stated as an API rather than as a warning.
DECISION 8: the game-powered families read in-universe, with a per-family sender
rather than one voice. The crown writes about the crown's business and nothing
else. This is also why the bodies had to be the module's: §5.2 says core stays
ignorant of a module's domain, and check:modules reads identifiers, never prose —
so the boundary is honoured deliberately here rather than leaned on.
DECISION 9: nine bodies stay plain. A failed-login notice written as a letter is
indistinguishable in register from the phishing mail it warns about.
DECISION 10 corrects a fact the plan had wrong by omission. §8.6 records that
uo.points.rank_changed cannot address a person, and the same reasoning was
silently assumed to cover the governor. city.update's `governor` is written by
BridgeJson.Actor(), which emits acct and webId — the new governor is addressable
today, no protocol change. uo.governor.appointed is the 25th trigger.
Also records what building it found: the no-conditionals/empty-string collision
that the presentational fragments exist for, the render sweep that found three
defects no check could see (an optional in a subject line, multi-optional detail
lines, a trailing fragment used as a leading one), the trigger-bound template
that could not reference its own unsubscribe link, and the trigger-id/template-key
grammar mismatch.
docs/modules/uo/API.md gains §5.7 and §5.8.
Co-Authored-By: Claude <noreply@anthropic.com>
|
|||
| 561e5308e4 |
docs(engagement): MODULE_API 1.8.0, the UO trigger catalogue, the news path
The documentation half of Phase 11a (website#177, Module-uo#22). Three files,
each carrying one obligation from 6.0b's Phase 11a row.
**MODULE_API.md -> 1.8.0.** A seventh audience ceiling, `admin`, as a child of
`staff`. One addition, no removal, so minor -- every declaration valid under
1.7.0 is valid now and no stored value changes. 2.4's ceiling bullet now names
seven values rather than six, and says the thing a module author actually needs:
`admin` is the ONLY pair in the whole lattice with real containment, so
`permits('staff','admin')` holds and it is the only place `permits` is true
between two values below `authenticated`.
Also corrects two 2.3 rows that were stale rather than wrong: `ctx.inbox.push`
has not thrown since Phase 7, and both members now name the consumer that
actually calls them.
**modules/uo/API.md gains 5**, the full trigger catalogue -- which 6.0b asked for
in as many words ("the full trigger catalogue, its audiences and its ceilings,
not one entry"). The 24 triggers grouped by ceiling with the reason for each
ceiling; the four rows that deliberately do NOT ship with their recorded reasons;
the two that need a patch tier or a v5 overlay; the three things a rule cannot
express and are therefore done in the mapper; the three registered audiences; and
the ingest ORDERING, which is load-bearing rather than incidental.
**BACKEND_DESIGN.md -- the news publish path.** It described a direct
`pushDispatch.publish('news.post', ...)` from the create/publish-post path. That
call is gone: it is an emit through the engagement engine now, so news push rides
a rule, and core seeds that rule disabled. The note says so as an operator-visible
upgrade step, names the separate one-shot settings key and states what did NOT
change -- the announce legs and the post hooks still fire exactly as they did.
Co-Authored-By: Claude <noreply@anthropic.com>
|
|||
| 11d20a9a17 |
docs(modules): name Oxide, and pair one sidecar to one server
Two corrections from the org lead on the transport rewrite. It is an OXIDE plugin, not "a mod loaded by the server's mod framework". Oxide is what modded Rust servers run, and naming it is the difference between a design a reader can start from and one they have to go and pick a framework for. And the architecture is ONE SERVER, ONE SIDECAR - not one sidecar fronting a community's several servers, which is the arrangement a UO-shaped reading reaches for and which this document had. Rust servers in practice sit on separate VMs, so a shared sidecar would have to be reached across a network by plugins that are supposed to talk to it over loopback: it trades the invariant that makes the design safe for a saving in process count. The cost lands on the module, which is the right place for it - it holds one client per configured server rather than one client to an aggregator - and it makes the Team provider's `complete` answerable rather than vague, since "every team there is" now means every team on THIS server. Five of six sidecars reachable is `complete` left off, and core adds and updates without archiving. Recorded as a reevaluable assumption rather than a principle, because that is what it is. Nothing in the contract objects either way: core is not in this conversation at all, which finding 2 now says. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 4e98ed6bd5 |
docs(modules): the Rust dry run reaches the game through a MOD, not RCON
Overruled by the org lead: RCON is not used. Rust gets the same three-part shape UO has - a plugin inside the game that dials out, a sidecar that persists before it forwards, a module that talks only to the sidecar - and the plugin is a mod loaded by the server's mod framework, exposing data through hooks. The document had RCON as its premise, so the correction reaches further than the transport paragraph: - The reason Rust is a good second game changes. It was "its server speaks a protocol nobody has to write". It is now "its server is a BINARY" - the opposite of ServUO, which is source a shard owner compiles - so the way in is a published mod API and the shard-dials-out invariant has to survive that change of footing. It does, unchanged, which is a stronger result than the one the document originally claimed. - The announce leg sends a command down the socket the mod already holds, rather than calling rcon.say. - The provider refuses when no mod is connected, not when RCON is unreachable. - Two hooks answer questions UO had to work for: a wipe arrives as an event, and membership is real-time - so this module's Team provider is event-driven with a baseline on connect rather than sweep-driven. The provider contract does not change by a line, which is the part worth keeping: core never needed to know how the data arrives. The 2026-08-12 correction block stays and a second one is added beside it rather than editing the history out - this document's own convention, and the thing that makes it worth reading twice. It also records what the correction COSTS: this project no longer has a worked example of "a game that already speaks a remote-control protocol, so its sidecar is thin". Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 7e08a55f76 |
docs(modules): the Rust dry run, revisited for Teams
The kit's README sends a reader here FIRST - it is the shortest honest picture of
the whole job - and it predated Teams, so it taught a second game to build its
teams as private module data and never mentioned the provider. The two places the
contract changed since it was written are now in it, and nothing else moved:
all four findings stand, including the identity gap, which is still the one a
real second module hits first.
What Rust adds that UO does not, and why it was worth revisiting rather than
noting:
- externalId must survive a rename and a Rust team HAS no name - it is a numeric
team id in the save. The right answer, and the one a designer is least likely
to reach for.
- `complete` is per SERVER, not per community. Six servers are six team spaces,
so a provider that can reach five must leave `complete` off or core archives
every team on the sixth.
- A wipe empties every team, so { ok: true, complete: true, teams: [] } is TRUE
once a month and core archiving all of them is correct - which is exactly why
an unreachable RCON must answer { ok: false } instead. The two states are one
API call apart and only the module can tell them apart.
- The team route carries a server id as well as a team id, so the external id is
<serverId>:<teamId>. Core stores that and never parses it; an external id is
opaque to core by design, and this is the case that shows why.
Also: rust_teams stays the module's table and core's teams stays core's, which is
the boundary worth stating in the one document where both appear; and the kit is
nine members now, not seven.
Co-Authored-By: Claude <noreply@anthropic.com>
|
|||
| c33a8a5509 |
docs(modules): correct F3 in the acceptance record
Only the Gitea release workflow carries the publishing placeholders. The report said both flavours; the GitHub one uses GITHUB_REPOSITORY and friends and has nothing to rename. Found while fixing it in the kit (Integration-kit#4), which is where the single checklist row landed. Also records what that implies about a run like this: every finding was re-derived before being acted on, and it was not ceremony — this one was wrong in a detail and F5 understated its problem in a way that changed the fix. A cold agent's report is evidence, not a verdict, which is the standard the rest of the plan already applies to a passing suite. Separate PR because #147 was already merged; a push to a merged branch is silent. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 3116e7bbf6 |
docs(modules): close Phase 5 — the acceptance run, and the page shell it found
Slice 3 of Phase 5 (MODULE_SYSTEM.md 2.11.1), and the phase's last slice. docs/modules/kit-acceptance.md is decision 5's deliverable: a cold agent given the Integration Kit and the documents it links to — never core's source, never module-uo — built a working module for a second game, which was then installed into a real core and taken through MODULE_API.md 7.7's browser smoke. Verdict recorded whichever way it went, and it went **yes, with caveats**: one pass, no core source, and three of the four normative documents never opened. The finding that justifies the two-stage shape is the one the agent structurally could not reach, because it had no core to render against. A module page built exactly as the kit teaches renders OUTSIDE the site: PublicLayout supplies the chrome and not the body, and the `shell-... page-body` wrapper every core public page writes for itself is two class names that appear in no contract. That is 3.4's own stated failure — "a module page that does not look like the site it is installed in" — reached by following 3.4. Fixed in core rather than documented at the reader, so the class names stay core's private business and the theming workstream keeps its freedom to rename them: PublicLayout takes an opt-in `shell` width, MODULE_API_VERSION 1.5.0 (website#148, merges first). - MODULE_API.md 1.1: 1.5.0's entry, and a new bump-table row — adding an OPTIONAL prop or argument is minor. "A member's signature changes" is major because a call already written changes meaning, and an optional prop changes none; the table now says what it means rather than leaving it to be argued. - MODULE_API.md 3.4: the shell prop, why a module names a width and never a class, and the eight-vs-seven miscount the run also turned up — the kit had faithfully carried it out of the contract into the template, which is the never-re-specify rule working exactly as designed on a wrong input. - rust-dryrun.md: coreApi ^1.3.0 -> ^1.5.0, as a dated correction per decision 33. It is the only complete module.json in the kit's reading path and nothing checks a JSON block inside a Markdown file, which is the reusable half. - MODULE_SYSTEM.md 2.11.1: slice 3 recorded, plus the third finding worth generalising — a check whose failure message asserts a diagnosis has to be right about it. `check:swagger` failed on a pristine template on Windows (CRLF) while blaming the routes, green on the Linux runner forever. - Decision 34: core owns the page body as well as the chrome. The banner does not come off. Decision 32 makes that a person's to remove, this run exercised the website-module half only (the module has no sidecar, so chapters 3 and 4 were never tested), and an agent does not skim or give up. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 89aff5c26b |
docs(modules): settle Phase 5 — the Integration Kit, and the sidecar rule
Phase 4 closed; this is the plan for Phase 5, the Integration Kit, settled with the org lead on six decisions. MODULE_SYSTEM.md - §2.11.1 (new): Phase 5's shape — measured starting state, the six decisions, the four slices, the acceptance mechanism. - §2.11: layer 2 rewritten. The sidecar is the default and only path to a game, and it is a non-blocking dumb forwarder that persists before it forwards. A game that already ships a remote-control surface gets a thin sidecar, not none. - §2.10: a MODULE_API_VERSION bump now carries a pass over the kit. - Part 3: decisions 28–33. - The Phase 5 line in Part 2's phase list: STARTED 2026-08-12. MODULE_API.md - §2.7 gains one prohibition: a module does not open a connection to a game server from the website process — a game socket, RCON, a query port, an engine admin API — with the three reasons it exists (the website is the internet-facing process, the sidecar owns the durable copy, neither side can stall the other). Normative prose with no CI behind it, stated so that every second module does not re-decide it. - §1.1: the 1.4.0 entry. No member changed; minor deliberately. modules/rust-dryrun.md - §2 "Talking to the game" rewritten from "No sidecar" to a thin RCON sidecar, with a dated Correction note recording what the exercise originally concluded and why it was overruled. Corrected in view rather than silently rewritten. Pairs with website#… (the 1.4.0 bump, into edge); either order. Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 42867d7ef8 |
docs(modules): close Phase 4 — de-UO the backend reference, record the acceptance run
Phase 4 slice 4. BACKEND_DESIGN.md had never been de-UO'd: Phase 3 rewrote core's code and README, but §5.2's identifier check reads code, not prose, so nothing ever looked at the design document. It still described 27 shard_* tables, 20 UO route rows and the shard visibility ladder as core's, a phase after core stopped being able to serve any of them. Moved, text unchanged: BACKEND_DESIGN §3 six shard_* schema sections (226 lines) → modules/uo/SCHEMA.md BACKEND_DESIGN §4 13 public + 7 admin UO route rows → modules/uo/API.md BACKEND_DESIGN §6.5 the audience ladder (70 lines) → modules/uo/API.md §4 Core keeps the seam and gains the eight /admin/modules routes it had never documented. §6.5 becomes "Module-owned audience boundaries": core's security boundary ends at authentication, roles and the session, and a module that serves game data brings its own. Also fixed on the way: users.router.js was still listed as 15 routes (it is 9 — six went to the extension slot), and the push section still promised config/shardStreams.js "moves out with it" four slices after it left. The acceptance table now carries results. Criterion 2 was proved for real against module-uo v0.3.0 on an empty database, which turned up the uninstall ordering defect fixed in website#146. AI disclosure: this contribution was AI-assisted (Claude Code). Co-Authored-By: Claude <noreply@anthropic.com> |
|||
| 872352389d |
docs(website): close phase 3 — slice 5, the fragment obligation, and the rust dry run
Records the slice that closes the extraction, and adds the two documents phase 3 owed: docs/modules/uo/ and the module-rust dry run. **The slice found an obligation neither repo had built.** MODULE_API.md §2.8 and §6.1a settle the OpenAPI fragment in detail — a module ships one, core merges the fragments of started modules into /api/docs.json. Neither half existed, so the 72 URLs module-uo serves were in no spec at all. §2.8 and §6.1a now record what was built, including the four things settled while building it: the filename is fixed rather than declared, a module namespaces what it DEFINES and references core's shared schemas by core's name, the generator derives its prefixes from the module's own register() call, and swagger-autogen's diagnostics have to be captured because it reports a broken annotation and then prints Success. **§5.3 gains the design decision the frozen manifest actually made:** it is a SUBTRACTION, not a prefix filter. Generating the manifest without the module and then with it answers "what does the module serve" AND "did core lose anything", and the second is the one §1.2 promises to the shipped Android app. A module that shadowed a core route cannot appear as an addition anywhere. **BACKEND_DESIGN.md §4.0.1** is new: /api/docs.json is assembled per request, the two generated artifacts are core's alone, and the route count was still 228. **docs/modules/** is new, per §2.10 (module documentation aggregates here, not in module repos): docs/modules/uo/README.md orients a reader on what module-uo serves, owns and needs from an operator, and links out to the feature docs that already existed rather than restating them. **docs/modules/rust-dryrun.md** is phase 3's fourth acceptance criterion. A written, deliberately unimplemented module for Rust — chosen because it wipes monthly, runs several servers rather than one shard, identifies by Steam, and ships RCON so there is no sidecar to write. The contract generalises: same manifest, same seven registration calls, same schema rules, and six of the UI kit's seven members wanted by a game with nothing in common with the one the kit was curated from. It found one real gap — **a module cannot register an identity provider**, and "Sign in with Steam" is what a Rust community expects. Recorded as the first candidate for a future MODULE_API_VERSION bump rather than bolted on: an identity provider participates in session creation, and §2.7's link-only SSO policy has to survive it. Also: website-README.md refreshed from the repo (it was several changes stale), and three settled decisions added (18-20). Pairs with Module-uo#6 and website#141. Co-Authored-By: Claude <noreply@anthropic.com> |