| # Preset Composition | ||
| Planr composes two independent inputs: | ||
| - a provider-neutral Usage Policy v1 TOML file; | ||
| - a versioned host binding containing exact client/model/effort and dispatch capabilities. | ||
| Preview is the default: | ||
| ```bash | ||
| planr agents preset apply ./policy.toml --binding ./codex-binding.toml --preview --json | ||
| planr agents preset apply ./policy.toml --binding ./codex-binding.toml --confirm --json | ||
| ``` | ||
| The equivalent MCP tool is `planr_preset_apply` with `policy`, `binding`, and optional `confirm` fields. Both surfaces call one application service and return the same compatibility, permission, verification-age, provenance, conflict, and artifact shapes. | ||
| ## Built-in Catalog and Safe Packs | ||
| `planr agents preset list --json` (MCP: `planr_presets_list`) lists the four embedded policy presets, five host bindings, source checksums, and 20 declared safe pairs. Built-ins resolve by id or id plus `.toml`, so they work from any repository without copying package files first: | ||
| ```bash | ||
| planr agents preset apply balanced --binding codex-openai --preview --json | ||
| planr agents preset apply read-only-audit --binding mixed-host --confirm --json | ||
| ``` | ||
| Policies: `balanced`, `low-usage`, `max-quality`, and `read-only-audit`. Bindings: `codex-openai`, `cursor-openai`, `cursor-fable-grok`, `claude-native`, and `mixed-host`. The exact inspectable sources ship under `presets/policies/` and `presets/bindings/` and are also compiled into the binary. | ||
| A response whose inputs came from the embedded catalog carries `pack.status = "safe"` and `pack.safe = true`. Explicit file inputs remain custom even when their internal ids match a built-in; they are allowed, carry `pack.status = "custom"`, and add a warning requiring review of compatibility, permission, and artifact diffs. This prevents copied or modified files from inheriting safe-pack status by name alone. | ||
| ## Host Binding v1 | ||
| Bindings use strict TOML: unknown fields fail parsing. Abstract role keys must cover every role in `policy.execution.roles`; bindings may add a driver profile without adding it to execution permissions. | ||
| ```toml | ||
| schema_version = 1 | ||
| id = "codex-team" | ||
| version = "1.0.0" | ||
| host = "codex" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["codex-0.138-cross-tier-smoke"] | ||
| billing_assumptions = ["local subscription"] | ||
| known_limitations = ["effective model requires host evidence"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = true | ||
| fork_none = true | ||
| fork_all = true | ||
| max_partial_fork_turns = 4 | ||
| [profiles.driver] | ||
| profile = "sol" | ||
| client = "codex" | ||
| model = "gpt-5.5" | ||
| effort = "xhigh" | ||
| [profiles.worker] | ||
| profile = "luna" | ||
| client = "codex" | ||
| model = "gpt-5.4-mini" | ||
| effort = "high" | ||
| skill = "planr-work" | ||
| # Omit for the deterministic `none` default. | ||
| fork_turns = { mode = "partial", turns = 2 } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "verify-codex-team" | ||
| verified_at_unix = 1900000000 | ||
| max_age_seconds = 2592000 | ||
| [[artifacts]] | ||
| path = ".codex/agents/luna.toml" | ||
| kind = "codex_agent" | ||
| content = '''model = "gpt-5.4-mini" | ||
| model_reasoning_effort = "high" | ||
| ''' | ||
| ``` | ||
| Codex cross-tier children never accept `fork_turns = { mode = "all" }`, because full history inheritance defeats the requested model/effort override. Omitted fork configuration defaults to `none`. A partial fork must be positive, at or below `max_partial_fork_turns`, and backed by non-empty `capability_evidence`. Secret-like values are forbidden in all binding metadata, including capability evidence, billing assumptions, limitations, profile metadata, routes, and artifact paths/kinds; composition rejects them with field-only diagnostics before producing dispatch/warning output or mutating the repository. | ||
| ## Preview and Lock | ||
| The preview includes: | ||
| - composed abstract-role to registry-profile and dispatch-context mappings; | ||
| - compatibility errors and host/billing limitations; | ||
| - execution-permission additions relative to the active policy; | ||
| - verification id, age, maximum age, and fresh/stale state; | ||
| - source ids, versions, SHA-256 hashes, Planr version, and applied-at value; | ||
| - every target's kind, size, proposed/existing hash, `create`, `unchanged`, or `conflict` action, and deterministic old/proposed configuration. TOML is projected structurally so policy limits/transitions and registry routes/profiles/fallbacks are directly auditable; other text is line-projected. Secret-like keys and credential-shaped values are replaced with `[REDACTED]`. | ||
| Confirmed apply writes the previewed bytes and records `policy_applied`. The lock at `.planr/preset.lock.toml` contains source hashes and hashes for every generated non-lock artifact. Tests and reproducible packaging can pin `PLANR_PRESET_NOW_UNIX` and `PLANR_PRESET_APPLIED_AT`; ordinary operation records current time. | ||
| ## Repository Boundary | ||
| Only these repository-relative targets are valid: | ||
| - `.planr/policy.toml` | ||
| - `.planr/agents.toml` | ||
| - `.planr/preset.lock.toml` | ||
| - files below `.codex/{agents,skills}/`, `.claude/{agents,skills}/`, or `.cursor/{agents,skills}/` | ||
| Planr rejects absolute paths, `.`/`..` traversal, non-normalized paths, symlink crossings, home/global targets, and repository `.codex/config.toml` before the first write. Existing different files are conflicts and are never overwritten; there is no force or privileged mode. |
| # Preset Evaluation | ||
| Planr provides three explicit evidence levels over the same versioned fixture and lifecycle engine: | ||
| - the default offline policy simulation performs no external execution and can reach `verified` but never `recommended`; | ||
| - the opt-in live-host runner executes every task through a user-selected local adapter and evaluates Planr-owned workspace artifacts, but arbitrary-process route and cost claims cannot reach `recommended` without independent instrumentation. | ||
| - the instrumented live-host runner joins those artifacts to Ed25519-signed telemetry receipts and can reach `recommended` when trusted route, usage, and all other thresholds pass. | ||
| `evaluations/preset-suite-v1.toml` defines distinct inputs, artifact kinds, and candidate/task-bound output oracles for exploration, implementation, mechanical, browser, visual, security, and subagent workflows. Input and result artifacts are SHA-256 hashed. | ||
| ```sh | ||
| planr agents preset evaluate | ||
| planr --json agents preset evaluate --at-unix 1783987200 | ||
| planr agents preset evaluate --report-dir reports/preset-v1 | ||
| planr agents preset evaluate \ | ||
| --live-host-command /absolute/path/to/host-adapter \ | ||
| --live-host-arg optional-argument \ | ||
| --trusted-telemetry-signer production-collector \ | ||
| --trusted-telemetry-collector /absolute/path/to/collector | ||
| ``` | ||
| Evaluation defaults to current wall-clock time, so evidence becomes `stale` after fixture expiry. `--at-unix` is the deterministic override for tests and reproducible exports. `--host` requires binding compatibility. The signer and collector flags are all-or-none and require live mode. MCP exposes the equivalent optional `at_unix`, `host`, `live_host_command`, `live_host_args`, `trusted_telemetry_signer`, and `trusted_telemetry_collector` inputs. | ||
| ## Offline simulation | ||
| Offline artifacts say `evidence_scope = "policy_simulation"`, `task_executed = false`, `outcome_oracle_evaluated = false`, and `recommendation_eligible = false`. Projected tool, token, credit, and latency values carry `metrics_source = "estimated_projection"` and `metering_confidence = "estimated"`. | ||
| Binding configuration populates only requested and resolved route stages. Effective model, effort, and context-fork dimensions remain `unavailable`, so route coverage is zero and the recommendation list is empty. Offline `verified` means only that policy composition, transition simulation, and hashes are complete. | ||
| ## Explicit live-host runner | ||
| `--live-host-command` must be an absolute executable path. Planr starts it once per candidate/task fixture, writes one JSON request to stdin, waits for process completion, measures elapsed time, and parses one JSON response from stdout. This is an explicit arbitrary-process boundary: only run a trusted adapter. No live process is started without the flag. | ||
| For each run, Planr creates a Planr-controlled temporary workspace containing `challenge.json`. The request contains schema/suite/candidate/policy/binding/task versions, task kind, input, input hash, required artifact kind, and absolute workspace/challenge/artifact paths. It deliberately does not expose the expected output or challenge contents. The adapter must read the challenge, write a strict schema-v1 artifact to `artifact_path`, hash those exact bytes, and bind its response back to that artifact: | ||
| ```json | ||
| { | ||
| "schema_version": 1, | ||
| "host_id": "example-host", | ||
| "host_version": "1.0.0", | ||
| "candidate_id": "balanced-codex-openai", | ||
| "task_id": "browser-report-smoke", | ||
| "input_sha256": "<request input hash>", | ||
| "artifact_kind": "browser_trace", | ||
| "artifact_sha256": "<SHA-256 of the artifact file>", | ||
| "output": "balanced-codex-openai:browser-report-smoke:browser-report-inspected", | ||
| "effective_model": "model-id", | ||
| "effective_effort": "high", | ||
| "effective_context_fork": { "mode": "none" }, | ||
| "tool_calls": 1, | ||
| "tokens": 10, | ||
| "credits_micros": 100, | ||
| "retries": 1, | ||
| "availability_fallbacks": 1, | ||
| "quality_escalations": 1, | ||
| "corrections": 0, | ||
| "violations": 0 | ||
| } | ||
| ``` | ||
| The artifact file is separate from stdout and has this strict shape: | ||
| ```json | ||
| { | ||
| "schema_version": 1, | ||
| "candidate_id": "balanced-codex-openai", | ||
| "task_id": "browser-report-smoke", | ||
| "input_sha256": "<request input hash>", | ||
| "artifact_kind": "browser_trace", | ||
| "challenge_sha256": "<SHA-256 of Planr's challenge file>", | ||
| "output": "balanced-codex-openai:browser-report-smoke:browser-report-inspected" | ||
| } | ||
| ``` | ||
| Planr reads the artifact itself after the process exits, rejects missing, symlinked, oversized, malformed, or extra-field artifacts, computes its SHA-256 independently, verifies candidate/task/input/artifact and challenge binding, and finally evaluates the versioned task oracle. Neither a constant responder nor a request-aware mapper that only echoes dynamic request fields and public success labels writes this Planr-controlled artifact, so both have `task_executed = false`, `evidence_complete = false`, status `unverified`, and cannot recommend. A wholly failed live attempt also sets the report-level `reproducible_evidence = false`; merely attempting the adapter is never described as evaluated live evidence. | ||
| Production policy checks remain authoritative: tool-budget and write-capability checks are evaluated from the candidate policy, never adapter claims. A read-only candidate fails the implementation and mechanical write fixtures even if the adapter returns otherwise successful JSON. | ||
| The adapter reports effective route and usage values, but process exit does not make those claims trusted. Effective route fields are labeled `host_report` with `estimated` enforcement, so they do not increment `verified_route_runs`. Tool/token/credit metering is also `estimated`; only elapsed process latency is independently measured by Planr. Retry/fallback/escalation/correction/violation counts remain visible host reports. The process-adapter boundary therefore always sets `recommendation_eligible = false`, even when every workspace artifact passes. | ||
| ## Independently signed telemetry | ||
| Recommendation-capable live evaluation uses a separately provisioned telemetry collector. The repository trust registry at `.planr/trusted-telemetry.toml` pins both an Ed25519 signer key and the SHA-256 of the collector executable; the evaluation invocation supplies only the registry signer id and the absolute collector path. A caller-selected public key, run id, or precomputed receipt bundle is not accepted. The registry shape is: | ||
| ```toml | ||
| schema_version = 1 | ||
| [[signers]] | ||
| id = "production-collector" | ||
| public_key_hex = "<64-hex-character-ed25519-public-key>" | ||
| collector_sha256 = "<64-hex-character-executable-sha256>" | ||
| ``` | ||
| Planr creates a fresh run UUID before evaluation and a fresh challenge nonce for every task. Only after the live adapter exits and Planr reads and hashes the workspace artifact does it invoke the hash-pinned collector. The collector receives a strict JSON identity request containing the Planr-owned run id, evaluation and suite identity, candidate/task/input/artifact identity, exact artifact SHA-256, and challenge nonce. It independently adds host route, usage, transition, correction, and violation measurements, then returns `{ "payload": ..., "signature_hex": "..." }`. The adapter never receives a signing key, and pre-run receipts cannot know the fresh run/challenge/artifact tuple. | ||
| Before invocation Planr verifies that the collector is a regular absolute-path file whose SHA-256 matches the selected registry entry, and it rechecks the digest for every task. It rejects the trust upgrade for a failed collector, malformed response, bad signature, wrong run/suite/time, wrong task/input/artifact/challenge binding, blank host identity, or host identity mismatch with the live adapter response. Missing or rejected receipts do not abort artifact evaluation: that result falls back to `host_reported`/`estimated`, gets zero verified route credit, and is recommendation-ineligible. Registry, signer, and collector configuration errors fail the evaluation before execution. | ||
| For a verified post-run receipt, Planr uses only signed route, usage, transition, correction, and violation values. Effective route dimensions carry `evidence = "telemetry_receipt"` and `enforcement = "verified"`; tool/token/credit dimensions and result `metering_confidence` are `trusted`; process latency remains Planr-observed. These results use `metrics_source = "trusted_telemetry"` and may satisfy recommendation gates. | ||
| ## Lifecycle and thresholds | ||
| - `recommended`: every task artifact and oracle passes, every effective route and usage measurement has a valid signed telemetry receipt, and all thresholds pass; | ||
| - `verified`: offline simulation is complete, or every required live workspace artifact and outcome oracle passed but recommendation-grade instrumentation is unavailable or a threshold failed; | ||
| - `stale`: evaluation time is later than fixture expiry; | ||
| - `incompatible`: requested host is unsupported by the binding; | ||
| - `unverified`: required composition, transition, hash, live task execution, challenge-bound artifact, or outcome-oracle evidence is incomplete. | ||
| Only independently instrumented live task evidence can set `recommendation_evidence_complete = true`. Hashing simulator-authored JSON, process stdout, or an adapter-authored route/cost claim cannot promote it. | ||
| ## Immutable output | ||
| `--report-dir` accepts only a normalized repository-relative directory and rejects absolute paths, traversal, non-directory components, and symlink crossings. It creates `verification.json` and `report.md` without overwrite; either existing target fails the command. | ||
| The report includes suite/Planr/configured-model provenance. Live results additionally include host id/version, Planr-read workspace artifact and oracle hashes, host route/usage evidence with explicit estimated or trusted provenance, observed process latency, corrections/transitions/violations, policy-capability checks, and result hashes. | ||
| ## Sol/Luna Codex contract | ||
| `evaluations/sol-luna-codex-v1.toml` independently proves that cross-tier `fork_turns = "all"` is rejected, `none` preserves Luna parameters, missing effective evidence cannot route-verify, and process-evidenced effective model/effort/fork values can verify. |
| # Preset Registry | ||
| Planr's registry is an optional distribution boundary, never a runtime dependency. Active projects keep using their repository-local `.planr/policy.toml`, `.planr/agents.toml`, and preset lock when the registry is unavailable. Previously imported packs remain available under `.planr/registry/cache/` for deterministic offline use. | ||
| ## Trust and integrity | ||
| A schema-v1 registry manifest identifies a registry version and one or more versioned entries. Each entry declares: | ||
| - kind (`policy`, `host-binding`, or `pack`), lifecycle (`published`, `deprecated`, or `revoked`), and evaluation status; | ||
| - Planr version bounds and compatible hosts; | ||
| - verification and review timestamps, a verification artifact, and optional replacement/revocation metadata; | ||
| - for `verified` or `recommended` entries, the exact policy, binding, and evaluation-suite ids and versions covered by that evidence; | ||
| - the normalized path, declarative kind, byte length, and SHA-256 digest of every artifact; | ||
| - an optional Ed25519 signature. | ||
| Signatures do not establish their own trust. Planr verifies them only against keys provisioned separately in `.planr/registry/trusted-maintainers.toml` (or an explicitly selected trust store): | ||
| ```toml | ||
| schema_version = 1 | ||
| [[maintainers]] | ||
| id = "planr-maintainers" | ||
| public_key = "<64 hexadecimal Ed25519 public-key characters>" | ||
| revoked = false | ||
| ``` | ||
| An unsigned pack can be checksum-verified, but a manifest's `recommended` claim is demoted to `verified` unless a non-revoked pinned maintainer signature verifies. A trusted signature is necessary but not sufficient: Planr also re-runs canonical policy/binding composition and safe-artifact validation, binds the shipped bytes to the current built-in evaluation inputs, and validates the current suite/task provenance. Candidate metrics and threshold gates are derived again from the task results; result hashes, task-oracle coverage, the canonical Codex dispatch contract, trusted route/metering evidence, and the report recommendation record must all agree before `recommended` survives. A merely signed status label or stale suite therefore cannot promote an entry. | ||
| Invalid signatures, revoked entries/signers, incompatible Planr/host constraints, checksum/size mismatches, traversal, symlinks, binary or executable content, secret-like content, semantically invalid bindings, and malformed declarative artifacts fail closed. Every registry policy, including an `experimental` entry without evaluation metadata, uses the same public-distribution safety check: commands, hooks, environment grants, network/MCP access, secret references, and overwrite permission are rejected. Manifest ids, versions, hosts, references, reasons, paths, and signer metadata are screened field-by-field for secret-like values before they can reach diagnostics; public keys and signature bytes are shape-validated but deliberately not classified as secrets. Stale verification or deprecation remains visible and removes recommendation. | ||
| ## Verification and offline import | ||
| Remote retrieval is deliberately outside the command. Downloading a manifest and its content is an explicit operator action; Planr then verifies local inputs: | ||
| ```sh | ||
| planr agents preset registry verify registry.toml \ | ||
| --entry balanced-codex \ | ||
| --content-root ./download \ | ||
| --host codex | ||
| ``` | ||
| `--host` may be omitted only for entries whose `compatible_hosts` list is empty. A constrained entry with no explicit host fails closed. | ||
| Import is preview-first. The preview lists exactly the manifest-declared files and deterministic cache target. `--confirm` copies only those verified files into a manifest-hash-addressed immutable directory and atomically stores both the original registry manifest and a cache receipt: | ||
| ```sh | ||
| planr agents preset registry import registry.toml \ | ||
| --entry balanced-codex \ | ||
| --content-root ./download \ | ||
| --host codex | ||
| planr agents preset registry import registry.toml \ | ||
| --entry balanced-codex \ | ||
| --content-root ./download \ | ||
| --host codex \ | ||
| --confirm | ||
| ``` | ||
| `planr agents preset registry list` requires no source manifest or network. The mutable receipt is inventory metadata, not a checksum or trust authority. Every read binds the cache path to the stored manifest hash, re-verifies the original manifest and maintainer signature against the repository trust store, compares receipt inventory with the manifest, and recomputes artifact sizes and hashes from the manifest. Coordinated edits to content and receipt therefore remain detectable. The command reports `current` or `stale` freshness from the review timestamp and marks malformed or tampered cache entries unusable. Extra source files are never imported. | ||
| The equivalent MCP tools are `planr_preset_registry_verify`, `planr_preset_registry_import`, and `planr_preset_registry_list`. MCP import also previews unless `confirm` is true. |
| schema_version = 1 | ||
| id = "planr-preset-suite" | ||
| version = "1.8.0" | ||
| verified_at_unix = 1783987200 | ||
| expires_at_unix = 1815523200 | ||
| [thresholds] | ||
| minimum_runs = 7 | ||
| minimum_quality_score_bps = 8500 | ||
| minimum_reliability_bps = 9000 | ||
| maximum_average_credits_micros = 1200000 | ||
| maximum_p95_latency_ms = 5000 | ||
| maximum_transition_contract_failures = 0 | ||
| maximum_safety_stop_failures = 0 | ||
| require_verified_routes = true | ||
| require_result_hashes = true | ||
| [[candidates]] | ||
| id = "balanced-codex-openai" | ||
| policy = "balanced" | ||
| binding = "codex-openai" | ||
| [[candidates]] | ||
| id = "low-usage-codex-openai" | ||
| policy = "low-usage" | ||
| binding = "codex-openai" | ||
| [[candidates]] | ||
| id = "max-quality-codex-openai" | ||
| policy = "max-quality" | ||
| binding = "codex-openai" | ||
| [[candidates]] | ||
| id = "read-only-audit-codex-openai" | ||
| policy = "read-only-audit" | ||
| binding = "codex-openai" | ||
| [[tasks]] | ||
| id = "explore-routing-boundaries" | ||
| version = "1.0.0" | ||
| kind = "exploration" | ||
| objective = "Inspect policy and host routing boundaries without mutation." | ||
| input = "inspect-routing-boundaries" | ||
| artifact_kind = "inspection_manifest" | ||
| expected_output = "routing-boundaries-inspected" | ||
| work_units = 8 | ||
| transition = "retry" | ||
| requires_write = false | ||
| minimum_tool_budget = 1 | ||
| [[tasks]] | ||
| id = "implement-bounded-policy-change" | ||
| version = "1.0.0" | ||
| kind = "implementation" | ||
| objective = "Implement a bounded policy change and preserve verification evidence." | ||
| input = "implement-bounded-policy-change" | ||
| artifact_kind = "patch_manifest" | ||
| expected_output = "bounded-policy-change-implemented" | ||
| work_units = 14 | ||
| transition = "quality_escalation" | ||
| requires_write = true | ||
| minimum_tool_budget = 80 | ||
| [[tasks]] | ||
| id = "mechanical-schema-rewrite" | ||
| version = "1.0.0" | ||
| kind = "mechanical" | ||
| objective = "Apply a deterministic schema rewrite across owned files." | ||
| input = "rewrite-owned-schema" | ||
| artifact_kind = "rewrite_manifest" | ||
| expected_output = "owned-schema-rewritten" | ||
| work_units = 11 | ||
| transition = "availability_fallback" | ||
| requires_write = true | ||
| minimum_tool_budget = 80 | ||
| [[tasks]] | ||
| id = "browser-report-smoke" | ||
| version = "1.0.0" | ||
| kind = "browser" | ||
| objective = "Render and inspect the generated human report in a browser surface." | ||
| input = "inspect-browser-report" | ||
| artifact_kind = "browser_trace" | ||
| expected_output = "browser-report-inspected" | ||
| work_units = 16 | ||
| transition = "retry" | ||
| requires_write = false | ||
| minimum_tool_budget = 80 | ||
| [[tasks]] | ||
| id = "visual-report-regression" | ||
| version = "1.0.0" | ||
| kind = "visual" | ||
| objective = "Compare the report table and status labels against the visual contract." | ||
| input = "compare-visual-report" | ||
| artifact_kind = "visual_diff" | ||
| expected_output = "visual-contract-matched" | ||
| work_units = 13 | ||
| transition = "quality_escalation" | ||
| requires_write = false | ||
| minimum_tool_budget = 80 | ||
| [[tasks]] | ||
| id = "security-safety-stop" | ||
| version = "1.0.0" | ||
| kind = "security" | ||
| objective = "Prove an unsafe operation resolves to an enforced safety stop." | ||
| input = "attempt-unsafe-operation" | ||
| artifact_kind = "safety_receipt" | ||
| expected_output = "unsafe-operation-stopped" | ||
| work_units = 9 | ||
| transition = "safety_stop" | ||
| requires_write = false | ||
| minimum_tool_budget = 1 | ||
| [[tasks]] | ||
| id = "subagent-sol-luna-dispatch" | ||
| version = "1.0.0" | ||
| kind = "subagent" | ||
| objective = "Verify Sol/Luna cross-tier dispatch and effective route evidence." | ||
| input = "dispatch-sol-luna-worker" | ||
| artifact_kind = "dispatch_trace" | ||
| expected_output = "sol-luna-dispatch-verified" | ||
| work_units = 12 | ||
| transition = "availability_fallback" | ||
| requires_write = false | ||
| minimum_tool_budget = 1 |
| schema_version = 1 | ||
| id = "sol-luna-codex" | ||
| version = "1.0.0" | ||
| host = "codex" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["fixture-codex-none-fork-contract-v1"] | ||
| billing_assumptions = ["deterministic evaluation fixture; no provider call"] | ||
| known_limitations = ["effective model and effort require host evidence"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = true | ||
| fork_none = true | ||
| fork_all = true | ||
| max_partial_fork_turns = 4 | ||
| [profiles.driver] | ||
| profile = "sol" | ||
| client = "codex" | ||
| model = "gpt-5.5" | ||
| effort = "xhigh" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "luna" | ||
| client = "codex" | ||
| model = "gpt-5.4-mini" | ||
| effort = "high" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "fixture-sol-luna-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 |
| schema_version = 1 | ||
| id = "claude-native" | ||
| version = "1.0.0" | ||
| host = "claude-code" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["builtin-claude-project-agent-contract"] | ||
| billing_assumptions = ["Planr records declared tiers; Claude Code remains billing authority"] | ||
| known_limitations = ["session environment may preempt the requested subagent model"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = true | ||
| fork_none = true | ||
| fork_all = false | ||
| [profiles.driver] | ||
| profile = "claude-native-driver" | ||
| client = "claude-code" | ||
| model = "opus" | ||
| effort = "high" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "claude-native-worker" | ||
| client = "claude-code" | ||
| model = "sonnet" | ||
| effort = "medium" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "builtin-claude-native-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 | ||
| [[artifacts]] | ||
| path = ".claude/agents/planr-preset-worker.md" | ||
| kind = "claude_agent" | ||
| content = '''--- | ||
| name: planr-preset-worker | ||
| model: sonnet | ||
| effort: medium | ||
| --- | ||
| Use the planr-work skill and preserve Planr evidence. | ||
| ''' |
| schema_version = 1 | ||
| id = "codex-openai" | ||
| version = "1.0.0" | ||
| host = "codex" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["builtin-codex-none-fork-contract"] | ||
| billing_assumptions = ["Planr records declared tiers; Codex remains billing authority"] | ||
| known_limitations = ["effective model and effort require host-reported evidence"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = true | ||
| fork_none = true | ||
| fork_all = false | ||
| max_partial_fork_turns = 4 | ||
| [profiles.driver] | ||
| profile = "codex-openai-driver" | ||
| client = "codex" | ||
| model = "gpt-5.5" | ||
| effort = "xhigh" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "codex-openai-worker" | ||
| client = "codex" | ||
| model = "gpt-5.4-mini" | ||
| effort = "high" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "builtin-codex-openai-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 | ||
| [[artifacts]] | ||
| path = ".codex/agents/planr-preset-worker.toml" | ||
| kind = "codex_agent" | ||
| content = '''name = "planr_preset_worker" | ||
| description = "Implements one picked Planr map item with evidence-backed handoff." | ||
| model = "gpt-5.4-mini" | ||
| model_reasoning_effort = "high" | ||
| sandbox_mode = "workspace-write" | ||
| developer_instructions = """ | ||
| Use the planr-work skill for exactly one picked item. Preserve the task scope, record changed files and real verification commands, request review, and stop. | ||
| """ | ||
| ''' |
| schema_version = 1 | ||
| id = "cursor-fable-grok" | ||
| version = "1.0.0" | ||
| host = "cursor" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["builtin-cursor-project-agent-contract"] | ||
| billing_assumptions = ["Planr records declared tiers; Cursor remains billing authority"] | ||
| known_limitations = ["workspace policy or Max Mode may override the requested model"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = false | ||
| fork_none = true | ||
| fork_all = false | ||
| [profiles.driver] | ||
| profile = "cursor-fable-driver" | ||
| client = "cursor" | ||
| model = "fable-5" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "cursor-grok-worker" | ||
| client = "cursor" | ||
| model = "grok-code-fast-1" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "builtin-cursor-fable-grok-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 | ||
| [[artifacts]] | ||
| path = ".cursor/agents/planr-preset-worker.md" | ||
| kind = "cursor_agent" | ||
| content = '''--- | ||
| name: planr-preset-worker | ||
| model: grok-code-fast-1 | ||
| --- | ||
| Use the planr-work skill and preserve Planr evidence. | ||
| ''' |
| schema_version = 1 | ||
| id = "cursor-openai" | ||
| version = "1.0.0" | ||
| host = "cursor" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["builtin-cursor-project-agent-contract"] | ||
| billing_assumptions = ["Planr records declared tiers; Cursor remains billing authority"] | ||
| known_limitations = ["workspace policy or Max Mode may override the requested model"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = false | ||
| fork_none = true | ||
| fork_all = false | ||
| [profiles.driver] | ||
| profile = "cursor-openai-driver" | ||
| client = "cursor" | ||
| model = "gpt-5.5" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "cursor-openai-worker" | ||
| client = "cursor" | ||
| model = "gpt-5.4-mini" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "builtin-cursor-openai-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 | ||
| [[artifacts]] | ||
| path = ".cursor/agents/planr-preset-worker.md" | ||
| kind = "cursor_agent" | ||
| content = '''--- | ||
| name: planr-preset-worker | ||
| model: gpt-5.4-mini | ||
| --- | ||
| Use the planr-work skill and preserve Planr evidence. | ||
| ''' |
| schema_version = 1 | ||
| id = "mixed-host" | ||
| version = "1.0.0" | ||
| host = "mixed-host" | ||
| driver_role = "driver" | ||
| default_role = "driver" | ||
| capability_evidence = ["builtin-explicit-cross-client-contract"] | ||
| billing_assumptions = ["Each host remains authoritative for its own billing and availability"] | ||
| known_limitations = ["effective model evidence must be collected independently from each host"] | ||
| [capabilities] | ||
| model_override = true | ||
| effort_override = true | ||
| fork_none = true | ||
| fork_all = false | ||
| [profiles.driver] | ||
| profile = "mixed-cursor-driver" | ||
| client = "cursor" | ||
| model = "fable-5" | ||
| cost_tier = "premium" | ||
| [profiles.worker] | ||
| profile = "mixed-codex-worker" | ||
| client = "codex" | ||
| model = "gpt-5.4-mini" | ||
| effort = "high" | ||
| cost_tier = "standard" | ||
| skill = "planr-work" | ||
| fork_turns = { mode = "none" } | ||
| [[routes]] | ||
| work_type = "code" | ||
| role = "worker" | ||
| fallback_roles = ["driver"] | ||
| [verification] | ||
| id = "builtin-mixed-host-v1" | ||
| verified_at_unix = 1783987200 | ||
| max_age_seconds = 31536000 | ||
| [[artifacts]] | ||
| path = ".cursor/agents/planr-preset-driver.md" | ||
| kind = "cursor_agent" | ||
| content = '''--- | ||
| name: planr-preset-driver | ||
| model: fable-5 | ||
| --- | ||
| Drive the Planr map and keep final verdict ownership. | ||
| ''' | ||
| [[artifacts]] | ||
| path = ".codex/agents/planr-preset-worker.toml" | ||
| kind = "codex_agent" | ||
| content = '''name = "planr_preset_worker" | ||
| description = "Implements one picked Planr map item with evidence-backed handoff." | ||
| model = "gpt-5.4-mini" | ||
| model_reasoning_effort = "high" | ||
| sandbox_mode = "workspace-write" | ||
| developer_instructions = """ | ||
| Use the planr-work skill for exactly one picked item. Preserve the task scope, record changed files and real verification commands, request review, and stop. | ||
| """ | ||
| ''' |
| schema_version = 1 | ||
| id = "balanced" | ||
| version = "1.0.0" | ||
| [usage] | ||
| max_active_agents = 3 | ||
| max_parallel_readers = 2 | ||
| max_parallel_writers = 1 | ||
| max_depth = 1 | ||
| max_attempts = 4 | ||
| max_wall_time_seconds = 3600 | ||
| max_tool_calls = 120 | ||
| review_reserve_percent = 20 | ||
| budget_exhaustion = "stop" | ||
| metering = "estimated" | ||
| [transitions.retry] | ||
| max_same_route_retries = 1 | ||
| [transitions.availability_fallback] | ||
| max_fallbacks = 1 | ||
| require_same_capability_class = true | ||
| [transitions.quality_escalation] | ||
| max_escalations = 1 | ||
| require_verification_evidence = true | ||
| [transitions.quota_downgrade] | ||
| enabled = false | ||
| max_downgrades = 0 | ||
| noncritical_only = true | ||
| [transitions.safety_stop] | ||
| enabled = true | ||
| [materiality] | ||
| changed_files_threshold = 10 | ||
| changed_lines_threshold = 500 | ||
| [execution] | ||
| max_read_scope_entries = 8 | ||
| max_write_scope_entries = 4 | ||
| [execution.roles.worker] | ||
| tools = ["cargo", "git", "rg"] | ||
| [execution.roles.worker.filesystem] | ||
| read_roots = ["src", "tests", "docs"] | ||
| write_roots = ["src", "tests", "docs"] | ||
| allow_overwrite = false |
| schema_version = 1 | ||
| id = "low-usage" | ||
| version = "1.0.0" | ||
| [usage] | ||
| max_active_agents = 2 | ||
| max_parallel_readers = 1 | ||
| max_parallel_writers = 1 | ||
| max_depth = 1 | ||
| max_attempts = 3 | ||
| max_wall_time_seconds = 1800 | ||
| max_tool_calls = 60 | ||
| review_reserve_percent = 25 | ||
| budget_exhaustion = "downgrade_noncritical" | ||
| metering = "estimated" | ||
| [transitions.retry] | ||
| max_same_route_retries = 0 | ||
| [transitions.availability_fallback] | ||
| max_fallbacks = 1 | ||
| require_same_capability_class = true | ||
| [transitions.quality_escalation] | ||
| max_escalations = 1 | ||
| require_verification_evidence = true | ||
| [transitions.quota_downgrade] | ||
| enabled = true | ||
| max_downgrades = 1 | ||
| noncritical_only = true | ||
| [transitions.safety_stop] | ||
| enabled = true | ||
| [materiality] | ||
| changed_files_threshold = 6 | ||
| changed_lines_threshold = 250 | ||
| [execution] | ||
| max_read_scope_entries = 6 | ||
| max_write_scope_entries = 3 | ||
| [execution.roles.worker] | ||
| tools = ["cargo", "git", "rg"] | ||
| [execution.roles.worker.filesystem] | ||
| read_roots = ["src", "tests", "docs"] | ||
| write_roots = ["src", "tests", "docs"] | ||
| allow_overwrite = false |
| schema_version = 1 | ||
| id = "max-quality" | ||
| version = "1.0.0" | ||
| [usage] | ||
| max_active_agents = 4 | ||
| max_parallel_readers = 3 | ||
| max_parallel_writers = 1 | ||
| max_depth = 1 | ||
| max_attempts = 5 | ||
| max_wall_time_seconds = 7200 | ||
| max_tool_calls = 240 | ||
| review_reserve_percent = 40 | ||
| budget_exhaustion = "stop" | ||
| metering = "estimated" | ||
| [transitions.retry] | ||
| max_same_route_retries = 1 | ||
| [transitions.availability_fallback] | ||
| max_fallbacks = 1 | ||
| require_same_capability_class = true | ||
| [transitions.quality_escalation] | ||
| max_escalations = 2 | ||
| require_verification_evidence = true | ||
| [transitions.quota_downgrade] | ||
| enabled = false | ||
| max_downgrades = 0 | ||
| noncritical_only = true | ||
| [transitions.safety_stop] | ||
| enabled = true | ||
| [materiality] | ||
| changed_files_threshold = 5 | ||
| changed_lines_threshold = 200 | ||
| [execution] | ||
| max_read_scope_entries = 12 | ||
| max_write_scope_entries = 6 | ||
| [execution.roles.worker] | ||
| tools = ["cargo", "git", "rg"] | ||
| [execution.roles.worker.filesystem] | ||
| read_roots = ["src", "tests", "docs"] | ||
| write_roots = ["src", "tests", "docs"] | ||
| allow_overwrite = false |
| schema_version = 1 | ||
| id = "read-only-audit" | ||
| version = "1.0.0" | ||
| [usage] | ||
| max_active_agents = 3 | ||
| max_parallel_readers = 3 | ||
| max_parallel_writers = 0 | ||
| max_depth = 1 | ||
| max_attempts = 2 | ||
| max_wall_time_seconds = 1800 | ||
| max_tool_calls = 80 | ||
| review_reserve_percent = 20 | ||
| budget_exhaustion = "stop" | ||
| metering = "estimated" | ||
| [transitions.retry] | ||
| max_same_route_retries = 0 | ||
| [transitions.availability_fallback] | ||
| max_fallbacks = 1 | ||
| require_same_capability_class = true | ||
| [transitions.quality_escalation] | ||
| max_escalations = 0 | ||
| require_verification_evidence = true | ||
| [transitions.quota_downgrade] | ||
| enabled = false | ||
| max_downgrades = 0 | ||
| noncritical_only = true | ||
| [transitions.safety_stop] | ||
| enabled = true | ||
| [materiality] | ||
| changed_files_threshold = 1 | ||
| changed_lines_threshold = 1 | ||
| [execution] | ||
| max_read_scope_entries = 12 | ||
| max_write_scope_entries = 1 | ||
| [execution.roles.worker] | ||
| tools = ["git", "rg"] | ||
| [execution.roles.worker.filesystem] | ||
| read_roots = ["src", "tests", "docs"] | ||
| write_roots = [] | ||
| allow_overwrite = false |
Sorry, the diff of this file is not supported yet
| import assert from "node:assert/strict"; | ||
| import test from "node:test"; | ||
| import { | ||
| assertAlchemyRuntime, | ||
| MINIMUM_ALCHEMY_NODE_MAJOR, | ||
| } from "../scripts/check-alchemy-runtime.mjs"; | ||
| test("accepts the minimum supported Alchemy deployment runtime", () => { | ||
| assert.equal(assertAlchemyRuntime("22.0.0"), MINIMUM_ALCHEMY_NODE_MAJOR); | ||
| }); | ||
| test("rejects Node runtimes supported by the Planr CLI but not by Alchemy", () => { | ||
| assert.throws( | ||
| () => assertAlchemyRuntime("18.16.1"), | ||
| /Cloudflare deployment requires Node\.js 22 or newer; current runtime is 18\.16\.1/, | ||
| ); | ||
| assert.throws( | ||
| () => assertAlchemyRuntime("20.19.5"), | ||
| /Cloudflare deployment requires Node\.js 22 or newer/, | ||
| ); | ||
| }); |
+216
| import { formatBasisPoints, previewCommand, statusLabel, visibleCompositions } from "./catalog-model.mjs"; | ||
| const nodes = { | ||
| selectA: document.querySelector("#composition-a"), | ||
| selectB: document.querySelector("#composition-b"), | ||
| cardA: document.querySelector("#comparison-a"), | ||
| cardB: document.querySelector("#comparison-b"), | ||
| comparison: document.querySelector("#comparison"), | ||
| empty: document.querySelector("#empty-state"), | ||
| emptyMessage: document.querySelector("#empty-message"), | ||
| filter: document.querySelector("#recommended-only"), | ||
| published: document.querySelector("#stat-published"), | ||
| recommended: document.querySelector("#stat-recommended"), | ||
| trust: document.querySelector("#stat-trust"), | ||
| generated: document.querySelector("#generated-at"), | ||
| copyStatus: document.querySelector("#copy-status"), | ||
| fixtureBanner: document.querySelector("#fixture-banner"), | ||
| }; | ||
| function catalogLocation() { | ||
| const local = ["127.0.0.1", "localhost"].includes(location.hostname); | ||
| const fixture = new URLSearchParams(location.search).get("fixture"); | ||
| if (local && fixture === "recommended") { | ||
| nodes.fixtureBanner.hidden = false; | ||
| return "./test-fixtures/recommended.json"; | ||
| } | ||
| return "./data/catalog.json"; | ||
| } | ||
| function text(parent, element, value, className) { | ||
| const node = document.createElement(element); | ||
| if (className) node.className = className; | ||
| node.textContent = value; | ||
| parent.append(node); | ||
| return node; | ||
| } | ||
| function formatDate(unix) { | ||
| if (!Number.isFinite(unix)) return "Not published"; | ||
| return new Intl.DateTimeFormat("en", { dateStyle: "medium", timeZone: "UTC" }).format(new Date(unix * 1000)); | ||
| } | ||
| function formatNumber(value) { | ||
| return Number.isFinite(Number(value)) ? new Intl.NumberFormat("en").format(Number(value)) : "—"; | ||
| } | ||
| function optionLabel(entry) { | ||
| return `${entry.policy.id} + ${entry.binding.id} · ${statusLabel(entry.status)}`; | ||
| } | ||
| function fillSelect(select, entries, preferredIndex) { | ||
| const previous = select.value; | ||
| select.replaceChildren(); | ||
| for (const entry of entries) { | ||
| const option = document.createElement("option"); | ||
| option.value = entry.id; | ||
| option.textContent = optionLabel(entry); | ||
| select.append(option); | ||
| } | ||
| select.value = entries.some((entry) => entry.id === previous) ? previous : entries[Math.min(preferredIndex, entries.length - 1)]?.id ?? ""; | ||
| } | ||
| function metric(parent, label, value) { | ||
| const wrapper = document.createElement("div"); | ||
| wrapper.className = "metric"; | ||
| text(wrapper, "span", label); | ||
| text(wrapper, "strong", value); | ||
| parent.append(wrapper); | ||
| } | ||
| function section(parent, title) { | ||
| const wrapper = document.createElement("section"); | ||
| wrapper.className = "card-section"; | ||
| text(wrapper, "h4", title); | ||
| parent.append(wrapper); | ||
| return wrapper; | ||
| } | ||
| function definitionRow(list, termValue, detailValue) { | ||
| const row = document.createElement("div"); | ||
| text(row, "dt", termValue); | ||
| text(row, "dd", detailValue); | ||
| list.append(row); | ||
| } | ||
| function renderCard(card, entry) { | ||
| card.replaceChildren(); | ||
| if (!entry) return; | ||
| card.dataset.compositionId = entry.id; | ||
| const top = document.createElement("div"); | ||
| top.className = "card-top"; | ||
| const title = document.createElement("div"); | ||
| text(title, "span", `${entry.binding.id} · ${entry.binding.host}`, "binding-name"); | ||
| text(title, "h3", entry.policy.id); | ||
| const badge = text(top, "span", statusLabel(entry.status), `badge status-${entry.status}`); | ||
| badge.setAttribute("aria-label", `Status: ${statusLabel(entry.status)}`); | ||
| top.prepend(title); | ||
| card.append(top); | ||
| text(card, "p", `Policy ${entry.policy.version} · Binding ${entry.binding.version} · Entry ${entry.entryVersion}`, "meta-line"); | ||
| const metrics = document.createElement("div"); | ||
| metrics.className = "metric-grid"; | ||
| metric(metrics, "Active agents", formatNumber(entry.policy.usage.max_active_agents)); | ||
| metric(metrics, "Parallel writers", formatNumber(entry.policy.usage.max_parallel_writers)); | ||
| metric(metrics, "Max depth", formatNumber(entry.policy.usage.max_depth)); | ||
| metric(metrics, "Metering", entry.policy.usage.metering ?? "unavailable"); | ||
| card.append(metrics); | ||
| const compatibility = section(card, "Compatibility"); | ||
| const compatibilityList = document.createElement("dl"); | ||
| compatibilityList.className = "compatibility-list"; | ||
| definitionRow( | ||
| compatibilityList, | ||
| "Supported hosts", | ||
| entry.compatibility.hosts?.length ? entry.compatibility.hosts.join(", ") : "No compatible host declared", | ||
| ); | ||
| definitionRow( | ||
| compatibilityList, | ||
| "Planr versions", | ||
| `${entry.compatibility.minPlanrVersion ?? "Any"} through ${entry.compatibility.maxPlanrVersion ?? "latest"}`, | ||
| ); | ||
| definitionRow( | ||
| compatibilityList, | ||
| "Lifecycle", | ||
| entry.replacement | ||
| ? `${statusLabel(entry.status)}; replace with ${entry.replacement}` | ||
| : statusLabel(entry.status), | ||
| ); | ||
| compatibility.append(compatibilityList); | ||
| const enforcement = section(card, "Enforcement matrix"); | ||
| const list = document.createElement("dl"); | ||
| list.className = "enforcement-list"; | ||
| for (const item of entry.enforcement) { | ||
| const row = document.createElement("div"); | ||
| const term = text(row, "dt", item.dimension); | ||
| text(term, "span", item.state.replaceAll("_", " "), "state"); | ||
| text(row, "dd", item.detail); | ||
| list.append(row); | ||
| } | ||
| enforcement.append(list); | ||
| const evidence = section(card, "Evaluation & provenance"); | ||
| text(evidence, "p", `${entry.evaluation.suiteId} ${entry.evaluation.suiteVersion} · evaluated ${formatDate(entry.evaluation.evaluatedAtUnix)} · review ${formatDate(entry.evaluation.reviewAtUnix)}`, "meta-line"); | ||
| text(evidence, "p", `Quality ${formatBasisPoints(entry.evaluation.metrics?.average_quality_score_bps)} · runs ${formatNumber(entry.evaluation.metrics?.runs)} · oracle passes ${formatNumber(entry.evaluation.metrics?.oracle_passes)}`, "meta-line"); | ||
| text(evidence, "p", `Manifest ${entry.registry.manifestSha256}`, "hash"); | ||
| text(evidence, "p", `Signer ${entry.registry.signer} · signature verified · trusted maintainer`, "meta-line"); | ||
| const commandSection = section(card, "Preview safely"); | ||
| const commandBlock = document.createElement("div"); | ||
| commandBlock.className = "command-block"; | ||
| const command = previewCommand(entry.policy.id, entry.binding.id); | ||
| text(commandBlock, "code", command); | ||
| const button = text(commandBlock, "button", "Copy preview command", "copy-button"); | ||
| button.type = "button"; | ||
| button.dataset.command = command; | ||
| commandSection.append(commandBlock); | ||
| } | ||
| function wireCopy() { | ||
| document.addEventListener("click", async (event) => { | ||
| const button = event.target.closest("button[data-command]"); | ||
| if (!button) return; | ||
| try { | ||
| await navigator.clipboard.writeText(button.dataset.command); | ||
| button.textContent = "Copied"; | ||
| nodes.copyStatus.textContent = `Copied preview command for ${button.closest(".preset-card").dataset.compositionId}`; | ||
| setTimeout(() => { button.textContent = "Copy preview command"; }, 1400); | ||
| } catch { | ||
| nodes.copyStatus.textContent = "Clipboard access was unavailable; select the command text to copy it manually."; | ||
| } | ||
| }); | ||
| } | ||
| function render(catalog) { | ||
| const entries = visibleCompositions(catalog, nodes.filter.checked); | ||
| nodes.published.textContent = formatNumber(catalog.compositions.length); | ||
| nodes.recommended.textContent = formatNumber(catalog.compositions.filter((entry) => entry.recommended).length); | ||
| nodes.trust.textContent = catalog.source?.state === "verified_registry_projection" ? "Signed registry" : "Publication gated"; | ||
| nodes.generated.textContent = Number.isFinite(catalog.generatedAtUnix) | ||
| ? `Projection generated ${formatDate(catalog.generatedAtUnix)} · ${catalog.source.trust}` | ||
| : "No signed registry projection published."; | ||
| fillSelect(nodes.selectA, entries, 0); | ||
| fillSelect(nodes.selectB, entries, 1); | ||
| const empty = entries.length === 0; | ||
| nodes.empty.hidden = !empty; | ||
| nodes.comparison.hidden = empty; | ||
| if (empty) { | ||
| nodes.emptyMessage.textContent = catalog.source?.message ?? (nodes.filter.checked ? "No published composition currently carries an evaluation-backed recommendation." : "No trusted compositions are published."); | ||
| return; | ||
| } | ||
| const find = (id) => entries.find((entry) => entry.id === id); | ||
| renderCard(nodes.cardA, find(nodes.selectA.value)); | ||
| renderCard(nodes.cardB, find(nodes.selectB.value)); | ||
| } | ||
| async function start() { | ||
| wireCopy(); | ||
| const response = await fetch(catalogLocation(), { cache: "no-store" }); | ||
| if (!response.ok) throw new Error(`catalog request failed with HTTP ${response.status}`); | ||
| const catalog = await response.json(); | ||
| if (catalog.schemaVersion !== 1 || !Array.isArray(catalog.compositions)) throw new Error("unsupported catalog data"); | ||
| const update = () => render(catalog); | ||
| nodes.filter.addEventListener("change", update); | ||
| nodes.selectA.addEventListener("change", update); | ||
| nodes.selectB.addEventListener("change", update); | ||
| update(); | ||
| } | ||
| start().catch((error) => { | ||
| nodes.empty.hidden = false; | ||
| nodes.comparison.hidden = true; | ||
| nodes.emptyMessage.textContent = `Catalog unavailable: ${error.message}`; | ||
| nodes.trust.textContent = "Unavailable"; | ||
| }); |
| #!/usr/bin/env node | ||
| import { spawnSync } from "node:child_process"; | ||
| import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; | ||
| import { mkdir } from "node:fs/promises"; | ||
| import { tmpdir } from "node:os"; | ||
| import { dirname, isAbsolute, join, resolve } from "node:path"; | ||
| import { projectComposition, safeIdentifier } from "./catalog-model.mjs"; | ||
| function usage(message) { | ||
| if (message) console.error(message); | ||
| console.error( | ||
| "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>]", | ||
| ); | ||
| process.exit(2); | ||
| } | ||
| function parseArgs(argv) { | ||
| const values = { entries: [], planrBin: "planr" }; | ||
| for (let index = 0; index < argv.length; index += 1) { | ||
| const flag = argv[index]; | ||
| const value = argv[index + 1]; | ||
| if (!value) usage(`missing value for ${flag}`); | ||
| index += 1; | ||
| if (flag === "--entry") values.entries.push(value); | ||
| else if (flag === "--manifest") values.manifest = value; | ||
| else if (flag === "--content-root") values.contentRoot = value; | ||
| else if (flag === "--trust-store") values.trustStore = value; | ||
| else if (flag === "--at-unix") values.atUnix = Number(value); | ||
| else if (flag === "--output") values.output = value; | ||
| else if (flag === "--planr-bin") values.planrBin = value; | ||
| else usage(`unknown argument ${flag}`); | ||
| } | ||
| if ( | ||
| !values.manifest || | ||
| !values.contentRoot || | ||
| !values.trustStore || | ||
| !values.output || | ||
| !Number.isSafeInteger(values.atUnix) || | ||
| values.entries.length === 0 | ||
| ) { | ||
| usage("all required arguments must be provided"); | ||
| } | ||
| return values; | ||
| } | ||
| function runJson(binary, args, cwd) { | ||
| const result = spawnSync(binary, args, { cwd, encoding: "utf8" }); | ||
| if (result.error) throw result.error; | ||
| if (result.status !== 0) { | ||
| throw new Error(result.stderr.trim() || result.stdout.trim() || `${binary} exited ${result.status}`); | ||
| } | ||
| try { | ||
| return JSON.parse(result.stdout); | ||
| } catch (error) { | ||
| throw new Error(`${binary} returned invalid JSON: ${error.message}`); | ||
| } | ||
| } | ||
| function entrySpec(raw) { | ||
| const separator = raw.lastIndexOf("="); | ||
| if (separator < 1 || separator === raw.length - 1) usage(`invalid --entry ${raw}; expected id=host`); | ||
| return { id: raw.slice(0, separator), host: raw.slice(separator + 1) }; | ||
| } | ||
| function artifactPath(verified, kind, contentRoot) { | ||
| const matches = verified.entry.artifacts.filter((artifact) => artifact.kind === kind); | ||
| if (matches.length !== 1) throw new Error(`entry ${verified.entry.id} must contain one ${kind}`); | ||
| return resolve(contentRoot, matches[0].path); | ||
| } | ||
| const options = parseArgs(process.argv.slice(2)); | ||
| const invocationRoot = process.cwd(); | ||
| const planrBin = | ||
| isAbsolute(options.planrBin) || !options.planrBin.includes("/") | ||
| ? options.planrBin | ||
| : resolve(invocationRoot, options.planrBin); | ||
| const manifest = resolve(invocationRoot, options.manifest); | ||
| const contentRoot = resolve(invocationRoot, options.contentRoot); | ||
| const trustStore = resolve(invocationRoot, options.trustStore); | ||
| const scratch = mkdtempSync(join(tmpdir(), "planr-preset-site-")); | ||
| try { | ||
| const compositions = options.entries.map((raw) => { | ||
| const { id, host } = entrySpec(raw); | ||
| const verified = runJson( | ||
| planrBin, | ||
| [ | ||
| "--json", | ||
| "agents", | ||
| "preset", | ||
| "registry", | ||
| "verify", | ||
| manifest, | ||
| "--entry", | ||
| id, | ||
| "--content-root", | ||
| contentRoot, | ||
| "--trust-store", | ||
| trustStore, | ||
| "--at-unix", | ||
| String(options.atUnix), | ||
| "--host", | ||
| host, | ||
| ], | ||
| scratch, | ||
| ); | ||
| const verificationPath = artifactPath(verified, "verification", contentRoot); | ||
| const policyId = safeIdentifier(verified.entry.evaluation?.policy_id, "policy id"); | ||
| const bindingId = safeIdentifier(verified.entry.evaluation?.binding_id, "binding id"); | ||
| const preview = runJson( | ||
| planrBin, | ||
| ["--json", "agents", "preset", "apply", policyId, "--binding", bindingId], | ||
| scratch, | ||
| ); | ||
| const verificationEnvelope = JSON.parse(readFileSync(verificationPath, "utf8")); | ||
| return projectComposition({ verified, preview, verificationEnvelope }); | ||
| }); | ||
| const catalog = { | ||
| schemaVersion: 1, | ||
| generatedAtUnix: options.atUnix, | ||
| source: { | ||
| state: "verified_registry_projection", | ||
| manifest: options.manifest, | ||
| entryCount: compositions.length, | ||
| trust: "planr_registry_v1", | ||
| }, | ||
| compositions, | ||
| }; | ||
| const output = resolve(invocationRoot, options.output); | ||
| await mkdir(dirname(output), { recursive: true }); | ||
| writeFileSync(output, `${JSON.stringify(catalog, null, 2)}\n`, { mode: 0o644 }); | ||
| console.log(`wrote ${compositions.length} verified composition(s) to ${output}`); | ||
| } finally { | ||
| rmSync(scratch, { recursive: true, force: true }); | ||
| } |
| import assert from "node:assert/strict"; | ||
| import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; | ||
| import { tmpdir } from "node:os"; | ||
| import { join } from "node:path"; | ||
| import test from "node:test"; | ||
| import { | ||
| buildSite, | ||
| PUBLIC_SITE_FILES, | ||
| verifyPublication, | ||
| } from "../scripts/build-site.mjs"; | ||
| async function fixture() { | ||
| const root = await mkdtemp(join(tmpdir(), "planr-site-build-")); | ||
| const sourceRoot = join(root, "website"); | ||
| const outputRoot = join(root, "dist", "website"); | ||
| for (const relativePath of PUBLIC_SITE_FILES) { | ||
| const path = join(sourceRoot, relativePath); | ||
| await mkdir(join(path, ".."), { recursive: true }); | ||
| await writeFile(path, `public:${relativePath}\n`); | ||
| } | ||
| return { root, sourceRoot, outputRoot }; | ||
| } | ||
| test("builds only the public runtime allowlist and preserves nested catalog bytes", async (t) => { | ||
| const paths = await fixture(); | ||
| t.after(() => rm(paths.root, { recursive: true, force: true })); | ||
| await writeFile(join(paths.sourceRoot, "build-catalog.mjs"), "must not be public\n"); | ||
| await mkdir(join(paths.sourceRoot, "registry"), { recursive: true }); | ||
| await writeFile(join(paths.sourceRoot, "registry", "trusted-maintainers.toml"), "not public\n"); | ||
| const files = await buildSite(paths); | ||
| assert.deepEqual(files, [...PUBLIC_SITE_FILES].sort()); | ||
| assert.equal( | ||
| await readFile(join(paths.outputRoot, "data", "catalog.json"), "utf8"), | ||
| "public:data/catalog.json\n", | ||
| ); | ||
| }); | ||
| test("fails before replacing output when a required source artifact is missing", async (t) => { | ||
| const paths = await fixture(); | ||
| t.after(() => rm(paths.root, { recursive: true, force: true })); | ||
| await mkdir(paths.outputRoot, { recursive: true }); | ||
| await writeFile(join(paths.outputRoot, "sentinel"), "keep on validation failure\n"); | ||
| await rm(join(paths.sourceRoot, "styles.css")); | ||
| await assert.rejects(() => buildSite(paths), /missing public website artifact: styles\.css/); | ||
| assert.equal(await readFile(join(paths.outputRoot, "sentinel"), "utf8"), "keep on validation failure\n"); | ||
| }); | ||
| test("publication verification rejects unexpected nested output", async (t) => { | ||
| const paths = await fixture(); | ||
| t.after(() => rm(paths.root, { recursive: true, force: true })); | ||
| await buildSite(paths); | ||
| await mkdir(join(paths.outputRoot, "registry"), { recursive: true }); | ||
| await writeFile(join(paths.outputRoot, "registry", "manifest.toml"), "unexpected\n"); | ||
| await assert.rejects(() => verifyPublication(paths.outputRoot), /publish output mismatch/); | ||
| }); | ||
| test("refuses an output directory inside the source tree", async (t) => { | ||
| const paths = await fixture(); | ||
| t.after(() => rm(paths.root, { recursive: true, force: true })); | ||
| await assert.rejects( | ||
| () => buildSite({ sourceRoot: paths.sourceRoot, outputRoot: join(paths.sourceRoot, "dist") }), | ||
| /must not be inside/, | ||
| ); | ||
| }); |
| const IDENTIFIER = /^[A-Za-z0-9._-]+$/; | ||
| export function safeIdentifier(value, label = "identifier") { | ||
| if (typeof value !== "string" || !IDENTIFIER.test(value)) { | ||
| throw new Error(`${label} is not a safe registry identifier`); | ||
| } | ||
| return value; | ||
| } | ||
| export function previewCommand(policyId, bindingId) { | ||
| return `planr agents preset apply ${safeIdentifier(policyId, "policy id")} --binding ${safeIdentifier(bindingId, "binding id")}`; | ||
| } | ||
| export function statusLabel(status) { | ||
| const labels = { | ||
| experimental: "Experimental", | ||
| verified: "Verified", | ||
| recommended: "Recommended", | ||
| stale: "Stale", | ||
| deprecated: "Deprecated", | ||
| }; | ||
| return labels[status] ?? "Unverified"; | ||
| } | ||
| export function formatBasisPoints(value) { | ||
| const basisPoints = Number(value); | ||
| return Number.isFinite(basisPoints) ? `${(basisPoints / 100).toFixed(2)}%` : "—"; | ||
| } | ||
| function findArtifact(entry, kind) { | ||
| const artifacts = entry.artifacts.filter((artifact) => artifact.kind === kind); | ||
| if (artifacts.length !== 1) { | ||
| throw new Error(`registry entry must contain exactly one ${kind} artifact`); | ||
| } | ||
| return artifacts[0]; | ||
| } | ||
| function proposedConfig(preview, kind) { | ||
| const artifact = preview.artifacts.find((candidate) => candidate.kind === kind); | ||
| const value = artifact?.config_diff?.proposed?.value; | ||
| if (!value || typeof value !== "object") { | ||
| throw new Error(`preset preview is missing proposed ${kind} configuration`); | ||
| } | ||
| return value; | ||
| } | ||
| function matchingCandidate(report, policyId, bindingId) { | ||
| const candidate = report.candidates?.find( | ||
| (value) => value.policy?.id === policyId && value.binding?.id === bindingId, | ||
| ); | ||
| if (!candidate) { | ||
| throw new Error(`evaluation report has no candidate for ${policyId} + ${bindingId}`); | ||
| } | ||
| return candidate; | ||
| } | ||
| function recommendationMatches(report, policyId, bindingId) { | ||
| return Boolean( | ||
| report.recommended?.some( | ||
| (value) => value.policy === policyId && value.binding === bindingId && value.status === "recommended", | ||
| ), | ||
| ); | ||
| } | ||
| export function projectComposition({ verified, preview, verificationEnvelope }) { | ||
| if (!verified?.integrity_verified || !verified?.signature_verified || !verified?.trusted_maintainer) { | ||
| throw new Error("website projection requires an integrity-verified entry signed by a trusted maintainer"); | ||
| } | ||
| if (!verified.compatible) { | ||
| throw new Error("website projection requires a compatible registry entry"); | ||
| } | ||
| if (!preview?.pack?.safe) { | ||
| throw new Error("website projection requires a canonical safe-pack preview"); | ||
| } | ||
| const entry = verified.entry; | ||
| const evaluation = entry.evaluation; | ||
| if (!evaluation) { | ||
| throw new Error("website projection requires evaluation binding metadata"); | ||
| } | ||
| const policyId = safeIdentifier(evaluation.policy_id, "policy id"); | ||
| const bindingId = safeIdentifier(evaluation.binding_id, "binding id"); | ||
| const report = verificationEnvelope.report ?? verificationEnvelope; | ||
| const candidate = matchingCandidate(report, policyId, bindingId); | ||
| const reportRecommended = recommendationMatches(report, policyId, bindingId); | ||
| if (verified.recommended && (!reportRecommended || candidate.status !== "recommended")) { | ||
| throw new Error("registry recommendation does not match canonical evaluation evidence"); | ||
| } | ||
| const policy = proposedConfig(preview, "active_policy"); | ||
| const agents = proposedConfig(preview, "agent_registry"); | ||
| if (policy.id !== policyId || preview.composition?.binding?.id !== bindingId) { | ||
| throw new Error("preset preview provenance does not match registry evaluation binding"); | ||
| } | ||
| const runs = Number(candidate.metrics?.runs ?? 0); | ||
| const verifiedRoutes = Number(candidate.metrics?.verified_route_runs ?? 0); | ||
| const policyArtifact = findArtifact(entry, "policy"); | ||
| const bindingArtifact = findArtifact(entry, "host-binding"); | ||
| const verificationArtifact = findArtifact(entry, "verification"); | ||
| return { | ||
| id: `${safeIdentifier(entry.id, "entry id")}@${entry.version}`, | ||
| entryId: entry.id, | ||
| entryVersion: entry.version, | ||
| status: verified.effective_status, | ||
| statusLabel: statusLabel(verified.effective_status), | ||
| recommended: verified.recommended, | ||
| freshness: verified.freshness, | ||
| lifecycle: entry.lifecycle, | ||
| replacement: entry.replacement ?? null, | ||
| policy: { | ||
| id: policyId, | ||
| version: evaluation.policy_version, | ||
| usage: policy.usage, | ||
| transitions: policy.transitions, | ||
| materiality: policy.materiality, | ||
| execution: policy.execution, | ||
| }, | ||
| binding: { | ||
| id: bindingId, | ||
| version: evaluation.binding_version, | ||
| host: preview.composition.host, | ||
| profiles: agents.profiles, | ||
| dispatch: preview.composition.dispatch, | ||
| }, | ||
| compatibility: { | ||
| hosts: entry.compatible_hosts, | ||
| minPlanrVersion: entry.min_planr_version, | ||
| maxPlanrVersion: entry.max_planr_version, | ||
| }, | ||
| enforcement: [ | ||
| { | ||
| dimension: "Policy limits", | ||
| state: "verified", | ||
| detail: "Planr validates count, time, concurrency, transition, and safety-stop limits before dispatch.", | ||
| }, | ||
| { | ||
| dimension: "Execution permissions", | ||
| state: "verified", | ||
| detail: "Registry-safe policy: no commands, hooks, network/MCP grants, secrets, or overwrite permission.", | ||
| }, | ||
| { | ||
| dimension: "Model and effort", | ||
| state: "host_enforced", | ||
| detail: "The host binding requests concrete routes; the host retains final execution authority.", | ||
| }, | ||
| { | ||
| dimension: "Effective route evidence", | ||
| state: runs > 0 && verifiedRoutes === runs ? "verified" : "unavailable", | ||
| detail: `${verifiedRoutes} of ${runs} evaluation runs carried verified effective-route evidence.`, | ||
| }, | ||
| ], | ||
| evaluation: { | ||
| suiteId: report.suite?.id, | ||
| suiteVersion: report.suite?.version, | ||
| evaluatedAtUnix: report.suite?.evaluated_at_unix, | ||
| reviewAtUnix: entry.review_at_unix, | ||
| status: candidate.status, | ||
| metrics: candidate.metrics, | ||
| thresholds: candidate.threshold_results, | ||
| resultHashes: candidate.results?.map((result) => result.result_sha256) ?? [], | ||
| fixtureSha256: report.suite?.fixture_sha256, | ||
| }, | ||
| registry: { | ||
| id: verified.registry_id, | ||
| version: verified.registry_version, | ||
| manifestSha256: verified.manifest_sha256, | ||
| signer: entry.signature?.signer, | ||
| signatureVerified: true, | ||
| trustedMaintainer: true, | ||
| artifacts: [policyArtifact, bindingArtifact, verificationArtifact].map((artifact) => ({ | ||
| path: artifact.path, | ||
| kind: artifact.kind, | ||
| sha256: artifact.sha256, | ||
| sizeBytes: artifact.size_bytes, | ||
| })), | ||
| }, | ||
| command: previewCommand(policyId, bindingId), | ||
| }; | ||
| } | ||
| export function visibleCompositions(catalog, recommendedOnly = false) { | ||
| const compositions = Array.isArray(catalog?.compositions) ? catalog.compositions : []; | ||
| return recommendedOnly ? compositions.filter((entry) => entry.recommended) : compositions; | ||
| } |
| import assert from "node:assert/strict"; | ||
| import test from "node:test"; | ||
| import { | ||
| formatBasisPoints, | ||
| previewCommand, | ||
| projectComposition, | ||
| safeIdentifier, | ||
| visibleCompositions, | ||
| } from "./catalog-model.mjs"; | ||
| function fixture() { | ||
| const verified = { | ||
| registry_id: "official", | ||
| registry_version: "2026.07", | ||
| manifest_sha256: "a".repeat(64), | ||
| integrity_verified: true, | ||
| signature_verified: true, | ||
| trusted_maintainer: true, | ||
| compatible: true, | ||
| freshness: "current", | ||
| effective_status: "recommended", | ||
| recommended: true, | ||
| entry: { | ||
| id: "balanced-codex", | ||
| version: "1.0.0", | ||
| lifecycle: "published", | ||
| compatible_hosts: ["codex"], | ||
| min_planr_version: "1.3.0", | ||
| max_planr_version: "1.9.0", | ||
| review_at_unix: 1815523200, | ||
| evaluation: { | ||
| policy_id: "balanced", | ||
| policy_version: "1.0.0", | ||
| binding_id: "codex-openai", | ||
| binding_version: "1.0.0", | ||
| }, | ||
| signature: { signer: "planr-maintainers" }, | ||
| artifacts: [ | ||
| { path: "pack/policy.toml", kind: "policy", sha256: "1".repeat(64), size_bytes: 1 }, | ||
| { path: "pack/binding.toml", kind: "host-binding", sha256: "2".repeat(64), size_bytes: 2 }, | ||
| { path: "pack/verification.json", kind: "verification", sha256: "3".repeat(64), size_bytes: 3 }, | ||
| ], | ||
| }, | ||
| }; | ||
| const policy = { | ||
| id: "balanced", | ||
| usage: { max_active_agents: 3, max_parallel_writers: 1, max_depth: 1, metering: "trusted" }, | ||
| transitions: { retry: { max_same_route_retries: 1 }, safety_stop: { enabled: true } }, | ||
| materiality: { changed_files_threshold: 10 }, | ||
| execution: { roles: { worker: { commands: [], hooks: [], network_hosts: [], mcp_servers: [] } } }, | ||
| }; | ||
| const preview = { | ||
| pack: { safe: true }, | ||
| composition: { host: "codex", binding: { id: "codex-openai" }, dispatch: {} }, | ||
| artifacts: [ | ||
| { kind: "active_policy", config_diff: { proposed: { value: policy } } }, | ||
| { kind: "agent_registry", config_diff: { proposed: { value: { profiles: {} } } } }, | ||
| ], | ||
| }; | ||
| const candidate = { | ||
| policy: { id: "balanced" }, | ||
| binding: { id: "codex-openai" }, | ||
| status: "recommended", | ||
| metrics: { runs: 7, verified_route_runs: 7, average_quality_score_bps: 9600 }, | ||
| threshold_results: [{ name: "quality", pass: true }], | ||
| results: [{ result_sha256: "4".repeat(64) }], | ||
| }; | ||
| const verificationEnvelope = { | ||
| report: { | ||
| suite: { id: "planr-preset-suite", version: "1.8.0", evaluated_at_unix: 1783987200, fixture_sha256: "5".repeat(64) }, | ||
| candidates: [candidate], | ||
| recommended: [{ policy: "balanced", binding: "codex-openai", status: "recommended" }], | ||
| }, | ||
| }; | ||
| return { verified, preview, verificationEnvelope }; | ||
| } | ||
| test("projects only trusted, safe, evidence-bound registry entries", () => { | ||
| const projected = projectComposition(fixture()); | ||
| assert.equal(projected.status, "recommended"); | ||
| assert.equal(projected.registry.signatureVerified, true); | ||
| assert.equal(projected.enforcement.at(-1).state, "verified"); | ||
| assert.equal(projected.command, "planr agents preset apply balanced --binding codex-openai"); | ||
| }); | ||
| test("refuses unsigned metadata and recommendation drift", () => { | ||
| const unsigned = fixture(); | ||
| unsigned.verified.signature_verified = false; | ||
| assert.throws(() => projectComposition(unsigned), /trusted maintainer/); | ||
| const drifted = fixture(); | ||
| drifted.verificationEnvelope.report.recommended = []; | ||
| assert.throws(() => projectComposition(drifted), /does not match/); | ||
| }); | ||
| test("publishes lifecycle-demoted recommendations with visible replacement metadata", () => { | ||
| const stale = fixture(); | ||
| stale.verified.freshness = "stale"; | ||
| stale.verified.effective_status = "stale"; | ||
| stale.verified.recommended = false; | ||
| const staleProjected = projectComposition(stale); | ||
| assert.equal(staleProjected.status, "stale"); | ||
| assert.equal(staleProjected.recommended, false); | ||
| const deprecated = fixture(); | ||
| deprecated.verified.effective_status = "deprecated"; | ||
| deprecated.verified.recommended = false; | ||
| deprecated.verified.entry.lifecycle = "deprecated"; | ||
| deprecated.verified.entry.replacement = "balanced-codex-v2"; | ||
| const deprecatedProjected = projectComposition(deprecated); | ||
| assert.equal(deprecatedProjected.status, "deprecated"); | ||
| assert.equal(deprecatedProjected.replacement, "balanced-codex-v2"); | ||
| }); | ||
| test("copy commands accept identifiers only and filtering is deterministic", () => { | ||
| assert.equal(previewCommand("balanced", "codex-openai"), "planr agents preset apply balanced --binding codex-openai"); | ||
| assert.throws(() => safeIdentifier("balanced; curl evil"), /safe registry identifier/); | ||
| assert.deepEqual( | ||
| visibleCompositions({ compositions: [{ recommended: true }, { recommended: false }] }, true), | ||
| [{ recommended: true }], | ||
| ); | ||
| assert.equal(formatBasisPoints(9600), "96.00%"); | ||
| assert.equal(formatBasisPoints(undefined), "—"); | ||
| }); |
| import assert from "node:assert/strict"; | ||
| import { existsSync } from "node:fs"; | ||
| import { homedir } from "node:os"; | ||
| import { dirname, join, resolve } from "node:path"; | ||
| import { spawnSync } from "node:child_process"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import test from "node:test"; | ||
| import { cloudflareTestSteps } from "../scripts/cloudflare-test.mjs"; | ||
| test("launcher keeps deploy and destroy pinned to the test stage", () => { | ||
| assert.deepEqual(cloudflareTestSteps("deploy"), [ | ||
| ["pnpm", ["site:check"]], | ||
| ["pnpm", ["exec", "alchemy", "deploy", "--stage", "test"]], | ||
| ]); | ||
| assert.deepEqual(cloudflareTestSteps("destroy"), [ | ||
| ["pnpm", ["exec", "alchemy", "destroy", "--stage", "test"]], | ||
| ]); | ||
| assert.throws(() => cloudflareTestSteps("prod"), /usage:/); | ||
| }); | ||
| const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); | ||
| const launcher = join(repositoryRoot, "scripts", "cloudflare-test.mjs"); | ||
| for (const version of ["v18.16.1", "v20.19.5"]) { | ||
| const node = join(homedir(), ".nvm", "versions", "node", version, "bin", "node"); | ||
| test( | ||
| `direct launcher rejects ${version} before invoking pnpm`, | ||
| { skip: !existsSync(node) && `local ${version} runtime is unavailable` }, | ||
| () => { | ||
| const result = spawnSync(node, [launcher, "deploy"], { | ||
| cwd: repositoryRoot, | ||
| encoding: "utf8", | ||
| }); | ||
| assert.equal(result.status, 1); | ||
| assert.match(result.stderr, /Cloudflare deployment requires Node\.js 22 or newer/); | ||
| assert.doesNotMatch(result.stderr, /Corepack|ERR_VM|addAbortListener|experimental-strip-types/); | ||
| }, | ||
| ); | ||
| } |
| { | ||
| "schemaVersion": 1, | ||
| "generatedAtUnix": 1783987200, | ||
| "source": { | ||
| "state": "verified_registry_projection", | ||
| "manifest": "website/registry/manifest.toml", | ||
| "entryCount": 1, | ||
| "trust": "planr_registry_v1" | ||
| }, | ||
| "compositions": [ | ||
| { | ||
| "id": "balanced-codex@1.0.0", | ||
| "entryId": "balanced-codex", | ||
| "entryVersion": "1.0.0", | ||
| "status": "recommended", | ||
| "statusLabel": "Recommended", | ||
| "recommended": true, | ||
| "freshness": "current", | ||
| "lifecycle": "published", | ||
| "replacement": null, | ||
| "policy": { | ||
| "id": "balanced", | ||
| "version": "1.0.0", | ||
| "usage": { | ||
| "budget_exhaustion": "stop", | ||
| "max_active_agents": 3, | ||
| "max_attempts": 4, | ||
| "max_depth": 1, | ||
| "max_parallel_readers": 2, | ||
| "max_parallel_writers": 1, | ||
| "max_tool_calls": 120, | ||
| "max_wall_time_seconds": 3600, | ||
| "metering": "estimated", | ||
| "review_reserve_percent": 20 | ||
| }, | ||
| "transitions": { | ||
| "availability_fallback": { | ||
| "max_fallbacks": 1, | ||
| "require_same_capability_class": true | ||
| }, | ||
| "quality_escalation": { | ||
| "max_escalations": 1, | ||
| "require_verification_evidence": true | ||
| }, | ||
| "quota_downgrade": { | ||
| "enabled": false, | ||
| "max_downgrades": 0, | ||
| "noncritical_only": true | ||
| }, | ||
| "retry": { | ||
| "max_same_route_retries": 1 | ||
| }, | ||
| "safety_stop": { | ||
| "enabled": true | ||
| } | ||
| }, | ||
| "materiality": { | ||
| "changed_files_threshold": 10, | ||
| "changed_lines_threshold": 500 | ||
| }, | ||
| "execution": { | ||
| "max_read_scope_entries": 8, | ||
| "max_write_scope_entries": 4, | ||
| "roles": { | ||
| "worker": { | ||
| "approvals": [], | ||
| "commands": [], | ||
| "environment": [], | ||
| "filesystem": { | ||
| "allow_overwrite": false, | ||
| "read_roots": [ | ||
| "docs", | ||
| "src", | ||
| "tests" | ||
| ], | ||
| "write_roots": [ | ||
| "docs", | ||
| "src", | ||
| "tests" | ||
| ] | ||
| }, | ||
| "hooks": [], | ||
| "mcp_servers": [], | ||
| "network_hosts": [], | ||
| "secret_references": "[REDACTED]", | ||
| "tools": [ | ||
| "cargo", | ||
| "git", | ||
| "rg" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "binding": { | ||
| "id": "codex-openai", | ||
| "version": "1.0.0", | ||
| "host": "codex", | ||
| "profiles": { | ||
| "codex-openai-driver": { | ||
| "capabilities": [], | ||
| "client": "codex", | ||
| "cost_tier": "premium", | ||
| "effort": "xhigh", | ||
| "model": "gpt-5.5" | ||
| }, | ||
| "codex-openai-worker": { | ||
| "capabilities": [], | ||
| "client": "codex", | ||
| "cost_tier": "standard", | ||
| "effort": "high", | ||
| "model": "gpt-5.4-mini", | ||
| "skill": "planr-work" | ||
| } | ||
| }, | ||
| "dispatch": { | ||
| "driver": { | ||
| "capability_evidence": [ | ||
| "builtin-codex-none-fork-contract" | ||
| ], | ||
| "effort_override": false, | ||
| "fork_turns": { | ||
| "mode": "none" | ||
| }, | ||
| "model_override": false | ||
| }, | ||
| "worker": { | ||
| "capability_evidence": [ | ||
| "builtin-codex-none-fork-contract" | ||
| ], | ||
| "effort_override": true, | ||
| "fork_turns": { | ||
| "mode": "none" | ||
| }, | ||
| "model_override": true | ||
| } | ||
| } | ||
| }, | ||
| "compatibility": { | ||
| "hosts": [ | ||
| "codex" | ||
| ], | ||
| "minPlanrVersion": "1.3.0", | ||
| "maxPlanrVersion": "1.9.0" | ||
| }, | ||
| "enforcement": [ | ||
| { | ||
| "dimension": "Policy limits", | ||
| "state": "verified", | ||
| "detail": "Planr validates count, time, concurrency, transition, and safety-stop limits before dispatch." | ||
| }, | ||
| { | ||
| "dimension": "Execution permissions", | ||
| "state": "verified", | ||
| "detail": "Registry-safe policy: no commands, hooks, network/MCP grants, secrets, or overwrite permission." | ||
| }, | ||
| { | ||
| "dimension": "Model and effort", | ||
| "state": "host_enforced", | ||
| "detail": "The host binding requests concrete routes; the host retains final execution authority." | ||
| }, | ||
| { | ||
| "dimension": "Effective route evidence", | ||
| "state": "verified", | ||
| "detail": "7 of 7 evaluation runs carried verified effective-route evidence." | ||
| } | ||
| ], | ||
| "evaluation": { | ||
| "suiteId": "planr-preset-suite", | ||
| "suiteVersion": "1.8.0", | ||
| "evaluatedAtUnix": 1783987200, | ||
| "reviewAtUnix": 1815523200, | ||
| "status": "recommended", | ||
| "metrics": { | ||
| "actual_task_runs": 7, | ||
| "availability_fallbacks": 7, | ||
| "average_credits_micros": 100, | ||
| "average_quality_score_bps": 10000, | ||
| "corrections": 7, | ||
| "metrics_source": "trusted_telemetry", | ||
| "oracle_passes": 7, | ||
| "p95_latency_ms": 228, | ||
| "quality_escalations": 7, | ||
| "reliability_bps": 10000, | ||
| "retries": 7, | ||
| "runs": 7, | ||
| "safety_stop_attempts": 1, | ||
| "safety_stop_failures": 0, | ||
| "total_credits_micros": 700, | ||
| "total_tokens": 70, | ||
| "total_tool_calls": 7, | ||
| "transition_contract_failures": 0, | ||
| "verified_result_hashes": 7, | ||
| "verified_route_runs": 7, | ||
| "violations": 0 | ||
| }, | ||
| "thresholds": [ | ||
| { | ||
| "actual": "7", | ||
| "name": "minimum_runs", | ||
| "pass": true, | ||
| "rule": ">= 7" | ||
| }, | ||
| { | ||
| "actual": "10000", | ||
| "name": "quality", | ||
| "pass": true, | ||
| "rule": ">= 8500 bps" | ||
| }, | ||
| { | ||
| "actual": "10000", | ||
| "name": "reliability", | ||
| "pass": true, | ||
| "rule": ">= 9000 bps" | ||
| }, | ||
| { | ||
| "actual": "100", | ||
| "name": "average_cost", | ||
| "pass": true, | ||
| "rule": "<= 1200000 credits_micros" | ||
| }, | ||
| { | ||
| "actual": "228", | ||
| "name": "p95_latency", | ||
| "pass": true, | ||
| "rule": "<= 5000 ms" | ||
| }, | ||
| { | ||
| "actual": "0", | ||
| "name": "transition_contract", | ||
| "pass": true, | ||
| "rule": "<= 0 failures" | ||
| }, | ||
| { | ||
| "actual": "0", | ||
| "name": "safety_stop", | ||
| "pass": true, | ||
| "rule": "<= 0 failures" | ||
| }, | ||
| { | ||
| "actual": "7/7", | ||
| "name": "actual_task_runs", | ||
| "pass": true, | ||
| "rule": "all fixtures executed" | ||
| }, | ||
| { | ||
| "actual": "7/7", | ||
| "name": "output_oracles", | ||
| "pass": true, | ||
| "rule": "all task output oracles pass" | ||
| }, | ||
| { | ||
| "actual": "7/7", | ||
| "name": "route_evidence", | ||
| "pass": true, | ||
| "rule": "all runs verified" | ||
| }, | ||
| { | ||
| "actual": "7/7", | ||
| "name": "result_hashes", | ||
| "pass": true, | ||
| "rule": "all result hashes verified" | ||
| } | ||
| ], | ||
| "resultHashes": [ | ||
| "c721c30da6e5f988de6741e8d4e611f7f45ad36425559a6b67ae4723869619be", | ||
| "e026c322f1f094f7241d352a8e9c70f10b89d456e7f42825b3c689dc5d785dc9", | ||
| "c74bf8de8296ff94ba45deeac158b619e3eb99c4e1b5251169bc2f1586518979", | ||
| "f76f06ae46bc2ce05bd631ee1f702e348a8dd3b8d4b2e53d59c409af4f330015", | ||
| "2165057d66dcc73722a57d01fb237a73f4da12bc70ab8757c817975925bfbbd8", | ||
| "052de2acf92d0ac000a15b92b26d5f173fe4ff606ee1dbe37df1e4a535cbdff8", | ||
| "41bf6ab560a23c450e9def8944c934091b598d270978fef8aaae0933f806619e" | ||
| ], | ||
| "fixtureSha256": "733c94e049d784eec2fa222f60c230e3f185ccefc84c23c313b1d7e85b60ab22" | ||
| }, | ||
| "registry": { | ||
| "id": "planr-official", | ||
| "version": "2026.07", | ||
| "manifestSha256": "1512df7f667dd1c8f92b2eb39a06739336048dde58327195ada85b419a6cb4c7", | ||
| "signer": "planr-release-2026", | ||
| "signatureVerified": true, | ||
| "trustedMaintainer": true, | ||
| "artifacts": [ | ||
| { | ||
| "path": "presets/policies/balanced.toml", | ||
| "kind": "policy", | ||
| "sha256": "827667b678224f6b09f7c3faab188ede4c7bad49cd2fc9fa8e46f01d61147080", | ||
| "sizeBytes": 993 | ||
| }, | ||
| { | ||
| "path": "presets/bindings/codex-openai.toml", | ||
| "kind": "host-binding", | ||
| "sha256": "2f1f1d91000aff8e6a7424f0380c16c46d24ee38c94de5a9beee2158c0bf6575", | ||
| "sizeBytes": 1449 | ||
| }, | ||
| { | ||
| "path": "website/registry/verification.json", | ||
| "kind": "verification", | ||
| "sha256": "7d1e36372b050ecab5080ea029fd3594f8f8f1df2f1d391c36aebd0fb56cf99a", | ||
| "sizeBytes": 276027 | ||
| } | ||
| ] | ||
| }, | ||
| "command": "planr agents preset apply balanced --binding codex-openai" | ||
| } | ||
| ] | ||
| } |
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <meta | ||
| name="description" | ||
| content="Compare verified Planr policy and host-binding compositions with evaluation and registry provenance." | ||
| /> | ||
| <meta | ||
| http-equiv="Content-Security-Policy" | ||
| content="default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" | ||
| /> | ||
| <title>Planr Preset Catalog</title> | ||
| <link rel="stylesheet" href="./styles.css" /> | ||
| <script type="module" src="./app.mjs"></script> | ||
| </head> | ||
| <body> | ||
| <a class="skip-link" href="#catalog">Skip to catalog</a> | ||
| <header class="site-header"> | ||
| <a class="brand" href="./" aria-label="Planr preset catalog home"> | ||
| <span class="brand-mark" aria-hidden="true">P</span> | ||
| <span>Planr <strong>Presets</strong></span> | ||
| </a> | ||
| <p class="header-note">Evidence before recommendation.</p> | ||
| </header> | ||
| <main id="catalog"> | ||
| <section class="hero" aria-labelledby="hero-title"> | ||
| <div class="hero-copy"> | ||
| <p class="eyebrow">Public catalog · read-only</p> | ||
| <h1 id="hero-title">Choose policy intent.<br />See host reality.</h1> | ||
| <p class="lede"> | ||
| Compare portable Planr policies with versioned host bindings. Every published status comes from | ||
| registry verification and evaluation evidence—not editorial judgment. | ||
| </p> | ||
| </div> | ||
| <dl class="catalog-stats" aria-label="Catalog summary"> | ||
| <div><dt>Published</dt><dd id="stat-published">—</dd></div> | ||
| <div><dt>Recommended</dt><dd id="stat-recommended">—</dd></div> | ||
| <div><dt>Trust source</dt><dd id="stat-trust">Loading</dd></div> | ||
| </dl> | ||
| </section> | ||
| <section id="fixture-banner" class="fixture-banner" hidden aria-label="Test fixture notice"> | ||
| Local test fixture. Recommendation labels on this page are synthetic and are not published registry metadata. | ||
| </section> | ||
| <section class="authority-note" aria-labelledby="authority-title"> | ||
| <div aria-hidden="true" class="authority-icon">↳</div> | ||
| <div> | ||
| <h2 id="authority-title">Planr governs policy; the host executes the route.</h2> | ||
| <p> | ||
| A binding can request a model, effort, and context-fork mode. Codex, Claude Code, Cursor, or another host | ||
| retains the final concrete execution choice. The catalog distinguishes binding intent from verified | ||
| effective-route evidence. | ||
| </p> | ||
| </div> | ||
| </section> | ||
| <section class="compare-shell" aria-labelledby="compare-title"> | ||
| <div class="section-heading"> | ||
| <div> | ||
| <p class="eyebrow">Side-by-side</p> | ||
| <h2 id="compare-title">Compare compositions</h2> | ||
| </div> | ||
| <label class="toggle"> | ||
| <input id="recommended-only" type="checkbox" /> | ||
| <span>Recommended only</span> | ||
| </label> | ||
| </div> | ||
| <div class="selectors" aria-label="Comparison choices"> | ||
| <label> | ||
| <span>Composition A</span> | ||
| <select id="composition-a" aria-controls="comparison-a"></select> | ||
| </label> | ||
| <span class="versus" aria-hidden="true">vs</span> | ||
| <label> | ||
| <span>Composition B</span> | ||
| <select id="composition-b" aria-controls="comparison-b"></select> | ||
| </label> | ||
| </div> | ||
| <div id="empty-state" class="empty-state" hidden> | ||
| <p class="eyebrow">Publication gate active</p> | ||
| <h3>No trusted compositions are published.</h3> | ||
| <p id="empty-message"> | ||
| Generate this catalog from a signed registry manifest after Planr verifies its evaluation evidence. | ||
| </p> | ||
| <code>node website/build-catalog.mjs --help</code> | ||
| </div> | ||
| <div id="comparison" class="comparison-grid" aria-live="polite"> | ||
| <article id="comparison-a" class="preset-card" aria-label="Composition A"></article> | ||
| <article id="comparison-b" class="preset-card" aria-label="Composition B"></article> | ||
| </div> | ||
| </section> | ||
| <section class="legend" aria-labelledby="legend-title"> | ||
| <div> | ||
| <p class="eyebrow">Status language</p> | ||
| <h2 id="legend-title">Five states, no ambiguity</h2> | ||
| </div> | ||
| <dl> | ||
| <div><dt>Experimental</dt><dd>Valid enough to inspect; evaluation binding is not required.</dd></div> | ||
| <div><dt>Verified</dt><dd>Integrity and current evaluation checks pass; recommendation gates did not.</dd></div> | ||
| <div><dt>Recommended</dt><dd>Trusted signature plus current canonical evaluation and telemetry gates pass.</dd></div> | ||
| <div><dt>Stale</dt><dd>The review date or current suite validity has expired.</dd></div> | ||
| <div><dt>Deprecated</dt><dd>Published for continuity with an explicit replacement path.</dd></div> | ||
| </dl> | ||
| </section> | ||
| </main> | ||
| <footer> | ||
| <p>Local-first by design. A catalog outage never affects installed packs or active projects.</p> | ||
| <p id="generated-at">Catalog provenance loading…</p> | ||
| </footer> | ||
| <div id="copy-status" class="sr-only" aria-live="polite"></div> | ||
| </body> | ||
| </html> | ||
| # Planr Preset Catalog | ||
| This directory is a dependency-free static website. Production catalog data is never authored by hand: `build-catalog.mjs` invokes Planr's canonical registry verifier, requires a trusted maintainer signature and safe policy/binding preview, binds status to the evaluation report, and writes `data/catalog.json`. | ||
| The committed catalog is generated from the included `registry/manifest.toml`, canonical `registry/verification.json`, and separately pinned public maintainer trust store. Its private release and telemetry signing keys are intentionally not committed. If verification expires, is deprecated, or no longer earns recommendation, the generated catalog preserves the visible lifecycle state instead of inventing or retaining a recommendation. | ||
| ```sh | ||
| node website/build-catalog.mjs \ | ||
| --planr-bin ./target/release/planr \ | ||
| --manifest website/registry/manifest.toml \ | ||
| --content-root . \ | ||
| --trust-store website/registry/trusted-maintainers.toml \ | ||
| --entry balanced-codex=codex \ | ||
| --at-unix 1783987200 \ | ||
| --output website/data/catalog.json | ||
| npm run site:test | ||
| npm run site:serve | ||
| ``` | ||
| ## Cloudflare test deployment | ||
| Cloudflare infrastructure is owned by the repository-root `alchemy.run.ts`. The | ||
| deployment publishes a clean `dist/website` containing only runtime assets; tests, | ||
| fixtures, registry inputs, trust stores, and build tooling are never uploaded. | ||
| Install the pinned development dependency and authenticate Cloudflare using an existing | ||
| Alchemy/Cloudflare profile, Wrangler login, or a least-privilege API token. Set the | ||
| account id from `.env.example` in the process environment when account inference would | ||
| be ambiguous. Planr never runs login/configure commands and never changes user-level | ||
| configuration. This static stack has no secret bindings, so it deliberately requires no | ||
| `ALCHEMY_PASSWORD` or repository-local secret file. | ||
| The published Planr CLI retains its declared Node.js 18+ compatibility. Repository | ||
| dependency installation and the separate Cloudflare deployment toolchain require Node.js | ||
| 22+ because the pinned pnpm/Alchemy lockfile contains current tooling with that minimum. | ||
| Select Node 22 or newer before invoking pnpm. On an unknown machine, this pnpm-free | ||
| preflight is safe to run first even with Node 18 or 20: | ||
| ```sh | ||
| node scripts/check-alchemy-runtime.mjs | ||
| ``` | ||
| The authoritative deployment launcher performs the same check before it starts pnpm or | ||
| Alchemy, so older runtimes receive Planr's error instead of a Corepack or dependency crash. | ||
| ```sh | ||
| pnpm install | ||
| pnpm site:check | ||
| node scripts/cloudflare-test.mjs deploy | ||
| ``` | ||
| After Node 22+ is active, `pnpm deploy:test` is an equivalent convenience alias. | ||
| The committed `pnpm-workspace.yaml` is a repository-only dependency security policy: | ||
| it allows the `esbuild` and `workerd` install scripts required by Alchemy and explicitly | ||
| keeps the unused transitive `sharp` build disabled. It does not modify global pnpm state. | ||
| `deploy:test` always targets the isolated Alchemy stage `test`. It creates a standalone | ||
| Cloudflare Worker website and prints its Cloudflare-assigned `workers.dev` URL. The | ||
| initial deployment deliberately configures no custom domain, DNS route, backend resource, | ||
| or adoption of an existing Worker. | ||
| Removing that test website is an explicit, separate operation: | ||
| ```sh | ||
| node scripts/cloudflare-test.mjs destroy | ||
| ``` | ||
| After Node 22+ is active, `pnpm destroy:test` is an equivalent convenience alias. | ||
| Never add private registry or telemetry signing keys to the deployment environment. The | ||
| site deploy consumes only the already verified public `website/data/catalog.json`. | ||
| Release operators must regenerate the evaluation report through `planr agents preset evaluate` with an independently pinned telemetry collector, update the manifest artifact digest, and sign the registry entry with the corresponding offline maintainer key before rebuilding this file. The public `registry/report.md` summarizes that evidence; the machine report and signature remain the verification source of truth. | ||
| The report-wide `reproducible_evidence` flag summarizes the entire candidate matrix. Registry publication independently requires the selected candidate's complete reproducible evidence, passing thresholds, verified route receipts, and matching entry in `report.recommended`; candidates that fail those gates are not published as recommendations. | ||
| The localhost-only `?fixture=recommended` query remains available for isolated UI regression checks. It is visibly labeled, ignored on non-local hosts, and never substitutes for live verification of the production `data/catalog.json` path. |
| schema_version = 1 | ||
| id = "planr-official" | ||
| version = "2026.07" | ||
| generated_at_unix = 1783987200 | ||
| [[entries]] | ||
| id = "balanced-codex" | ||
| version = "1.0.0" | ||
| kind = "pack" | ||
| lifecycle = "published" | ||
| verification_status = "recommended" | ||
| verified_at_unix = 1783987200 | ||
| review_at_unix = 1815523200 | ||
| compatible_hosts = ["codex"] | ||
| min_planr_version = "1.3.0" | ||
| max_planr_version = "1.9.0" | ||
| verification_path = "website/registry/verification.json" | ||
| [entries.evaluation] | ||
| policy_id = "balanced" | ||
| policy_version = "1.0.0" | ||
| binding_id = "codex-openai" | ||
| binding_version = "1.0.0" | ||
| suite_id = "planr-preset-suite" | ||
| suite_version = "1.8.0" | ||
| [[entries.artifacts]] | ||
| path = "presets/policies/balanced.toml" | ||
| kind = "policy" | ||
| sha256 = "827667b678224f6b09f7c3faab188ede4c7bad49cd2fc9fa8e46f01d61147080" | ||
| size_bytes = 993 | ||
| [[entries.artifacts]] | ||
| path = "presets/bindings/codex-openai.toml" | ||
| kind = "host-binding" | ||
| sha256 = "2f1f1d91000aff8e6a7424f0380c16c46d24ee38c94de5a9beee2158c0bf6575" | ||
| size_bytes = 1449 | ||
| [[entries.artifacts]] | ||
| path = "website/registry/verification.json" | ||
| kind = "verification" | ||
| sha256 = "7d1e36372b050ecab5080ea029fd3594f8f8f1df2f1d391c36aebd0fb56cf99a" | ||
| size_bytes = 276027 | ||
| [entries.signature] | ||
| signer = "planr-release-2026" | ||
| algorithm = "ed25519" | ||
| value = "122e213bab8e51b3030685a48b1a3520bc5e2397515dc3aa38da7ed333211e8202e4802949e667add6f29389af29510c2570c4511385206e61c17d4aa914d208" |
| # Preset Evaluation Verification | ||
| Fixture: `planr-preset-suite` v1.8.0 (`733c94e049d784eec2fa222f60c230e3f185ccefc84c23c313b1d7e85b60ab22`) | ||
| Verified: 1783987200 / expires: 1815523200 / evaluated: 1783987200 | ||
| Runner: planr-instrumented-live-host-runner schema 1 / Planr 1.3.0 / macos aarch64 | ||
| This report joined Planr-read challenge-bound task artifacts with independently signed Ed25519 telemetry receipts. Verified receipts provide trusted effective-route and usage measurements; missing or invalid receipts remain recommendation-ineligible. | ||
| | Candidate | Status | Source | Quality | Reliability | Avg cost | p95 latency | Hashes | Label | | ||
| |---|---|---|---:|---:|---:|---:|---:|---| | ||
| | `balanced-codex-openai` | `Recommended` | `TrustedTelemetry` | 10000 bps | 10000 bps | 100 μcredits | 228 ms | 7/7 | recommended | | ||
| | `low-usage-codex-openai` | `Unverified` | `TrustedTelemetry` | 9663 bps | 4285 bps | 100 μcredits | 197 ms | 7/7 | — | | ||
| | `max-quality-codex-openai` | `Recommended` | `TrustedTelemetry` | 10000 bps | 10000 bps | 100 μcredits | 171 ms | 7/7 | recommended | | ||
| | `read-only-audit-codex-openai` | `Unverified` | `TrustedTelemetry` | 9831 bps | 7142 bps | 100 μcredits | 169 ms | 7/7 | — | | ||
| ## Versioned task inputs | ||
| - `Exploration` `explore-routing-boundaries` v1.0.0: `277f2f9155f85365324c6ea6cd717baa47bc9c482446217b30d9f94c6d6684df` | ||
| - `Implementation` `implement-bounded-policy-change` v1.0.0: `f4704037e6a63746e100d31aa98843adeca458fdb336a3317565b62693d676d2` | ||
| - `Mechanical` `mechanical-schema-rewrite` v1.0.0: `96882c36d71af8c0637a17cfb47db66dbef0fbd170c963ba500c8d9f23d48a7a` | ||
| - `Browser` `browser-report-smoke` v1.0.0: `fc453ccf8422fa0093edb60b0162e83982e005b07ee569de5dff222b13319827` | ||
| - `Visual` `visual-report-regression` v1.0.0: `d71938957b10cbcf8dd67fde7e43853ba66fc23ab3b436fa3287caa6a2b5a1f3` | ||
| - `Security` `security-safety-stop` v1.0.0: `dd439a0094897e7ebed01c301fed542e910e8e2b7d6b2a5942c1c44b47082e7f` | ||
| - `Subagent` `subagent-sol-luna-dispatch` v1.0.0: `38372ba41431ccf4f2e9b56ea5f93db55e52156e3fa1c2956bf3dca15d63654f` | ||
| ## Codex Sol/Luna contract | ||
| - [x] `fork_turns = all` rejected | ||
| - [x] `fork_turns = none` parameters verified | ||
| - [x] missing effective model/effort cannot verify | ||
| - [x] process-exit effective route evidence verifies | ||
| Reproducible evidence: **fail**. |
| schema_version = 1 | ||
| [[maintainers]] | ||
| id = "planr-release-2026" | ||
| public_key = "e8631493d6da6ea3983f17585270f558e7757da5bb08bd228756da318d0a6b14" | ||
| revoked = false |
Sorry, the diff of this file is too big to display
| #!/usr/bin/env node | ||
| import { createReadStream, statSync } from "node:fs"; | ||
| import { createServer } from "node:http"; | ||
| import { extname, join, normalize, resolve } from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
| const root = resolve(fileURLToPath(new URL(".", import.meta.url))); | ||
| const port = Number(process.env.PORT ?? 4173); | ||
| const types = { | ||
| ".css": "text/css; charset=utf-8", | ||
| ".html": "text/html; charset=utf-8", | ||
| ".json": "application/json; charset=utf-8", | ||
| ".mjs": "text/javascript; charset=utf-8", | ||
| ".svg": "image/svg+xml", | ||
| }; | ||
| const server = createServer((request, response) => { | ||
| const pathname = decodeURIComponent(new URL(request.url, "http://localhost").pathname); | ||
| const relative = normalize(pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "")); | ||
| const path = resolve(join(root, relative)); | ||
| if (!path.startsWith(`${root}/`)) { | ||
| response.writeHead(403).end("Forbidden"); | ||
| return; | ||
| } | ||
| try { | ||
| if (!statSync(path).isFile()) throw new Error("not a file"); | ||
| response.writeHead(200, { | ||
| "Content-Type": types[extname(path)] ?? "application/octet-stream", | ||
| "Cache-Control": "no-store", | ||
| "X-Content-Type-Options": "nosniff", | ||
| }); | ||
| createReadStream(path).pipe(response); | ||
| } catch { | ||
| response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }).end("Not found"); | ||
| } | ||
| }); | ||
| server.listen(port, "127.0.0.1", () => { | ||
| console.log(`Planr preset catalog listening on http://127.0.0.1:${port}`); | ||
| }); | ||
| :root { | ||
| color-scheme: dark; | ||
| --ink: #f4f0e7; | ||
| --muted: #a9a49a; | ||
| --quiet: #77736c; | ||
| --paper: #11110f; | ||
| --panel: #181815; | ||
| --panel-2: #20201c; | ||
| --line: #35342e; | ||
| --acid: #d9ff57; | ||
| --blue: #7bd9ff; | ||
| --amber: #ffc76a; | ||
| --red: #ff8e7d; | ||
| --green: #8fe3a8; | ||
| --radius: 18px; | ||
| font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | ||
| font-synthesis: none; | ||
| } | ||
| * { box-sizing: border-box; } | ||
| html { scroll-behavior: smooth; } | ||
| body { | ||
| margin: 0; | ||
| background: | ||
| radial-gradient(circle at 85% 5%, rgb(217 255 87 / 8%), transparent 30rem), | ||
| linear-gradient(180deg, #151511 0, var(--paper) 36rem); | ||
| color: var(--ink); | ||
| min-width: 300px; | ||
| } | ||
| button, select, input { font: inherit; } | ||
| button, select { color: inherit; } | ||
| a { color: inherit; } | ||
| .skip-link { | ||
| position: fixed; | ||
| left: 1rem; | ||
| top: 1rem; | ||
| z-index: 10; | ||
| padding: .75rem 1rem; | ||
| background: var(--acid); | ||
| color: #111; | ||
| clip-path: inset(50%); | ||
| height: 1px; | ||
| width: 1px; | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
| transform: translateY(-200%); | ||
| } | ||
| .skip-link:focus { | ||
| clip-path: none; | ||
| height: auto; | ||
| width: auto; | ||
| overflow: visible; | ||
| transform: translateY(0); | ||
| } | ||
| .site-header, main, footer { width: min(1180px, calc(100% - 2.5rem)); margin-inline: auto; } | ||
| .site-header { | ||
| min-height: 78px; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| border-bottom: 1px solid var(--line); | ||
| } | ||
| .brand { display: inline-flex; gap: .7rem; align-items: center; text-decoration: none; letter-spacing: -.02em; } | ||
| .brand-mark { | ||
| display: grid; | ||
| place-items: center; | ||
| width: 2rem; | ||
| height: 2rem; | ||
| border: 1px solid var(--acid); | ||
| border-radius: 50%; | ||
| color: var(--acid); | ||
| font-family: ui-monospace, SFMono-Regular, Menlo, monospace; | ||
| } | ||
| .brand strong { color: var(--acid); font-weight: 650; } | ||
| .header-note { color: var(--quiet); font-size: .85rem; } | ||
| .hero { | ||
| min-height: 440px; | ||
| display: grid; | ||
| grid-template-columns: minmax(0, 1.5fr) minmax(290px, .7fr); | ||
| gap: 5rem; | ||
| align-items: center; | ||
| padding-block: 6rem 4.5rem; | ||
| } | ||
| .eyebrow { | ||
| margin: 0 0 .75rem; | ||
| color: var(--acid); | ||
| font: 600 .72rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; | ||
| letter-spacing: .13em; | ||
| text-transform: uppercase; | ||
| } | ||
| h1, h2, h3, p { text-wrap: pretty; } | ||
| h1 { margin: 0; font-size: clamp(3rem, 7vw, 6.5rem); line-height: .91; letter-spacing: -.07em; font-weight: 600; } | ||
| h2 { margin: 0; font-size: clamp(1.6rem, 3vw, 2.65rem); line-height: 1; letter-spacing: -.045em; } | ||
| h3 { letter-spacing: -.025em; } | ||
| .lede { max-width: 720px; margin: 2rem 0 0; color: var(--muted); font-size: clamp(1rem, 1.8vw, 1.25rem); line-height: 1.6; } | ||
| .catalog-stats { margin: 0; border-top: 1px solid var(--line); } | ||
| .catalog-stats div { display: grid; grid-template-columns: 1fr auto; gap: 1rem; padding: 1.15rem 0; border-bottom: 1px solid var(--line); } | ||
| .catalog-stats dt { color: var(--muted); } | ||
| .catalog-stats dd { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } | ||
| .fixture-banner { margin-bottom: 1rem; padding: .9rem 1.1rem; border: 1px solid var(--amber); background: rgb(255 199 106 / 8%); color: var(--amber); border-radius: 12px; } | ||
| .authority-note { | ||
| display: grid; | ||
| grid-template-columns: auto 1fr; | ||
| gap: 1.25rem; | ||
| margin-bottom: 5rem; | ||
| padding: 1.4rem; | ||
| border: 1px solid var(--blue); | ||
| border-radius: var(--radius); | ||
| background: rgb(123 217 255 / 5%); | ||
| } | ||
| .authority-icon { color: var(--blue); font-size: 2rem; line-height: 1; } | ||
| .authority-note h2 { font-size: 1.1rem; letter-spacing: -.015em; } | ||
| .authority-note p { margin: .5rem 0 0; color: var(--muted); line-height: 1.55; max-width: 920px; } | ||
| .compare-shell { padding-block: 2rem 6rem; } | ||
| .section-heading { display: flex; justify-content: space-between; align-items: end; gap: 2rem; } | ||
| .toggle { display: flex; align-items: center; gap: .65rem; color: var(--muted); cursor: pointer; } | ||
| .toggle input { width: 1.15rem; height: 1.15rem; accent-color: var(--acid); } | ||
| .selectors { display: grid; grid-template-columns: 1fr auto 1fr; gap: 1rem; align-items: end; margin: 2.2rem 0 1.2rem; } | ||
| .selectors label { display: grid; gap: .55rem; color: var(--muted); font-size: .8rem; } | ||
| select { | ||
| width: 100%; | ||
| padding: .95rem 2.5rem .95rem 1rem; | ||
| border: 1px solid var(--line); | ||
| border-radius: 12px; | ||
| background: var(--panel); | ||
| } | ||
| select:focus-visible, button:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; } | ||
| .versus { padding-bottom: 1rem; color: var(--quiet); font: .75rem ui-monospace, monospace; text-transform: uppercase; } | ||
| .comparison-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.2rem; } | ||
| .preset-card { min-width: 0; padding: clamp(1.2rem, 3vw, 2rem); border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); } | ||
| .card-top { display: flex; align-items: start; justify-content: space-between; gap: 1rem; } | ||
| .card-top h3 { margin: .2rem 0 0; font-size: clamp(1.45rem, 3vw, 2.1rem); } | ||
| .binding-name { color: var(--muted); font-family: ui-monospace, monospace; font-size: .82rem; } | ||
| .badge { display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .55rem; border: 1px solid currentColor; border-radius: 999px; font: 600 .68rem ui-monospace, monospace; text-transform: uppercase; letter-spacing: .06em; } | ||
| .badge::before { content: "●"; font-size: .55rem; } | ||
| .status-recommended { color: var(--acid); } | ||
| .status-verified { color: var(--blue); } | ||
| .status-stale, .status-deprecated { color: var(--amber); } | ||
| .status-experimental, .status-unverified { color: var(--muted); } | ||
| .meta-line { color: var(--quiet); font: .75rem/1.5 ui-monospace, monospace; } | ||
| .metric-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; margin: 1.5rem 0; overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: var(--line); } | ||
| .metric { padding: 1rem; background: var(--panel-2); } | ||
| .metric span { display: block; color: var(--quiet); font-size: .72rem; margin-bottom: .4rem; } | ||
| .metric strong { font: 500 1.15rem ui-monospace, monospace; } | ||
| .card-section { padding: 1.25rem 0; border-top: 1px solid var(--line); } | ||
| .card-section h4 { margin: 0 0 .9rem; font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; } | ||
| .enforcement-list { display: grid; gap: .7rem; margin: 0; } | ||
| .enforcement-list div { display: grid; grid-template-columns: minmax(120px, .45fr) 1fr; gap: 1rem; } | ||
| .enforcement-list dt { font-size: .82rem; } | ||
| .enforcement-list dd { margin: 0; color: var(--muted); font-size: .82rem; line-height: 1.45; } | ||
| .compatibility-list { display: grid; gap: .7rem; margin: 0; } | ||
| .compatibility-list div { display: grid; grid-template-columns: minmax(120px, .45fr) 1fr; gap: 1rem; } | ||
| .compatibility-list dt { font-size: .82rem; } | ||
| .compatibility-list dd { margin: 0; color: var(--muted); font-size: .82rem; line-height: 1.45; } | ||
| .state { display: inline-block; margin-left: .35rem; color: var(--blue); font: .65rem ui-monospace, monospace; text-transform: uppercase; } | ||
| .hash { overflow-wrap: anywhere; color: var(--quiet); font: .7rem/1.5 ui-monospace, monospace; } | ||
| .command-block { display: grid; gap: .75rem; } | ||
| .command-block code { display: block; overflow-x: auto; padding: 1rem; border-radius: 10px; background: #0b0b09; color: var(--acid); font: .75rem/1.5 ui-monospace, monospace; } | ||
| .copy-button { justify-self: start; padding: .7rem .95rem; border: 1px solid var(--acid); border-radius: 9px; background: transparent; color: var(--acid); cursor: pointer; } | ||
| .copy-button:hover { background: var(--acid); color: #111; } | ||
| .empty-state { padding: 3rem; border: 1px dashed var(--line); border-radius: var(--radius); background: var(--panel); } | ||
| .empty-state h3 { margin: .25rem 0 .75rem; font-size: 1.6rem; } | ||
| .empty-state p:not(.eyebrow) { color: var(--muted); } | ||
| .empty-state code { color: var(--acid); overflow-wrap: anywhere; } | ||
| .legend { display: grid; grid-template-columns: .65fr 1.35fr; gap: 5rem; padding: 5rem 0; border-top: 1px solid var(--line); } | ||
| .legend dl { margin: 0; } | ||
| .legend dl div { display: grid; grid-template-columns: 140px 1fr; gap: 1rem; padding: .8rem 0; border-bottom: 1px solid var(--line); } | ||
| .legend dt { font-weight: 650; } | ||
| .legend dd { margin: 0; color: var(--muted); line-height: 1.5; } | ||
| footer { display: flex; justify-content: space-between; gap: 2rem; padding: 2.5rem 0 4rem; border-top: 1px solid var(--line); color: var(--quiet); font-size: .78rem; } | ||
| .sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } | ||
| @media (max-width: 760px) { | ||
| .site-header, main, footer { width: min(100% - 1.4rem, 1180px); } | ||
| .header-note { display: none; } | ||
| .hero { min-height: auto; grid-template-columns: 1fr; gap: 3rem; padding-block: 4rem 3rem; } | ||
| h1 { font-size: clamp(3rem, 16vw, 5.5rem); } | ||
| .authority-note { margin-bottom: 3rem; } | ||
| .section-heading { align-items: start; flex-direction: column; } | ||
| .selectors { grid-template-columns: 1fr; } | ||
| .versus { display: none; } | ||
| .comparison-grid { grid-template-columns: 1fr; } | ||
| .legend { grid-template-columns: 1fr; gap: 2rem; } | ||
| .legend dl div { grid-template-columns: 110px 1fr; } | ||
| footer { flex-direction: column; } | ||
| } | ||
| @media (prefers-reduced-motion: reduce) { | ||
| html { scroll-behavior: auto; } | ||
| *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } | ||
| } |
| { | ||
| "schemaVersion": 1, | ||
| "generatedAtUnix": 1783987200, | ||
| "source": { | ||
| "state": "verified_registry_projection", | ||
| "entryCount": 2, | ||
| "trust": "localhost_test_fixture", | ||
| "message": "Synthetic browser fixture; never used by the production catalog." | ||
| }, | ||
| "compositions": [ | ||
| { | ||
| "id": "balanced-codex@1.0.0", | ||
| "entryId": "balanced-codex", | ||
| "entryVersion": "1.0.0", | ||
| "status": "recommended", | ||
| "recommended": true, | ||
| "freshness": "current", | ||
| "lifecycle": "published", | ||
| "policy": { | ||
| "id": "balanced", | ||
| "version": "1.0.0", | ||
| "usage": { "max_active_agents": 3, "max_parallel_writers": 1, "max_depth": 1, "metering": "trusted" } | ||
| }, | ||
| "binding": { "id": "codex-openai", "version": "1.0.0", "host": "codex" }, | ||
| "compatibility": { "hosts": ["codex"], "minPlanrVersion": "1.3.0", "maxPlanrVersion": "1.9.0" }, | ||
| "enforcement": [ | ||
| { "dimension": "Policy limits", "state": "verified", "detail": "Planr validates count, time, concurrency, transition, and safety-stop limits before dispatch." }, | ||
| { "dimension": "Execution permissions", "state": "verified", "detail": "No commands, hooks, network/MCP grants, secret references, or overwrite permission." }, | ||
| { "dimension": "Model and effort", "state": "host_enforced", "detail": "Codex retains final concrete execution authority." }, | ||
| { "dimension": "Effective route evidence", "state": "verified", "detail": "7 of 7 evaluation runs carried verified effective-route evidence." } | ||
| ], | ||
| "evaluation": { | ||
| "suiteId": "planr-preset-suite", | ||
| "suiteVersion": "1.8.0", | ||
| "evaluatedAtUnix": 1783987200, | ||
| "reviewAtUnix": 1815523200, | ||
| "metrics": { "average_quality_score_bps": 9600, "runs": 7, "oracle_passes": 7 } | ||
| }, | ||
| "registry": { "manifestSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "signer": "localhost-fixture" } | ||
| }, | ||
| { | ||
| "id": "low-usage-claude@1.0.0", | ||
| "entryId": "low-usage-claude", | ||
| "entryVersion": "1.0.0", | ||
| "status": "recommended", | ||
| "recommended": true, | ||
| "freshness": "current", | ||
| "lifecycle": "published", | ||
| "policy": { | ||
| "id": "low-usage", | ||
| "version": "1.0.0", | ||
| "usage": { "max_active_agents": 2, "max_parallel_writers": 1, "max_depth": 1, "metering": "trusted" } | ||
| }, | ||
| "binding": { "id": "claude-native", "version": "1.0.0", "host": "claude-code" }, | ||
| "compatibility": { "hosts": ["claude-code"], "minPlanrVersion": "1.3.0", "maxPlanrVersion": "1.9.0" }, | ||
| "enforcement": [ | ||
| { "dimension": "Policy limits", "state": "verified", "detail": "Planr validates count, time, concurrency, transition, and safety-stop limits before dispatch." }, | ||
| { "dimension": "Execution permissions", "state": "verified", "detail": "No commands, hooks, network/MCP grants, secret references, or overwrite permission." }, | ||
| { "dimension": "Model and effort", "state": "host_enforced", "detail": "Claude Code retains final concrete execution authority." }, | ||
| { "dimension": "Effective route evidence", "state": "verified", "detail": "7 of 7 evaluation runs carried verified effective-route evidence." } | ||
| ], | ||
| "evaluation": { | ||
| "suiteId": "planr-preset-suite", | ||
| "suiteVersion": "1.8.0", | ||
| "evaluatedAtUnix": 1783987200, | ||
| "reviewAtUnix": 1815523200, | ||
| "metrics": { "average_quality_score_bps": 9100, "runs": 7, "oracle_passes": 7 } | ||
| }, | ||
| "registry": { "manifestSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "signer": "localhost-fixture" } | ||
| } | ||
| ] | ||
| } |
@@ -43,2 +43,7 @@ # Planr Architecture | ||
| - `src/agents.rs`: agent profile registry core. Owns `.planr/agents.toml` parsing, registry validation warnings, and the pure advisory `resolve_route` precedence logic (override > work_type > plan > default); no storage or host concerns. | ||
| - `src/usage_policy.rs`: provider-neutral Usage Policy v1 core. Owns strict `.planr/policy.toml` parsing, policy and task-contract vocabulary, materiality classification, budget/concurrency validation, and the pure five-way transition resolver; it contains no provider ids, host dispatch, or execution-permission behavior. | ||
| - `src/execution_policy.rs`: execution admission core. Owns per-role filesystem, network, tool/MCP, structured command, environment, hook, secret, and approval grants; permission-diff previews; bounded task-contract admission; fail-closed command grammar; and isolated write-scope concurrency. It never selects models or mutates graph state. `src/app/policy.rs` binds that pure decision to the current SQLite lease owner and pick token, and only treats an admission from that exact lease generation as authoritative. | ||
| - `src/route_audit.rs`: provider-neutral run-observation contract. Owns strict requested/resolved/effective route stages, model/effort/fork enforcement confidence, transition provenance, policy/binding versions, and per-dimension metering confidence. It rejects requested-only values in the effective stage rather than inferring host execution. | ||
| - `src/preset.rs`: pure preset-composition core. Owns versioned host-binding types, abstract-role compatibility, model/effort/fork capability validation, deterministic registry projection, verification age, permission additions, and provenance-lock vocabulary; it performs no writes. | ||
| - `src/app/presets.rs`: shared CLI/MCP preset application service and repository-artifact writer. Owns preview/conflict reporting, canonical-root and allowlist enforcement, symlink/traversal rejection, create-only apply, rollback on write failure, and `policy_applied`/rejection events. | ||
| - `src/app/agents.rs`: routing application boundary. Owns the `agents` and `item route` command handlers, the shared `*_value` JSON shapes reused by MCP, per-item route facts assembly, and registry-aware role content selection for installs. | ||
@@ -45,0 +50,0 @@ - `src/app/agents_init.rs`: registry bootstrap boundary. Owns `planr agents init` — the static cost-tiering scaffold and, per the agent-pool plan, the flag-spec builder and interactive wizard. |
@@ -52,3 +52,4 @@ # CLI Reference | ||
| planr debug bundle [--item <item-id>] --preview | ||
| planr log add --item <item-id> --summary "..." [--files a --files b | --files a,b] [--cmd "..."] [--kind completion|progress|verification] [--profile <id>] | ||
| planr trace item <item-id> | ||
| planr log add --item <item-id> --summary "..." [--files a --files b | --files a,b] [--cmd "..."] [--kind completion|progress|verification] [--profile <id>] [--route-audit <observation.json>] | ||
| # machine consumers: --cmd writes the `commands` field in log JSON; --tests writes `tests` | ||
@@ -70,2 +71,7 @@ planr review request <item-id> | ||
| planr agents check | ||
| planr agents preset list [--json] | ||
| planr agents preset apply <policy-path-or-id> --binding <binding-path-or-id> [--preview|--confirm] | ||
| planr agents preset registry verify <manifest> --entry <id> --content-root <dir> [--trust-store <path>] [--at-unix <timestamp>] [--host <host>] | ||
| planr agents preset registry import <manifest> --entry <id> --content-root <dir> [--trust-store <path>] [--at-unix <timestamp>] [--host <host>] [--preview|--confirm] | ||
| planr agents preset registry list [--at-unix <timestamp>] | ||
| planr doctor [--client codex|claude|cursor|all] | ||
@@ -128,3 +134,3 @@ planr install codex|claude|cursor [--dry-run] [--no-mcp] [--force] [--no-hooks] | ||
| `trace item` on a review item inlines the target item and its evidence logs under `target`, so a reviewer's first trace already contains what is being audited. The human (non-JSON) mode renders the packet: status, owner, links, logs. When the item has a declared route or a run recorded a profile, the trace gains a `routing` section — the declared route next to every run's actual client/profile with an advisory `mismatch` marker and a mismatch count; items without either keep the exact pre-routing trace shape. | ||
| `trace item` on a review item inlines the target item and its evidence logs under `target`, so a reviewer's first trace already contains what is being audited. The human (non-JSON) mode renders the packet: status, owner, links, logs. When the item has a declared route, a run profile, or a route observation, the trace gains a `routing` section — the declared route next to every run's actual client/profile and, when supplied, its independent requested → resolved → effective model/effort/context-fork stages, transition reason, policy/binding provenance, and per-dimension metering confidence. Unknown effective host values remain `null`/`unavailable`; requested values are never copied forward as proof. MCP `planr_trace_item` returns the identical JSON shape. | ||
@@ -141,5 +147,15 @@ `doctor` also reports the agent registry in all states without ever failing: absent (informational hint), degraded (parse error with line context), or loaded with profile/route counts, validation warnings, and per-artifact drift — a rendered role file whose content no longer matches what the current registry would render is flagged `drifted` with a `planr install <client> --force` hint, while files without the generated-from header are the user's (`manual`) and never flagged. | ||
| `policy show` and `policy check` inspect the provider-neutral Usage Policy v1 file (`.planr/policy.toml`). A missing policy is an explicit successful state that preserves existing advisory routing; a malformed or unsafe policy fails `check` with parser/field diagnostics and leaves enforcement unavailable. Usage Policy v1 fixes delegation depth at one and keeps retry, availability fallback, quality escalation, quota downgrade, and safety stop as distinct transition contracts. Its separate `execution` section declares bounded per-role filesystem, network, tool/MCP, structured command, environment, hook, secret-reference, and approval grants. `policy admit <request.json>` requires the picked item's current `item_id` and `pick_token`, authorizes the current worker as lease owner, evaluates a bounded task contract before delegation, previews any permission addition, records the decision and lease generation, rejects unclassified commands and destructive or out-of-scope work, permits overlapping readers, and admits concurrent writers only for disjoint scopes isolated in distinct worktrees. Released or recovered leases never inherit historical admitted scopes. MCP `planr_policy_show`, `planr_policy_check`, and `planr_policy_admit`, plus HTTP `POST /v1/policy/admit`, call the same admission service and return the same decision shape. | ||
| `agents preset list` enumerates the embedded catalog: `balanced`, `low-usage`, `max-quality`, and `read-only-audit` policies; `codex-openai`, `cursor-openai`, `cursor-fable-grok`, `claude-native`, and `mixed-host` bindings; source checksums; and every declared safe pair. MCP `planr_presets_list` returns the identical catalog. | ||
| `agents preset apply <policy-path-or-id> --binding <binding-path-or-id>` composes a provider-neutral Usage Policy v1 file with a strict, versioned host binding. Embedded id pairs report `pack.status = safe`; explicit file inputs remain allowed custom compositions and receive a clear non-safe warning even if they reuse built-in ids. Preview is the default and reports compatibility, per-role permission additions, verification age, provenance, content hashes, every normalized repository-relative target, create/unchanged/conflict state, and a redacted old/proposed configuration projection. Policy limits/transitions and generated registry routes, concrete models/efforts, and fallbacks are therefore auditable before confirmation without returning secret-like values. `--confirm` writes exactly the conflict-free preview to `.planr/policy.toml`, `.planr/agents.toml`, `.planr/preset.lock.toml`, and allowlisted repository-local host artifacts; it never overwrites different existing files. Absolute paths, traversal, symlink crossings, user/home/global configuration, and repository `.codex/config.toml` fail before the first write. Secret-like binding metadata is rejected with field-only diagnostics before dispatch/warning output or mutation. Bindings own exact client/model/effort and fork capabilities: Codex cross-tier dispatch is keyed by the binding host identity, defaults to `none`, rejects `all`, and accepts a positive partial fork only with declared limits and non-blank capability evidence. Host-specific bindings require every profile client to match the host; explicit cross-client mappings use `host = "mixed-host"`. MCP `planr_preset_apply` calls the same application service and returns the identical shape (`confirm` defaults to false). Binding schema and repository-boundary details: [Preset Composition](PRESET_COMPOSITION.md). | ||
| `agents preset evaluate [--report-dir <dir>] [--at-unix <timestamp>] [--host <host>] [--live-host-command <absolute-executable> [--live-host-arg <arg>]... [--trusted-telemetry-signer <registry-id> --trusted-telemetry-collector <absolute-executable>]]` defaults to the embedded offline simulation: no process/network execution, estimated projections, unavailable effective routes, and no recommendation. Explicit live mode creates a Planr-controlled challenge/workspace for every versioned task. The adapter must read the challenge and write a bounded strict-schema artifact whose independently computed hash, candidate/task/input/artifact binding, challenge binding, and task oracle Planr validates. Live `evidence_complete` requires every required task execution and oracle pass; missing or invalid artifacts produce `unverified` candidates and make report-level `reproducible_evidence` false. Policy tool/write capabilities remain authoritative. Without telemetry, host route and tool/token/credit claims remain estimated and recommendation-ineligible. The all-or-none telemetry flags select a signer and collector independently pinned in `.planr/trusted-telemetry.toml`. Planr creates a fresh run UUID and task challenge, invokes the hash-pinned collector only after independently reading the artifact, verifies the registry-pinned Ed25519 signature and exact run/suite/time/task/input/artifact/challenge/host bindings, then promotes only receipt-backed effective route and usage dimensions to `telemetry_receipt`/`trusted`. Caller-supplied keys, run ids, and precomputed bundles are not accepted; missing, replayed, or tampered post-run receipts fail closed. Valid complete telemetry makes `recommended` reachable on CLI and MCP. Constant responders, public-label request mappers, and read-only candidates on write fixtures cannot recommend. Full protocol: [Preset Evaluation](PRESET_EVALUATION.md). | ||
| `agents preset registry verify/import/list` implements the optional declarative registry boundary. Verification checks version compatibility, host compatibility, lifecycle/revocation, exact byte lengths and SHA-256 digests, canonical policy/binding semantics and safe artifacts, and optional Ed25519 signatures against separately pinned maintainer keys. `recommended` additionally requires a current canonical evaluation bound to the shipped policy/binding bytes, independently derived metrics and thresholds, task/oracle/result-hash completeness, and trusted route/metering evidence; a trusted signature alone cannot promote it. Import previews by default and, on `--confirm`, atomically caches only manifest-declared files plus the original manifest below `.planr/registry/cache/`. Offline listing treats the receipt as inventory only and re-verifies the manifest hash, signature, manifest-owned checksums, and freshness, so coordinated content/receipt tampering fails closed and a registry outage cannot affect active projects or already verified content. See [Preset Registry](PRESET_REGISTRY.md). | ||
| `agents init` writes a commented starter registry with the cost-tiering defaults (premium driver that keeps the verdicts, standard steerable implementer, budget helper for token-hungry side work, plus code/fix/review/default routes) and names the follow-up commands; an existing `.planr/agents.toml` is never overwritten without `--force`. Individual pools generate from repeatable spec flags — `--profile designer=claude-code/opus@high#premium` declares a profile, `--skill designer=frontend-design` pairs it with a skill, `--route frontend=designer,driver` routes a use-case work type with fallbacks, `--default-route` catches the rest; validation is fail-closed (unknown profile references and malformed specs error, naming the grammar, before anything is written), and consistent specs generate a zero-warning registry. `--interactive` walks the same questions as guided prompts (requires a terminal; conflicts with spec flags) and can render the host role files at the end. `agents list` and `agents check` inspect the agent profile registry (`.planr/agents.toml`): named profiles (host client, model, effort, cost tier) and advisory routes from work selectors to profiles. When a registry resolves for a picked item, the pick packet carries a `routing` block — profile, client, model, effort, cost tier, fallback chain, and the matched selector — resolved with precedence per-item override > `work_type` > `plan` > default route. `item route` shows an item's resolved route and its source (`override` or `policy`), pins a registry profile with `--set <profile>` (emits `route_overridden`), and unpins with `--clear` (emits `route_override_cleared`); a pinned profile that later disappears from the registry falls back to policy routing with a repair hint. The same JSON shapes are exposed over MCP: `planr_agents_list` and `planr_item_route` read, `planr_item_route_set` and `planr_item_route_clear` mutate. Routing is advisory: Planr never dispatches models, a missing registry just omits the block, a malformed one degrades picking to no-routing (only `agents check` fails, with the parser's line context), and validation warnings (unknown profile references, duplicate selectors, review work on a budget tier, secret-like values) never affect exit codes. When a registry is present, `install codex|claude|cursor` renders the subagent role files with model pins from it — the `work_type=code` route pins the worker, the `work_type=review` route pins the reviewer, and a role only pins profiles whose `client` matches the install target (a Cursor-profile review route never writes a Cursor model into a Codex TOML). Rendered files carry a `# generated from .planr/agents.toml` header; without a registry (or when no matching route resolves) the shipped static role files are written byte-identically, and existing files are never overwritten without `install --force`. Full guide: [Model Routing](MODEL_ROUTING.md). | ||
| `log add` and `done` accept `--profile <id>` — the registry profile the run actually executed on (`PLANR_PROFILE` env is the fallback, so rendered role files can export it). The profile is stored on the recorded run; when it differs from the item's declared route, Planr emits an advisory `route_mismatch_observed` event (declared, actual, run id) visible in `event list --item`. Runs without a profile, logs without commands/tests, and projects without a registry produce no comparison and no event, and mismatches never block logging, review, or closure. The MCP `planr_log_add` tool and `POST .../log` accept the same optional `profile`. | ||
| `log add` and `done` accept `--profile <id>` — the registry profile the run actually executed on (`PLANR_PROFILE` env is the fallback, so rendered role files can export it). They also accept `--route-audit <observation.json>` for the strict three-stage observation contract. A route audit records each stage's model, effort, and context-fork value with enforcement state (`verified`, `requested_only`, `estimated`, or `unavailable`), an evidence-source enum, typed transition plus reason, policy/binding ids and versions, and wall-time/tool/token/credit values with independent confidence. It is stored in run metadata, projected into its durable log and trace, and emits local route-stage/transition events. The profile remains backward compatible: when it differs from the item's declared route, Planr emits advisory `route_mismatch_observed`. Runs without commands/tests record neither a run nor a route observation. MCP `planr_log_add` and HTTP item log accept the same optional `route_observation` object. | ||
@@ -146,0 +162,0 @@ `artifact add` infers the mime type from the file extension when `--path` is given without `--mime` (PNG screenshots land as `image/png`, not `text/plain`); inline `--content` defaults to `text/plain`. The same inference applies on MCP `planr_artifact_add` and HTTP `POST /v1/artifacts`. |
| { | ||
| "tools": [ | ||
| "planr_project_show", | ||
| "planr_trace_item", | ||
| "planr_map_show", | ||
@@ -22,2 +23,11 @@ "planr_map_status", | ||
| "planr_agents_list", | ||
| "planr_presets_list", | ||
| "planr_preset_evaluate", | ||
| "planr_preset_apply", | ||
| "planr_preset_registry_verify", | ||
| "planr_preset_registry_import", | ||
| "planr_preset_registry_list", | ||
| "planr_policy_show", | ||
| "planr_policy_check", | ||
| "planr_policy_admit", | ||
| "planr_item_route", | ||
@@ -103,5 +113,11 @@ "planr_item_route_set", | ||
| "planr agents check", | ||
| "planr agents preset list", | ||
| "planr agents preset apply", | ||
| "planr agents preset registry verify", | ||
| "planr agents preset registry import", | ||
| "planr agents preset registry list", | ||
| "planr item route", | ||
| "planr trace item", | ||
| "planr serve --port" | ||
| ] | ||
| } |
@@ -44,3 +44,7 @@ # MCP Contract | ||
| - event list and debug bundle preview | ||
| - log add and read | ||
| - trace item, log add, and log read (including three-stage route observations) | ||
| - built-in policy/binding catalog listing plus safe-pack/custom composition status | ||
| - declarative registry verification with canonical evaluation/safe-binding gates, preview-first immutable import, and manifest-anchored integrity/signature/freshness-checked offline cache listing | ||
| - policy preset preview/apply by path or built-in id with repository-only target validation and deterministic provenance lock | ||
| - deterministic offline preset simulation plus explicit opt-in live-host execution with Planr-controlled challenge workspaces, strict task artifacts read and hashed by Planr, candidate/task outcome oracles, failed-live-attempt `unverified`/incomplete lifecycle semantics, production policy-capability checks, estimated arbitrary-process claims, optional Ed25519-verified run/suite/time/task/challenge-bound telemetry that alone can promote effective route and usage evidence to trusted/recommendation-eligible, observed process latency, transition/correction/violation counts, result hashes, and deterministic lifecycle thresholds | ||
| - review annotate, ingest, artifact, evidence, and close | ||
@@ -47,0 +51,0 @@ - item close, context create, and search |
@@ -120,5 +120,5 @@ # Model Routing | ||
| Every host has a silent override path — the `CLAUDE_CODE_SUBAGENT_MODEL` env var, Cursor plan/admin/Max-Mode policy, Codex full-history forks, org allowlists — so a pin alone is not proof. The audit loop closes this: workers report the profile they actually ran on via `planr log add`/`planr done --profile <id>` (or the `PLANR_PROFILE` env var, which rendered role files can export), the profile lands on the recorded run, and when it differs from the item's declared route Planr emits an advisory `route_mismatch_observed` event with the declared and actual ids. | ||
| Every host has a silent override path — the `CLAUDE_CODE_SUBAGENT_MODEL` env var, Cursor plan/admin/Max-Mode policy, Codex full-history forks, org allowlists — so a pin alone is not proof. The audit loop closes this at two levels: workers report the profile they actually ran on via `planr log add`/`planr done --profile <id>` (or `PLANR_PROFILE`) for backward-compatible mismatch checks, and can attach a strict `--route-audit <observation.json>` that keeps requested, host-resolved, and effective model/effort/fork values separate. Every dimension carries enforcement confidence and a constrained evidence source; missing host evidence stays unavailable instead of inheriting the request. | ||
| - `planr trace item <id>` shows the declared route next to every run's actual client/profile with a `mismatch` marker. | ||
| - `planr trace item <id>` (MCP: `planr_trace_item`) shows the declared route next to every run's actual client/profile and three-stage observation with a `mismatch` marker. | ||
| - Runs also record the host they observably executed under (`observed_client`, detected from environment variables the hosts set themselves — no flags); a run whose host differs from the declared route's client emits an advisory `client_mismatch_observed` event, which catches exactly the deviation profile self-report cannot: a different host standing in for the declared client, even when the model matched. | ||
@@ -125,0 +125,0 @@ - `planr doctor` reports the registry state (absent, degraded with parse context, loaded with counts and warnings) and flags rendered role files that drifted from the current registry (`planr install <client> --force` re-renders). |
+15
-2
| { | ||
| "name": "planr", | ||
| "version": "1.3.0", | ||
| "version": "1.4.0", | ||
| "description": "Local-first planning and execution coordination for coding agents.", | ||
@@ -18,2 +18,5 @@ "license": "MIT", | ||
| "plugins", | ||
| "presets", | ||
| "evaluations", | ||
| "website", | ||
| "README.md", | ||
@@ -24,6 +27,16 @@ "LICENSE.md", | ||
| "scripts": { | ||
| "alchemy:check-runtime": "node scripts/check-alchemy-runtime.mjs", | ||
| "build:native": "cargo build --release", | ||
| "test": "cargo test", | ||
| "pack:check": "npm pack --dry-run" | ||
| "pack:check": "npm pack --dry-run", | ||
| "deploy:test": "node scripts/cloudflare-test.mjs deploy", | ||
| "destroy:test": "node scripts/cloudflare-test.mjs destroy", | ||
| "site:build": "node scripts/build-site.mjs", | ||
| "site:check": "pnpm site:test && pnpm site:build", | ||
| "site:test": "node --test website/*.test.mjs", | ||
| "site:serve": "node website/serve.mjs" | ||
| }, | ||
| "devDependencies": { | ||
| "alchemy": "0.93.7" | ||
| }, | ||
| "engines": { | ||
@@ -30,0 +43,0 @@ "node": ">=18" |
| { | ||
| "name": "planr", | ||
| "description": "Skill-driven planning and execution loop for coding agents: one planr entry point, an autonomous planr-loop, and evidence-backed task graph skills powered by the planr CLI.", | ||
| "version": "1.3.0", | ||
| "version": "1.4.0", | ||
| "author": { | ||
@@ -6,0 +6,0 @@ "name": "instructa" |
| { | ||
| "name": "planr", | ||
| "version": "1.3.0", | ||
| "version": "1.4.0", | ||
| "description": "Skill-driven planning and execution loop for coding agents: one $planr entry point, an autonomous $planr-loop, and evidence-backed task graph skills powered by the planr CLI.", | ||
@@ -5,0 +5,0 @@ "author": { |
+12
-0
@@ -25,2 +25,14 @@ # Planr | ||
| ## New in 1.4.0: Verified Presets & Catalog | ||
| Planr now ships a verified preset system for composing a provider-neutral usage policy with a host binding, previewing every repository-local change, and applying the result only after confirmation: | ||
| ```bash | ||
| planr agents preset list | ||
| planr agents preset apply balanced --binding codex-openai --preview | ||
| planr agents preset apply balanced --binding codex-openai --confirm | ||
| ``` | ||
| The built-in catalog includes four policies, five host bindings, and 20 declared safe pairs. Reproducible evaluation, signed registry verification, and the public [Planr Preset Catalog](https://planr-test-catalog.office-35d.workers.dev/) keep recommendation evidence inspectable without making the registry a runtime dependency. Full guides: [Preset Composition](docs/PRESET_COMPOSITION.md) · [Preset Evaluation](docs/PRESET_EVALUATION.md) · [Preset Registry](docs/PRESET_REGISTRY.md). | ||
| ## New in 1.3.0: Native Host Hooks | ||
@@ -27,0 +39,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Network access
Supply chain riskThis module accesses the network.
Found 2 instances
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
32057178
33.56%99
47.76%8791
4576.06%210
6.06%1
Infinity%10
233.33%10
100%