feat(ci): packaging, release and the frozen manifest #2

Merged
whitlocktech merged 1 commits from feat/phase-2-packaging into main 2026-09-16 10:35:00 +00:00
Member

Phase 2 of docs/modules/rust/PLAN.mdpackaging and release.

Phase 1 built five guards and ran them by hand. This repo had no .gitea/workflows/ at all, so nothing gated the branch that gets released and there was no way to release it. This adds both halves.

What is here

File What it does
.gitea/workflows/release.yml the derived-version engine link, installer and Module-uo already run — assembles the bundle, publishes the tarball, the install manifest with its sha256, and SHA256SUMS
.gitea/workflows/pr-checks.yml server tests · check:imports · check:bundle · check:swagger · client build · client tests · check:externals · frozen-manifest
ci/bundle.json what a release copies, declared once, read by two things
ci/core-ref.json the core this module is proved against — pinned at efa9db7, main @ MODULE_API 1.10.0
server/scripts/checkBundle.js is the include list still sufficient, and does the assembled tarball resolve?
server/scripts/frozenManifest.js derives the module's URL surface from two cores
routes.manifest.json generated, not written: the six routes this module serves

The frozen manifest already paid for itself

It was run locally against the pinned ref before being committed: six routes, all documented, no core route removed or changed.

That is the first time a running core has confirmed /rust collides with nothing. Phase 1 could only check it by reading — §13's own registration comment says so — because core answers several public routes mounted at the tier root (/status, /version) that the loader's collision probe cannot see. From here, a core answers it on every PR.

The bundle ships no node_modules

The shipped half declares no runtime dependencies; everything it needs arrives on ctx. So the release runs no npm ci and packs no dependency tree (org lead decision, phase 2). The whole value of that is the day it stops being true being a loud day, so checkBundle.js holds both halves: the include list still covers everything server/index.js reaches, and no dependencies entry has appeared without the release learning to pack it.

Verified by breaking it — dropping "model" from the list:

ci/bundle.json does not ship everything server/index.js reaches.
  server/model   (2 files reachable)
Add "model" to ci/bundle.json's server[].

Module-uo's v1.0.0 is why this exists: server/commands/ arrived in a cutover, the include list did not learn about it, and the module installed and then died at the register stage on the operator's box. This module has the check from its first release rather than after its first outage.

Checked locally

  • npm test --prefix server — 72 → 88 passing (16 new across the two scripts)
  • check:imports, check:bundle, check:swagger — green
  • The Assemble the bundle step, run step-for-step with node standing in for jq: bundle contents check ok, every relative require in the tarball resolves, 38,997 bytes, 30 entries, no node_modules, no tests, no client/src, no ci/

One prerequisite this PR cannot satisfy

release.yml needs the Actions secret REGISTRY_TOKEN (a Gitea token with write:repository) on RunicGateway/Module-Rust. The bot account cannot read or set repo secrets, so I could not confirm whether it is already there. The workflow checks for it before it builds or tags anything and fails with a named error if it is missing — deliberately, because the failure it is avoiding is a tag pushed and a release that 401s, which is the state servuo-plugins got stuck in.

The phase's acceptance criterion — an operator installs the module from Admin → Modules and it reaches started — needs a published release, so that is the next step once this merges.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4

