planr 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -61
- package/docs/ARCHITECTURE.md +4 -4
- package/docs/CLAUDE_CODE.md +2 -2
- package/docs/CLI_REFERENCE.md +6 -14
- package/docs/CODEX.md +6 -6
- package/docs/CURSOR.md +2 -2
- package/docs/EXAMPLE_WEBAPP.md +63 -97
- package/docs/GOALS.md +8 -25
- package/docs/INSTALL.md +3 -3
- package/docs/MCP_CONTRACT.md +6 -9
- package/docs/MODEL_ROUTING.md +20 -177
- package/docs/RELEASE.md +1 -1
- package/docs/ROUTING_BUNDLES.md +32 -0
- package/docs/SKILLS.md +35 -50
- package/docs/documentation/CONTRACT.md +164 -0
- package/docs/documentation/COVERAGE.md +91 -0
- package/docs/documentation/INFORMATION_ARCHITECTURE.md +147 -0
- package/docs/fixtures/mcp-contract.json +3 -11
- package/npm/native/darwin-arm64/planr +0 -0
- package/npm/native/darwin-x86_64/planr +0 -0
- package/npm/native/linux-arm64/planr +0 -0
- package/npm/native/linux-x86_64/planr +0 -0
- package/package.json +25 -14
- package/plugins/planr/.claude-plugin/plugin.json +1 -1
- package/plugins/planr/.codex-plugin/plugin.json +2 -2
- package/plugins/planr/skills/planr-loop/SKILL.md +1 -1
- package/docs/PRESET_COMPOSITION.md +0 -109
- package/docs/PRESET_EVALUATION.md +0 -118
- package/docs/PRESET_REGISTRY.md +0 -61
- package/evaluations/preset-suite-v1.toml +0 -127
- package/evaluations/sol-luna-codex-v1.toml +0 -42
- package/plugins/planr/skills/planr-loop/agents/planr-reviewer.toml +0 -23
- package/plugins/planr/skills/planr-loop/agents/planr-worker.toml +0 -21
- package/presets/bindings/claude-native.toml +0 -52
- package/presets/bindings/codex-openai.toml +0 -56
- package/presets/bindings/cursor-fable-grok.toml +0 -49
- package/presets/bindings/cursor-openai.toml +0 -49
- package/presets/bindings/mixed-host.toml +0 -64
- package/presets/policies/balanced.toml +0 -50
- package/presets/policies/low-usage.toml +0 -50
- package/presets/policies/max-quality.toml +0 -50
- package/presets/policies/read-only-audit.toml +0 -50
- package/website/README.md +0 -79
- package/website/_headers +0 -7
- package/website/alchemy-runtime.test.mjs +0 -21
- package/website/app.mjs +0 -216
- package/website/build-catalog.mjs +0 -135
- package/website/build-site.test.mjs +0 -69
- package/website/catalog-model.mjs +0 -185
- package/website/catalog-model.test.mjs +0 -124
- package/website/cloudflare-launcher.test.mjs +0 -38
- package/website/data/catalog.json +0 -307
- package/website/index.html +0 -122
- package/website/registry/manifest.toml +0 -48
- package/website/registry/report.md +0 -33
- package/website/registry/trusted-maintainers.toml +0 -6
- package/website/registry/verification.json +0 -7258
- package/website/serve.mjs +0 -41
- package/website/styles.css +0 -201
- package/website/test-fixtures/recommended.json +0 -72
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { mkdir } from "node:fs/promises";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
7
|
-
import { projectComposition, safeIdentifier } from "./catalog-model.mjs";
|
|
8
|
-
|
|
9
|
-
function usage(message) {
|
|
10
|
-
if (message) console.error(message);
|
|
11
|
-
console.error(
|
|
12
|
-
"usage: node website/build-catalog.mjs --manifest <registry.toml> --content-root <dir> --trust-store <trusted.toml> --entry <id=host> [--entry <id=host> ...] --at-unix <unix> --output <catalog.json> [--planr-bin <planr>]",
|
|
13
|
-
);
|
|
14
|
-
process.exit(2);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function parseArgs(argv) {
|
|
18
|
-
const values = { entries: [], planrBin: "planr" };
|
|
19
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
20
|
-
const flag = argv[index];
|
|
21
|
-
const value = argv[index + 1];
|
|
22
|
-
if (!value) usage(`missing value for ${flag}`);
|
|
23
|
-
index += 1;
|
|
24
|
-
if (flag === "--entry") values.entries.push(value);
|
|
25
|
-
else if (flag === "--manifest") values.manifest = value;
|
|
26
|
-
else if (flag === "--content-root") values.contentRoot = value;
|
|
27
|
-
else if (flag === "--trust-store") values.trustStore = value;
|
|
28
|
-
else if (flag === "--at-unix") values.atUnix = Number(value);
|
|
29
|
-
else if (flag === "--output") values.output = value;
|
|
30
|
-
else if (flag === "--planr-bin") values.planrBin = value;
|
|
31
|
-
else usage(`unknown argument ${flag}`);
|
|
32
|
-
}
|
|
33
|
-
if (
|
|
34
|
-
!values.manifest ||
|
|
35
|
-
!values.contentRoot ||
|
|
36
|
-
!values.trustStore ||
|
|
37
|
-
!values.output ||
|
|
38
|
-
!Number.isSafeInteger(values.atUnix) ||
|
|
39
|
-
values.entries.length === 0
|
|
40
|
-
) {
|
|
41
|
-
usage("all required arguments must be provided");
|
|
42
|
-
}
|
|
43
|
-
return values;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function runJson(binary, args, cwd) {
|
|
47
|
-
const result = spawnSync(binary, args, { cwd, encoding: "utf8" });
|
|
48
|
-
if (result.error) throw result.error;
|
|
49
|
-
if (result.status !== 0) {
|
|
50
|
-
throw new Error(result.stderr.trim() || result.stdout.trim() || `${binary} exited ${result.status}`);
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
return JSON.parse(result.stdout);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
throw new Error(`${binary} returned invalid JSON: ${error.message}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function entrySpec(raw) {
|
|
60
|
-
const separator = raw.lastIndexOf("=");
|
|
61
|
-
if (separator < 1 || separator === raw.length - 1) usage(`invalid --entry ${raw}; expected id=host`);
|
|
62
|
-
return { id: raw.slice(0, separator), host: raw.slice(separator + 1) };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function artifactPath(verified, kind, contentRoot) {
|
|
66
|
-
const matches = verified.entry.artifacts.filter((artifact) => artifact.kind === kind);
|
|
67
|
-
if (matches.length !== 1) throw new Error(`entry ${verified.entry.id} must contain one ${kind}`);
|
|
68
|
-
return resolve(contentRoot, matches[0].path);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const options = parseArgs(process.argv.slice(2));
|
|
72
|
-
const invocationRoot = process.cwd();
|
|
73
|
-
const planrBin =
|
|
74
|
-
isAbsolute(options.planrBin) || !options.planrBin.includes("/")
|
|
75
|
-
? options.planrBin
|
|
76
|
-
: resolve(invocationRoot, options.planrBin);
|
|
77
|
-
const manifest = resolve(invocationRoot, options.manifest);
|
|
78
|
-
const contentRoot = resolve(invocationRoot, options.contentRoot);
|
|
79
|
-
const trustStore = resolve(invocationRoot, options.trustStore);
|
|
80
|
-
const scratch = mkdtempSync(join(tmpdir(), "planr-preset-site-"));
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const compositions = options.entries.map((raw) => {
|
|
84
|
-
const { id, host } = entrySpec(raw);
|
|
85
|
-
const verified = runJson(
|
|
86
|
-
planrBin,
|
|
87
|
-
[
|
|
88
|
-
"--json",
|
|
89
|
-
"agents",
|
|
90
|
-
"preset",
|
|
91
|
-
"registry",
|
|
92
|
-
"verify",
|
|
93
|
-
manifest,
|
|
94
|
-
"--entry",
|
|
95
|
-
id,
|
|
96
|
-
"--content-root",
|
|
97
|
-
contentRoot,
|
|
98
|
-
"--trust-store",
|
|
99
|
-
trustStore,
|
|
100
|
-
"--at-unix",
|
|
101
|
-
String(options.atUnix),
|
|
102
|
-
"--host",
|
|
103
|
-
host,
|
|
104
|
-
],
|
|
105
|
-
scratch,
|
|
106
|
-
);
|
|
107
|
-
const verificationPath = artifactPath(verified, "verification", contentRoot);
|
|
108
|
-
const policyId = safeIdentifier(verified.entry.evaluation?.policy_id, "policy id");
|
|
109
|
-
const bindingId = safeIdentifier(verified.entry.evaluation?.binding_id, "binding id");
|
|
110
|
-
const preview = runJson(
|
|
111
|
-
planrBin,
|
|
112
|
-
["--json", "agents", "preset", "apply", policyId, "--binding", bindingId],
|
|
113
|
-
scratch,
|
|
114
|
-
);
|
|
115
|
-
const verificationEnvelope = JSON.parse(readFileSync(verificationPath, "utf8"));
|
|
116
|
-
return projectComposition({ verified, preview, verificationEnvelope });
|
|
117
|
-
});
|
|
118
|
-
const catalog = {
|
|
119
|
-
schemaVersion: 1,
|
|
120
|
-
generatedAtUnix: options.atUnix,
|
|
121
|
-
source: {
|
|
122
|
-
state: "verified_registry_projection",
|
|
123
|
-
manifest: options.manifest,
|
|
124
|
-
entryCount: compositions.length,
|
|
125
|
-
trust: "planr_registry_v1",
|
|
126
|
-
},
|
|
127
|
-
compositions,
|
|
128
|
-
};
|
|
129
|
-
const output = resolve(invocationRoot, options.output);
|
|
130
|
-
await mkdir(dirname(output), { recursive: true });
|
|
131
|
-
writeFileSync(output, `${JSON.stringify(catalog, null, 2)}\n`, { mode: 0o644 });
|
|
132
|
-
console.log(`wrote ${compositions.length} verified composition(s) to ${output}`);
|
|
133
|
-
} finally {
|
|
134
|
-
rmSync(scratch, { recursive: true, force: true });
|
|
135
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import test from "node:test";
|
|
6
|
-
import {
|
|
7
|
-
buildSite,
|
|
8
|
-
PUBLIC_SITE_FILES,
|
|
9
|
-
verifyPublication,
|
|
10
|
-
} from "../scripts/build-site.mjs";
|
|
11
|
-
|
|
12
|
-
async function fixture() {
|
|
13
|
-
const root = await mkdtemp(join(tmpdir(), "planr-site-build-"));
|
|
14
|
-
const sourceRoot = join(root, "website");
|
|
15
|
-
const outputRoot = join(root, "dist", "website");
|
|
16
|
-
for (const relativePath of PUBLIC_SITE_FILES) {
|
|
17
|
-
const path = join(sourceRoot, relativePath);
|
|
18
|
-
await mkdir(join(path, ".."), { recursive: true });
|
|
19
|
-
await writeFile(path, `public:${relativePath}\n`);
|
|
20
|
-
}
|
|
21
|
-
return { root, sourceRoot, outputRoot };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
test("builds only the public runtime allowlist and preserves nested catalog bytes", async (t) => {
|
|
25
|
-
const paths = await fixture();
|
|
26
|
-
t.after(() => rm(paths.root, { recursive: true, force: true }));
|
|
27
|
-
await writeFile(join(paths.sourceRoot, "build-catalog.mjs"), "must not be public\n");
|
|
28
|
-
await mkdir(join(paths.sourceRoot, "registry"), { recursive: true });
|
|
29
|
-
await writeFile(join(paths.sourceRoot, "registry", "trusted-maintainers.toml"), "not public\n");
|
|
30
|
-
|
|
31
|
-
const files = await buildSite(paths);
|
|
32
|
-
|
|
33
|
-
assert.deepEqual(files, [...PUBLIC_SITE_FILES].sort());
|
|
34
|
-
assert.equal(
|
|
35
|
-
await readFile(join(paths.outputRoot, "data", "catalog.json"), "utf8"),
|
|
36
|
-
"public:data/catalog.json\n",
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("fails before replacing output when a required source artifact is missing", async (t) => {
|
|
41
|
-
const paths = await fixture();
|
|
42
|
-
t.after(() => rm(paths.root, { recursive: true, force: true }));
|
|
43
|
-
await mkdir(paths.outputRoot, { recursive: true });
|
|
44
|
-
await writeFile(join(paths.outputRoot, "sentinel"), "keep on validation failure\n");
|
|
45
|
-
await rm(join(paths.sourceRoot, "styles.css"));
|
|
46
|
-
|
|
47
|
-
await assert.rejects(() => buildSite(paths), /missing public website artifact: styles\.css/);
|
|
48
|
-
assert.equal(await readFile(join(paths.outputRoot, "sentinel"), "utf8"), "keep on validation failure\n");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("publication verification rejects unexpected nested output", async (t) => {
|
|
52
|
-
const paths = await fixture();
|
|
53
|
-
t.after(() => rm(paths.root, { recursive: true, force: true }));
|
|
54
|
-
await buildSite(paths);
|
|
55
|
-
await mkdir(join(paths.outputRoot, "registry"), { recursive: true });
|
|
56
|
-
await writeFile(join(paths.outputRoot, "registry", "manifest.toml"), "unexpected\n");
|
|
57
|
-
|
|
58
|
-
await assert.rejects(() => verifyPublication(paths.outputRoot), /publish output mismatch/);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("refuses an output directory inside the source tree", async (t) => {
|
|
62
|
-
const paths = await fixture();
|
|
63
|
-
t.after(() => rm(paths.root, { recursive: true, force: true }));
|
|
64
|
-
|
|
65
|
-
await assert.rejects(
|
|
66
|
-
() => buildSite({ sourceRoot: paths.sourceRoot, outputRoot: join(paths.sourceRoot, "dist") }),
|
|
67
|
-
/must not be inside/,
|
|
68
|
-
);
|
|
69
|
-
});
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
const IDENTIFIER = /^[A-Za-z0-9._-]+$/;
|
|
2
|
-
|
|
3
|
-
export function safeIdentifier(value, label = "identifier") {
|
|
4
|
-
if (typeof value !== "string" || !IDENTIFIER.test(value)) {
|
|
5
|
-
throw new Error(`${label} is not a safe registry identifier`);
|
|
6
|
-
}
|
|
7
|
-
return value;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function previewCommand(policyId, bindingId) {
|
|
11
|
-
return `planr agents preset apply ${safeIdentifier(policyId, "policy id")} --binding ${safeIdentifier(bindingId, "binding id")}`;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function statusLabel(status) {
|
|
15
|
-
const labels = {
|
|
16
|
-
experimental: "Experimental",
|
|
17
|
-
verified: "Verified",
|
|
18
|
-
recommended: "Recommended",
|
|
19
|
-
stale: "Stale",
|
|
20
|
-
deprecated: "Deprecated",
|
|
21
|
-
};
|
|
22
|
-
return labels[status] ?? "Unverified";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function formatBasisPoints(value) {
|
|
26
|
-
const basisPoints = Number(value);
|
|
27
|
-
return Number.isFinite(basisPoints) ? `${(basisPoints / 100).toFixed(2)}%` : "—";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function findArtifact(entry, kind) {
|
|
31
|
-
const artifacts = entry.artifacts.filter((artifact) => artifact.kind === kind);
|
|
32
|
-
if (artifacts.length !== 1) {
|
|
33
|
-
throw new Error(`registry entry must contain exactly one ${kind} artifact`);
|
|
34
|
-
}
|
|
35
|
-
return artifacts[0];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function proposedConfig(preview, kind) {
|
|
39
|
-
const artifact = preview.artifacts.find((candidate) => candidate.kind === kind);
|
|
40
|
-
const value = artifact?.config_diff?.proposed?.value;
|
|
41
|
-
if (!value || typeof value !== "object") {
|
|
42
|
-
throw new Error(`preset preview is missing proposed ${kind} configuration`);
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function matchingCandidate(report, policyId, bindingId) {
|
|
48
|
-
const candidate = report.candidates?.find(
|
|
49
|
-
(value) => value.policy?.id === policyId && value.binding?.id === bindingId,
|
|
50
|
-
);
|
|
51
|
-
if (!candidate) {
|
|
52
|
-
throw new Error(`evaluation report has no candidate for ${policyId} + ${bindingId}`);
|
|
53
|
-
}
|
|
54
|
-
return candidate;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function recommendationMatches(report, policyId, bindingId) {
|
|
58
|
-
return Boolean(
|
|
59
|
-
report.recommended?.some(
|
|
60
|
-
(value) => value.policy === policyId && value.binding === bindingId && value.status === "recommended",
|
|
61
|
-
),
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function projectComposition({ verified, preview, verificationEnvelope }) {
|
|
66
|
-
if (!verified?.integrity_verified || !verified?.signature_verified || !verified?.trusted_maintainer) {
|
|
67
|
-
throw new Error("website projection requires an integrity-verified entry signed by a trusted maintainer");
|
|
68
|
-
}
|
|
69
|
-
if (!verified.compatible) {
|
|
70
|
-
throw new Error("website projection requires a compatible registry entry");
|
|
71
|
-
}
|
|
72
|
-
if (!preview?.pack?.safe) {
|
|
73
|
-
throw new Error("website projection requires a canonical safe-pack preview");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const entry = verified.entry;
|
|
77
|
-
const evaluation = entry.evaluation;
|
|
78
|
-
if (!evaluation) {
|
|
79
|
-
throw new Error("website projection requires evaluation binding metadata");
|
|
80
|
-
}
|
|
81
|
-
const policyId = safeIdentifier(evaluation.policy_id, "policy id");
|
|
82
|
-
const bindingId = safeIdentifier(evaluation.binding_id, "binding id");
|
|
83
|
-
const report = verificationEnvelope.report ?? verificationEnvelope;
|
|
84
|
-
const candidate = matchingCandidate(report, policyId, bindingId);
|
|
85
|
-
const reportRecommended = recommendationMatches(report, policyId, bindingId);
|
|
86
|
-
if (verified.recommended && (!reportRecommended || candidate.status !== "recommended")) {
|
|
87
|
-
throw new Error("registry recommendation does not match canonical evaluation evidence");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const policy = proposedConfig(preview, "active_policy");
|
|
91
|
-
const agents = proposedConfig(preview, "agent_registry");
|
|
92
|
-
if (policy.id !== policyId || preview.composition?.binding?.id !== bindingId) {
|
|
93
|
-
throw new Error("preset preview provenance does not match registry evaluation binding");
|
|
94
|
-
}
|
|
95
|
-
const runs = Number(candidate.metrics?.runs ?? 0);
|
|
96
|
-
const verifiedRoutes = Number(candidate.metrics?.verified_route_runs ?? 0);
|
|
97
|
-
const policyArtifact = findArtifact(entry, "policy");
|
|
98
|
-
const bindingArtifact = findArtifact(entry, "host-binding");
|
|
99
|
-
const verificationArtifact = findArtifact(entry, "verification");
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
id: `${safeIdentifier(entry.id, "entry id")}@${entry.version}`,
|
|
103
|
-
entryId: entry.id,
|
|
104
|
-
entryVersion: entry.version,
|
|
105
|
-
status: verified.effective_status,
|
|
106
|
-
statusLabel: statusLabel(verified.effective_status),
|
|
107
|
-
recommended: verified.recommended,
|
|
108
|
-
freshness: verified.freshness,
|
|
109
|
-
lifecycle: entry.lifecycle,
|
|
110
|
-
replacement: entry.replacement ?? null,
|
|
111
|
-
policy: {
|
|
112
|
-
id: policyId,
|
|
113
|
-
version: evaluation.policy_version,
|
|
114
|
-
usage: policy.usage,
|
|
115
|
-
transitions: policy.transitions,
|
|
116
|
-
materiality: policy.materiality,
|
|
117
|
-
execution: policy.execution,
|
|
118
|
-
},
|
|
119
|
-
binding: {
|
|
120
|
-
id: bindingId,
|
|
121
|
-
version: evaluation.binding_version,
|
|
122
|
-
host: preview.composition.host,
|
|
123
|
-
profiles: agents.profiles,
|
|
124
|
-
dispatch: preview.composition.dispatch,
|
|
125
|
-
},
|
|
126
|
-
compatibility: {
|
|
127
|
-
hosts: entry.compatible_hosts,
|
|
128
|
-
minPlanrVersion: entry.min_planr_version,
|
|
129
|
-
maxPlanrVersion: entry.max_planr_version,
|
|
130
|
-
},
|
|
131
|
-
enforcement: [
|
|
132
|
-
{
|
|
133
|
-
dimension: "Policy limits",
|
|
134
|
-
state: "verified",
|
|
135
|
-
detail: "Planr validates count, time, concurrency, transition, and safety-stop limits before dispatch.",
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
dimension: "Execution permissions",
|
|
139
|
-
state: "verified",
|
|
140
|
-
detail: "Registry-safe policy: no commands, hooks, network/MCP grants, secrets, or overwrite permission.",
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
dimension: "Model and effort",
|
|
144
|
-
state: "host_enforced",
|
|
145
|
-
detail: "The host binding requests concrete routes; the host retains final execution authority.",
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
dimension: "Effective route evidence",
|
|
149
|
-
state: runs > 0 && verifiedRoutes === runs ? "verified" : "unavailable",
|
|
150
|
-
detail: `${verifiedRoutes} of ${runs} evaluation runs carried verified effective-route evidence.`,
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
evaluation: {
|
|
154
|
-
suiteId: report.suite?.id,
|
|
155
|
-
suiteVersion: report.suite?.version,
|
|
156
|
-
evaluatedAtUnix: report.suite?.evaluated_at_unix,
|
|
157
|
-
reviewAtUnix: entry.review_at_unix,
|
|
158
|
-
status: candidate.status,
|
|
159
|
-
metrics: candidate.metrics,
|
|
160
|
-
thresholds: candidate.threshold_results,
|
|
161
|
-
resultHashes: candidate.results?.map((result) => result.result_sha256) ?? [],
|
|
162
|
-
fixtureSha256: report.suite?.fixture_sha256,
|
|
163
|
-
},
|
|
164
|
-
registry: {
|
|
165
|
-
id: verified.registry_id,
|
|
166
|
-
version: verified.registry_version,
|
|
167
|
-
manifestSha256: verified.manifest_sha256,
|
|
168
|
-
signer: entry.signature?.signer,
|
|
169
|
-
signatureVerified: true,
|
|
170
|
-
trustedMaintainer: true,
|
|
171
|
-
artifacts: [policyArtifact, bindingArtifact, verificationArtifact].map((artifact) => ({
|
|
172
|
-
path: artifact.path,
|
|
173
|
-
kind: artifact.kind,
|
|
174
|
-
sha256: artifact.sha256,
|
|
175
|
-
sizeBytes: artifact.size_bytes,
|
|
176
|
-
})),
|
|
177
|
-
},
|
|
178
|
-
command: previewCommand(policyId, bindingId),
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function visibleCompositions(catalog, recommendedOnly = false) {
|
|
183
|
-
const compositions = Array.isArray(catalog?.compositions) ? catalog.compositions : [];
|
|
184
|
-
return recommendedOnly ? compositions.filter((entry) => entry.recommended) : compositions;
|
|
185
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
formatBasisPoints,
|
|
5
|
-
previewCommand,
|
|
6
|
-
projectComposition,
|
|
7
|
-
safeIdentifier,
|
|
8
|
-
visibleCompositions,
|
|
9
|
-
} from "./catalog-model.mjs";
|
|
10
|
-
|
|
11
|
-
function fixture() {
|
|
12
|
-
const verified = {
|
|
13
|
-
registry_id: "official",
|
|
14
|
-
registry_version: "2026.07",
|
|
15
|
-
manifest_sha256: "a".repeat(64),
|
|
16
|
-
integrity_verified: true,
|
|
17
|
-
signature_verified: true,
|
|
18
|
-
trusted_maintainer: true,
|
|
19
|
-
compatible: true,
|
|
20
|
-
freshness: "current",
|
|
21
|
-
effective_status: "recommended",
|
|
22
|
-
recommended: true,
|
|
23
|
-
entry: {
|
|
24
|
-
id: "balanced-codex",
|
|
25
|
-
version: "1.0.0",
|
|
26
|
-
lifecycle: "published",
|
|
27
|
-
compatible_hosts: ["codex"],
|
|
28
|
-
min_planr_version: "1.3.0",
|
|
29
|
-
max_planr_version: "1.9.0",
|
|
30
|
-
review_at_unix: 1815523200,
|
|
31
|
-
evaluation: {
|
|
32
|
-
policy_id: "balanced",
|
|
33
|
-
policy_version: "1.0.0",
|
|
34
|
-
binding_id: "codex-openai",
|
|
35
|
-
binding_version: "1.0.0",
|
|
36
|
-
},
|
|
37
|
-
signature: { signer: "planr-maintainers" },
|
|
38
|
-
artifacts: [
|
|
39
|
-
{ path: "pack/policy.toml", kind: "policy", sha256: "1".repeat(64), size_bytes: 1 },
|
|
40
|
-
{ path: "pack/binding.toml", kind: "host-binding", sha256: "2".repeat(64), size_bytes: 2 },
|
|
41
|
-
{ path: "pack/verification.json", kind: "verification", sha256: "3".repeat(64), size_bytes: 3 },
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
const policy = {
|
|
46
|
-
id: "balanced",
|
|
47
|
-
usage: { max_active_agents: 3, max_parallel_writers: 1, max_depth: 1, metering: "trusted" },
|
|
48
|
-
transitions: { retry: { max_same_route_retries: 1 }, safety_stop: { enabled: true } },
|
|
49
|
-
materiality: { changed_files_threshold: 10 },
|
|
50
|
-
execution: { roles: { worker: { commands: [], hooks: [], network_hosts: [], mcp_servers: [] } } },
|
|
51
|
-
};
|
|
52
|
-
const preview = {
|
|
53
|
-
pack: { safe: true },
|
|
54
|
-
composition: { host: "codex", binding: { id: "codex-openai" }, dispatch: {} },
|
|
55
|
-
artifacts: [
|
|
56
|
-
{ kind: "active_policy", config_diff: { proposed: { value: policy } } },
|
|
57
|
-
{ kind: "agent_registry", config_diff: { proposed: { value: { profiles: {} } } } },
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
const candidate = {
|
|
61
|
-
policy: { id: "balanced" },
|
|
62
|
-
binding: { id: "codex-openai" },
|
|
63
|
-
status: "recommended",
|
|
64
|
-
metrics: { runs: 7, verified_route_runs: 7, average_quality_score_bps: 9600 },
|
|
65
|
-
threshold_results: [{ name: "quality", pass: true }],
|
|
66
|
-
results: [{ result_sha256: "4".repeat(64) }],
|
|
67
|
-
};
|
|
68
|
-
const verificationEnvelope = {
|
|
69
|
-
report: {
|
|
70
|
-
suite: { id: "planr-preset-suite", version: "1.8.0", evaluated_at_unix: 1783987200, fixture_sha256: "5".repeat(64) },
|
|
71
|
-
candidates: [candidate],
|
|
72
|
-
recommended: [{ policy: "balanced", binding: "codex-openai", status: "recommended" }],
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
return { verified, preview, verificationEnvelope };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
test("projects only trusted, safe, evidence-bound registry entries", () => {
|
|
79
|
-
const projected = projectComposition(fixture());
|
|
80
|
-
assert.equal(projected.status, "recommended");
|
|
81
|
-
assert.equal(projected.registry.signatureVerified, true);
|
|
82
|
-
assert.equal(projected.enforcement.at(-1).state, "verified");
|
|
83
|
-
assert.equal(projected.command, "planr agents preset apply balanced --binding codex-openai");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("refuses unsigned metadata and recommendation drift", () => {
|
|
87
|
-
const unsigned = fixture();
|
|
88
|
-
unsigned.verified.signature_verified = false;
|
|
89
|
-
assert.throws(() => projectComposition(unsigned), /trusted maintainer/);
|
|
90
|
-
|
|
91
|
-
const drifted = fixture();
|
|
92
|
-
drifted.verificationEnvelope.report.recommended = [];
|
|
93
|
-
assert.throws(() => projectComposition(drifted), /does not match/);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("publishes lifecycle-demoted recommendations with visible replacement metadata", () => {
|
|
97
|
-
const stale = fixture();
|
|
98
|
-
stale.verified.freshness = "stale";
|
|
99
|
-
stale.verified.effective_status = "stale";
|
|
100
|
-
stale.verified.recommended = false;
|
|
101
|
-
const staleProjected = projectComposition(stale);
|
|
102
|
-
assert.equal(staleProjected.status, "stale");
|
|
103
|
-
assert.equal(staleProjected.recommended, false);
|
|
104
|
-
|
|
105
|
-
const deprecated = fixture();
|
|
106
|
-
deprecated.verified.effective_status = "deprecated";
|
|
107
|
-
deprecated.verified.recommended = false;
|
|
108
|
-
deprecated.verified.entry.lifecycle = "deprecated";
|
|
109
|
-
deprecated.verified.entry.replacement = "balanced-codex-v2";
|
|
110
|
-
const deprecatedProjected = projectComposition(deprecated);
|
|
111
|
-
assert.equal(deprecatedProjected.status, "deprecated");
|
|
112
|
-
assert.equal(deprecatedProjected.replacement, "balanced-codex-v2");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("copy commands accept identifiers only and filtering is deterministic", () => {
|
|
116
|
-
assert.equal(previewCommand("balanced", "codex-openai"), "planr agents preset apply balanced --binding codex-openai");
|
|
117
|
-
assert.throws(() => safeIdentifier("balanced; curl evil"), /safe registry identifier/);
|
|
118
|
-
assert.deepEqual(
|
|
119
|
-
visibleCompositions({ compositions: [{ recommended: true }, { recommended: false }] }, true),
|
|
120
|
-
[{ recommended: true }],
|
|
121
|
-
);
|
|
122
|
-
assert.equal(formatBasisPoints(9600), "96.00%");
|
|
123
|
-
assert.equal(formatBasisPoints(undefined), "—");
|
|
124
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import test from "node:test";
|
|
8
|
-
import { cloudflareTestSteps } from "../scripts/cloudflare-test.mjs";
|
|
9
|
-
|
|
10
|
-
test("launcher keeps deploy and destroy pinned to the test stage", () => {
|
|
11
|
-
assert.deepEqual(cloudflareTestSteps("deploy"), [
|
|
12
|
-
["pnpm", ["site:check"]],
|
|
13
|
-
["pnpm", ["exec", "alchemy", "deploy", "--stage", "test"]],
|
|
14
|
-
]);
|
|
15
|
-
assert.deepEqual(cloudflareTestSteps("destroy"), [
|
|
16
|
-
["pnpm", ["exec", "alchemy", "destroy", "--stage", "test"]],
|
|
17
|
-
]);
|
|
18
|
-
assert.throws(() => cloudflareTestSteps("prod"), /usage:/);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
22
|
-
const launcher = join(repositoryRoot, "scripts", "cloudflare-test.mjs");
|
|
23
|
-
for (const version of ["v18.16.1", "v20.19.5"]) {
|
|
24
|
-
const node = join(homedir(), ".nvm", "versions", "node", version, "bin", "node");
|
|
25
|
-
test(
|
|
26
|
-
`direct launcher rejects ${version} before invoking pnpm`,
|
|
27
|
-
{ skip: !existsSync(node) && `local ${version} runtime is unavailable` },
|
|
28
|
-
() => {
|
|
29
|
-
const result = spawnSync(node, [launcher, "deploy"], {
|
|
30
|
-
cwd: repositoryRoot,
|
|
31
|
-
encoding: "utf8",
|
|
32
|
-
});
|
|
33
|
-
assert.equal(result.status, 1);
|
|
34
|
-
assert.match(result.stderr, /Cloudflare deployment requires Node\.js 22 or newer/);
|
|
35
|
-
assert.doesNotMatch(result.stderr, /Corepack|ERR_VM|addAbortListener|experimental-strip-types/);
|
|
36
|
-
},
|
|
37
|
-
);
|
|
38
|
-
}
|