Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs/GENESIS-RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,21 @@ Confirm `GET /health` reports `"env":"production"`. Bare `wrangler deploy` witho

Gate evidence: [PRODUCTION-GENESIS-GATE.md](PRODUCTION-GENESIS-GATE.md).

## Successor world (RFC-0121) — not production this landing
## Successor world (RFC-0121)

Rehearse locally (preview, or preview + activate). `--successor --activate` stops after activation; it does not inhabit. Agent ENTER is covered by unit tests. Flipping local `DEFAULT_WORLD_ID` is a later step.
Local rehearsal (preview, or preview + activate). `--successor --activate` stops after activation; it does not inhabit. The rehearsal script still refuses `https://noema.guru`.

```bash
ADMIN_TOKEN=… BASE=http://127.0.0.1:8787 ./scripts/genesis_rehearsal.sh --successor
# explicit local activate only; never against https://noema.guru
# does not ENTER / LOOK — successor PLAY is not this script
ADMIN_TOKEN=… BASE=http://127.0.0.1:8787 ./scripts/genesis_rehearsal.sh --successor --activate
```

Later production cutover (human gate, separate campaign): preview successor on production (override deny lifted then), compare genesis_id ≠ `genesis.ef578f4ffceeccd0` and room_count 10, `confirm: true` activate on `world.perihelion-reach-2`, set `DEFAULT_WORLD_ID=world.perihelion-reach-2`, deploy. Do not `force`. Do not reseed `genesis.ef578f4ffceeccd0`.
Production cutover (human-gated, two deploys):

1. Deploy Worker that allows Admin `world_id: world.perihelion-reach-2` on preview/activate. Keep `DEFAULT_WORLD_ID=world-01`. `force` and reseed stay `POLICY_DENIED`. Omitted `world_id` still targets the live 5-room DO.
2. Admin preview successor on production. Require `genesis_id ≠ genesis.ef578f4ffceeccd0` and `room_count: 10`.
3. Admin `confirm: true` activate on `world.perihelion-reach-2`. No `force`. Do not reseed `genesis.ef578f4ffceeccd0`.
4. Set production `DEFAULT_WORLD_ID=world.perihelion-reach-2` and deploy. PLAY then uses the successor DO. Do not add PLAY to the old DO.

## Recovery

Expand Down
4 changes: 0 additions & 4 deletions workers/noema/src/genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ export function resolveAdminGenesisWorldId(
const fallback = String(env.DEFAULT_WORLD_ID || "world-01").trim() || "world-01";
const value = String(requested || "").trim();
if (!value) return { ok: true, world_id: fallback };
const envName = (env.NOEMA_ENV || "local").toLowerCase();
if (envName === "production") {
return { ok: false, code: "POLICY_DENIED", message: "world_id override forbidden in production" };
}
if (value !== SUCCESSOR_WORLD_ID) {
return { ok: false, code: "INVALID_REQUEST", message: "world_id override this campaign must be world.perihelion-reach-2" };
}
Expand Down
39 changes: 25 additions & 14 deletions workers/noema/test/genesis-admin-world-id.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@ describe("resolveAdminGenesisWorldId", () => {
expect(r).toEqual({ ok: true, world_id: "world.perihelion-reach-2" });
});

it("production denies any override", () => {
it("production allows successor override", () => {
const r = resolveAdminGenesisWorldId("world.perihelion-reach-2", {
NOEMA_ENV: "production",
DEFAULT_WORLD_ID: "world.perihelion-reach",
DEFAULT_WORLD_ID: "world-01",
});
expect(r).toEqual({ ok: false, code: "POLICY_DENIED", message: "world_id override forbidden in production" });
expect(r).toEqual({ ok: true, world_id: "world.perihelion-reach-2" });
});

it("production still rejects other explicit ids", () => {
const r = resolveAdminGenesisWorldId("world.other", {
NOEMA_ENV: "production",
DEFAULT_WORLD_ID: "world-01",
});
expect(r.ok).toBe(false);
if (!r.ok) expect(r.code).toBe("INVALID_REQUEST");
});

it("rejects other explicit ids", () => {
Expand Down Expand Up @@ -151,14 +160,15 @@ describe("POST /v1/admin/genesis/preview world_id routing", () => {
expect(fetches.every((c) => c.headers?.["x-noema-world-id"] === SUCCESSOR_WORLD_ID)).toBe(true);
});

it("production denies any override before DO lookup", async () => {
it("production routes successor preview to successor DO", async () => {
const calls: DoCall[] = [];
const res = await adminPost("/v1/admin/genesis/preview", SUCCESSOR_PREVIEW, calls, "production");
expect(res.status).toBe(403);
const data = (await res.json()) as { error: { code: string; message: string } };
expect(data.error.code).toBe("POLICY_DENIED");
expect(data.error.message).toBe("world_id override forbidden in production");
expect(calls.some((c) => c.op === "idFromName")).toBe(false);
expect(res.status).toBe(200);
const data = (await res.json()) as { result: { world_id: string; genesis_id: string } };
expect(data.result.world_id).toBe(SUCCESSOR_WORLD_ID);
expect(data.result.genesis_id).not.toBe(FROZEN_GENESIS_ID);
expect(calls.filter((c) => c.op === "idFromName").map((c) => c.name)).toEqual([SUCCESSOR_WORLD_ID]);
expect(calls.filter((c) => c.op === "fetch").every((c) => c.headers?.["x-noema-world-id"] === SUCCESSOR_WORLD_ID)).toBe(true);
});

it("rejects other explicit ids", async () => {
Expand Down Expand Up @@ -190,18 +200,19 @@ describe("POST /v1/admin/genesis/activate world_id routing", () => {
expect(act?.body?.world_id).toBe(SUCCESSOR_WORLD_ID);
});

it("production denies activate override", async () => {
it("production routes successor activate to successor DO", async () => {
const calls: DoCall[] = [];
const res = await adminPost(
"/v1/admin/genesis/activate",
{ genesis_id: "genesis.successor", confirm: true, world_id: SUCCESSOR_WORLD_ID },
calls,
"production",
);
expect(res.status).toBe(403);
const data = (await res.json()) as { error: { code: string } };
expect(data.error.code).toBe("POLICY_DENIED");
expect(calls.some((c) => c.op === "idFromName")).toBe(false);
expect(res.status).toBe(200);
expect(calls.filter((c) => c.op === "idFromName").map((c) => c.name)).toEqual([SUCCESSOR_WORLD_ID]);
const act = calls.find((c) => String(c.url).includes("/genesis-activate"));
expect(act?.headers?.["x-noema-world-id"]).toBe(SUCCESSOR_WORLD_ID);
expect(act?.body?.world_id).toBe(SUCCESSOR_WORLD_ID);
});

it("omitted activate uses DEFAULT_WORLD_ID and does not send DO world_id", async () => {
Expand Down
Loading