Phase 2 of [`docs/modules/rust/PLAN.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/modules/rust/PLAN.md) — *packaging and release*. Phase 1 built five guards and ran them **by hand**. This repo had no `.gitea/workflows/` at all, so nothing gated the branch that gets released and there was no way to release it. This adds both halves. ## What is here | File | What it does | |---|---| | `.gitea/workflows/release.yml` | the derived-version engine `link`, `installer` and `Module-uo` already run — assembles the bundle, publishes the tarball, the install manifest with its `sha256`, and `SHA256SUMS` | | `.gitea/workflows/pr-checks.yml` | server tests · `check:imports` · `check:bundle` · `check:swagger` · client build · client tests · `check:externals` · **frozen-manifest** | | `ci/bundle.json` | what a release copies, declared once, read by two things | | `ci/core-ref.json` | the core this module is proved against — pinned at `efa9db7`, `main` @ MODULE_API 1.10.0 | | `server/scripts/checkBundle.js` | is the include list still sufficient, and does the assembled tarball resolve? | | `server/scripts/frozenManifest.js` | derives the module's URL surface from two cores | | `routes.manifest.json` | generated, not written: the six routes this module serves | ## The frozen manifest already paid for itself It was run locally against the pinned ref before being committed: **six routes, all documented, no core route removed or changed.** That is the first time a *running core* has confirmed `/rust` collides with nothing. Phase 1 could only check it by reading — §13's own registration comment says so — because core answers several public routes mounted at the **tier root** (`/status`, `/version`) that the loader's collision probe cannot see. From here, a core answers it on every PR. ## The bundle ships no `node_modules` The shipped half declares no runtime dependencies; everything it needs arrives on `ctx`. So the release runs no `npm ci` and packs no dependency tree (org lead decision, phase 2). The whole value of that is the day it stops being true being a **loud** day, so `checkBundle.js` holds both halves: the include list still covers everything `server/index.js` reaches, **and** no `dependencies` entry has appeared without the release learning to pack it. Verified by breaking it — dropping `"model"` from the list: ``` ci/bundle.json does not ship everything server/index.js reaches. server/model (2 files reachable) Add "model" to ci/bundle.json's server[]. ``` Module-uo's v1.0.0 is why this exists: `server/commands/` arrived in a cutover, the include list did not learn about it, and the module installed and then died at the register stage on the operator's box. This module has the check from its first release rather than after its first outage. ## Checked locally - `npm test --prefix server` — 72 → **88 passing** (16 new across the two scripts) - `check:imports`, `check:bundle`, `check:swagger` — green - The `Assemble the bundle` step, run step-for-step with `node` standing in for `jq`: bundle contents check ok, every relative require in the tarball resolves, **38,997 bytes**, 30 entries, no `node_modules`, no tests, no `client/src`, no `ci/` ## One prerequisite this PR cannot satisfy `release.yml` needs the Actions secret **`REGISTRY_TOKEN`** (a Gitea token with `write:repository`) on `RunicGateway/Module-Rust`. The bot account cannot read or set repo secrets, so I could not confirm whether it is already there. The workflow checks for it **before** it builds or tags anything and fails with a named error if it is missing — deliberately, because the failure it is avoiding is a tag pushed and a release that 401s, which is the state `servuo-plugins` got stuck in. The phase's acceptance criterion — *an operator installs the module from Admin → Modules and it reaches `started`* — needs a published release, so that is the next step once this merges. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 1 commit 2026-09-16 07:10:40 +00:00
feat(ci): packaging, release and the frozen manifest
All checks were successful
PR Checks / server-tests (pull_request) Successful in 21s
PR Checks / client-build (pull_request) Successful in 21s
PR Checks / frozen-manifest (pull_request) Successful in 39s
b33d21d71b
Phase 2 of docs/modules/rust/PLAN.md. Phase 1 built five guards and ran them by
hand; this repo had no workflows at all, so nothing gated the branch that gets
released and there was no way to release it.

Three pieces:

- **release.yml** — the derived-version engine link, installer and Module-uo
  already run (conventional-commit subjects since the newest tag; module.json's
  version survives as a floor; workflow_dispatch as the backdoor), assembling the
  bundle from an include list and publishing the tarball, the install manifest
  carrying its sha256, and SHA256SUMS. The tag is the number that ships and CI
  stamps it into the bundle's own module.json.
- **pr-checks.yml** — server tests, check:imports, check:bundle, check:swagger,
  the client build, client tests and check:externals, plus frozen-manifest.
- **frozen-manifest** — clones core at the sha pinned in ci/core-ref.json,
  generates its route table without this module and with it, and takes the
  difference. It ran locally against that exact ref: six routes, all documented,
  no core route moved. That is the first proof by a running core that /rust
  collides with nothing — phase 1 could only check it by reading, because core
  mounts /status and /version at a tier root where the loader's own collision
  probe cannot see them.

The bundle carries no node_modules, because the shipped half declares no runtime
dependencies (org lead, phase 2). checkBundle.js holds both halves of that: the
include list still covers everything server/index.js reaches, and no dependency
has appeared without the release learning to pack it. Verified by breaking it —
dropping "model" from the list names the exact edit and exits 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
whitlocktech merged commit 9018e55488 into main 2026-09-16 10:35:00 +00:00
whitlocktech deleted branch feat/phase-2-packaging 2026-09-16 10:35:01 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Module-Rust#2
No description provided.