feat(policy): OPR.0.4.8.3 Seam B complete — materialize-time provenance, restore re-derivation (both paths), successor continuity, expansion/agent-image/fixture surfaces, R6 discrimination pins incl. DB-reopen full_bypass restore proof

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
v-openrig-buildandClaude Fable 5 committed 2026-08-04 08:08:16 +00:00
1 parent 8cebaec5a4
commit 9e94c274c9
13 files changed
+289 -14

No files matched your search

@@ -36,6 +36,8 @@ export class CodexResumeAdapter {
resumeToken: string | null,
_cwd: string,
codexConfigProfile?: string | null,
// OPR.0.4.8.3 Seam B: persisted resolved posture threaded from restore.
resolvedPosture?: "floor" | "full_bypass",
): Promise<ResumeResult> {
if (!this.canResume(resumeType, resumeToken)) {
return { ok: false, code: "no_resume", message: "Codex resume not available" };
@@ -61,6 +63,8 @@ export class CodexResumeAdapter {
resumeToken ?? "",
codexConfigProfile,
resumeType === "codex_last",
undefined,
resolvedPosture,
);
const textResult = await this.tmux.sendText(tmuxSessionName, cmd);
@@ -1,7 +1,8 @@
import { writeFileSync } from "node:fs";
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import type Database from "better-sqlite3";
import type { RigRepository } from "./rig-repository.js";
import { resolvePermissionPolicyAttachment } from "./permission-policy/policy-ref.js";
import type { SessionRegistry } from "./session-registry.js";
import type { EventBus } from "./event-bus.js";
import type { SnapshotRepository } from "./snapshot-repository.js";
@@ -983,7 +984,7 @@ export class RestoreOrchestrator {
await this.rollbackToZeroSession(node.id, sessionName, launchResult?.session.id, priorState);
return { nodeId: node.id, logicalId: node.logicalId, status: "awaiting-decision", error: `Original session unresumable: resume requested but no token available. No session is running. Re-run with --fresh ${node.logicalId} for a deliberate fresh-primed seat, or restore the original session manually.` };
} else {
const resumeOutcome = await this.attemptResume(sessionName, resumeType, resumeToken, node.cwd ?? "/", node.codexConfigProfile, node.model);
const resumeOutcome = await this.attemptResume(sessionName, resumeType, resumeToken, node.cwd ?? "/", node.codexConfigProfile, node.model, this.resolveRestorePosture(node.id, rigId));
if (resumeOutcome.kind === "resumed") {
baseStatus = "resumed";
} else if (resumeOutcome.kind === "attention_required") {
@@ -1104,6 +1105,9 @@ export class RestoreOrchestrator {
...launchResult.binding,
cwd: node.cwd ?? ".",
codexConfigProfile: node.codexConfigProfile ?? undefined,
// OPR.0.4.8.3 Seam B: the pod-aware restore path binds the restored posture too
// (both restore paths consume persisted provenance — preflight surface 3).
launchPosture: this.resolveRestorePosture(node.id, rigId),
// OPR.0.4.6.PI1 VM leg finding: the restore binding dropped the
// node's model declaration, so a resumed Pi seat relaunched with
// no --model — and the runner's provider-key allowlist (keyed off
@@ -1235,6 +1239,44 @@ export class RestoreOrchestrator {
return row.resume_type === resumeType && row.resume_token === resumeToken;
}
/**
* OPR.0.4.8.3 Seam B — a restored seat's launch posture WITHOUT the in-memory RigSpec
* (dev-guard restart-provenance ruling):
* 1. Persisted node provenance (migration 057) is the primary source. For a CUSTOM
* attachment with readable provenance (declaringDir + the node's raw ref) the policy
* is REOPENED + re-derived (a custom surface:flag policy restores to full_bypass);
* an unreadable file degrades to the PERSISTED posture (still restart-stable).
* 2. No provenance (e.g. organic claim/self-attach seats): the persisted RIG-level ref
* resolves — builtin refs resolve dirlessly; a custom rig ref without provenance
* degrades to the resolver's advisory floor (honest absence of a declaring dir).
* 3. Nothing attached → undefined (the env-driven 0.4.8.2 decision, unchanged).
*/
private resolveRestorePosture(nodeId: string, rigId: string): "floor" | "full_bypass" | undefined {
try {
const prov = this.rigRepo.getNodePolicyProvenance(nodeId);
if (prov) {
if (prov.origin === "custom" && prov.declaringDir) {
const ref = prov.nodeRef ?? this.rigRepo.getRigPermissionPolicy(rigId);
if (ref) {
try {
return resolvePermissionPolicyAttachment(ref, prov.declaringDir, {
readFile: (p) => readFileSync(p, "utf-8"),
}).launchPosture;
} catch { /* unreadable at restore → persisted posture below */ }
}
}
return prov.launchPosture;
}
const rigRef = this.rigRepo.getRigPermissionPolicy(rigId);
if (rigRef) {
return resolvePermissionPolicyAttachment(rigRef, "", {
readFile: (p) => readFileSync(p, "utf-8"),
}).launchPosture;
}
} catch { /* posture resolution must never block a restore */ }
return undefined;
}
private async attemptResume(
sessionName: string,
resumeType: string,
@@ -1242,6 +1284,9 @@ export class RestoreOrchestrator {
cwd: string,
codexConfigProfile?: string | null,
model?: string | null,
// OPR.0.4.8.3 Seam B: the seat's restored launch posture (persisted provenance,
// custom policies re-validated when readable). Absent = env decision.
resolvedPosture?: "floor" | "full_bypass",
): Promise<
| { kind: "resumed" }
| { kind: "retry_fresh" }
@@ -1249,7 +1294,7 @@ export class RestoreOrchestrator {
| { kind: "attention_required"; message: string; evidence?: string }
> {
if (this.claudeResume.canResume(resumeType, resumeToken)) {
const result = await this.claudeResume.resume(sessionName, resumeType, resumeToken, cwd);
const result = await this.claudeResume.resume(sessionName, resumeType, resumeToken, cwd, resolvedPosture);
if (result.ok) return { kind: "resumed" };
if (result.code === "retry_fresh") return { kind: "retry_fresh" };
// L3: surface attention_required from the Claude probe (resume-selection prompt).
@@ -1264,7 +1309,7 @@ export class RestoreOrchestrator {
}
if (this.codexResume.canResume(resumeType, resumeToken)) {
const result = await this.codexResume.resume(sessionName, resumeType, resumeToken, cwd, codexConfigProfile);
const result = await this.codexResume.resume(sessionName, resumeType, resumeToken, cwd, codexConfigProfile, resolvedPosture);
if (result.ok) return { kind: "resumed" };
if (result.code === "retry_fresh") return { kind: "retry_fresh" };
// Codex auth-refusal: stored OAuth token can no longer be refreshed.
@@ -1285,7 +1330,7 @@ export class RestoreOrchestrator {
// session file returns retry_fresh, which the caller maps to the
// awaiting-decision stop-and-ask — never a silent fresh start (BR-6).
if (this.piResume?.canResume(resumeType, resumeToken)) {
const result = await this.piResume.resume(sessionName, resumeType, resumeToken, cwd, model);
const result = await this.piResume.resume(sessionName, resumeType, resumeToken, cwd, model, resolvedPosture);
if (result.ok) return { kind: "resumed" };
if (result.code === "retry_fresh") return { kind: "retry_fresh" };
if (result.code === "attention_required") {
@@ -142,6 +142,8 @@ export class RigExpansionService {
...(member.agentRef ? { agent_ref: member.agentRef } : {}),
...(member.profile ? { profile: member.profile } : {}),
...(member.codexConfigProfile ? { codex_config_profile: member.codexConfigProfile } : {}),
// OPR.0.4.8.3 Seam B: permission_policy rides the fragment→spec map like role.
...(member.permissionPolicy ? { permission_policy: member.permissionPolicy } : {}),
...(member.cwd ? { cwd: member.cwd } : {}),
...(member.model ? { model: member.model } : {}),
// OPR.0.4.6.FAC1: role rides the fragment→spec map (a
+5 -1
View File
@@ -213,11 +213,14 @@ export class RigRepository {
resolvedTarget: string | null;
declaringDir: string | null;
launchPosture: "floor" | "full_bypass";
/** The node's own raw ref (member-level; null when the attachment came from the rig). */
nodeRef: string | null;
} | null {
if (!this.hasNodeColumn("policy_launch_posture")) return null;
const row = this.db.prepare(
"SELECT policy_origin, policy_resolved_target, policy_declaring_dir, policy_launch_posture FROM nodes WHERE id = ?",
"SELECT permission_policy, policy_origin, policy_resolved_target, policy_declaring_dir, policy_launch_posture FROM nodes WHERE id = ?",
).get(nodeId) as {
permission_policy: string | null;
policy_origin: string | null;
policy_resolved_target: string | null;
policy_declaring_dir: string | null;
@@ -229,6 +232,7 @@ export class RigRepository {
resolvedTarget: row.policy_resolved_target,
declaringDir: row.policy_declaring_dir,
launchPosture: row.policy_launch_posture as "floor" | "full_bypass",
nodeRef: row.permission_policy,
};
}
@@ -595,6 +595,10 @@ export class PodRigInstantiator {
cwdOverride: opts?.cwdOverride,
});
logicalIdToNodeId.set(qualifiedId, node.id);
// Seam B R2: restart-stable provenance persists AT MATERIALIZE (a
// materialized-but-never-launched seat must still restore its posture).
const materializeAttachment = this.resolveMemberPolicyAttachment(member.permissionPolicy, rigSpec.permissionPolicy, rigRoot);
if (materializeAttachment) this.persistNodePolicyProvenance(node.id, materializeAttachment);
nodeResults.push({ logicalId: qualifiedId, status: "materialized" });
persistedEvents.push(event);
}
@@ -908,6 +912,10 @@ export class PodRigInstantiator {
});
createdNodeId = node.id;
createdEvent = event;
// Seam B R2: add_member persists provenance at creation too (rig ref from the DB —
// no in-memory rigSpec exists on this path).
const addMemberAttachment = this.resolveMemberPolicyAttachment(member.permissionPolicy, this.deps.rigRepo.getRigPermissionPolicy(rigId), rigRoot);
if (addMemberAttachment) this.persistNodePolicyProvenance(node.id, addMemberAttachment);
if (resolvedEdges.length > 0) {
const logicalIdToNodeId = new Map(rig.nodes.map((n) => [n.logicalId, n.id]));
logicalIdToNodeId.set(qualifiedId, node.id);
@@ -1464,11 +1472,11 @@ export class PodRigInstantiator {
* materialized rig). Returns undefined when no ref is attached at either level.
*/
private resolveMemberPolicyAttachment(
member: RigSpecPodMember,
rigSpec: PodRigSpec,
memberRef: string | undefined,
rigRef: string | undefined | null,
rigRoot: string,
): ResolvedPolicyAttachment | undefined {
const ref = resolvePermissionPolicyRefValue(member.permissionPolicy, rigSpec.permissionPolicy);
const ref = resolvePermissionPolicyRefValue(memberRef, rigRef);
if (!ref) return undefined;
return resolvePermissionPolicyAttachment(ref, rigRoot, {
readFile: (p) => this.deps.fsOps.readFile(p),
@@ -1530,7 +1538,7 @@ export class PodRigInstantiator {
// OPR.0.4.8.3 Seam B: resolve the seat's permission-policy attachment once for this
// launch (member > rig precedence; declaring dir = the rig root, where the declaring
// RigSpec + its relative policy files live). Also persists restart-stable provenance.
const policyAttachment = this.resolveMemberPolicyAttachment(input.member, input.rigSpec, input.rigRoot);
const policyAttachment = this.resolveMemberPolicyAttachment(input.member.permissionPolicy, input.rigSpec.permissionPolicy, input.rigRoot);
if (policyAttachment) this.persistNodePolicyProvenance(input.nodeId, policyAttachment);
const sessionNameErrors = validateSessionComponents(input.pod.id, input.member.id, input.rigSpec.name);
@@ -1768,7 +1776,7 @@ export class PodRigInstantiator {
cwdOverride?: string;
}): Promise<{ status: "launched" | "failed"; error?: string; sessionName?: string; warnings?: string[] }> {
const effectiveCwd = resolveLaunchCwd(input.member.cwd, input.rigRoot, input.cwdOverride);
const terminalPolicyAttachment = this.resolveMemberPolicyAttachment(input.member, input.rigSpec, input.rigRoot);
const terminalPolicyAttachment = this.resolveMemberPolicyAttachment(input.member.permissionPolicy, input.rigSpec.permissionPolicy, input.rigRoot);
if (terminalPolicyAttachment) this.persistNodePolicyProvenance(input.nodeId, terminalPolicyAttachment);
const sessionNameErrors = validateSessionComponents(input.pod.id, input.member.id, input.rigSpec.name);
if (sessionNameErrors.length > 0) {
@@ -274,8 +274,10 @@ export class SeatHandoverService {
// READY agent (§2.1b seam, B1): createSession -> resolve pane -> real
// runtime startup (launchHarness + readiness) -> upsertDiscoveredSession.
// The successor is a live agent, not a bare shell, before it can commit.
const successorPosture = this.rigRepo.getNodePolicyProvenance(node.id)?.launchPosture;
const launch = await this.successorLauncher.createSuccessor({
node: { id: node.id, runtime: node.runtime, cwd: node.cwd },
// Seam B: the successor is the SAME seat continuing — persisted policy posture carries.
node: { id: node.id, runtime: node.runtime, cwd: node.cwd, ...(successorPosture ? { launchPosture: successorPosture } : {}) },
departingSessionName: latestSession.session_name,
});
if (!launch.ok) {
@@ -38,6 +38,10 @@ export interface SuccessorNode {
id: string;
runtime: string | null;
cwd: string | null;
/** OPR.0.4.8.3 Seam B: the departing seat's PERSISTED resolved launch posture —
* the successor is a CONTINUITY edge of the same seat, so its policy posture
* carries (populated by the caller from node provenance; absent = env decision). */
launchPosture?: "floor" | "full_bypass";
}
export type SuccessorLaunchResult =
@@ -250,6 +254,8 @@ export class SuccessorSessionLauncher {
cmuxSurface: null,
updatedAt: "",
cwd: cwd ?? "",
// Seam B: continuity — the successor launches at the departing seat's posture.
...(node.launchPosture ? { launchPosture: node.launchPosture } : {}),
};
let launch: Awaited<ReturnType<RuntimeAdapter["launchHarness"]>>;
+3
View File
@@ -1165,6 +1165,9 @@ export interface ExpansionPodFragment {
cwd?: string;
model?: string;
codexConfigProfile?: string;
/** OPR.0.4.8.3 Seam B: per-seat permission_policy REF — threaded through the
* expansion ingress exactly like role (never silently dropped). */
permissionPolicy?: string;
restorePolicy?: string;
label?: string;
/**
@@ -68,6 +68,9 @@ interface ForkSourceShape {
profile: string | null;
cwd: string | null;
codexConfigProfile: string | null;
/** OPR.0.4.8.3 Seam B: the source seat's raw permission_policy ref — a forked/imaged
* successor preserves the source's policy attachment (preflight surface 2). */
permissionPolicy: string | null;
}
function resolveForkSourceNode(db: Database.Database, sourceSession: string): ForkSourceShape | null {
@@ -86,6 +89,7 @@ function resolveForkSourceNode(db: Database.Database, sourceSession: string): Fo
profile: (row["profile"] as string | null) ?? null,
cwd: (row["cwd"] as string | null) ?? null,
codexConfigProfile: (row["codex_config_profile"] as string | null) ?? null,
permissionPolicy: (row["permission_policy"] as string | null) ?? null,
};
}
@@ -254,6 +258,7 @@ export function agentImagesRoutes(deps: AgentImageRoutesDeps): Hono {
...(source.profile ? { profile: source.profile } : {}),
...(source.cwd ? { cwd: source.cwd } : {}),
...(source.codexConfigProfile ? { codex_config_profile: source.codexConfigProfile } : {}),
...(source.permissionPolicy ? { permission_policy: source.permissionPolicy } : {}),
session_source: sessionSource,
};
+6
View File
@@ -127,6 +127,12 @@ function normalizeExpansionPodFragment(raw: Record<string, unknown>): ExpansionP
: undefined,
cwd: typeof m["cwd"] === "string" ? m["cwd"] : undefined,
model: typeof m["model"] === "string" ? m["model"] : undefined,
permissionPolicy:
typeof m["permissionPolicy"] === "string"
? m["permissionPolicy"]
: typeof m["permission_policy"] === "string"
? m["permission_policy"]
: undefined,
restorePolicy:
typeof m["restorePolicy"] === "string"
? m["restorePolicy"]
@@ -31,6 +31,9 @@ import { contextUsageSchema } from "../../src/db/migrations/018_context_usage.js
import { externalCliAttachmentSchema } from "../../src/db/migrations/019_external_cli_attachment.js";
import { rigServicesSchema } from "../../src/db/migrations/020_rig_services.js";
import { seatHandoverObservabilitySchema } from "../../src/db/migrations/021_seat_handover_observability.js";
import { nodePermissionPolicySchema } from "../../src/db/migrations/055_node_permission_policy.js";
import { rigPermissionPolicySchema } from "../../src/db/migrations/056_rig_permission_policy.js";
import { nodePolicyProvenanceSchema } from "../../src/db/migrations/057_node_policy_provenance.js";
import { nodeCodexConfigProfileSchema } from "../../src/db/migrations/022_node_codex_config_profile.js";
import { streamItemsSchema } from "../../src/db/migrations/023_stream_items.js";
import { queueItemsSchema } from "../../src/db/migrations/024_queue_items.js";
@@ -55,7 +58,7 @@ import { sessionsNodeIdIndexSchema } from "../../src/db/migrations/053_sessions_
// index-independent).
export function createMigratedDb(): Database.Database {
const db = createDb();
migrate(db, [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema, eventsNodeTypeIndexSchema, sessionsNodeIdIndexSchema]);
migrate(db, [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, nodePermissionPolicySchema, rigPermissionPolicySchema, nodePolicyProvenanceSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema, eventsNodeTypeIndexSchema, sessionsNodeIdIndexSchema]);
return db;
}
+8 -1
View File
@@ -23,6 +23,9 @@ import { contextUsageSchema } from "../../src/db/migrations/018_context_usage.js
import { externalCliAttachmentSchema } from "../../src/db/migrations/019_external_cli_attachment.js";
import { rigServicesSchema } from "../../src/db/migrations/020_rig_services.js";
import { seatHandoverObservabilitySchema } from "../../src/db/migrations/021_seat_handover_observability.js";
import { nodePermissionPolicySchema } from "../../src/db/migrations/055_node_permission_policy.js";
import { rigPermissionPolicySchema } from "../../src/db/migrations/056_rig_permission_policy.js";
import { nodePolicyProvenanceSchema } from "../../src/db/migrations/057_node_policy_provenance.js";
import { nodeCodexConfigProfileSchema } from "../../src/db/migrations/022_node_codex_config_profile.js";
// PL-019: GET /api/rigs/:id/graph + GET /api/rigs/:rigId/nodes/:logicalId
// now perform a read-side join over queue_items (in-progress qitem
@@ -88,9 +91,13 @@ import { AgentActivityStore } from "../../src/domain/agent-activity-store.js";
import { createApp } from "../../src/server.js";
import fs from "node:fs";
/** Seam B R6: the canonical full-fixture migration list, exported so file-backed
* DB-reopen tests migrate IDENTICALLY to createFullTestDb. */
export const migrationsForFullTestDb = [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, nodePermissionPolicySchema, rigPermissionPolicySchema, nodePolicyProvenanceSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema];
export function createFullTestDb(): Database.Database {
const db = createDb();
migrate(db, [coreSchema, bindingsSessionsSchema, eventsSchema, snapshotsSchema, checkpointsSchema, resumeMetadataSchema, nodeSpecFieldsSchema, packagesSchema, installJournalSchema, journalSeqSchema, bootstrapSchema, discoverySchema, discoveryFkFix, agentspecRebootSchema, startupContextSchema, chatMessagesSchema, podNamespaceSchema, contextUsageSchema, externalCliAttachmentSchema, rigServicesSchema, seatHandoverObservabilitySchema, nodeCodexConfigProfileSchema, streamItemsSchema, queueItemsSchema, queueTransitionsSchema, rigPolicySchema, rigArchiveSchema, resumeProvenanceSchema, resumeVerificationSchema, seatIdentityVerdictsSchema]);
migrate(db, migrationsForFullTestDb);
return db;
}
@@ -0,0 +1,180 @@
// OPR.0.4.8.3 Seam B (R6) — full-thread discrimination pins: the permission_policy REF +
// resolved posture survive materialize → columns → export → DB-REOPEN, with member-over-rig
// precedence and the dev-guard restart ruling (a custom surface:flag policy restores to
// full_bypass) proven against a REOPENED database handle. Every pin here discriminates
// against the pre-Seam-B tip (none of the columns/threading exist at 80336ff0/4694e86d).
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import type Database from "better-sqlite3";
import { mkdtempSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { createFullTestDb, createTestApp, migrationsForFullTestDb } from "./helpers/test-app.js";
import { createDb } from "../src/db/connection.js";
import { migrate } from "../src/db/migrate.js";
import { RigRepository } from "../src/domain/rig-repository.js";
import { resolvePermissionPolicyAttachment } from "../src/domain/permission-policy/policy-ref.js";
import { claudePostureFlag, codexPostureArg, piTrust } from "../src/adapters/yolo-mode.js";
const RIG_ROOT = "/project/rigs/policy-rig";
const CUSTOM_POLICY = `---
name: operator-full
version: "1"
description: full-bypass flag policy for the plumb pins
surface: flag
launch_posture: full_bypass
allowed_actions: []
ask_actions: []
denied_actions: []
watch_actions: []
---
# Operator full
`;
function agentYaml(name: string): string {
return `name: ${name}\nversion: "1.0.0"\nresources:\n skills: []\nprofiles:\n default:\n uses:\n skills: []`;
}
function rawMember(id: string, over: Record<string, unknown> = {}): Record<string, unknown> {
return { id, agent_ref: "local:agents/impl", profile: "default", runtime: "claude-code", cwd: ".", ...over };
}
function rawSpec(members: Record<string, unknown>[], over: Record<string, unknown> = {}): Record<string, unknown> {
return {
version: "0.2",
name: "policy-rig",
pods: [{ id: "dev", label: "Dev", members, edges: [] }],
edges: [],
...over,
};
}
function fsOps() {
return {
readFile: (p: string) => {
if (p.includes("agents/impl")) return agentYaml("impl");
if (p.includes("policies/operator-full.md")) return CUSTOM_POLICY;
throw new Error(`Not found: ${p}`);
},
exists: (p: string) => p.includes("agents/impl") || p.includes("policies/operator-full.md"),
};
}
describe("Seam B R6 — full-thread plumb (materialize → columns → export)", () => {
let db: Database.Database;
let setup: ReturnType<typeof createTestApp>;
beforeEach(() => {
db = createFullTestDb();
setup = createTestApp(db, { podInstantiatorFsOps: fsOps() });
});
afterEach(() => { db.close(); });
it("rig-level + member refs land on their rows; member OVERRIDES rig at resolution (precedence)", async () => {
const spec = rawSpec(
[rawMember("impl", { permission_policy: "builtin:locked" }), rawMember("helper")],
{ permission_policy: "builtin:yolo" },
);
const outcome = await setup.podInstantiator.materializeStructured(spec, RIG_ROOT);
expect(outcome.ok).toBe(true);
const rigId = (outcome as { ok: true; result: { rigId: string } }).result.rigId;
// rig row carries the rig ref
expect(setup.rigRepo.getRigPermissionPolicy(rigId)).toBe("builtin:yolo");
// member's OWN ref on its node column; ref-less pod-mate NULL (rig ref lives on the rig)
const refOf = (lid: string) => (db.prepare("SELECT permission_policy FROM nodes WHERE logical_id = ?").get(lid) as { permission_policy: string | null }).permission_policy;
expect(refOf("dev.impl")).toBe("builtin:locked");
expect(refOf("dev.helper")).toBeNull();
// provenance: member ref WINS over rig ref → impl floor (locked), helper full_bypass (rig yolo)
const nodeId = (lid: string) => (db.prepare("SELECT id FROM nodes WHERE logical_id = ?").get(lid) as { id: string }).id;
expect(setup.rigRepo.getNodePolicyProvenance(nodeId("dev.impl"))).toMatchObject({ origin: "builtin", launchPosture: "floor", resolvedTarget: null });
expect(setup.rigRepo.getNodePolicyProvenance(nodeId("dev.helper"))).toMatchObject({ origin: "builtin", launchPosture: "full_bypass", resolvedTarget: null });
});
it("export round-trips BOTH levels: member ref on the pod member, rig ref at the top level", async () => {
const spec = rawSpec(
[rawMember("impl", { permission_policy: "policies/operator-full.md" })],
{ permission_policy: "builtin:standard" },
);
const outcome = await setup.podInstantiator.materializeStructured(spec, RIG_ROOT);
expect(outcome.ok).toBe(true);
const rigId = (outcome as { ok: true; result: { rigId: string } }).result.rigId;
const exported = setup.rigSpecExporter.exportRig(rigId) as import("../src/domain/types.js").RigSpec;
expect(exported.permissionPolicy).toBe("builtin:standard");
expect(exported.pods[0]!.members[0]!.permissionPolicy).toBe("policies/operator-full.md");
});
it("a CUSTOM surface:flag policy resolves to full_bypass at materialize with restart-stable provenance", async () => {
const spec = rawSpec([rawMember("impl", { permission_policy: "policies/operator-full.md" })]);
const outcome = await setup.podInstantiator.materializeStructured(spec, RIG_ROOT);
expect(outcome.ok).toBe(true);
const nodeId = (db.prepare("SELECT id FROM nodes WHERE logical_id = 'dev.impl'").get() as { id: string }).id;
const prov = setup.rigRepo.getNodePolicyProvenance(nodeId);
expect(prov).toMatchObject({
origin: "custom",
launchPosture: "full_bypass", // the guard ruling's crux: custom flag CAN be full_bypass
declaringDir: RIG_ROOT,
resolvedTarget: `${RIG_ROOT}/policies/operator-full.md`,
nodeRef: "policies/operator-full.md",
});
});
});
describe("Seam B R6 — DB-REOPEN restart proof (dev-guard ruling)", () => {
let dir: string;
beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "seam-b-reopen-")); });
afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
it("a custom surface:flag=full_bypass attachment restores to full_bypass from a REOPENED database", async () => {
const dbFile = join(dir, "daemon.sqlite");
const db1 = createDb(dbFile);
// identical canonical migration set as createFullTestDb, against the FILE-backED db
migrate(db1, migrationsForFullTestDb);
const setup1 = createTestApp(db1, { podInstantiatorFsOps: fsOps() });
const outcome = await setup1.podInstantiator.materializeStructured(
rawSpec([rawMember("impl", { permission_policy: "policies/operator-full.md" })]),
RIG_ROOT,
);
expect(outcome.ok).toBe(true);
const nodeId = (db1.prepare("SELECT id FROM nodes WHERE logical_id = 'dev.impl'").get() as { id: string }).id;
db1.close(); // ── daemon restart boundary ──
const db2 = createDb(dbFile); // REOPEN: no in-memory RigSpec exists anymore
const repo2 = new RigRepository(db2);
const prov = repo2.getNodePolicyProvenance(nodeId);
expect(prov).not.toBeNull();
expect(prov!.launchPosture).toBe("full_bypass"); // persisted posture is restart-stable
// AND the ruling's re-derivation: reopen + re-validate the policy from provenance alone
const rederived = resolvePermissionPolicyAttachment(prov!.nodeRef!, prov!.declaringDir!, {
readFile: (p) => { expect(p).toBe(`${RIG_ROOT}/policies/operator-full.md`); return CUSTOM_POLICY; },
});
expect(rederived.launchPosture).toBe("full_bypass");
expect(rederived.surface).toBe("flag");
db2.close();
});
});
describe("Seam B R6 — resolved posture drives the REAL launch helpers on all three harnesses", () => {
const yoloOn = { OPENRIG_YOLO: "1" } as NodeJS.ProcessEnv;
const yoloOff = {} as NodeJS.ProcessEnv;
it("full_bypass posture lifts a seat even with YOLO OFF (custom flag policy — the ruling)", () => {
expect(claudePostureFlag(yoloOff, "full_bypass")).toBe("--dangerously-skip-permissions");
expect(codexPostureArg("", yoloOff, "full_bypass")).toBe(" -s danger-full-access");
expect(piTrust(undefined, yoloOff, "full_bypass")).toBe("approve"); // Pi = resource trust, not a permission policy
});
it("floor posture HOLDS a seat at the floor even with global YOLO ON (attached policy is authoritative)", () => {
expect(claudePostureFlag(yoloOn, "floor")).toBe("--permission-mode acceptEdits");
expect(codexPostureArg(" -p prof", yoloOn, "floor")).toBe(" -p prof");
expect(piTrust("no-approve", yoloOn, "floor")).toBe("no-approve");
});
it("ABSENT posture preserves the 0.4.8.2 env behavior byte-for-byte (no policy attached)", () => {
expect(claudePostureFlag(yoloOn)).toBe("--dangerously-skip-permissions");
expect(claudePostureFlag(yoloOff)).toBe("--permission-mode acceptEdits");
expect(codexPostureArg("", yoloOff)).toBe(" -s workspace-write");
expect(piTrust(undefined, yoloOff)).toBe("no-approve");
});
});