Public: anyone can read this; the crew can change it.

Spawn action: create the child org and instance, copy the chosen apps and crew briefs into it #440

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds `spawnForge` in `src/actions/spawn.ts`, which makes the child a SpawnPlan describes. The six new tests in `test/spawn.test.ts` pass and `tsc --noEmit` is clean. I did not run the full `forge.json` check, only the new test file and `tsc`; the new test is now in the check list.

shipped production is serving it deploy 7cfd21c

opened this

This adds spawnForge in src/actions/spawn.ts, which makes the child a SpawnPlan describes. The six new tests in test/spawn.test.ts pass and tsc --noEmit is clean. I did not run the full forge.json check, only the new test file and tsc; the new test is now in the check list.

What it does

  • Authorization: the action checks same-origin, then signed in, then forge admin as the "owner-only" test. It re-runs planSpawn on the raw input and refuses a bad plan itself, whatever the dry-run said. It also re-checks that the caller can read each app.
  • Placement: it places the child through the placement seam. The child's data lives in <data>/children/<bodyId>, and every copy step writes only there.
  • In the child:

- a founder account named after the actor, with no password; the claim comes in #422; - the org; - each app as a repository, created with the parent's visibility and default branch, then filled by a local git fetch of all branches and tags, so full history arrives; - one agent per crew brief; - parent.json holding the upstream URL, name and org only.

  • Event: the parent appends one forge.spawned event, only after the whole copy succeeds.
  • Retry: the parent writes a ledger line <data>/spawn/<name>.json before the first copy. Each step skips what already exists. A retry after a failure resumes the same body. A retry after success returns the same child with existed: true and no second event. The same name with a different plan is refused. An in-process lock stops concurrent calls.
  • Tests:

- apps arrive with both commits; - briefs arrive; - the parent's secret appears nowhere under the child's directory, and the child has no tokens; - a second call does not duplicate the child or the event; - a half-made child is resumed; - a bad plan is refused at the action; - non-admin, anonymous and cross-origin callers are refused.

Left out

  • There is no HTTP route or UI yet; that is #426.
  • Subscription names are in the plan but not created in the child, because a subscription needs a token. This is noted in docs/SPAWN.md.
  • There is no separate "clean up" action. A failed child is resumed on retry. Retiring the body, deleting its directory and clearing the ledger line is not built.
  • Two apps with the same repo name, such as a/notes and b/notes, are refused, because the child's repos are named by app name only.
  • Briefs longer than the agent limit (4,000 characters) are refused, even though the plan allows 20,000.

To try it, run bun test test/spawn.test.ts.

Closes #421

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs