Skip to content

Modules: one module generator in the Platform, with the personal form (M2 1/3, #359) - #394

Merged
immakermatty merged 4 commits into
mainfrom
agent/LP-359-module-generator
Oct 14, 2026
Merged

immakermatty merged 4 commits into
mainfrom
agent/LP-359-module-generator

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Part 1 of 3 of M2 in plan DEV-6662 (#359): personal module creation, lazurio module create personalspace/<slug>. This PR is the generator core only; no command uses it yet. PR 2 (the checks and the dry run) and PR 3 (the real run) stack on this branch.

Why

The Operator decided on 2026-10-13 (root decision 0171, addendum of 2026-10-13) that a person's own modules live in their Personalspace as private repositories on their own GitHub account, and that the Platform holds the one module generator (root 0167): the root's planModuleScaffold and its stack templates move here, the personal form now, the Organization form under #267. No hosted Environment can run lazurio module create today (#267).

What

  • src/modules/scaffold.ts: the pure planner ported from HumanAndMachines/Lazurio lazurio/module-scaffold-lib.mjs at a5615be0: the same six stacks, slug rules, placeholder engine, tree hash, slug-derived port choice, slot insert and refusal codes, in strict TypeScript with English messages.
  • The templates (src/modules/templates/, 46 files from the root byte for byte plus _personal/AGENTS.md.tmpl) are embedded in the compiled executable as text through src/modules/scaffold-templates.ts. Every file ends in .tmpl, so none of them is an input of this repository's TypeScript, Biome or tests. Provenance and each deviation are in src/modules/templates/README.md.
  • An Organization's plan is the root's byte for byte. The test holds the root's tree hash of every stack at a5615be0. A compiled binary run from / produces the same hash, so the templates really are embedded.
  • The personal form (owner: {kind: "personal", login}):
    • company is the person's login. The runtime id is personalspace-<slug>-v1, not SHAPING's <login>-<slug>-v1: the catalog shows a runtime id as the app's display.id, and no output of a personal module names its owner (the rule the Personalspace tests enforce; a test checks that the catalog of generated personal modules never contains the login);
    • the slot is private to its owner: default_access: "private", required_roles: ["owner"], git.url git@github.com:<login>/<slug>.git, status: "active", no Teams (teams_not_allowed);
    • the _personal layer gives the module its own AGENTS.md.
    • Four placeholders (owner_label, pool_label, module_name, verify_command) replace the root's Organization-only wording. For an Organization they render the root's exact words.
  • The reserved personal lease range: 23000–23099 (personalModulePortPool), beside the Environment's own ports (environmentPorts). The reasons are in F52 and below.
  • The gateway's labels join the reserved slugs: t3code, mausbot, mausbot-webhook, browser, oauth2, api and well-known. A module's hostname is its slug.
  • Docs: the decision F52 in docs/decisions.md.

The chosen port range and why

23000–23099 is 100 ports, the size of an Organization's pool. It is chosen to be:

  • Below every Organization pool on record. Those start at 24000 and grow upward in blocks of 100: the root manual's example is 24000–24099 and the Organization template's 24900–24999. Machines' example Organization uses 20100–20199.
  • Clear of the Environment's own ports: 443 and 4180 (gateway), 20000 (Launchpad), 3773 (T3 Code), 4102 and 4103 (MausBot and its webhook), 4848 and 9222 (browser), 4789 (Executor) and 4174 (resident Launchpad).
  • Below every ephemeral range: Linux starts at 32768, macOS and Windows at 49152.

Deliberately not here

Verification

  • bun run check:static: lint, typecheck, the public guard, the proof build and its smoke all pass.
  • bun test --parallel=4 --timeout=20000: 2620 pass, 7 skip, 0 fail across 239 files.
  • New tests/module-scaffold.test.ts (23 tests):
    • the root's planner tests on the port;
    • the root's tree hashes;
    • the personal form against the template's own placeholders and example slot (tests/fixtures/personalspace-template.ts, the template's main cf50036);
    • every stack's runtime and preparation read by the Platform's own readers;
    • every generated personal module listed by the catalog as personalspace/<module>: Bun apps executable, python-uv listed with default-app-invalid as the plan warns, none without an app;
    • the embedded templates equal to their directory.

Risks

🤖 Generated with Claude Code

Note

Add Platform module generator for personal-form modules (M2 1/3)

Adds a Platform-owned scaffold planner plus a full set of embedded stack templates (Astro, Astro Starlight, Bun service, Vite React, Python uv, and none). This is the personal-module third of milestone 2.

  • scaffold.ts builds the module manifest, workspace slot, port lease (personal pool 23,000–23,099), and renders templates with placeholder substitution. It validates owners, teams, versions, pools, ports, slugs, and template paths, and raises stable ModuleScaffoldError codes.
  • scaffold-templates.ts exposes the immutable template catalog with CRLF-to-LF normalization; templates.d.ts types .tmpl imports.
  • Generated apps share one pattern: listener lookup through module-kit, exit status 2 on missing/invalid listener configuration, health path from Lazurio runtime metadata, strict ports, and no .env loading.
  • Python apps run a threaded HTTP server with Host-header allowlist (403 for foreign hosts), and generated start-contract tests verify health, foreign-host rejection, and SIGTERM exit 0 across stacks.
  • Decision record for F52 and template documentation are added in decisions.md and the template README.

Macroscope summarized b61f447.

immakermatty and others added 2 commits October 13, 2026 21:21
…e personal form

The Platform gets the single generator of a new Lazurio Module (root
decision 0171, addendum of 2026-10-13, point 3): planModuleScaffold, the
stack templates and the slot insert of HumanAndMachines/Lazurio at
a5615be0, as a pure TypeScript planner (src/modules/scaffold.ts) with the
templates embedded as text in the compiled executable
(src/modules/scaffold-templates.ts). An Organization's plan is the root's
byte for byte; the tests hold the root's tree hashes for every stack.

The planner also has the personal form decided on 2026-10-13: company is
the person's login, the slot is private to its owner with the person's
own repository, the _personal layer gives the module its own AGENTS.md,
and the reserved personal pool 23000-23099 (personalModulePortPool) is
declared beside the Environment's own ports. The gateway's labels join
the reserved slugs. No command uses it yet: `lazurio module create
personalspace/<slug>` follows on this branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dules

Records the Operator's decision of 2026-10-13 (root 0171 addendum, plan
DEV-6662 M2): the generator ported from the root at a5615be0, the
personal form (company is the login, a private slot, the _personal
layer), the reserved personal lease range 23000-23099 and why, and the
gateway's labels among the reserved slugs; the command follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The plan gave a personal app the runtime id <login>-<slug>-v1, and the
catalog shows a runtime id as the app's display id, so `lazurio module
list --json`, POST /api/catalog and the page would have named the
person's login, which no output of a personal module does (the rule the
Personalspace tests enforce). A personal app's runtime id is now
personalspace-<slug>-v1; an Organization's stays the root's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head review of b61f447 (DEV-6662 M2 1/3). The focused scaffold suite passes (23/23), check:static passes, diff --check is clean, and CI is green. No CLI is introduced by this PR; the findings concern the new pure generator itself.

Changes requested:

  1. [P1] Constrain the personal pool at the generator boundary. planModuleScaffold accepts a personal owner with any otherwise-valid PortPool: owner={kind:"personal",login:"example"}, slug="notes", stack="bun-service", pool={start:24000,end:24099} returns lease 24052. normalizePool (src/modules/scaffold.ts:700-715) checks only generic port validity; choosePort then uses that range (744-785). This permits a personal module plan to lease an Organization-pool port, contrary to F52/0171 and the documented reserved range. The template PR validates its own manifest, but this public core has no such check and callers can pass an older or unvalidated manifest. Refuse any personal pool not wholly within 23000-23099 (including an explicit --port via that pool), and add negative tests to this suite.
  2. [P2] Align the personal login validator with the Personalspace contract. normalizeOwner accepts login "ab-" (src/modules/scaffold.ts:142,611-626), generating git@github.com:ab-/notes.git; the template validator isValidGitHubLogin rejects that owner. Please reject trailing-hyphen logins here too and test the boundary.

These are independent of #396/#399; please fix on a new head and request a fresh exact-head review.

…d a login GitHub would refuse

planModuleScaffold took any valid pool for a personal owner, so a caller
reading an older or unchecked manifest could lease a personal module a
port of an Organization's range. A personal module's pool now has to lie
within the reserved personal range (decision F52), its explicit port
included, whatever the caller read.

The login pattern also accepted a trailing hyphen ("ab-"), which GitHub
and the Personalspace template's isValidGitHubLogin refuse; it now
follows GitHub's rule for people and Organizations alike.

Review of #394 (P1, P2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous P1/P2 findings are resolved: personal pools are bounded to 23000–23099 before any lease (including an explicit port), and both owner kinds use the GitHub login pattern with no trailing hyphen. I checked the new boundary cases and template/owner paths at this exact head. Local: module-scaffold 23 pass, 0 fail; check:static passes; git diff --check clean. GitHub head checks pass; no unresolved threads. @immakermatty APPROVED for this exact commit; no merge performed.

@immakermatty
immakermatty merged commit 32427a4 into main Oct 14, 2026
7 checks passed
@immakermatty
immakermatty deleted the agent/LP-359-module-generator branch October 14, 2026 10:51
immakermatty added a commit that referenced this pull request Oct 14, 2026
Main took F52 for the module generator (#394), so the vault decision
of this branch, written as F52 before that merged, is F53: its
heading, the notes on F43 and F46 that point to it, the section and
anchor in docs/machine-handover.md, and every comment, doc and test
that names it. The module generator's own references to F52 are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 14, 2026
#391 renumbered the Environment's own vault from F52 to F53 when it was
rebased onto main, where F52 is the module generator (#394). This pull
request's references follow it: the addendum of 2026-10-14 is F53's in
decisions.md, hosted-entry.md, environment-access.md, environment-tools.md
and launchpad-development.md (with their anchors), and in the comments of
server.ts, members.ts and the vault page's HTTP tests. No reference to the
module generator's F52 changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 14, 2026
Main took F52 for the module generator (#394), so the vault decision
of this branch, written as F52 before that merged, is F53: its
heading, the notes on F43 and F46 that point to it, the section and
anchor in docs/machine-handover.md, and every comment, doc and test
that names it. The module generator's own references to F52 are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 14, 2026
#391 renumbered the Environment's own vault from F52 to F53 when it was
rebased onto main, where F52 is the module generator (#394). This pull
request's references follow it: the addendum of 2026-10-14 is F53's in
decisions.md, hosted-entry.md, environment-access.md, environment-tools.md
and launchpad-development.md (with their anchors), and in the comments of
server.ts, members.ts and the vault page's HTTP tests. No reference to the
module generator's F52 changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
immakermatty added a commit that referenced this pull request Oct 14, 2026
#391 renumbered the Environment's own vault from F52 to F53 when it was
rebased onto main, where F52 is the module generator (#394). This pull
request's references follow it: the addendum of 2026-10-14 is F53's in
decisions.md, hosted-entry.md, environment-access.md, environment-tools.md
and launchpad-development.md (with their anchors), and in the comments of
server.ts, members.ts and the vault page's HTTP tests. No reference to the
module generator's F52 changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants