Skip to content

Modules: lazurio module create personalspace/<slug> --dry-run, and module prepare dispatched (M2 2/3, #359) - #396

Merged
immakermatty merged 1 commit into
mainfrom
agent/LP-359-module-create-plan
Oct 14, 2026
Merged

immakermatty merged 1 commit into
mainfrom
agent/LP-359-module-create-plan

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Part 2 of 3 of M2 in plan DEV-6662 (#359). Stacked on #394 (the generator core): its base is agent/LP-359-module-generator, so the diff shows only this part. Merge #394 first; this PR's base then becomes main.

Why

lazurio module create personalspace/<slug> must check everything before anything is written, and show the person the exact plan before the real run creates a repository on their account (root decision 0171, addendum of 2026-10-13). Two CLI gaps meet here:

What

lazurio module create personalspace/<slug> --stack <stack> [--name] [--port N] --dry-run [--folder] [--json] (src/modules/module-create.ts, src/modules/module-create-cli.ts). Every check reads only, in this order:

  1. The name and the stack, with nothing read.
  2. A Personalspace on this preset and installed: personalspace-not-here, or personalspace-absent with the next step lazurio personalspace install.
  3. Held by gh's account. P0's holder decision (Catalog and module lifecycle take any single checkout in personalspace/ as this Environment's Personalspace #186), with its reasons: github-signed-out, github-unavailable, github-identity-mismatch, personalspace-ambiguous, -unavailable, -foreign, -not-owned, -public.
  4. The layout.
    • The task worktree is the cwd's checkout of the held Personalspace's Git (not the Personalspace itself). The slot goes into its modules.manifest.json: modules_manifest_missing, manifest_invalid.
    • A declared module_port_pool must lie in 23000–23099 (personalspace-pool-invalid). Without one, the slot's pull request will declare it.
    • Git must ignore workspace/<slug> (personalspace-workspace-tracked).
    • No directory or slot may have that name, letter case ignored: directory_exists, slot_exists.
  5. The plan. Its lease avoids every lease declared by the Folder's modules (every Organization's workspace/*, mission-control and design-system, and the Personalspace's modules) and the Environment's own ports.
  6. No <login>/<slug> on GitHub, letter case ignored (repository-exists). An existing repository is never taken over.

The placeholders of #377 (the coordinator's heads-up):

  • A Personalspace that still names the template's placeholder owner (owner-github-username) is a warning with a next step pointing to personalspace install leaves the template's placeholders in the person's Personalspace #377. It is never a refusal. Nothing here names the owner or runs the template's bootstrap, which also creates a gbrain repository.
  • The template's example slot workspace/personal-notes (no repository) stays an ordinary declared slot and is left as it is. It conflicts only with the slug personal-notes. A new slot goes after it.
  • Fixtures. Every test uses Personalspaces made of the template's own files at main cf50036, byte for byte with their placeholders (tests/fixtures/personalspace-template.ts).

The report is the root's lazurio.module_create.report.v1, with these additions:

  • repository (full_name, private: true, create);
  • manifest: the file that receives the slot, its pool, and whether the pool is added;
  • worktree and module_root;
  • plan: a digest of everything the person confirms (repository, module, stack, name, slot, lease, files and the pool the manifest gains).

With --json there is one JSON line per step, as personalspace install prints, then the report. Exit status: 0 planned, 2 refused or usage.

CLI dispatch. module create gets its own handler. Every verb of moduleVerbs now reaches runModuleCommand, so module prepare works (#114).

Git, read-only: ContentGit.worktree (top, git dir, common dir, branch) and ContentGit.ignored (git check-ignore).

Docs: F52 point 6, the section "Module creation" in docs/launchpad-development.md, and the CLI help (moduleHelp includes the create help).

Deliberately not here

Verification

  • bun run check:static passes.
  • bun test --parallel=4 --timeout=20000: 2636 pass, 7 skip, 0 fail across 241 files.
  • tests/personal-module-create.test.ts (10 tests, the content world: a stub GitHub with a call log and the product's real git):
    • the exact plan from a task worktree and from outside one, on a workstation and on a personal Remote Environment;
    • a filesystem snapshot unchanged after the dry run;
    • the stub's log shows only viewer and repository reads;
    • the leases of an Organization module and of a personal module avoided, and a declared pool used;
    • every refusal of the list above.
  • tests/module-create-cli.test.ts (6 tests, through runCli):
    • the usage errors, the refusals by name alone, the absent Personalspace, module prepare reaching the lifecycle (folder-unreadable instead of the catalog's usage), and the help;
    • the whole plan end to end in a child process of src/cli.ts, with a fake gh on its PATH and the real git. Its gh call log is exactly api user, api repos/example/example_GEN3 and api repos/example/notes.

Risks

  • The task worktree is recognized by its Git (it shares the Personalspace's common directory). The place personalspace/.worktrees/<task> is guidance in the next steps, not enforced. A worktree created next to the owner directory makes the Personalspace ambiguous, and the command then refuses with that reason.

🤖 Generated with Claude Code

Note

Add read-only lazurio module create dry-run planning and dispatch module prepare

  • Adds lazurio module create personalspace/<slug> --dry-run, a read-only plan that validates selector, stack, slug, holder ownership, manifest, pool, ports, worktree, and GitHub repository state, then reports an actionable plan or a stable refusal. No files, repositories, or slots are created; that happens in a later revision.
  • Planning uses a reserved personal port pool of 23000–23099 when the manifest omits one, avoids ports leased in visible Organization and Personalspace module manifests, and detects linked task worktrees via new read-only worktree and ignored Git capabilities in git.ts.
  • The CLI dispatcher in cli.ts now routes module create to the new planning command (exit 0 for actionable, 2 for refusals) and replaces the fixed verb list with moduleVerbs, so module prepare reaches the lifecycle command instead of falling through to usage.
  • Reports render as human-readable text or JSON lines via the runner in module-create-cli.ts; the schema, refusal codes, and deterministic plan digest live in module-create.ts.
  • Covered by fixture-based planner tests in personal-module-create.test.ts and an end-to-end CLI test using real Git and a fake gh.
  • Behavioral Change: module create previously fell through to module catalog usage; it now runs planning and exits 2 on usage errors and refusals. module prepare and other moduleVerbs entries now reach runModuleCommand instead of catalog usage.

Macroscope summarized 5661392.

…own module

The command's plan (decision F52, M2 of plan DEV-6662, #359): every
check a real run makes, reading only, and a dry run that writes nothing
and asks GitHub only GETs. In order: the name and the stack (the
Organization form is refused with a pointer to #267, --teams is a usage
error), a Personalspace on this preset and installed, the holder decision
of #186, the layout (the task worktree, the manifest that receives the
slot, its pool in the reserved range 23000-23099, workspace/<slug>
ignored by the Personalspace's Git, no directory or slot of that name),
the plan with a lease that avoids every lease the Folder's modules
declare, and no <login>/<slug> on GitHub. A Personalspace that still
holds the template's placeholders (#377) is a warning with its next step;
the template's example slot stays a declared slot.

The report is the root's lazurio.module_create.report.v1 with the
person's repository, the target manifest, the task worktree and the
plan's digest. src/cli.ts now dispatches module create, and module
prepare, which fell to the catalog's usage (#114). Docs: F52 point 6 and
"Module creation" in docs/launchpad-development.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty
immakermatty force-pushed the agent/LP-359-module-create-plan branch from d6c6254 to 5661392 Compare October 14, 2026 10:27
@immakermatty
immakermatty changed the base branch from agent/LP-359-module-generator to main October 14, 2026 10:27
@immakermatty
immakermatty marked this pull request as ready for review October 14, 2026 10:27

@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.

@immakermatty — APPROVED on exact head 5661392 (base 32427a4). I reviewed this planning PR independently of #399: selector/slug/stack refusals, holder and manifest checks, pool/slot/directory/repository checks, deterministic scaffold and digest, and CLI dispatch. In an isolated detached checkout, 39 focused tests passed, check:static passed, and git diff --check was clean. Live readback immediately before review: same head/base, 0 unresolved threads, no competing reviewer request; only the Macroscope check was skipped on this PR. No merge performed.

@immakermatty immakermatty reopened this Oct 14, 2026
@immakermatty
immakermatty merged commit 61bf661 into main Oct 14, 2026
7 checks passed
@immakermatty
immakermatty deleted the agent/LP-359-module-create-plan branch October 14, 2026 10:51
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