Files
docs/modules/kit-acceptance.md
wtclaude 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>
2026-08-12 17:41:37 -05:00

11 KiB

Integration Kit — the acceptance run

Phase 5's fifth decision (../website/MODULE_SYSTEM.md §2.11.1 d5). The Integration Kit is finished when someone outside this project builds a working module for a new game by following it alone. That cannot be manufactured, so this is the proxy: a cold agent given the kit and the documents it links to — never core's source, never module-uo — asked to build a module for a second game, whose result was then installed into a real core and loaded in a browser.

Run 2026-08-12, against Integration-kit at slice 2 (PR #3) and core at website edge 1b692bf.

The verdict below does not clear the draft banner. Decision 32 makes that a human's to remove, and this exercise is aimed at the same target with a machine. It is worth what it found, not more.


1. Verdict

Yes, with caveats. A competent developer who had never seen this platform can build a working module from this kit alone.

It did, in one pass, without reading core's source and without opening three of the four normative documentsMODULE_SYSTEM.md and both link/ specs were never touched, correctly, since the deliverable was a website module with no sidecar. MODULE_API.md was opened only at the end, to audit the kit rather than to build against it.

Its own summary of what carried the run:

What raises this to "yes" rather than "yes but barely" is the template. The four suites plus the two guard scripts constitute a machine-checkable specification of a valid module, and they are better documentation than any chapter could be — every rule they enforce comes with a comment explaining the bug that motivated it and how it looked when it went wrong.

And the caveat that mattered most, which is a process one rather than a comprehension one: the first command the kit tells a reader to run fails on a pristine copy (F1).

2. Method, and the one thing it could not enforce

Given Integration-kit at slice 2, tracked files only (so the template's chunk had to be built, as a reader's would); local mirrors of the five documents the kit links out to, at the same relative paths, because the kit's links point at a Gitea host a credential-less agent cannot reach
Denied core's source, module-uo, the web beyond the npm registry
Asked for a trivial but real module for Rust — valid module.json, a public route backed by its own table, a prebuilt chunk rendering it, a purge fragment, and whatever guards the kit says a module needs, passing
Told to log findings as they happened, into a running file, on the grounds that being stuck is worth more than remembering being stuck
Model Claude Opus 5

Rust was chosen deliberately over an unfamiliar game: it is the subject of rust-dryrun.md, which the kit's front page names as the first thing to read, so the run also measures whether that document is followable.

The honest limitation. A subagent on this machine inherits the workspace as its shell's default directory, so isolation is a clean tree plus explicit instructions plus a self-reported list of every path read — not a sandbox. The agent reported no read outside its root and disclosed one unrelated process slip unprompted, which is some evidence of good faith but is not proof. A future run should put the agent somewhere it cannot reach core.

3. What was built, and what it proved

module-rust — id rust, coreApi ^1.4.0, two public routes over two tables (rust_servers + rust_wipes, FK, child-first purge), a 8.0 kB chunk with two pages and a nav row. 44 server + 18 client tests, none skipped; check:imports, check:swagger, check:externals all green.

Installed into a real core on edge and booted against an empty database — the condition Phase 4 slice 3 established as the only one that can see a schema-ordering defect:

  • loader scanned it, schema fragment replayed (4 statements), onBoot ran, state started
  • /api/v1/public/modules published it; both routes served correct JSON
  • the chunk registered under enforced script-src 'self' with zero CSP reports and zero console errors, and a deep-linked param route rendered on a cold load — the readyState ordering path ../website/MODULE_API.md §7.7 exists for

Two things it did not do are worth recording as evidence the contract held for a stranger: it never reached into core (check:imports green), and it wrote no connection to a game server — in fact it wrote a test asserting that about itself, having read in chapter 2 that §2.7 is the one rule with no CI behind it.

4. Findings

Eight from the agent, one from the browser. B1 could not have been found by the agent — it had no core to render against, which is the structural gap this two-stage method exists to cover, and it is the most valuable single result of the run.

Every finding was re-derived before it was acted on, and that was not ceremony: one of them (F3) was wrong in a detail, and another (F5) turned out to understate the problem in a way that changed the fix. A report from a run like this is evidence, not a verdict — the same standard the rest of this plan applies to a passing test suite.

# Class Where What Disposition
B1 WRONG MODULE_API.md §3.4 A module page built exactly as the kit teaches renders outside the site. PublicLayout gives the chrome, not the body; core's nine public pages each wrap content in shell-… page-body, whose class names appear in no contract. Content at x=0, no padding, footer riding up under it FixedPublicLayout takes an opt-in shell prop, MODULE_API_VERSION 1.5.0 (website#148); §3.4 rewritten; template and chapter 2 updated
F1 WRONG template/README.md, book/02 npm run check:swagger fails on a pristine, unedited copy on Windows: the fragment compares byte-for-byte and a default Windows clone is CRLF. The message asserts a false cause — "the routes or their annotations changed" Fixedtemplate/.gitattributes (eol=lf) and the comparison normalises line endings
F2 WRONG rust-dryrun.md §1 coreApi: "^1.3.0" while everything else said 1.4.0 — in the only complete module.json in the kit's reading path Fixed^1.5.0, as a dated correction
F3 MISSING template/README.md The rename checklist claims to name every file carrying a placeholder. .gitea/workflows/release.yml carries gitea.example.com and your-org/your-module under a literal # CHANGE THESE and is absent — and checkRenameSites.js's pattern cannot match them, so CI is silent by construction Fixed — row added, pattern widened. Corrected: the report said both workflow flavours; only the Gitea one is affected, since GitHub supplies GITHUB_REPOSITORY and friends
F4 FRICTION template/.gitea/workflows/release.yml The bundle's include list is hardcoded (for d in boot.js core.js index.js db model router). Add any top-level directory under server/ and it is silently dropped from every release; the post-check only resolves the five paths in module.json Fixed — inverted to an exclusion list
F5 AMBIGUOUS world.router.js, book/02 The comment warns that a backtick in a single-quoted #swagger description ends the string early — but the same file uses two backtick spans and they survive verbatim Fixed, and sharpened: see below
F6 AMBIGUOUS template/ No .gitignore. The kit's own covers the template's paths, but a reader who copies the directory and runs git init inherits nothing — node_modules/ included Fixedtemplate/.gitignore ships
F7 WRONG MODULE_API.md §3.4 "The kit is those seven members" over a table publishing eight exports (five rows; PageState contributes three). Propagated faithfully into the template's comment and core's shared.js — the kit's never-re-specify discipline carrying a miscount out of the contract Fixed in all three
F8 note Three of the four normative documents were never opened, and never needed to be Recorded above

F5 is worse than reported, and the correction is the useful part

Measured directly rather than reasoned about. A backtick inside a single-quoted #swagger description is harmless — it survives verbatim into the fragment. A double quote is not, and it does not fail the way the comment predicts:

// #swagger.summary = 'A "quoted" world status'
   →  "summary": "A \""          ← silently truncated
   →  swagger-autogen prints Success, and swaggerFragment.js's error capture sees nothing

The template told a reader that the tool would tell them. For the one case that actually bites, it does not: the annotation is truncated, the generator reports success, and the only signal is a check:swagger diff whose message blames the routes. A chapter that predicts the wrong debugging heuristic is worse than one that predicts none (§2.11.1) — restated here for a comment inside the template, which no check in that repo can see either.

Two things the run recommends that are not defects

  • "Run every check on the untouched copy before you edit anything." The agent did this off its own bat, and it is what found F1; without a baseline it would have blamed its own edits. Chapter 1 now says so.
  • The template should ship the §2.7 self-check. Chapter 2 correctly says the no-game-connection rule has no CI behind it, because an outbound socket is not statically detectable in general. But a module can make a decidable claim about itself, and the agent wrote that check, with a positive control (it catches a real RCON dial) and a negative one (prose about RCON does not trip it). It called it "the check I would most want the template to ship", noting that for Rust new WebSocket(rconUrl) in boot.js is ten lines away. Adopted into the template, from its code.

5. What this does and does not settle

Settles: the kit is sufficient for the website-module half of the job. Someone can start from nothing and finish with a module that loads into a real core, serves, renders in the site's chrome, and passes every rule the contract has a check for — without reading core.

Does not settle: the sidecar and the game-plugin halves. This module has neither, so chapters 3 and 4 were never exercised and remain proved only by the fact that they describe systems that exist (link/, servuo-plugins/). Nor does it settle anything about a person's experience — an agent does not get frustrated, does not skim, and does not give up, which are three of the failure modes the banner is really about.

So the banner stays. The next run of this exercise should either be a person, or an agent placed somewhere it genuinely cannot reach core's source, and should be asked for a module with a sidecar.