import { afterEach, beforeEach, describe, it } from "@std/testing/bdd";
import {
SEED_ENVELOPE_SCHEMA_HASH,
seedStoredEnvelope,
writeSeedEnvelopeDoc,
} from "../../runner/test/cfc-seed-envelope.ts";
import { expect } from "@std/expect";
import { normalize } from "@std/path/posix";
import { createSession, Identity } from "@commonfabric/identity";
import type { DID } from "@commonfabric/identity/did";
import { PiecesController } from "@commonfabric/piece/ops";
import { Runtime } from "@commonfabric/runner";
import { cfcLabelViewForCellFailClosed } from "@commonfabric/runner/cfc";
import {
createLLMFriendlyLink,
parseLLMFriendlyLink,
} from "@commonfabric/runner/shared";
import {
EmulatedStorageManager,
newLoopbackServer,
StorageManager,
} from "@commonfabric/runner/storage/cache.deno";
import { CfHarnessEngine } from "../src/engine.ts";
import type { HarnessFabricSession } from "../src/fabric-session.ts";
import { resolveHandleToken } from "../src/handle-table.ts";
import {
createFabricInstantiationRecorder,
type FabricInstantiationRecord,
type FabricInstantiationRecorder,
type FabricPatternInstantiations,
} from "../src/fabric-instantiations.ts";
import { comparableEntityHash } from "../src/fabric-observations.ts";
import { scrubBareFabricIdentifiers } from "../src/fabric-identifier-scrub.ts";
import { resolveWellKnownGrantRefs } from "../src/well-known-grants.ts";
import { CAPABILITY_PROBE_SENTINEL } from "../src/diagnostics.ts";
import {
asSerializableValue,
policyRefusalMessage,
RUN_PATTERN_MAX_SOURCE_TEXT_BYTES,
runPatternPolicyRefusal,
type RunPatternToolErrorOutput,
type RunPatternToolInput,
type RunPatternToolSuccessOutput,
} from "../src/tools/run-pattern.ts";
import type {
CfcAddress,
CfcRefusalDetail,
CfcRefusalInput,
} from "@commonfabric/runner/cfc";
import type {
SandboxCommandRequest,
SandboxCommandResult,
SandboxRuntime,
SandboxRuntimeDescription,
SandboxShellRequest,
} from "../src/sandbox/types.ts";
const signer = await Identity.fromPassphrase("cf-harness run-pattern tool");
const DOUBLING_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { doubled: number; }",
"export default pattern(({ n }) => ({",
" doubled: computed(() => n * 2),",
"}));",
"",
].join("\n");
const NAMED_DOUBLING_PATTERN_SOURCE = [
"import { computed, NAME, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { doubled: number; $NAME: string; }",
"export default pattern(({ n }) => ({",
" [NAME]: 'Doubler',",
" doubled: computed(() => n * 2),",
"}));",
"",
].join("\n");
/** A pattern whose single input is an object, for the plain-JSON cases. */
const OVERVIEW_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Overview { title: string; }",
"interface Input { overview: Overview; }",
"interface Output { titleLength: number; }",
"export default pattern(({ overview }) => ({",
" titleLength: computed(() => overview.title.length),",
"}));",
"",
].join("\n");
const TITLE_LENGTH_RESULT_SCHEMA = {
type: "object",
properties: { titleLength: { type: "number" } },
required: ["titleLength"],
} as const;
/**
* What a `resultSchema`-sanitized result carries at a position it sealed:
* the single-key `@link` object over an `opaque:` target. A model reading a
* tool result can copy one of these back out, which is what the sealed-input
* refusal exists for.
*/
const SEALED_OPAQUE_LINK = {
"@link": "opaque:run-pattern-test%3Arun_pattern%3A1#/overview",
} as const;
/**
* The same seal without its `@link` wrapper. A model reading a sanitized
* result can lift the target string out and pass it on its own, which reaches
* an input at any position the argument schema admits a string.
*/
const SEALED_OPAQUE_TARGET = SEALED_OPAQUE_LINK["@link"];
/**
* A pattern whose argument schema carries an index signature, so
* `additionalProperties` is a schema: undeclared keys are permitted, and their
* values are what that schema describes.
*/
const OPEN_DOUBLING_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Input { n: number; [key: string]: number; }",
"interface Output { doubled: number; }",
"export default pattern(({ n }) => ({",
" doubled: computed(() => n * 2),",
"}));",
"",
].join("\n");
/**
* A pattern whose argument schema admits any undeclared key without saying
* what it may hold, so `additionalProperties` is `true`.
*/
const ANY_DOUBLING_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"// deno-lint-ignore no-explicit-any",
"interface Input { n: number; [key: string]: any; }",
"interface Output { doubled: number; }",
"export default pattern(({ n }) => ({",
" doubled: computed(() => n * 2),",
"}));",
"",
].join("\n");
/** A pattern whose single input is an array, for the nested-position cases. */
const ROW_COUNT_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Input { rows: string[]; }",
"interface Output { rowCount: number; }",
"export default pattern(({ rows }) => ({",
" rowCount: computed(() => rows.length),",
"}));",
"",
].join("\n");
const ROW_COUNT_RESULT_SCHEMA = {
type: "object",
properties: { rowCount: { type: "number" } },
required: ["rowCount"],
} as const;
/** A pattern whose single input is a string, for the link-shaped-string case. */
const LABEL_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Input { label: string; }",
"interface Output { labelLength: number; }",
"export default pattern(({ label }) => ({",
" labelLength: computed(() => label.length),",
"}));",
"",
].join("\n");
const LABEL_LENGTH_RESULT_SCHEMA = {
type: "object",
properties: { labelLength: { type: "number" } },
required: ["labelLength"],
} as const;
/** A pattern whose single input is the registry array, counting its entries. */
const ENTRY_COUNT_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Input { entries: unknown[]; }",
"interface Output { count: number; }",
"export default pattern(({ entries }) => ({",
" count: computed(() => entries.length),",
"}));",
"",
].join("\n");
const ENTRY_COUNT_RESULT_SCHEMA = {
type: "object",
properties: { count: { type: "number" } },
required: ["count"],
} as const;
const DOUBLED_RESULT_SCHEMA = {
type: "object",
properties: { doubled: { type: "number" } },
required: ["doubled"],
} as const;
/**
* A minimal default-pattern program exposing the piece registry, so the
* space has a REAL registry rather than the detached always-empty fallback.
*/
const DEFAULT_PATTERN_SOURCE = [
"import { handler, pattern, type Cell, type Stream } from 'commonfabric';",
"const addPiece = handler<{ piece: unknown }, { pieceRegistry: Cell }>(",
" true,",
" { type: 'object', properties: { pieceRegistry: { type: 'array', asCell: ['cell'] } } },",
" ({ piece }, { pieceRegistry }) => {",
" pieceRegistry.push(piece);",
" },",
");",
"export default pattern<",
" { pieceRegistry: unknown[] },",
" { pieceRegistry: unknown[]; addPiece: Stream<{ piece: unknown }> }",
">(({ pieceRegistry }) => ({",
" pieceRegistry,",
" addPiece: addPiece({ pieceRegistry }),",
"}));",
].join("\n");
const EXPENSE_SCHEMA = {
type: "object",
properties: {
description: { type: "string" },
amount: { type: "number" },
},
} as const;
/**
* A fabric session with persisted flow labels, at the enforcement posture the
* caller names: `enforce-strict` for the tests that pin what a pattern over
* labelled data leaves in the store and what its answer may release, and
* `observe` for the one that pins that nothing rejects there.
*/
async function createFabric(
cfcEnforcementMode: "observe" | "enforce-strict" = "enforce-strict",
) {
const storage = StorageManager.emulate({ as: signer });
const runtime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager: storage,
cfcEnforcementMode,
cfcFlowLabels: "persist",
});
const pieces = new PiecesController(
createSession({
identity: signer,
spaceDid: (await Identity.generate()).did(),
}),
runtime,
);
await pieces.synced();
return {
runtime,
pieces,
space: pieces.getSpace(),
dispose: async () => {
await runtime.dispose();
await storage.close();
},
};
}
const createStrictFabric = () => createFabric("enforce-strict");
/**
* A pattern over one plain input and one optional referenced input, where the
* referenced one only widens the answer. Run with the reference it derives
* from labelled data; run without it, it still computes a result — which is
* what makes dropping a refused input a replan rather than an abandonment.
*/
const OPTIONAL_SECRET_PATTERN_SOURCE = [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input { amount: number; source?: Reactive; }",
"interface Output { total: number; }",
"export default pattern(({ amount, source }) => ({",
" total: computed(() => {",
" const secret = source?.secret;",
" return amount + (typeof secret === 'string' ? secret.length : 0);",
" }),",
"}));",
"",
].join("\n");
const TOTAL_RESULT_SCHEMA = {
type: "object",
properties: { total: { type: "number" } },
required: ["total"],
} as const;
/**
* Seeds a document whose `secret` field carries confidentiality, and answers
* the LLM-friendly link an agent would pass as an input naming it.
*/
async function seedLabelledSecret(
runtime: Runtime,
space: ReturnType,
cause: string,
): Promise {
const seed = runtime.edit();
const sourceCell = runtime.getCell(
space,
cause,
{ type: "object", properties: { secret: { type: "string" } } },
seed,
);
const sourceId = sourceCell.getAsNormalizedFullLink().id;
writeSeedEnvelopeDoc(seed, space);
seedStoredEnvelope(seed, { space, scope: "space", id: sourceId, path: [] }, {
value: { secret: "s3cr3t" },
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [{ path: ["secret"], label: { confidentiality: ["secret"] } }],
},
},
});
expect((await seed.commit().settled).ok).toBeDefined();
return createLLMFriendlyLink(sourceCell.getAsNormalizedFullLink(), space);
}
/**
* An account's spending total beside declared pending and failed reads.
*/
const SPENDING_PATTERN_SOURCE = [
"import { computed, pattern } from 'commonfabric';",
"interface Transaction { amount: number; }",
"interface Account { balance: number; transactions: Transaction[]; }",
"interface Input { account: Account; }",
"interface Output { totalSpending: number; pending: boolean; errorMessage: string; }",
"export default pattern(({ account }) => ({",
" pending: true,",
" errorMessage: 'fixture read failed',",
" totalSpending: computed(() => account.transactions.reduce(",
" (sum, t) => sum + (t.amount < 0 ? -t.amount : 0),",
" 0,",
" )),",
"}));",
"",
].join("\n");
const TOTAL_SPENDING_RESULT_SCHEMA = {
type: "object",
properties: { totalSpending: { type: "number" } },
required: ["totalSpending"],
} as const;
/**
* How the holder document an agent is handed reaches the labeled account:
* through a link in its `account` field, the way an operator attaches one;
* through a link at its own root, above the field the agent addresses; or
* through its `account` field beside a `notes` field linking to an
* unlabeled document, so a dereference the holder records leads somewhere
* the account never goes.
*/
type AccountHolderShape = "field-link" | "root-link" | "two-fields";
/**
* Seeds an account the way an operator attaches one: a holder document that
* reaches the account document by a link, with the confidentiality on the
* account document and not on the holder. Returns the LLM-friendly link to
* the holder's `account` position, which is what the agent is handed — so
* the labeled document is one the agent's address never names, reached only
* by dereferencing what the holder holds — and, for the two-field shape, the
* link to its `notes` position as well.
*/
async function seedAccountHolder(
runtime: Runtime,
space: ReturnType,
cause: string,
shape: AccountHolderShape,
): Promise<{ account: string; notes: string }> {
const seed = runtime.edit();
const account = {
balance: 2000,
transactions: [{ amount: -120 }, { amount: 2000 }, { amount: -25 }],
};
const accountCell = runtime.getCell(
space,
`${cause}-account`,
undefined,
seed,
);
const accountId = accountCell.getAsNormalizedFullLink().id;
writeSeedEnvelopeDoc(seed, space);
// A root-linked holder continues into the account document at `account`,
// so that document carries the field and the label sits on it; the others
// link straight at the account, labeled at its root.
seedStoredEnvelope(seed, { space, scope: "space", id: accountId, path: [] }, {
value: shape === "root-link" ? { account } : account,
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [{
path: shape === "root-link" ? ["account"] : [],
label: { confidentiality: ["finance"] },
}],
},
},
});
const notesCell = runtime.getCell(space, `${cause}-notes`, undefined, seed);
const notesId = notesCell.getAsNormalizedFullLink().id;
seed.writeOrThrow({ space, scope: "space", id: notesId, path: [] }, {
value: { text: "unlabeled" },
});
const linkTo = (id: string) => ({
"/": { "link@1": { id, path: [], scope: "space", space } },
});
const holderCell = runtime.getCell(space, `${cause}-holder`, undefined, seed);
seed.writeOrThrow(
{
space,
scope: "space",
id: holderCell.getAsNormalizedFullLink().id,
path: [],
},
{
value: shape === "root-link"
? linkTo(accountId)
: shape === "two-fields"
? { account: linkTo(accountId), notes: linkTo(notesId) }
: { account: linkTo(accountId) },
},
);
expect((await seed.commit().settled).ok).toBeDefined();
return {
account: createLLMFriendlyLink(
holderCell.key("account").getAsNormalizedFullLink(),
space,
),
notes: createLLMFriendlyLink(
holderCell.key("notes").getAsNormalizedFullLink(),
space,
),
};
}
/** {@link seedAccountHolder} in the operator's shape, the account link alone. */
async function seedLabelledAccount(
runtime: Runtime,
space: ReturnType,
cause: string,
): Promise {
return (await seedAccountHolder(runtime, space, cause, "field-link")).account;
}
/**
* The session's pieces with the argument-document route to an input key taken
* away: once the piece is running, resolving its argument cell fails. That is
* the state a refusal report degrades under when the argument cell of a piece
* that ran will not resolve, and the caller's own resolved addresses are all
* that is left to answer with.
*/
function piecesWithUnresolvableArgument(
pieces: PiecesController,
): PiecesController {
let running = false;
return new Proxy(pieces, {
get(target, property) {
if (property === "runPersistent") {
return async (
...args: Parameters
) => {
const cell = await target.runPersistent(...args);
running = true;
return cell;
};
}
if (property === "getArgument") {
return (...args: Parameters) => {
if (running) {
throw new Error("the piece's argument cell does not resolve");
}
return target.getArgument(...args);
};
}
const value = Reflect.get(target, property);
return typeof value === "function" ? value.bind(target) : value;
},
});
}
/** The space a synthetic refusal detail addresses; never resolved. */
const REFUSAL_SPACE = "did:key:zRunPatternRefusal" as const;
/**
* A read of `id` at `path`, as a refusal detail addresses one. The id is a
* plain string rather than an `of:` URI, which the canonical entity-id seam
* passes through unchanged, so two details naming one id compare as one
* document.
*/
function refusalRead(id: string, path: readonly string[] = []): CfcAddress {
return { space: REFUSAL_SPACE, id, scope: "space", path: [...path] };
}
/** One read that carried `"secret"` into a refused operation. */
function refusalInput(
id: string,
labelPath: readonly string[] = ["secret"],
): CfcRefusalInput {
return { read: refusalRead(id), labelPath, atoms: ['"secret"'] };
}
/** A `writer-fit` refusal over one named read, with `overrides` applied. */
function refusalDetail(
overrides: Partial = {},
): CfcRefusalDetail {
return {
gate: "writer-fit",
offendingAtoms: ['"secret"'],
inputs: [],
attribution: "complete",
reason: "CFC enforcement rejected commit",
...overrides,
};
}
/** Resolves a refused read to an input key by the document it names. */
function inputKeysByDocument(
owned: Readonly>,
): (read: CfcAddress) => readonly string[] {
return (read) => {
const keys = owned[read.id];
return keys === undefined ? [] : typeof keys === "string" ? [keys] : keys;
};
}
/**
* Seeds the same labelled document as {@link seedLabelledSecret} under a
* COMPUTED entity id, and answers the link an agent would pass naming it. A
* kinded id names a different entity from the `of:` id over the same hash, so
* the canonical entity-id seam refuses to reduce it and nothing that compares
* documents by hash can place a read of one.
*/
async function seedLabelledComputedSecret(
runtime: Runtime,
space: ReturnType,
cause: string,
): Promise {
const seed = runtime.edit();
const plainId = runtime.getCell(space, cause, undefined, seed)
.getAsNormalizedFullLink().id;
const computedId: `${string}:${string}` = `computed:${
plainId.slice("of:".length)
}`;
const sourceCell = runtime.getCellFromLink(
{ id: computedId, path: [], space, scope: "space" },
{ type: "object", properties: { secret: { type: "string" } } },
seed,
);
writeSeedEnvelopeDoc(seed, space);
seedStoredEnvelope(
seed,
{ space, scope: "space", id: computedId, path: [] },
{
value: { secret: "s3cr3t" },
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [{
path: ["secret"],
label: { confidentiality: ["secret"] },
}],
},
},
},
);
expect((await seed.commit().settled).ok).toBeDefined();
return createLLMFriendlyLink(sourceCell.getAsNormalizedFullLink(), space);
}
function createStrictEngine(pieces: PiecesController): CfHarnessEngine {
return new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-strict-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces }),
});
}
/**
* The ids of the stored documents reachable from `rootId` whose stored form
* (value and metadata alike) contains `needle`, in traversal order. Documents
* are walked breadth-first through every id-shaped string in their stored
* form, so a value held only behind links is attributed to the document that
* actually stores it.
*/
function docsHolding(
runtime: Runtime,
space: ReturnType,
rootId: string,
needle: string,
): string[] {
const holding: string[] = [];
const pending: `${string}:${string}`[] = [rootId as `${string}:${string}`];
const seen = new Set(pending);
const verify = runtime.edit();
try {
while (pending.length > 0) {
const id = pending.shift()!;
let stored: unknown;
try {
stored = verify.readOrThrow({ space, scope: "space", id, path: [] });
} catch {
continue;
}
const serialized = JSON.stringify(stored);
if (serialized === undefined) continue;
if (serialized.includes(needle)) holding.push(id);
for (
const [quoted] of serialized.matchAll(
/"((?:of|data|computed|raw|stream):[^"]+)"/g,
)
) {
const next = JSON.parse(quoted) as `${string}:${string}`;
if (!seen.has(next)) {
seen.add(next);
pending.push(next);
}
}
}
} finally {
verify.abort();
}
return holding;
}
class FakeSandboxRuntime implements SandboxRuntime {
describe(): SandboxRuntimeDescription {
return {
kind: "runsc-cfc",
defaultWorkingDirectory: this.defaultWorkingDirectory(),
cfc: { runtimeRequested: true, workspaceMountPath: "/workspace" },
};
}
resolvePath(path: string, cwd = this.defaultWorkingDirectory()): string {
return normalize(path.startsWith("/") ? path : `${cwd}/${path}`);
}
isPathWithinWorkspace(path: string): boolean {
return path === "/workspace" || path.startsWith("/workspace/");
}
isPathWithinAllowedRoots(path: string): boolean {
return this.isPathWithinWorkspace(path);
}
defaultWorkingDirectory(): string {
return "/workspace";
}
run(_request: SandboxCommandRequest): Promise {
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
}
runShell(request: SandboxShellRequest): Promise {
if (request.command.includes(CAPABILITY_PROBE_SENTINEL)) {
return Promise.resolve({
stdout: "bash\tpresent\t/bin/bash\tGNU bash, version 5.2.26(1)-release",
stderr: "",
exitCode: 0,
});
}
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
}
}
const STRANDED_ENTITY =
"of:fid1:Lu5lEvAZXeeCOI6SprXO9EG6gDFeZbLWP-MexaaM_qc" as const;
const STRANDED_RECORDS: readonly FabricInstantiationRecord[] = [{
sequence: 1,
identity: "keyless:zStranded",
symbol: "default",
cell: comparableEntityHash(STRANDED_ENTITY)!,
link: {
id: STRANDED_ENTITY,
space: signer.did(),
scope: "space",
path: [],
},
}];
describe("run-pattern", () => {
let storageManager: ReturnType;
let runtime: Runtime;
let pieces: PiecesController;
let recorder: FabricInstantiationRecorder;
beforeEach(async () => {
storageManager = StorageManager.emulate({ as: signer });
recorder = createFabricInstantiationRecorder();
runtime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager,
onPatternInstantiated: recorder.observe,
});
pieces = new PiecesController(
createSession({
identity: signer,
spaceDid: (await Identity.generate()).did(),
}),
runtime,
);
await pieces.synced();
});
afterEach(async () => {
await runtime?.dispose();
await storageManager?.close();
});
function createEngine(
instantiations: FabricPatternInstantiations = recorder.instantiations,
) {
return new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces, instantiations }),
});
}
/** Counts `runPersistent` calls, the single path that persists a piece. */
function spyOnRunPersistent(): { calls: number } {
const spy = { calls: 0 };
const original = pieces.runPersistent.bind(pieces);
pieces.runPersistent = ((
...args: Parameters<
PiecesController["runPersistent"]
>
) => {
spy.calls += 1;
return original(...args);
}) as PiecesController["runPersistent"];
return spy;
}
describe("runPatternTool", () => {
describe("captured query status", () => {
for (
const { name, capture, pending, hasError } of [
{
name: "a pending read",
capture: { pending: true, error: "" },
pending: true,
hasError: false,
},
{
name: "a settled read",
capture: { pending: false, error: "", hasError: true },
pending: false,
hasError: false,
},
{
name: "an error branch",
capture: {
pending: false,
error: "private diagnostic",
hasError: false,
},
pending: false,
hasError: true,
},
{
name: "an errorMessage branch",
capture: { errorMessage: "private diagnostic" },
pending: false,
hasError: true,
},
{
name: "a result without status fields",
capture: {},
pending: false,
hasError: false,
},
]
) {
it(`returns host-computed booleans for ${name} without a resultSchema`, async () => {
const result = await createEngine().invokeBuiltinTool("run_pattern", {
sourceText: [
"import { pattern } from 'commonfabric';",
"interface Output { count: number; pending?: boolean; error?: string; errorMessage?: string; hasError?: boolean; }",
`export default pattern, Output>(() => (${
JSON.stringify({ count: 0, ...capture })
}));`,
].join("\n"),
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.pending).toBe(pending);
expect(output.hasError).toBe(hasError);
expect(output.value).toBeUndefined();
expect(output).not.toHaveProperty("error");
expect(output).not.toHaveProperty("errorMessage");
expect(output.releaseDecision?.reasonCode).toBe(
"cfc_release_allowed",
);
});
}
for (const mode of ["enforce-strict", "observe"] as const) {
it(`fits data-dependent status before returning it in ${mode}`, async () => {
const { runtime, pieces, space, dispose } = await createFabric(mode);
try {
const source = await seedLabelledSecret(
runtime,
space,
"query-status",
);
const engine = createStrictEngine(pieces);
for (const requestValue of [false, true]) {
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Input { source: { secret: string }; }",
"interface Output { pending: boolean; error: string; }",
"export default pattern(({ source }) => ({",
" pending: computed(() => source.secret.length > 0),",
" error: computed(() => source.secret),",
"}));",
].join("\n"),
inputs: { source },
...(requestValue
? {
resultSchema: {
type: "object",
properties: {
pending: { type: "boolean" },
error: { type: "string" },
},
required: ["pending"],
},
}
: {}),
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.resultRef).toMatch(/^\/of:/);
expect(output.rawValue).toMatchObject({
pending: true,
error: "s3cr3t",
});
expect(output.pending).toBe(
mode === "observe" ? true : undefined,
);
expect(output.hasError).toBe(
mode === "observe" ? true : undefined,
);
expect(output.releaseDecision?.reasonCode).toBe(
mode === "observe"
? "cfc_release_observed"
: "cfc_release_withheld",
);
if (requestValue && mode === "enforce-strict") {
expect(output.policyRefusal?.gates).toEqual(["sink-ceiling"]);
expect(output.outputConcerns?.map(({ concern }) => concern))
.toEqual(["error-branch"]);
} else {
expect(output.policyRefusal).toBeUndefined();
expect(output.valueError).toBeUndefined();
}
expect(output).not.toHaveProperty("error");
}
} finally {
await dispose();
}
});
}
});
it("runs inline `sourceText` and returns a `resultRef` with the sanitized `value`", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.resultRef).toMatch(/^\/of:/);
expect(output.pieceId.length).toBeGreaterThan(0);
expect((output.value as { doubled: number }).doubled).toBe(42);
expect(output.linkedStringCount).toBe(0);
});
it("runs a pattern whose internal state feeds computed result fields, which strands nothing", async () => {
// The result is an object literal, so the piece's own root is the
// compiled pattern's: every instantiation this run reports is
// content-addressed, and the guard has nothing to claim.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { cell, computed, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { doubled: number; }",
"export default pattern(({ n }) => {",
" const factor = cell(2);",
" return { doubled: computed(() => n * factor.get()) };",
"});",
"",
].join("\n"),
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { doubled: number }).doubled).toBe(42);
const recorded = recorder.instantiations.since(0);
expect(recorded.length).toBeGreaterThan(0);
expect(recorded.some((one) => one.identity.startsWith("keyless:"))).toBe(
false,
);
});
it("answers over an instance whose result will not read back", async () => {
// The disclosure reads every pattern the run materialized, and one it
// cannot read says nothing about the others or about the run. A cell
// whose pull rejects is what a torn-down instance and a store that
// stopped answering both look like from inside the scan.
const pristineFromLink = runtime.getCellFromLink.bind(runtime);
const runtimeWithLink = runtime as unknown as {
getCellFromLink: typeof pristineFromLink;
};
let settled = false;
// Counted, because every assertion below also holds for a scan that
// read every instance cleanly: what says the failure path ran is that
// it ran, not that the run came back.
let refusedReads = 0;
// `withTx` answers a cell of its own, and the scan pulls THAT one, so
// the refusal has to follow the cell across it rather than sit on the
// one the link produced.
const refusingRead = (cell: T): T =>
new Proxy(cell as object, {
get: (target, property, receiver) => {
if (property === "pull") {
return () => {
refusedReads += 1;
return Promise.reject(
new Error("the store stopped answering"),
);
};
}
const member = Reflect.get(target, property, receiver);
if (property === "withTx" && typeof member === "function") {
return (...args: unknown[]) =>
refusingRead((member as (...a: unknown[]) => unknown).apply(
target,
args,
));
}
return member;
},
}) as T;
runtimeWithLink.getCellFromLink = ((
...args: Parameters
) => {
const cell = pristineFromLink(...args);
return settled ? refusingRead(cell) : cell;
}) as typeof pristineFromLink;
const syncedBefore = pieces.synced.bind(pieces);
(pieces as unknown as { synced: () => Promise }).synced =
async () => {
await syncedBefore();
settled = true;
};
const result = await createEngine().invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(refusedReads).toBeGreaterThan(0);
expect(output.status).toBe("ok");
expect((output.value as { doubled: number }).doubled).toBe(42);
expect(output.outputConcerns).toBeUndefined();
});
it("fails the run when the invocation materialized a session-only pointer", async () => {
// What the runner reports for a root no stored artifact names: a
// `keyless:` pointer stamped somewhere in the created piece's graph.
const stranded: FabricPatternInstantiations = {
sequence: () => 0,
since: () => STRANDED_RECORDS,
keylessSince: () => STRANDED_RECORDS,
};
const result = await createEngine(stranded).invokeBuiltinTool(
"run_pattern",
{ sourceText: DOUBLING_PATTERN_SOURCE, inputs: { n: 21 } },
);
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("detected");
expect(output.message).toContain("session-only pattern pointer");
expect(output.message).not.toContain("computed()");
expect(output.message).not.toContain("keyless:zStranded");
expect(output.rawCauseMessage).toContain("keyless:zStranded");
});
it("returns a result when the session reports no instantiations at all", async () => {
// A session built without an instantiation recorder answers no question
// about pattern pointers, and the run proceeds on the rest.
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces }),
});
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { doubled: number }).doubled).toBe(42);
});
it("returns a sealed string position as the address of that position rather than an opaque link", async () => {
// The schema declares `label` as an unconstrained string, which the
// sanitizer withholds as text. The result is fabric-backed, so the
// withheld position goes over as a whole `@link` object addressing it
// — resultRef plus the sealed path — which the outbound swap mints
// into a token in one piece, whitespace in property names and all. Foreign seals are a different thing and stay
// refused at input; only seals this sanitization minted are addressed.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { doubled: number; label: string; }",
"export default pattern(({ n }) => ({",
" doubled: computed(() => n * 2),",
" label: computed(() => `doubled ${n}`),",
"}));",
"",
].join("\n"),
inputs: { n: 21 },
resultSchema: {
type: "object",
properties: {
doubled: { type: "number" },
label: { type: "string" },
},
required: ["doubled", "label"],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
const value = output.value as { doubled: number; label: string };
expect(value.doubled).toBe(42);
expect(value.label).toEqual({ "@link": `${output.resultRef}/label` });
expect(output.linkedStringCount).toBe(1);
});
it("addresses a sealed property whose name carries whitespace without truncating the path", async () => {
// "full name" is a valid JSON key the free-text scanner would cut an
// address short at; the whole `@link` object keeps it in one piece.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { 'full name': string; }",
"export default pattern(({ n }) => ({",
" 'full name': computed(() => `holder ${n}`),",
"}));",
"",
].join("\n"),
inputs: { n: 3 },
resultSchema: {
type: "object",
properties: { "full name": { type: "string" } },
required: ["full name"],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as Record)["full name"]).toEqual({
"@link": `${output.resultRef}/full name`,
});
});
it("addresses an empty property with a trailing slash", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Input { n: number; }",
"interface Output { '': string; }",
"export default pattern(({ n }) => ({",
" '': computed(() => `anon ${n}`),",
"}));",
"",
].join("\n"),
inputs: { n: 5 },
resultSchema: {
type: "object",
properties: { "": { type: "string" } },
required: [""],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as Record)[""]).toEqual({
"@link": `${output.resultRef}/`,
});
});
it("returns a structured error for a plain-function pattern whose compiled argument schema is undefined, rather than throwing out of the run", async () => {
const engine = createEngine();
// A bare default function compiles to a pattern with no argument
// schema at all — not even the boolean schema `pattern()` synthesizes
// — so the undeclared-input check must not assume one exists. The
// source itself is not a runnable pattern; what this pins is that its
// failure comes back as a tool output the model can read and correct.
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
'export const NAME = "Probe";',
"export default function({}: {}) { return { ok: true }; }",
"",
].join("\n"),
});
const output = result.output as { status: string; message?: string };
expect(output.status).toBe("error");
expect((output.message ?? "").length).toBeGreaterThan(0);
});
it("keeps a computed number when the result carries framework keys the schema does not declare", async () => {
// Every pattern result carries the framework's own keys, and a schema
// describing only what the pattern computes declares none of them. The
// sanitizer seals a whole object over one unmodeled key, so without the
// framework keys being dropped first the number goes over as an opaque
// link along with everything else.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: NAMED_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.rawValue as Record)["$NAME"]).toBe(
"Doubler",
);
expect(output.value).toEqual({ doubled: 42 });
expect(output.linkedStringCount).toBe(0);
});
it("refuses a result the schema rejects for what it carries under a framework key", async () => {
// The raw result is what the schema measures. A branch that asks what
// `$NAME` holds gets the answer the pattern gave, so a result that does
// not match is refused — projecting the framework keys out before
// validating would hand the branch the rest of the result and accept it.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: NAMED_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: {
oneOf: [{
type: "object",
properties: {
doubled: { type: "number" },
$NAME: { type: "string", const: "Approved" },
},
required: ["doubled", "$NAME"],
}],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toBeDefined();
});
it("keeps a framework key the schema declares through a composed branch", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: NAMED_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: {
oneOf: [{
type: "object",
properties: {
doubled: { type: "number" },
$NAME: { type: "string", const: "Doubler" },
},
required: ["doubled", "$NAME"],
}],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ doubled: 42, $NAME: "Doubler" });
});
it("passes a whole-string LLM-friendly link input as a live cell reference", async () => {
const space = pieces.getSpace();
const seed = runtime.getCell(space, "run-pattern-seed", {
type: "number",
});
const { error } = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set(7);
});
expect(error).toBeUndefined();
await runtime.idle();
const seedRef = createLLMFriendlyLink(
seed.getAsNormalizedFullLink(),
space,
);
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: seedRef },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { doubled: number }).doubled).toBe(14);
});
it("stores a by-reference input as a link, so the created piece's argument follows the referenced cell", async () => {
const space = pieces.getSpace();
const seed = runtime.getCell(space, "run-pattern-wired-seed", {
type: "number",
});
const seeded = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set(7);
});
expect(seeded.error).toBeUndefined();
await runtime.idle();
const seedRef = createLLMFriendlyLink(
seed.getAsNormalizedFullLink(),
space,
);
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: seedRef },
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
// The created piece's argument reads back as the referenced cell's
// value, at the key the pattern declares.
const piece = await pieces.get(output.pieceId);
expect(await piece.input.get(["n"])).toBe(7);
// And it holds the reference rather than a copy of what the reference
// pointed at when the piece was created: a later write to the seed
// reaches the piece's result.
const rewritten = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set(10);
});
expect(rewritten.error).toBeUndefined();
await runtime.idle();
await pieces.synced();
expect(await piece.result.get(["doubled"])).toBe(20);
});
it("returns an error naming the failing computation when the result settles to nothing", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Output { boom: number; }",
"export default pattern, Output>(() => ({",
" boom: computed(() => { throw new Error('boom in lift'); }),",
"}));",
"",
].join("\n"),
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("failed while settling");
// The thrown text is a data channel: it stays in the artifact field the
// prompt loop strips from model context, never in the message.
expect(output.message).not.toContain("boom in lift");
expect(output.rawCauseMessage).toContain("boom in lift");
expect(output.pieceId).toBeDefined();
expect(result.runState.status).not.toBe("failed");
});
it("reports a commit the boundary refused as a refusal, not as a thrown computation", async () => {
// The two exits share a guard — a piece whose result never landed — and
// are told apart only by the error's name, so a refusal reported as a
// thrown computation would carry the wrong message and lose its
// structured refusals. A pattern this PR's ownership route cannot get
// refused any more (every store such a run writes is one the runtime
// owns), so the record is raised by name here rather than by provoking
// the boundary: what is under test is which exit the tool takes and
// what it puts in the model-facing message, both of which read the name
// and nothing else about how the record arrived.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Output { boom: number; }",
"export default pattern, Output>(() => ({",
" boom: computed(() => {",
" const refusal = new Error('refused: /of:doc at //x');",
" refusal.name = 'CfcCommitRefusalError';",
" throw refusal;",
" }),",
"}));",
"",
].join("\n"),
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("policy refused to commit");
expect(output.message).not.toContain("failed while settling");
// A refusal the boundary described only in prose carries no structured
// refusals, so the message is the opaque one and the detail stays in
// the artifact field the prompt loop strips from model context.
expect(output.message).not.toContain("of:doc");
expect(output.rawCauseMessage).toContain("of:doc");
expect(output.pieceId).toBeDefined();
// The decision the run's policy trace carries: the commit boundary
// refused, and it states no sink or ceiling of its own, since the
// runner refused at the pattern's own sink requests.
expect(output.releaseDecision).toEqual({
reasonCode: "cfc_commit_refused",
boundary: "commit",
});
});
it("names the clause and the gate when the refused commit carried structured refusals", async () => {
// The other arm of the same exit: a refusal the boundary described
// structurally gets the described message rather than the opaque one,
// and the atoms reach the model while the documents do not.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Output { boom: number; }",
"export default pattern, Output>(() => ({",
" boom: computed(() => {",
" const refusal = new Error('refused: /of:ledger at //total') as",
" Error & { refusals?: unknown[] };",
" refusal.name = 'CfcCommitRefusalError';",
" refusal.refusals = [{",
" gate: 'writer-fit',",
" offendingAtoms: ['\"expense-note\"'],",
" inputs: [],",
" attribution: 'none',",
" reason: 'writer-fit confidentiality misfit',",
" }];",
" throw refusal;",
" }),",
"}));",
"",
].join("\n"),
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("policy refused to commit");
expect(output.message).toContain("expense-note");
expect(output.policyRefusal).toBeDefined();
// Document identifiers stay out of the model-facing message either way.
expect(output.message).not.toContain("of:ledger");
expect(output.rawCauseMessage).toContain("of:ledger");
expect(output.releaseDecision?.reasonCode).toBe("cfc_commit_refused");
expect(output.releaseDecision?.refusal).toEqual(output.policyRefusal);
});
it("returns an error naming the policy refusal when the answer carries a label the model may not read", async () => {
// A strict flow-label runtime over a labelled source: the pattern
// derives from the secret, and its result carries the secret's label.
// The answer is an egress, so the tool measures what it would release
// and refuses, with the reason (which names the labels and documents
// involved) kept in the artifact channel rather than the model-facing
// message.
const strictStorage = StorageManager.emulate({ as: signer });
const strictRuntime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager: strictStorage,
cfcEnforcementMode: "enforce-strict",
cfcFlowLabels: "persist",
});
const strictPieces = new PiecesController(
createSession({
identity: signer,
spaceDid: (await Identity.generate()).did(),
}),
strictRuntime,
);
try {
await strictPieces.synced();
const space = strictPieces.getSpace();
const seed = strictRuntime.edit();
const sourceCell = strictRuntime.getCell(
space,
"labelled-source",
{
type: "object",
properties: {
secret: { type: "string" },
amount: { type: "number" },
},
},
seed,
);
const sourceId = sourceCell.getAsNormalizedFullLink().id;
writeSeedEnvelopeDoc(seed, space);
seedStoredEnvelope(seed, {
space,
scope: "space",
id: sourceId,
path: [],
}, {
value: { secret: "s3cr3t", amount: 2 },
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [{
path: ["secret"],
label: { confidentiality: ["secret"] },
}],
},
},
});
expect((await seed.commit().settled).ok).toBeDefined();
const sourceRef = createLLMFriendlyLink(
sourceCell.getAsNormalizedFullLink(),
space,
);
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-strict-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces: strictPieces }),
});
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; amount: number; }",
"interface Input { source: Reactive; }",
"interface Output { copied: string; }",
"export default pattern(({ source }) => ({",
" copied: computed(() => `${source.secret}!`),",
"}));",
"",
].join("\n"),
inputs: { source: sourceRef },
resultSchema: {
type: "object",
properties: { copied: { type: "string" } },
required: ["copied"],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toContain("policy refused to release");
expect(output.valueError).toContain("withheld here");
// The refusal reason is a data channel: it stays in the artifact
// field the prompt loop strips from model context, never in the
// model-facing text.
expect(output.valueError).not.toContain("exceeds ceiling");
expect(output.rawCauseMessage).toContain(
'confidentiality exceeds ceiling for run_pattern: "secret"',
);
expect(output.pieceId).toBeDefined();
} finally {
await strictRuntime.dispose();
await strictStorage.close();
}
});
it("names the input key that carried the refused label, in the message and in `policyRefusal`", async () => {
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"refusal-names-input",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toContain("policy refused to release");
expect(output.valueError).toContain('input "source"');
expect(output.valueError).toContain("without it releases its values");
expect(output.valueError).not.toContain('"amount"');
expect(output.policyRefusal).toEqual({
gates: ["sink-ceiling"],
sinks: ["run_pattern"],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "complete",
});
} finally {
await dispose();
}
});
it("names a plain-JSON input by its key when the pattern's own argument schema labels it", async () => {
// The label sits on the piece's argument document, under the input's
// key, and no cell the caller addressed carries it: the refused read
// names the argument document, and its first path segment is the key.
const { pieces, dispose } = await createStrictFabric();
try {
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { type Confidential, pattern } from 'commonfabric';",
"interface Input {",
" amount: number;",
" note: Confidential;",
"}",
"interface Output { note: string; }",
"export default pattern(({ note }) => ({ note }));",
"",
].join("\n"),
inputs: { amount: 2, note: "s3cr3t" },
resultSchema: {
type: "object",
properties: { note: { type: "string" } },
required: ["note"],
},
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toContain('input "note"');
expect(output.valueError).not.toContain('"amount"');
expect(output.policyRefusal).toEqual({
gates: ["sink-ceiling"],
sinks: ["run_pattern"],
offendingAtoms: ['"secret"'],
inputKeys: ["note"],
attribution: "complete",
});
} finally {
await dispose();
}
});
it("lands a result when the run is repeated without the input its refusal named", async () => {
// The replan an actionable refusal is for: the first run is refused
// and names one of its own input keys as the whole remedy, and the
// second run — the same pattern, that key dropped — commits.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"refusal-replan",
);
const engine = createStrictEngine(pieces);
const inputs: Record = {
amount: 2,
source: sourceRef,
};
const refused = (await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs,
resultSchema: TOTAL_RESULT_SCHEMA,
})).output as RunPatternToolSuccessOutput;
expect(refused.status).toBe("ok");
expect(refused.value).toBeUndefined();
expect(refused.policyRefusal?.attribution).toBe("complete");
const named = refused.policyRefusal?.inputKeys ?? [];
expect(named).toEqual(["source"]);
// Drop exactly the keys the refusal named, changing nothing else.
const replanned: Record = { ...inputs };
for (const key of named) delete replanned[key];
const retried = (await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: replanned,
resultSchema: TOTAL_RESULT_SCHEMA,
})).output as RunPatternToolSuccessOutput;
expect(retried.status).toBe("ok");
expect((retried.value as { total: number }).total).toBe(2);
} finally {
await dispose();
}
});
it("leaves `policyRefusal` unchanged under the bare-fabric-identifier scrub", async () => {
// `policyRefusal` reaches the model as it stands — the prompt loop
// scrubs `message` and `valueError` and nothing else — so it must
// already hold no document id, space, or path into a document.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"refusal-carries-no-identifier",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
const encoded = JSON.stringify(output.policyRefusal);
expect(scrubBareFabricIdentifiers(encoded)).toBe(encoded);
expect(encoded).not.toContain(space);
} finally {
await dispose();
}
});
it("refuses an answer whose label sits on a field of the document it passes through", async () => {
// Resolving a value reads the links it holds; a label on a field is
// consumed where that field is read. This answer is the labelled
// document itself, passed through by reference, and its label sits one
// level down — so the measurement has to walk what it releases.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-field-label",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input { source: Reactive; }",
"interface Output { copy: Reactive; }",
"export default pattern(({ source }) => ({",
" copy: source,",
"}));",
"",
].join("\n"),
inputs: { source: sourceRef },
resultSchema: {
type: "object",
properties: {
copy: {
type: "object",
properties: { secret: { type: "string" } },
},
},
required: ["copy"],
},
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toContain("policy refused to release");
expect(output.policyRefusal?.offendingAtoms).toEqual(['"secret"']);
} finally {
await dispose();
}
});
it("names both keys when one labelled document is supplied under two", async () => {
// The remedy has to name every alias: dropping one of them leaves the
// other handing the same document to the pattern. With the argument
// document gone, one read of the shared document is all there is to
// trace, and both aliases resolve from the addresses the caller's own
// links reached.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-aliased-input",
);
const result = await createStrictEngine(
piecesWithUnresolvableArgument(pieces),
).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input {",
" amount: number;",
" source?: Reactive;",
" alsoSource?: Reactive;",
"}",
"interface Output { total: number; }",
"export default pattern(({ amount, source }) => ({",
" total: computed(() => {",
" const secret = source?.secret;",
" return amount + (typeof secret === 'string'",
" ? secret.length",
" : 0);",
" }),",
"}));",
"",
].join("\n"),
inputs: { amount: 2, source: sourceRef, alsoSource: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal?.inputKeys).toEqual(
expect.arrayContaining(["source", "alsoSource"]),
);
} finally {
await dispose();
}
});
it("refuses an answer whose label is two links down", async () => {
// The leaf the answer carries sits inside a nested object, behind a
// computed cell, behind the result document. Resolving the result is
// not the same as reading what it resolves to, so this is what says
// the measurement reaches the whole answer rather than its first hop.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-nested-label",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input { source: Reactive; }",
"interface Output { wrapper: { note: string } }",
"export default pattern(({ source }) => ({",
" wrapper: { note: computed(() => `${source.secret}!`) },",
"}));",
"",
].join("\n"),
inputs: { source: sourceRef },
resultSchema: {
type: "object",
properties: {
wrapper: {
type: "object",
properties: { note: { type: "string" } },
},
},
required: ["wrapper"],
},
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.valueError).toContain("policy refused to release");
expect(output.policyRefusal?.offendingAtoms).toEqual(['"secret"']);
} finally {
await dispose();
}
});
it("answers at the observe posture, where no gate rejects", async () => {
// The enforcement ladder decides whether a recorded reason rejects, and
// at `observe` none of them do. The answer carries the label either
// way; what changes is that nothing refuses over it.
const { runtime, pieces, space, dispose } = await createFabric("observe");
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-observe",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { total: number }).total).toBe(8);
// Nothing rejects here, and the measurement still ran: what raising
// the rung would refuse is recorded for whoever is staging it.
expect(output.releaseObservation?.offendingAtoms).toEqual(['"secret"']);
expect(output.releaseObservation?.inputKeys).toEqual(["source"]);
// The measurement said as a decision: it did not reject, and it
// carries the same attribution the observation does.
expect(output.releaseDecision).toEqual({
reasonCode: "cfc_release_observed",
boundary: "release",
sink: "run_pattern",
ceiling: [],
refusal: output.releaseObservation,
});
} finally {
await dispose();
}
});
it("answers when a labelled input reaches the argument document and not the result", async () => {
// What the answer carries is what releasing it resolves, not what the
// caller handed over. This pattern names the labelled input and never
// reads it, so the total it returns derives from the plain one alone.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-unread-input",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input { amount: number; source?: Reactive; }",
"interface Output { total: number; }",
"export default pattern(({ amount }) => ({",
" total: computed(() => amount + 1),",
"}));",
"",
].join("\n"),
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { total: number }).total).toBe(3);
// The boundary ran and admitted the flow, which the trace records as
// readily as a refusal: an operator reading only refusals cannot tell
// a gate that passed from one that never ran.
expect(output.releaseDecision).toEqual({
reasonCode: "cfc_release_allowed",
boundary: "release",
sink: "run_pattern",
ceiling: [],
});
} finally {
await dispose();
}
});
it("names the input from the caller's own addresses when the piece's argument cell will not resolve", async () => {
// The argument document is one of the two routes from a released clause
// back to an input key. With it gone the report stands on the addresses
// the caller's own links resolved to, which account for this clause on
// their own.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"refusal-without-argument-route",
);
const result = await createStrictEngine(
piecesWithUnresolvableArgument(pieces),
).invokeBuiltinTool("run_pattern", {
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal?.inputKeys).toEqual(["source"]);
expect(output.policyRefusal?.attribution).toBe("complete");
expect(output.valueError).toContain("without it releases its values");
} finally {
await dispose();
}
});
it("counts a refused read of a computed document, whose kinded id no input address can be compared against", async () => {
// A kinded entity id does not reduce to a hash, so neither route from a
// refused read to an input key can place it. The report counts it
// instead of guessing, and drops to `partial`.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledComputedSecret(
runtime,
space,
"refusal-over-computed-source",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal?.unattributedInputCount).toBe(1);
} finally {
await dispose();
}
});
it("silently omits refused status when no `resultSchema` asks for values", async () => {
// The host's status fit leaves the existing reference and concerns intact.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const accountRef = await seedLabelledAccount(
runtime,
space,
"release-reference-only",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: SPENDING_PATTERN_SOURCE,
inputs: { account: accountRef },
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.resultRef).toMatch(/^\/of:/);
expect(output.value).toBeUndefined();
expect(output.valueError).toBeUndefined();
expect(output.policyRefusal).toBeUndefined();
expect(output.releaseObservation).toBeUndefined();
expect(output.releaseDecision?.reasonCode).toBe("cfc_release_withheld");
expect(output.pending).toBeUndefined();
expect(output.hasError).toBeUndefined();
expect(output.outputConcerns?.map(({ concern }) => concern)).toEqual([
"error-branch",
"pending",
]);
expect((output.rawValue as { totalSpending: number }).totalSpending)
.toBe(145);
} finally {
await dispose();
}
});
it("withholds the values the ceiling refuses and still returns the result reference", async () => {
// A ceiling refusal is final for this read, so a pending output must
// not ask the caller to repeat it. Observed failures remain visible.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const accountRef = await seedLabelledAccount(
runtime,
space,
"release-values-withheld",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: SPENDING_PATTERN_SOURCE,
inputs: { account: accountRef },
resultSchema: TOTAL_SPENDING_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.resultRef).toMatch(/^\/of:/);
expect(output.value).toBeUndefined();
expect(output.linkedStringCount).toBeUndefined();
expect(output.valueError).toContain(
"policy refused to release its values",
);
expect(output.valueError).toContain("resultRef still names the result");
expect(output.valueError).not.toContain("exceeds ceiling");
expect(output.rawCauseMessage).toContain(
'confidentiality exceeds ceiling for run_pattern: "finance"',
);
expect(output.policyRefusal?.gates).toEqual(["sink-ceiling"]);
expect(output.policyRefusal?.offendingAtoms).toEqual(['"finance"']);
expect(output.releaseDecision).toEqual({
reasonCode: "cfc_release_withheld",
boundary: "release",
sink: "run_pattern",
ceiling: [],
refusal: output.policyRefusal,
});
expect(output.releaseDecision?.refusal?.inputKeys).toEqual(["account"]);
expect(output.rawValue).toMatchObject({
totalSpending: 145,
pending: true,
});
expect(output.outputConcerns?.map(({ concern }) => concern)).toEqual([
"error-branch",
]);
} finally {
await dispose();
}
});
it("keeps the withheld decision on a run that fails to settle after the fit", async () => {
// The fit runs before the result is read for its value, so a run that
// exits at a settle failure is a run the boundary already decided at.
// The decision is attached to that exit too: dropping it there would
// lose the trace's record of exactly the refusals a failing run
// provoked.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-decision-settle-failure",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { computed, pattern, Reactive } from 'commonfabric';",
"interface Source { secret: string; }",
"interface Input { amount: number; source: Reactive; }",
"interface Output { total: number; boom: number; }",
"export default pattern(({ amount, source }) => ({",
" total: computed(() => amount + (source.secret ?? '').length),",
" boom: computed(() => { throw new Error('boom in lift'); }),",
"}));",
"",
].join("\n"),
inputs: { amount: 2, source: sourceRef },
resultSchema: {
type: "object",
properties: {
total: { type: "number" },
boom: { type: "number" },
},
required: ["total", "boom"],
},
},
);
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("failed while settling");
expect(output.releaseDecision?.reasonCode).toBe("cfc_release_withheld");
expect(output.releaseDecision?.refusal?.offendingAtoms).toEqual([
'"secret"',
]);
expect(output.releaseDecision?.refusal?.inputKeys).toEqual(["source"]);
} finally {
await dispose();
}
});
it("names the input whose link reaches the labeled document through a link it holds", async () => {
// The caller's address names the holder document; the label lives on
// the document the holder's `account` field links to, which the
// caller's address never names. The refused read is of that second
// document, and what ties it back to `account` is the dereference the
// attribution read performed to get there. The refusal names `account`
// as the whole remedy rather than counting the document as one no
// input accounts for.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const accountRef = await seedLabelledAccount(
runtime,
space,
"release-field-addressed-input",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: SPENDING_PATTERN_SOURCE,
inputs: { account: accountRef },
resultSchema: TOTAL_SPENDING_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal).toEqual({
gates: ["sink-ceiling"],
sinks: ["run_pattern"],
offendingAtoms: ['"finance"'],
inputKeys: ["account"],
attribution: "complete",
});
expect(output.valueError).toContain('input "account"');
expect(output.valueError).toContain("without it releases its values");
} finally {
await dispose();
}
});
it("names the input when the link it follows sits above the input's address", async () => {
// The holder's root is itself a link, and the caller's address names
// the `account` field beneath it: the dereference the attribution read
// records starts above the address, so the address continues on the
// far side of the link, into the account document's own `account`
// field, where the label sits.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const refs = await seedAccountHolder(
runtime,
space,
"release-root-linked-holder",
"root-link",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: SPENDING_PATTERN_SOURCE,
inputs: { account: refs.account },
resultSchema: TOTAL_SPENDING_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal?.inputKeys).toEqual(["account"]);
expect(output.policyRefusal?.attribution).toBe("complete");
} finally {
await dispose();
}
});
it("does not name an input for a dereference the holder made somewhere else", async () => {
// The holder links to the labeled account under `account` and to an
// unlabeled document under `notes`, and both are inputs. The `notes`
// dereference is recorded against the same holder document, but it
// starts beside the `account` address rather than at, below, or above
// it, so it leads the `account` input nowhere — and `notes` is not
// named, since nothing it reaches carries the label.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const refs = await seedAccountHolder(
runtime,
space,
"release-two-field-holder",
"two-fields",
);
const result = await createStrictEngine(pieces).invokeBuiltinTool(
"run_pattern",
{
sourceText: [
"import { computed, pattern } from 'commonfabric';",
"interface Transaction { amount: number; }",
"interface Account { balance: number; transactions: Transaction[]; }",
"interface Notes { text: string; }",
"interface Input { account: Account; notes: Notes; }",
"interface Output { totalSpending: number; noteLength: number; }",
"export default pattern(({ account, notes }) => ({",
" totalSpending: computed(() => account.transactions.reduce(",
" (sum, t) => sum + (t.amount < 0 ? -t.amount : 0),",
" 0,",
" )),",
" noteLength: computed(() => notes.text.length),",
"}));",
"",
].join("\n"),
inputs: { account: refs.account, notes: refs.notes },
resultSchema: TOTAL_SPENDING_RESULT_SCHEMA,
},
);
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.policyRefusal?.inputKeys).toEqual(["account"]);
expect(output.policyRefusal?.attribution).toBe("complete");
expect(output.valueError).not.toContain('"notes"');
} finally {
await dispose();
}
});
it("commits no unlabelled copy when a pattern-body map reads labelled values inline", async () => {
// A pattern-body `.map()` runs as the built-in map, whose coordinator
// reads the list raw. With the labelled values inline in that list, the
// read carries their confidentiality into the stores the map writes —
// its result container and one result document per element. Those are
// stores the runtime owns, so each declares that confidentiality
// (§8.12.5 route 2) rather than refusing the write, and the run
// commits. Nothing copies the text even so: each element result is a
// link into the labelled source path, so the labelled text exists at
// rest only in the seeded source document.
//
// What the labels stop is the ANSWER. The model's context is outside
// every space, so the answer ceiling withholds the value the tool would
// return, and the handle it returns instead names a result that stays
// in the space.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const seed = runtime.edit();
const sourceCell = runtime.getCell(
space,
"body-map-inline-labelled",
{
type: "object",
properties: { expenses: { type: "array", items: EXPENSE_SCHEMA } },
},
seed,
);
const sourceId = sourceCell.getAsNormalizedFullLink().id;
writeSeedEnvelopeDoc(seed, space);
seedStoredEnvelope(seed, {
space,
scope: "space",
id: sourceId,
path: [],
}, {
value: {
expenses: [
{ description: "alpha-secret", amount: 1 },
{ description: "beta-secret", amount: 2 },
],
},
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [
{
path: ["expenses", "0", "description"],
label: { confidentiality: ["expense-note"] },
},
{
path: ["expenses", "1", "description"],
label: { confidentiality: ["expense-note"] },
},
],
},
},
});
expect((await seed.commit().settled).ok).toBeDefined();
const engine = createStrictEngine(pieces);
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { pattern, Reactive } from 'commonfabric';",
"interface Expense { description: string; amount: number; }",
"interface Source { expenses: Expense[]; }",
"interface Input { source: Reactive; }",
"interface Output { notes: string[]; }",
"export default pattern(({ source }) => ({",
" notes: source.expenses.map((e) => e.description),",
"}));",
"",
].join("\n"),
inputs: {
source: createLLMFriendlyLink(
sourceCell.getAsNormalizedFullLink(),
space,
),
},
});
const output = result.output as RunPatternToolSuccessOutput;
// The run commits and returns its handle; the answer ceiling gates the
// VALUE, which stays in the space. The sibling below reaches the same
// outcome from the linked shape, so inline values and linked ones now
// differ only in which document holds the text.
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.policyRefusal).toBeUndefined();
await runtime.idle();
await pieces.synced();
const piece = await pieces.get(output.pieceId);
expect(await piece.result.get(["notes"])).toEqual([
"alpha-secret",
"beta-secret",
]);
expect(
docsHolding(runtime, space, `of:${output.pieceId}`, "alpha-secret"),
).toEqual([sourceId]);
} finally {
await dispose();
}
});
it("resolves a pattern-body map over labelled documents to links, leaving the labelled text only in its sources", async () => {
// With each labelled value in its own document behind a link, the
// built-in map's coordinator reads only links, and each element result
// is itself a link into the labelled source path. The result reads
// back as the values, but no document at rest holds a copy at all: a
// reader reaching the text does so through the source's own label map.
// The call asks for no values, so what leaves the fabric for the model
// is the reference alone; the host's status fit withholds its booleans.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const expenseIds: string[] = [];
const elementLinks: {
"/": {
"link@1": {
id: string;
path: string[];
scope: string;
space: string;
};
};
}[] = [];
for (
const [i, description] of ["alpha-secret", "beta-secret"].entries()
) {
const seed = runtime.edit();
const cell = runtime.getCell(
space,
`body-map-linked-expense-${i}`,
EXPENSE_SCHEMA,
seed,
);
const id = cell.getAsNormalizedFullLink().id;
writeSeedEnvelopeDoc(seed, space);
seedStoredEnvelope(seed, { space, scope: "space", id, path: [] }, {
value: { description, amount: i + 1 },
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: {
version: 1,
entries: [{
path: ["description"],
label: { confidentiality: ["expense-note"] },
}],
},
},
});
expect((await seed.commit().settled).ok).toBeDefined();
expenseIds.push(id);
elementLinks.push({
"/": { "link@1": { id, path: [], scope: "space", space } },
});
}
const listSeed = runtime.edit();
const listCell = runtime.getCell(
space,
"body-map-linked-expense-list",
{ type: "array", items: EXPENSE_SCHEMA },
listSeed,
);
listSeed.writeOrThrow(
{
space,
scope: "space",
id: listCell.getAsNormalizedFullLink().id,
path: [],
},
{ value: elementLinks },
);
expect((await listSeed.commit().settled).ok).toBeDefined();
const engine = createStrictEngine(pieces);
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { pattern } from 'commonfabric';",
"interface Expense { description: string; amount: number; }",
"interface Input { expenses: Expense[]; }",
"interface Output { notes: string[]; }",
"export default pattern(({ expenses }) => ({",
" notes: expenses.map((e) => e.description),",
"}));",
"",
].join("\n"),
inputs: {
expenses: createLLMFriendlyLink(
listCell.getAsNormalizedFullLink(),
space,
),
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toBeUndefined();
expect(output.policyRefusal).toBeUndefined();
await runtime.idle();
await pieces.synced();
const piece = await pieces.get(output.pieceId);
expect(await piece.result.get(["notes"])).toEqual([
"alpha-secret",
"beta-secret",
]);
expect(
docsHolding(runtime, space, `of:${output.pieceId}`, "alpha-secret"),
).toEqual([expenseIds[0]]);
expect(
docsHolding(runtime, space, `of:${output.pieceId}`, "beta-secret"),
).toEqual([expenseIds[1]]);
} finally {
await dispose();
}
});
it("returns a `compile-error` output carrying the raw diagnostics without failing the run", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: "this is not a pattern ((",
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("compile-error");
expect(output).not.toHaveProperty("pending");
expect(output).not.toHaveProperty("hasError");
expect(output.message.length).toBeGreaterThan(0);
expect(result.runState.status).not.toBe("failed");
});
it("returns an error when `sourceText` is missing", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("requires sourceText");
});
it("returns an error for a `sourceText` over the size cap without creating a piece", async () => {
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: "x".repeat(RUN_PATTERN_MAX_SOURCE_TEXT_BYTES + 1),
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("256 KiB limit");
expect(spy.calls).toBe(0);
});
it("returns an error for a link input targeting another space without creating a piece", async () => {
const spy = spyOnRunPersistent();
const engine = createEngine();
const foreignRef = `/@did:key:z6MkforeignSpaceForRunPatternTest/of:fid1:${
"A".repeat(43)
}/`;
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: foreignRef },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("targets another space");
expect(spy.calls).toBe(0);
});
it("runs over an admitted foreign input while retaining its CFC labels", async () => {
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const foreign = (createSession({
identity: signer,
spaceDid: (await Identity.generate()).did(),
})).space;
const tx = runtime.edit();
const source = runtime.getCell(
foreign,
"foreign-labeled-input",
undefined,
tx,
);
const link = source.getAsNormalizedFullLink();
const label = {
confidentiality: ["secret"],
integrity: ["topic-source"],
};
writeSeedEnvelopeDoc(tx, foreign);
seedStoredEnvelope(tx, { ...link, path: [] }, {
value: { secret: "s3cr3t" },
cfc: {
version: 1,
schemaHash: SEED_ENVELOPE_SCHEMA_HASH,
labelMap: { version: 1, entries: [{ path: ["secret"], label }] },
},
});
expect((await tx.commit().settled).ok).toBeDefined();
const ref = createLLMFriendlyLink(link, space);
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `foreign-labels-${crypto.randomUUID()}`,
inputCells: [{ name: "source", ref }],
fabricSessionFactory: () =>
Promise.resolve({
pieces,
foreignSpaces: { [foreign]: "https://foreign.example/" },
}),
});
const [attached] = await engine.establishInputCells();
const admittedRef =
resolveHandleToken(engine.handleTable!, attached.token)!.ref;
expect(admittedRef).toContain(foreign);
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: [
"import { pattern } from 'commonfabric';",
"interface Input { source: { secret: string }; }",
"interface Output { secret: string; }",
"export default pattern(({ source }) => ({",
" secret: source.secret,",
"}));",
].join("\n"),
inputs: { source: admittedRef },
resultSchema: {
type: "object",
properties: { secret: { type: "string" } },
required: ["secret"],
},
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.rawValue).toEqual({ secret: "s3cr3t" });
expect(output.value).toBeUndefined();
expect(output.valueError).toContain("policy refused to release");
const forwarded = runtime.getCellFromLink(
parseLLMFriendlyLink(output.resultRef, space),
).key("secret");
expect(forwarded.get()).toBe("s3cr3t");
// The result holds a reference to the foreign value and no copy of
// its label: the label is read where the value is stored.
expect(cfcLabelViewForCellFailClosed(forwarded)?.entries)
.toContainEqual({
path: [],
label,
});
const foreignSecret = runtime.getCellFromLink(link).key("secret");
expect(foreignSecret.get()).toBe("s3cr3t");
expect(cfcLabelViewForCellFailClosed(foreignSecret)?.entries)
.toContainEqual({
path: [],
label,
});
} finally {
await dispose();
}
});
it("returns an error for a live-cell input whose value does not match the argument schema, creating no piece", async () => {
const space = pieces.getSpace();
const seed = runtime.getCell(
space,
"run-pattern-shape-mismatch",
{
type: "object",
properties: { foo: { type: "number" } },
} as const,
);
const { error } = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set({ foo: 1 });
});
expect(error).toBeUndefined();
await runtime.idle();
const seedRef = createLLMFriendlyLink(
seed.getAsNormalizedFullLink(),
space,
);
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: seedRef },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "n"');
expect(output.message).toContain("argument schema");
expect(output).not.toHaveProperty("pending");
expect(output).not.toHaveProperty("hasError");
expect(spy.calls).toBe(0);
});
it("returns an error naming an input the pattern's argument schema does not declare, creating no piece", async () => {
// A misnamed input is the mismatch a shape check cannot see: the
// pattern runs with its argument undefined, computes nothing from it,
// and renders a complete page holding no values. Refusing before the
// piece exists turns that into something the model can correct.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { count: 21 },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('"count"');
expect(output.message).toContain('it declares "n"');
expect(spy.calls).toBe(0);
});
it("returns an error naming an input that is a sealed opaque link, creating no piece", async () => {
// A seal is a redaction, so storing it would leave a dead literal where
// the pattern declared a live reference: the piece would run, compute
// nothing from that argument, and report `ok`.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: SEALED_OPAQUE_LINK },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "n"');
expect(output.message).toContain("sealed opaque link");
expect(output.message).toContain("redaction, not an address");
expect(output.message).toContain("cfh:a:");
expect(spy.calls).toBe(0);
});
it("returns an error naming the path of a sealed opaque link nested inside an object input, creating no piece", async () => {
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OVERVIEW_PATTERN_SOURCE,
inputs: { overview: { title: SEALED_OPAQUE_LINK } },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "overview"');
expect(output.message).toContain('at "overview.title"');
expect(output.message).toContain("redaction, not an address");
expect(spy.calls).toBe(0);
});
it("returns an error naming the path of a bare seal target string nested inside an object input, creating no piece", async () => {
// The seal is the `opaque:` target, not the object it arrived in. Lifted
// out of that object it reaches any position the argument schema admits
// a string — here `overview.title`, which the pattern declares as one —
// and it is the same redaction there.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OVERVIEW_PATTERN_SOURCE,
inputs: { overview: { title: SEALED_OPAQUE_TARGET } },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "overview"');
expect(output.message).toContain('at "overview.title"');
expect(output.message).toContain("redaction, not an address");
expect(spy.calls).toBe(0);
});
it("returns an error for an `@link` object carrying a seal target alongside another key, creating no piece", async () => {
// A model that copied the seal out along with a sibling key it wrote
// itself still passed back the redaction; the wrapper's shape is not
// what makes it one.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OVERVIEW_PATTERN_SOURCE,
inputs: {
overview: { "@link": SEALED_OPAQUE_TARGET, note: "the overview" },
},
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
// The object itself is the seal, so the refusal names the input rather
// than a path to the `@link` key inside it.
expect(output.message).toContain(
'input "overview" is a sealed opaque link',
);
expect(output.message).toContain("redaction, not an address");
expect(spy.calls).toBe(0);
});
it("runs an input the argument schema admits through an `additionalProperties` schema rather than by name", async () => {
// An `additionalProperties` schema is an index signature: it permits the
// undeclared key and says what it may hold. Refusing such a key would
// reject an input the pattern's own schema accepts.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPEN_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21, offset: 5 },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ doubled: 42 });
});
it("returns an error for an undeclared input whose value the `additionalProperties` schema rejects, creating no piece", async () => {
// The test above is this one's control: the same pattern accepts the
// same undeclared key when its value is what `additionalProperties`
// describes. Admitting an undeclared key is a statement about what it
// may hold, so the value is still measured — against that schema rather
// than against a declared property's.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPEN_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21, offset: "five" },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "offset"');
expect(output.message).toContain("argument schema");
expect(spy.calls).toBe(0);
});
it("returns an error for an undeclared live-cell input whose value the `additionalProperties` schema rejects, creating no piece", async () => {
const space = pieces.getSpace();
const seed = runtime.getCell(
space,
"run-pattern-open-seed",
{
type: "string",
} as const,
);
const { error } = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set("five");
});
expect(error).toBeUndefined();
await runtime.idle();
const seedRef = createLLMFriendlyLink(
seed.getAsNormalizedFullLink(),
space,
);
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPEN_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21, offset: seedRef },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "offset"');
expect(output.message).toContain("argument schema");
expect(spy.calls).toBe(0);
});
it("returns an error naming the position of a sealed opaque link inside an array input, creating no piece", async () => {
// A model composes an input out of what an earlier result handed it, and
// an array is one of the shapes it composes. The seal is the same
// redaction at index 1 of a list as it is at the top of an input.
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: ROW_COUNT_PATTERN_SOURCE,
inputs: { rows: ["first", SEALED_OPAQUE_TARGET] },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "rows"');
expect(output.message).toContain('at "rows[1]"');
expect(output.message).toContain("redaction, not an address");
expect(spy.calls).toBe(0);
});
it("runs an array input whose entries carry no sealed opaque link", async () => {
// The control for the case above: the same pattern and the same shape of
// input, so the refusal there is the seal rather than the array.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: ROW_COUNT_PATTERN_SOURCE,
inputs: { rows: ["first", "second"] },
resultSchema: ROW_COUNT_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ rowCount: 2 });
});
it("keeps a link-shaped string that does not parse as a link as the input's plain value", async () => {
// A string can wear the shape of an LLM-friendly link without being one:
// this one carries a piece handle too short to be a fabric identifier. It
// is the input's value, not a reference, so the pattern computes over the
// string itself.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: LABEL_PATTERN_SOURCE,
inputs: { label: "/of:fid1:short/x" },
resultSchema: LABEL_LENGTH_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ labelLength: "/of:fid1:short/x".length });
const piece = await pieces.get(output.pieceId);
expect(await piece.input.get(["label"])).toBe("/of:fid1:short/x");
});
it("runs an undeclared input the argument schema admits without saying what it may hold", async () => {
// `additionalProperties: true` states that an undeclared key is
// permitted and nothing about its value, so there is no schema to
// measure it against and no shape it can fail.
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: ANY_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21, offset: "five" },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ doubled: 42 });
const piece = await pieces.get(output.pieceId);
expect(await piece.input.get(["offset"])).toBe("five");
});
it("runs an undeclared live-cell input the argument schema admits without saying what it may hold", async () => {
const space = pieces.getSpace();
const seed = runtime.getCell(
space,
"run-pattern-any-seed",
{
type: "string",
} as const,
);
const { error } = await runtime.editWithRetry((tx) => {
seed.withTx(tx).set("five");
});
expect(error).toBeUndefined();
await runtime.idle();
const seedRef = createLLMFriendlyLink(
seed.getAsNormalizedFullLink(),
space,
);
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: ANY_DOUBLING_PATTERN_SOURCE,
inputs: { n: 21, offset: seedRef },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ doubled: 42 });
const piece = await pieces.get(output.pieceId);
expect(await piece.input.get(["offset"])).toBe("five");
});
it("returns an error for a plain-JSON input whose value does not match the argument schema, creating no piece", async () => {
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: "twenty-one" },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain('input "n"');
expect(output.message).toContain("argument schema");
expect(spy.calls).toBe(0);
});
it("runs a plain-JSON object input that matches its declared argument schema", async () => {
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OVERVIEW_PATTERN_SOURCE,
inputs: { overview: { title: "Weekly" } },
resultSchema: TITLE_LENGTH_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect(output.value).toEqual({ titleLength: 6 });
});
it("returns a `cancelled` output when the signal aborts while the settle race is underway", async () => {
// The abort lands after the race over the settle barrier has been
// entered — a microtask later, not before — so the live race path
// itself answers, rather than the pre-aborted short-circuit.
const controller = new AbortController();
const runtimeWithSettled = runtime as unknown as {
settled: () => Promise;
};
runtimeWithSettled.settled = () => {
queueMicrotask(() => controller.abort());
return new Promise(() => {});
};
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
}, { signal: controller.signal });
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("cancelled");
expect(output).not.toHaveProperty("pending");
expect(output).not.toHaveProperty("hasError");
});
it("serializes a plain value and answers undefined for one JSON cannot carry", () => {
expect(asSerializableValue({ n: 1 })).toEqual({ n: 1 });
expect(asSerializableValue(7n)).toBeUndefined();
});
it("returns a `cancelled` output and stops the piece when the signal aborts during the settle barrier", async () => {
const controller = new AbortController();
// Abort exactly when the tool reaches its post-create barrier, and
// hold the barrier open so only the signal can win the race.
const runtimeWithSettled = runtime as unknown as {
settled: () => Promise;
};
runtimeWithSettled.settled = () => {
controller.abort();
return new Promise(() => {});
};
const stopped: unknown[] = [];
const runner = runtime.runner as unknown as {
stop: (cell: unknown) => unknown;
};
const originalStop = runner.stop.bind(runtime.runner);
runner.stop = (cell) => {
stopped.push(cell);
return originalStop(cell);
};
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
}, { signal: controller.signal });
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("cancelled");
expect(output.message).toContain("cancelled");
expect(stopped.length).toBe(1);
expect(result.runState.status).not.toBe("failed");
});
it("returns a `cancelled` output when the signal aborts during the release measurement", async () => {
// The measurement opens its transactions before it awaits anything, so
// aborting on that call lands the signal while the phase is in flight.
const { runtime, pieces, space, dispose } = await createStrictFabric();
try {
const sourceRef = await seedLabelledSecret(
runtime,
space,
"release-cancelled",
);
const controller = new AbortController();
const pristineEdit = runtime.edit.bind(runtime);
let measurementReached = false;
const runtimeWithEdit = runtime as unknown as {
edit: () => ReturnType;
};
runtimeWithEdit.edit = () => {
// Every earlier transaction belongs to setup; the release
// measurement is what opens one after the piece has settled.
if (measurementReached) controller.abort();
return pristineEdit();
};
const stopped: unknown[] = [];
const runner = runtime.runner as unknown as {
stop: (cell: unknown) => unknown;
};
const originalStop = runner.stop.bind(runtime.runner);
runner.stop = (cell) => {
stopped.push(cell);
return originalStop(cell);
};
const engine = createStrictEngine(pieces);
const settledBefore = pieces.synced.bind(pieces);
(pieces as unknown as { synced: () => Promise }).synced =
async () => {
await settledBefore();
measurementReached = true;
};
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: OPTIONAL_SECRET_PATTERN_SOURCE,
inputs: { amount: 2, source: sourceRef },
resultSchema: TOTAL_RESULT_SCHEMA,
}, { signal: controller.signal });
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("cancelled");
expect(stopped.length).toBe(1);
} finally {
await dispose();
}
});
it("returns a `cancelled` output when the signal aborts during the output-concern scan", async () => {
// The scan is the one reader of `since()`, and it asks as it begins, so
// aborting there lands the signal while the scan is in flight — with
// the release measurement's own race already resolved.
const controller = new AbortController();
const aborting: FabricPatternInstantiations = {
sequence: () => recorder.instantiations.sequence(),
since: (from) => {
controller.abort();
return recorder.instantiations.since(from);
},
keylessSince: (from) => recorder.instantiations.keylessSince(from),
};
const stopped: unknown[] = [];
const runner = runtime.runner as unknown as {
stop: (cell: unknown) => unknown;
};
const originalStop = runner.stop.bind(runtime.runner);
runner.stop = (cell) => {
stopped.push(cell);
return originalStop(cell);
};
const result = await createEngine(aborting).invokeBuiltinTool(
"run_pattern",
{
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 21 },
resultSchema: DOUBLED_RESULT_SCHEMA,
},
{ signal: controller.signal },
);
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("cancelled");
expect(stopped.length).toBe(1);
});
it("surfaces a rejected session construction as a structured error and invokes the factory again on the next call", async () => {
let factoryCalls = 0;
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
fabricSessionFactory: () => {
factoryCalls += 1;
return Promise.reject(
new Error("authorization denied for the configured space"),
);
},
});
const first = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
});
const firstOutput = first.output as RunPatternToolErrorOutput;
expect(firstOutput.status).toBe("error");
expect(firstOutput.message).toContain("fabric session unavailable");
expect(firstOutput.message).toContain("authorization denied");
const second = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
});
expect((second.output as RunPatternToolErrorOutput).status).toBe(
"error",
);
expect(factoryCalls).toBe(2);
});
it("leaves the created piece out of the space's real registered piece list", async () => {
// A real default pattern first: without one, `getRegisteredPieces()`
// reads a detached always-empty fallback and the assertion below
// holds vacuously.
const defaultRoot = await pieces.create(DEFAULT_PATTERN_SOURCE, {
input: { pieceRegistry: [] },
});
await pieces.linkDefaultPattern(defaultRoot.getCell());
await runtime.idle();
await pieces.synced();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
});
expect((result.output as RunPatternToolSuccessOutput).status).toBe("ok");
const registered = await pieces.getRegisteredPieces();
expect(registered.length).toBe(0);
// The registry observes registration, proving the zero above is a
// decision by the tool and not an inert list.
const control = await pieces.create(DOUBLING_PATTERN_SOURCE, {
input: { n: 2 },
});
await pieces.add([control.getCell()]);
const afterAdd = await pieces.getRegisteredPieces();
expect(afterAdd.length).toBe(1);
});
it("returns a `cancelled` output without creating a piece when the signal is already aborted", async () => {
const spy = spyOnRunPersistent();
const controller = new AbortController();
controller.abort();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
}, { signal: controller.signal });
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("cancelled");
expect(output.message).toContain("cancelled");
expect(spy.calls).toBe(0);
});
it("returns an error for a `resultSchema` that is not a JSON Schema, creating no piece", async () => {
const spy = spyOnRunPersistent();
const engine = createEngine();
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
resultSchema: "{ not json",
} as unknown as RunPatternToolInput);
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain(
"resultSchema string must be valid JSON",
);
expect(spy.calls).toBe(0);
});
it("returns an error naming the configuration a run without a fabric session is missing", async () => {
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
});
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: 1 },
});
const output = result.output as RunPatternToolErrorOutput;
expect(output.status).toBe("error");
expect(output.message).toContain("requires a fabric session");
expect(output.message).toContain("--fabric-space");
});
});
describe("live-cell input validation from a cold session", () => {
// Two replicas on one loopback server: the writer seeds and pushes, the
// reader session starts cold, never having pulled what the input's
// referent links to. A single emulated manager cannot exercise this —
// seeding through it warms the very cache the validation read depends
// on. This is the shape of the piece-registry grant: the granted address
// resolves through a link, so a schema-less validation read in a fresh
// session measures `undefined` where the value is.
let server: ReturnType;
let spaceDid: DID;
let writerStorage: EmulatedStorageManager;
let writerRuntime: Runtime;
let writerPieces: PiecesController;
let readerStorage: EmulatedStorageManager | undefined;
let readerRuntime: Runtime | undefined;
beforeEach(async () => {
server = newLoopbackServer();
spaceDid = (await Identity.generate()).did();
writerStorage = EmulatedStorageManager.connectTo(server, { as: signer });
writerRuntime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager: writerStorage,
});
writerPieces = new PiecesController(
createSession({ identity: signer, spaceDid }),
writerRuntime,
);
await writerPieces.synced();
readerStorage = undefined;
readerRuntime = undefined;
});
// Connected after the writer has seeded and pushed, so the reader's
// first sight of every seeded doc is a cold pull from the server.
const connectReader = async (): Promise => {
readerStorage = EmulatedStorageManager.connectTo(server, { as: signer });
readerRuntime = new Runtime({
apiUrl: new URL("http://toolshed.test"),
storageManager: readerStorage,
});
const readerPieces = new PiecesController(
createSession({ identity: signer, spaceDid }),
readerRuntime,
);
await readerPieces.synced();
return readerPieces;
};
afterEach(async () => {
await readerRuntime?.dispose();
await readerStorage?.close();
await writerRuntime?.dispose();
await writerStorage?.close();
await server?.close();
});
it("validates a live-cell input whose referent sits behind a link the session has not pulled, and runs the pattern", async () => {
const space = writerPieces.getSpace();
const target = writerRuntime.getCell(
space,
"run-pattern-cold-target",
{ type: "number" },
);
const wrapper = writerRuntime.getCell(space, "run-pattern-cold-wrapper");
const seeded = await writerRuntime.editWithRetry((tx) => {
target.withTx(tx).set(7);
wrapper.withTx(tx).set(target.getAsLink());
});
expect(seeded.error).toBeUndefined();
await writerRuntime.idle();
await writerPieces.synced();
const wrapperRef = createLLMFriendlyLink(
wrapper.getAsNormalizedFullLink(),
space,
);
const readerPieces = await connectReader();
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces: readerPieces }),
});
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: DOUBLING_PATTERN_SOURCE,
inputs: { n: wrapperRef },
resultSchema: DOUBLED_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { doubled: number }).doubled).toBe(14);
});
it("validates the piece-registry grant address as a live-cell input and runs the pattern", async () => {
// The writer gives the space a default pattern whose stored document
// reads as `undefined` through the whole-document untyped view — a
// shape `getDefaultPattern` itself tolerates (see
// `packages/piece/test/ensure-default-pattern.test.ts`) — anchoring
// the piece-registry well-known grant the way a real space does. The
// emulated client materializes more eagerly than a remote session, so
// this pins the grant-address wiring rather than discriminating the
// schema-carrying validation read; that discrimination needs a live
// toolshed.
const mockDefaultPattern = writerRuntime.getCell(
writerPieces.getSpace(),
"run-pattern-mock-default-pattern",
{
type: "object",
properties: {
pieceRegistry: { type: "array" },
missing: { type: "string" },
},
required: ["missing"],
} as const,
);
await writerRuntime.editWithRetry((tx) => {
mockDefaultPattern.withTx(tx).setRawUntyped({ pieceRegistry: [] });
});
await writerPieces.linkDefaultPattern(mockDefaultPattern);
await writerRuntime.idle();
await writerPieces.synced();
// The reader resolves the grant the way the harness does at run
// start: an address-only walk that pulls nothing the registry lists.
const readerPieces = await connectReader();
const [grant] = await resolveWellKnownGrantRefs(
{ pieces: readerPieces } as HarnessFabricSession,
);
expect(grant?.name).toBe("piece-registry");
const engine = new CfHarnessEngine({
sandboxRuntime: new FakeSandboxRuntime(),
runId: `run-pattern-test-${crypto.randomUUID()}`,
fabricSessionFactory: () => Promise.resolve({ pieces: readerPieces }),
});
const result = await engine.invokeBuiltinTool("run_pattern", {
sourceText: ENTRY_COUNT_PATTERN_SOURCE,
inputs: { entries: grant!.ref },
resultSchema: ENTRY_COUNT_RESULT_SCHEMA,
});
const output = result.output as RunPatternToolSuccessOutput;
expect(output.status).toBe("ok");
expect((output.value as { count: number }).count).toBe(0);
});
});
describe("runPatternPolicyRefusal()", () => {
it("returns `undefined` for an empty refusal list", () => {
expect(runPatternPolicyRefusal([], () => [])).toBeUndefined();
});
it("names the sink whose ceiling refused for a `sink-ceiling` detail", () => {
expect(
runPatternPolicyRefusal([
refusalDetail({
gate: "sink-ceiling",
sink: "fetchText",
inputs: [refusalInput("source-doc")],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["sink-ceiling"],
sinks: ["fetchText"],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "complete",
});
});
it("deduplicates the gates, sinks, offending atoms and input keys of several details", () => {
expect(
runPatternPolicyRefusal(
[
refusalDetail({
gate: "sink-ceiling",
sink: "fetchText",
offendingAtoms: ['"secret"', '"medical"'],
inputs: [refusalInput("source-doc")],
}),
refusalDetail({
gate: "sink-ceiling",
sink: "fetchText",
offendingAtoms: ['"medical"'],
inputs: [refusalInput("source-doc", ["notes"])],
}),
refusalDetail({
gate: "writer-fit",
inputs: [refusalInput("other-doc")],
}),
],
inputKeysByDocument({
"source-doc": "source",
"other-doc": "notes",
}),
),
).toEqual({
gates: ["sink-ceiling", "writer-fit"],
sinks: ["fetchText"],
offendingAtoms: ['"secret"', '"medical"'],
inputKeys: ["source", "notes"],
attribution: "complete",
});
});
it("counts a structured offending atom into `withheldAtomCount` rather than naming it", () => {
// A `Caveat` atom carries the principal that introduced it, and nothing
// redacts a principal out of an atom already rendered to a string.
expect(
runPatternPolicyRefusal([
refusalDetail({
offendingAtoms: [
'"secret"',
'{"caveat":"only-for","source":"did:key:zIntroducer"}',
],
inputs: [refusalInput("source-doc")],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
withheldAtomCount: 1,
inputKeys: ["source"],
attribution: "complete",
});
});
it("counts an offending atom whose rendering is not JSON into `withheldAtomCount`", () => {
// `renderCfcAtom` is `JSON.stringify`, which produces no JSON text at
// all for an atom it cannot carry, so the rendering reaching this fold
// need not parse.
expect(
runPatternPolicyRefusal([
refusalDetail({
offendingAtoms: ['"secret"', "undefined"],
inputs: [refusalInput("source-doc")],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
withheldAtomCount: 1,
inputKeys: ["source"],
attribution: "complete",
});
});
it("names every input key one refused document was supplied under", () => {
// One document handed in under two keys is reached by dropping either
// alias alone, so a remedy naming one of them leaves the other
// carrying the label.
expect(
runPatternPolicyRefusal([
refusalDetail({ inputs: [refusalInput("source-doc")] }),
], inputKeysByDocument({ "source-doc": ["source", "alsoSource"] })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source", "alsoSource"],
attribution: "complete",
});
});
it("returns `partial` with the unowned read counted when one offending read belongs to no input key", () => {
expect(
runPatternPolicyRefusal([
refusalDetail({
inputs: [refusalInput("source-doc"), refusalInput("hidden-doc")],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
unattributedInputCount: 1,
attribution: "partial",
});
});
it("counts two paths of one unowned document as a single unattributed input", () => {
expect(
runPatternPolicyRefusal([
refusalDetail({
inputs: [
refusalInput("hidden-doc", ["notes"]),
refusalInput("hidden-doc", ["archive", "notes"]),
refusalInput("source-doc"),
],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
unattributedInputCount: 1,
attribution: "partial",
});
});
it("returns `none` when no offending read belongs to an input of the call", () => {
expect(
runPatternPolicyRefusal([
refusalDetail({
inputs: [
refusalInput("hidden-doc"),
refusalInput("other-hidden-doc"),
],
}),
], () => []),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: [],
unattributedInputCount: 2,
attribution: "none",
});
});
it("returns `partial` for a detail the boundary itself attributed partially, though every named read is an input key", () => {
expect(
runPatternPolicyRefusal([
refusalDetail({
attribution: "partial",
inputs: [refusalInput("source-doc")],
}),
], inputKeysByDocument({ "source-doc": "source" })),
).toEqual({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "partial",
});
});
});
describe("policyRefusalMessage()", () => {
it("names the single sink whose ceiling refused and the one input to drop", () => {
const message = policyRefusalMessage({
gates: ["sink-ceiling"],
sinks: ["fetchText"],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "complete",
}, "commit");
expect(message).toContain(
'the sink "fetchText" does not admit the confidentiality ("secret")',
);
expect(message).toContain(
'Every label refused here came in through input "source", so the same run without it proceeds',
);
});
it("says the values are withheld and the reference stands when the release boundary refused", () => {
// The answer's own sink refuses what the run already landed, so what
// the caller is told became of the result differs from a refused
// commit: it exists, in the space, under its own labels, and the
// caller holds its reference with the values withheld — so the remedy
// releases values rather than making the run proceed.
const message = policyRefusalMessage({
gates: ["sink-ceiling"],
sinks: ["run_pattern"],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "complete",
}, "release");
expect(message).toContain("policy refused to release its values");
expect(message).toContain("resultRef still names the result");
expect(message).toContain("without it releases its values");
expect(message).not.toContain("never landed");
});
it("names both sinks when two ceilings refused", () => {
expect(policyRefusalMessage({
gates: ["sink-ceiling"],
sinks: ["fetchText", "postMessage"],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
attribution: "complete",
}, "commit")).toContain(
'the sinks "fetchText", "postMessage" does not admit',
);
});
it("names the write it attempted and both inputs to drop when no sink refused", () => {
const message = policyRefusalMessage({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"', '"medical"'],
inputKeys: ["source", "notes"],
attribution: "complete",
}, "commit");
expect(message).toContain(
'the write it attempted does not admit the confidentiality ("secret", "medical")',
);
expect(message).toContain(
'Every label refused here came in through inputs "source", "notes", so the same run without them proceeds',
);
});
it("omits the confidentiality parenthetical when every offending atom was withheld", () => {
const message = policyRefusalMessage({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: [],
withheldAtomCount: 2,
inputKeys: ["source"],
attribution: "complete",
}, "commit");
expect(message).toContain(
"the write it attempted does not admit the confidentiality this run carries",
);
});
it("states that dropping the named inputs only narrows the flow for a `partial` attribution", () => {
expect(policyRefusalMessage({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source", "notes"],
unattributedInputCount: 1,
attribution: "partial",
}, "commit")).toContain(
'Some of what was refused came in through inputs "source", "notes"; dropping them narrows the flow without necessarily clearing it, since reads this call does not own carry refused labels too',
);
});
it("states that dropping the single named input only narrows the flow for a `partial` attribution", () => {
expect(policyRefusalMessage({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: ["source"],
unattributedInputCount: 1,
attribution: "partial",
}, "commit")).toContain(
'came in through input "source"; dropping it narrows the flow',
);
});
it("states that no input of the call accounts for the refusal for a `none` attribution", () => {
expect(policyRefusalMessage({
gates: ["writer-fit"],
sinks: [],
offendingAtoms: ['"secret"'],
inputKeys: [],
unattributedInputCount: 1,
attribution: "none",
}, "commit")).toContain(
"No input of this call accounts for what was refused, so dropping an input will not clear it",
);
});
});
});