Skip to content
Draft
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
10 changes: 10 additions & 0 deletions .changeset/keeper-door-guest-room-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
---

A1 of the prx→guest-room convergence: add `@bounded-systems/guest-room` as a prx
dependency (github dep until it publishes to JSR) and describe the keeper door
through guest-room's capability model — a `DoorCatalog` preset + a rulebook
rendered by guest-room's `resolveDoor`/`grantedDoorLines`/`deniedDoorSection`.
First real dependency edge from prx onto the flagship runtime. Additive: the
keeperd transport is unchanged; the keeper endpoint env stays sourced from
`keeperd/endpoint.ts`. No public API or behavior change, no release.
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/prx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@bounded-systems/gh": "npm:@jsr/bounded-systems__gh@^0.1.0",
"@bounded-systems/git": "npm:@jsr/bounded-systems__git@^0.1.0",
"@bounded-systems/github-budget": "npm:@jsr/bounded-systems__github-budget@^0.1.0",
"@bounded-systems/guest-room": "npm:@jsr/bounded-systems__guest-room@^0.1.0",
"@bounded-systems/host": "npm:@jsr/bounded-systems__host@^0.2.0",
"@bounded-systems/machine-schema": "npm:@jsr/bounded-systems__machine-schema@^0.2.0",
"@bounded-systems/policy": "npm:@jsr/bounded-systems__policy@^0.2.0",
Expand Down
66 changes: 66 additions & 0 deletions packages/prx/src/door/guest-room-catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// A1 of the prx→guest-room convergence (see .github-private
// docs/convergence-prx-claude-box.md): describe prx's keeper door using the
// published @bounded-systems/guest-room capability model instead of an ad-hoc
// local description. This is the first real dependency edge from prx onto
// guest-room — the same model that powers claude-box now describes prx's door.
//
// Scope: ADDITIVE. It does not change the keeperd transport (still the bespoke
// client in ../keeperd/); it makes the door's *definition + rulebook* come from
// the guest-room contract. The endpoint env stays sourced from
// ../keeperd/endpoint.ts, so there is still one source of truth for the socket.

import {
capabilityPreamble,
deniedDoors,
type DoorCatalog,
type DoorGrant,
deniedDoorSection,
type Env,
grantedDoorLines,
resolveDoor,
} from "@bounded-systems/guest-room";
import { processEnv } from "@bounded-systems/env";

import { DEFAULT_LOCAL_KEEPER_SOCKET } from "../keeperd/endpoint.ts";

/**
* prx's door catalog, expressed in the guest-room model. Today it is the keeper
* door (signed git-writes via keeperd); the other prx doors (beads, …) join here
* as they move onto the runtime.
*/
export const prxDoorCatalog: DoorCatalog = {
keeper: {
flag: "--keeper",
inBox: "/run/prx/doors/keeperd.sock",
env: "PRX_KEEPER_SOCKET",
hostDefault: DEFAULT_LOCAL_KEEPER_SOCKET,
grants: "signed git writes via keeperd",
use: "Route every git write through the keeper door; keeperd imports the commit and performs the signed push.",
deny: "No git-write authority in this room — relaunch with the keeper door.",
},
};

/** Resolve prx's keeper door to a concrete grant via the guest-room engine. */
export function keeperDoorGrant(env: Env = processEnv()): DoorGrant {
return resolveDoor(prxDoorCatalog, "keeper", undefined, env);
}

/**
* Render the honest rulebook for a prx room over the given granted doors, using
* guest-room's renderer: the capability preamble, a card per granted door, and
* the denied section — so the surface is honest about what is absent, not only
* what is present.
*/
export function renderPrxRulebook(
workcell: string,
grantedDoorNames: readonly string[],
env: Env = processEnv(),
): string {
const granted = grantedDoorNames.map((n) => resolveDoor(prxDoorCatalog, n, undefined, env));
const denied = deniedDoors(prxDoorCatalog, new Set(grantedDoorNames));
return [
...capabilityPreamble(workcell),
...grantedDoorLines(granted),
...deniedDoorSection(denied),
].join("\n");
}
28 changes: 28 additions & 0 deletions packages/prx/test/door/guest-room-catalog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// A1: prx describes its keeper door through the guest-room capability model.
import { describe, expect, test } from "bun:test";

import {
keeperDoorGrant,
prxDoorCatalog,
renderPrxRulebook,
} from "../../src/door/guest-room-catalog.ts";

describe("prx keeper door via the guest-room model", () => {
test("the catalog defines the keeper door against the keeperd endpoint env", () => {
expect(prxDoorCatalog.keeper).toBeDefined();
expect(prxDoorCatalog.keeper!.env).toBe("PRX_KEEPER_SOCKET");
});

test("resolveDoor maps the preset into a concrete grant", () => {
const grant = keeperDoorGrant({ PRX_KEEPER_SOCKET: "/run/prx/doors/keeperd.sock" });
expect(grant.name).toBe("keeper");
expect(grant.env).toBe("PRX_KEEPER_SOCKET");
expect(grant.grants).toContain("signed git writes");
});

test("the rulebook is honest — a card for keeper, and a DENIED section", () => {
const book = renderPrxRulebook("prx", ["keeper"], { PRX_KEEPER_SOCKET: "/x.sock" });
expect(book).toContain("keeper: signed git writes");
expect(book).toContain("DENIED");
});
});
Loading