planr 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +9 -56
  2. package/docs/ARCHITECTURE.md +4 -4
  3. package/docs/CLI_REFERENCE.md +6 -14
  4. package/docs/CODEX.md +6 -6
  5. package/docs/EXAMPLE_WEBAPP.md +63 -97
  6. package/docs/GOALS.md +8 -25
  7. package/docs/INSTALL.md +2 -2
  8. package/docs/MCP_CONTRACT.md +2 -5
  9. package/docs/MODEL_ROUTING.md +20 -177
  10. package/docs/ROUTING_BUNDLES.md +32 -0
  11. package/docs/SKILLS.md +5 -3
  12. package/docs/fixtures/mcp-contract.json +3 -11
  13. package/npm/native/darwin-arm64/planr +0 -0
  14. package/npm/native/darwin-x86_64/planr +0 -0
  15. package/npm/native/linux-arm64/planr +0 -0
  16. package/npm/native/linux-x86_64/planr +0 -0
  17. package/package.json +2 -14
  18. package/plugins/planr/.claude-plugin/plugin.json +1 -1
  19. package/plugins/planr/.codex-plugin/plugin.json +1 -1
  20. package/plugins/planr/skills/planr-loop/SKILL.md +1 -1
  21. package/docs/PRESET_COMPOSITION.md +0 -109
  22. package/docs/PRESET_EVALUATION.md +0 -118
  23. package/docs/PRESET_REGISTRY.md +0 -61
  24. package/evaluations/preset-suite-v1.toml +0 -127
  25. package/evaluations/sol-luna-codex-v1.toml +0 -42
  26. package/plugins/planr/skills/planr-loop/agents/planr-reviewer.toml +0 -23
  27. package/plugins/planr/skills/planr-loop/agents/planr-worker.toml +0 -21
  28. package/presets/bindings/claude-native.toml +0 -52
  29. package/presets/bindings/codex-openai.toml +0 -56
  30. package/presets/bindings/cursor-fable-grok.toml +0 -49
  31. package/presets/bindings/cursor-openai.toml +0 -49
  32. package/presets/bindings/mixed-host.toml +0 -64
  33. package/presets/policies/balanced.toml +0 -50
  34. package/presets/policies/low-usage.toml +0 -50
  35. package/presets/policies/max-quality.toml +0 -50
  36. package/presets/policies/read-only-audit.toml +0 -50
  37. package/website/README.md +0 -79
  38. package/website/_headers +0 -7
  39. package/website/alchemy-runtime.test.mjs +0 -21
  40. package/website/app.mjs +0 -216
  41. package/website/build-catalog.mjs +0 -135
  42. package/website/build-site.test.mjs +0 -69
  43. package/website/catalog-model.mjs +0 -185
  44. package/website/catalog-model.test.mjs +0 -124
  45. package/website/cloudflare-launcher.test.mjs +0 -38
  46. package/website/data/catalog.json +0 -307
  47. package/website/index.html +0 -122
  48. package/website/registry/manifest.toml +0 -48
  49. package/website/registry/report.md +0 -33
  50. package/website/registry/trusted-maintainers.toml +0 -6
  51. package/website/registry/verification.json +0 -7258
  52. package/website/serve.mjs +0 -41
  53. package/website/styles.css +0 -201
  54. package/website/test-fixtures/recommended.json +0 -72
@@ -1,196 +1,39 @@
1
- # Model Routing
1
+ # Model routing
2
2
 
3
- Declare which agent and model each kind of work should run on once, in one file and Planr hands the recommendation to whoever picks the work.
4
-
5
- The pattern this makes declarative: strongest model plans and judges, a cheap fast steerable model implements, token-hungry side work (browser verification, codebase analysis) goes to budget profiles. Without a registry that knowledge lives in CLAUDE.md prose, Codex agent TOMLs, and Cursor frontmatter — three dialects that drift. With a registry it travels inside every pick packet.
6
-
7
- Routing is **advisory by design**: Planr never calls model providers and never blocks a pick because a profile is unavailable. Your host (Codex, Claude Code, Cursor, any MCP client) stays the dispatch authority.
8
-
9
- Want the whole flow on a concrete project first? [Worked Example: Routing a Small Web App](EXAMPLE_WEBAPP.md) walks a frontend/backend todo app from pool declaration to the audit trail, with real outputs.
10
-
11
- ## Quick Start
12
-
13
- One command writes a working starter registry — the cost-tiering defaults with a premium driver, a standard implementer, and a budget helper, commented so the tiers explain themselves:
14
-
15
- ```bash
16
- planr agents init # writes .planr/agents.toml; never overwrites without --force
17
- ```
18
-
19
- Or declare `.planr/agents.toml` by hand:
3
+ Planr Core treats routing as optional, advisory repository data. `.planr/agents.toml` declares opaque profiles and routes; Planr resolves them into pick packets but never calls a provider or claims that a requested model actually ran.
20
4
 
21
5
  ```toml
22
- [profiles.fable-driver]
23
- client = "cursor"
24
- model = "fable-5"
6
+ [profiles.worker]
7
+ client = "host-a"
8
+ model = "model-id"
9
+ agent_type = "repository-role"
25
10
  effort = "high"
26
- cost_tier = "premium"
27
- capabilities = ["orchestration", "review", "planning"]
28
- notes = "Planner/architect and judge. Verdicts stay on this tier."
29
-
30
- [profiles.gpt55-coder]
31
- client = "codex"
32
- model = "gpt-5.5"
33
- effort = "xhigh"
34
- cost_tier = "standard"
35
- capabilities = ["code", "steerable"]
36
- notes = "Primary implementer: strong, fast, cheap on subscription."
11
+ skill = "planr-work"
37
12
 
38
13
  [[routes]]
39
14
  match = { work_type = "code" }
40
- profile = "gpt55-coder"
41
- fallbacks = ["fable-driver"]
42
-
43
- [[routes]]
44
- match = { work_type = "review" }
45
- profile = "fable-driver"
46
-
47
- [route_default]
48
- profile = "gpt55-coder"
49
- fallbacks = ["fable-driver"]
50
- ```
51
-
52
- Validate and inspect:
53
-
54
- ```bash
55
- planr agents check # non-zero exit only on parse failure; warnings pass
56
- planr agents list # resolved profiles, routes, and warnings
15
+ profile = "worker"
57
16
  ```
58
17
 
59
- From then on, every pick carries the recommendation:
18
+ Create a neutral scaffold or an explicit registry:
60
19
 
61
20
  ```bash
62
- planr pick --json
63
- ```
64
-
65
- ```json
66
- "routing": {
67
- "profile": "gpt55-coder",
68
- "client": "codex",
69
- "model": "gpt-5.5",
70
- "effort": "xhigh",
71
- "cost_tier": "standard",
72
- "fallbacks": ["fable-driver"],
73
- "matched_selector": "work_type=code"
74
- }
21
+ planr agents init
22
+ planr agents init --profile worker=host-a/model-id@high#standard --route code=worker
23
+ planr agents check
24
+ planr agents list --json
75
25
  ```
76
26
 
77
- A driver session dispatches the right worker from the packet alone and when the primary hits a rate limit, the fallback order is already in hand. No mid-run config edits.
78
-
79
- ## The Registry File
80
-
81
- `.planr/agents.toml` has three parts:
82
-
83
- - `[profiles.<id>]` — a named agent setting. `client` (which host dispatches it: `codex`, `claude-code`, `cursor`, `generic-mcp`) and `model` are required; `effort`, `cost_tier` (`premium` | `standard` | `budget`), `capabilities`, and `notes` are optional. Model ids and aliases pass through verbatim — Planr does not validate them against provider catalogs, so new models need no Planr release.
84
- - `[[routes]]` — `match` selects work (`work_type = "code"` or `plan = "pln-1234abcd"`), `profile` names the primary, `fallbacks` the ordered alternatives.
85
- - `[route_default]` — catches everything no route matched.
86
-
87
- Resolution precedence per item: **per-item override > `work_type` route > `plan` route > default**. Within a level, the first declared route wins. If a chain's primary profile id is unknown, the first known fallback is promoted; a chain with no known profiles falls through to the next precedence level, so a typo never swallows lower routes. `matched_selector` in the output tells you which rule fired (`override`, `work_type=<v>`, `plan=<v>`, or `default`).
27
+ Resolution order is per-item override, work type, plan, then default route. Unknown profiles fail open to the next applicable route. Host names, model ids, role selectors, effort values, and fallback behavior are opaque to Core.
88
28
 
89
- ## Per-Item Overrides
29
+ Workers may report observed routing with logs and route-audit evidence. Requested-only values never become effective proof; missing effective evidence remains explicitly unavailable.
90
30
 
91
- When one item needs a different setting than its policy route a gnarly refactor that deserves the premium tier, a bulk doc pass that can run on budget — pin it:
31
+ Host-specific model policies and generated repository roles are optional. The `planr-routing` workspace package compiles them into RoutingBundle v1, and Core safely previews and applies that bundle:
92
32
 
93
33
  ```bash
94
- planr item route <item-id> # resolved route + source: override or policy
95
- planr item route <item-id> --set fable-driver # pin; validates the profile id, emits route_overridden
96
- planr item route <item-id> --clear # unpin; policy applies again, emits route_override_cleared
34
+ planr-routing compile balanced --host codex-openai --output routing-bundle.json
35
+ planr routing bundle preview routing-bundle.json
36
+ planr routing bundle apply routing-bundle.json
97
37
  ```
98
38
 
99
- The pin beats every policy route and shows up in the pick packet with `"matched_selector": "override"`. Overrides are repair-friendly: `--set` rejects a profile id the registry does not declare (when the registry is missing or malformed it warns and stores anyway, so offline edits stay possible), and a pin whose profile is later deleted from the registry is never an error policy routing takes over and `item route` prints a repair hint. Both mutations are recorded as graph events, so `planr event list --item <id>` shows who re-routed what, when.
100
-
101
- Tier the roles, not just the models: workers run safely on cheaper tiers because the pick packet bounds their scope, while review verdicts should stay on the strongest tier — `agents check` warns when review work routes to a `budget` profile. Background: [Cost Tiering](GOALS.md#cost-tiering).
102
-
103
- ## Host-Native Rendering
104
-
105
- Routes only matter if the host actually dispatches the declared model, so `planr install codex|claude|cursor` closes the gap: when a registry is present, the provisioned subagent role files are rendered with pins taken from it instead of the shipped static defaults. The `work_type=code` route pins the worker role, the `work_type=review` route pins the reviewer role, and each render uses the host's exact vocabulary — Codex TOML gets `model` and `model_reasoning_effort` (with `developer_instructions` always present, since Codex silently ignores a role file without it), Claude frontmatter gets `model:` and `effort:`, Cursor frontmatter gets `model:` only.
106
-
107
- Two safety rules keep this predictable:
108
-
109
- - **Client matching**: a role file only pins profiles whose `client` matches the install target, scanning the route's fallback chain for the first match. A review route pointing at a Cursor profile never writes a Cursor model id into a Codex TOML — that role keeps its static default instead.
110
- - **Provision-once**: existing files are never overwritten. After editing the registry, re-render explicitly with `planr install <client> --force`. Rendered files start with a `# generated from .planr/agents.toml` header so you (and future audit tooling) can tell them from hand-maintained ones.
111
-
112
- Without a registry, installs write the static role files byte-identically to previous releases.
113
-
114
- ## Prompt Routing
115
-
116
- `planr prompt routing [--client codex|claude|cursor|all]` prints a paste-ready block for the driver session: the prioritization table (every route, profile, and fallback in precedence order), per-host dispatch guidance including the traps that silently defeat pins (Codex requires `fork_turns: "none"` and a session restart after re-rendering; the `CLAUDE_CODE_SUBAGENT_MODEL` env var preempts Claude frontmatter; Cursor plan mode, admin policy, and Max Mode override silently), and process-dispatch snippets (`codex exec`, `pi`, `opencode run`) for hosts without role files, pre-filled from the `work_type=code` route. `--json` carries the same content structured.
117
-
118
- ## Run Audit
119
-
120
- 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.
121
-
122
- - `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.
123
- - 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.
124
- - `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).
125
- - `planr export`/`import` carry the registry with the package, preview-first; an existing registry at the destination is never silently overwritten.
126
-
127
- Everything here is advisory (ADR-001): mismatches never fail logging, reviews, or closes. No profile reported, no run recorded, or no registry means no comparison and no event.
128
-
129
- One legitimate mismatch source to know: a driver adding a live-verification log to a routed item runs on the driver profile by design, which emits a `route_mismatch_observed` event. The payload carries `log_kind`, so audit consumers can discount `verification` entries and alarm only on `completion` mismatches.
130
-
131
- For single-host pools (e.g. all-Cursor), declare the host's *exact* model slugs (`claude-opus-4-8-thinking-high`, not `opus`): dispatch APIs resolve slugs, not aliases, and a driver forced to map `fable-5` onto the nearest slug at dispatch time is a silent translation the audit cannot see.
132
-
133
- ## Failure Behavior
134
-
135
- - **No registry file**: nothing changes. Pick packets simply have no `routing` key.
136
- - **Malformed registry**: `planr agents check` fails with the parser's line context; everything else (`pick`, `map`, `install`) keeps working with routing omitted — installs fall back to the static role files.
137
- - **Warnings** (unknown profile references, empty or duplicate selectors, budget-tier review routes, secret-like values) never block anything; `agents check` lists them and still exits zero.
138
- - Never put credentials in the registry — it holds configuration strings only, and secret-like values are flagged.
139
-
140
- ## Use-Case Pools
141
-
142
- Work types are free-form, and that makes them the use-case dimension: beyond the built-in vocabulary (`code`, `fix`, `review`, `docs`, ...), any string you pass to `--work-type` routes. Combined with per-profile skill pairing, the registry becomes a small agent pool — each use case names who runs it, on what model, with which skill:
143
-
144
- ```toml
145
- [profiles.designer]
146
- client = "claude-code"
147
- model = "opus"
148
- effort = "high"
149
- cost_tier = "premium"
150
- skill = "frontend-design" # dispatch this profile *with* this skill
151
-
152
- [profiles.backender]
153
- client = "codex"
154
- model = "gpt-5.5"
155
- effort = "xhigh"
156
- cost_tier = "standard"
157
- skill = "planr-work"
158
-
159
- [[routes]]
160
- match = { work_type = "frontend" }
161
- profile = "designer"
162
- fallbacks = ["driver"]
163
-
164
- [[routes]]
165
- match = { work_type = "design" }
166
- profile = "designer"
167
-
168
- [[routes]]
169
- match = { work_type = "backend" }
170
- profile = "backender"
171
- fallbacks = ["driver"]
172
- ```
173
-
174
- Create items with the use-case work type (`planr item create ... --work-type frontend`) — or retag existing ones with `planr item update <id> --work-type frontend`, which is how planning agents tag `map build` output against the declared routes (the planning skills read `agents list` and do this without user involvement) — and the pick packet carries the full pairing — `"profile": "designer"`, `"model": "opus"`, `"skill": "frontend-design"` — so the driver dispatches profile and skill together (`Use $frontend-design on item <id>` on the profile's client/model). Workers pull their slice of the pool with `planr pick --work-type frontend`. `skill` is passthrough vocabulary like model ids: Planr never validates it against installed skills, and profiles without one omit the key entirely. A profile that needs different skills for different use cases is simply two profiles.
175
-
176
- Declare the `client` you will actually dispatch on. A loop running inside one host dispatches that host's subagents — an in-Cursor driver that dispatches Cursor subagents with per-dispatch models is running `client = "cursor"` profiles in practice, even when the model matches. A `client = "codex"` profile is only honest when the driver really spawns a Codex process (`codex exec ...`). This matters for the audit: workers report the *profile id*, so a profile whose declared client differs from the real dispatch host passes the mismatch check on the model alone — the client deviation stays invisible.
177
-
178
- When do you actually need more than one client? Hosts with a full model catalog (Cursor) can serve an entire pool natively — an all-`cursor` registry with different models per profile is the normal case there. Cross-client profiles exist for two real situations: vendor-locked hosts (Claude Code dispatches only Anthropic models, Codex CLI only OpenAI models — a Claude-Code driver that wants a GPT implementer must process-dispatch via `codex exec`), and subscription economics (the same model can bill differently per host, so routing backend work through a flat-rate CLI subscription instead of the driver host's quota is a legitimate cost decision).
179
-
180
- ## Host Matrix
181
-
182
- Where each host reads its model configuration from, and what silently defeats a pin there (state of July 2026):
183
-
184
- | Host | Native mechanism | Rendered by `planr install`? | Silent overrides / traps |
185
- | --- | --- | --- | --- |
186
- | Cursor | `.cursor/agents/*.md` frontmatter `model: <id>` (default `inherit`) | yes (`cursor`) | Team-admin model policy, plan availability, and Max-Mode-only models override without error; legacy request-based plans force Composer for subagents; subagent transcripts record no model field, so the actual model cannot be verified from artifacts after the fact — the dispatch parameters in the driver session are the only record |
187
- | Claude Code | `planr-worker.md`/`planr-reviewer.md` frontmatter `model:` + `effort:` | yes (`claude`) | `CLAUDE_CODE_SUBAGENT_MODEL` clamps frontmatter and per-invocation models with no signal ([#57718](https://github.com/anthropics/claude-code/issues/57718)); since v2.1.196 `inherit` behaves as unset; org `availableModels` allowlists fall back silently |
188
- | Codex CLI | `.codex/agents/*.toml` with `model` + `model_reasoning_effort` | yes (`codex`) | `fork_turns = "all"` intentionally drops the child's `agent_type`/`model` — use `fork_turns = "none"` or a partial fork; the role registry loads at session start ([#26408](https://github.com/openai/codex/issues/26408)), so re-renders need a restart |
189
- | opencode | `opencode.json` `agent.<name>.model = "provider/model-id"` or `.opencode/agents/*.md` frontmatter | no — use the `planr prompt routing` process snippet | Subagent inherits the primary model when unset; malformed `provider/model-id` strings (quoting, trailing newline) raise `ProviderModelNotFoundError` ([#5623](https://github.com/sst/opencode/issues/5623)) |
190
- | Pi | none by design — process-level dispatch (`pi --provider --model --thinking`) or the `pi-subagents` extension (`.pi/agents/*.md`) | no — use the `planr prompt routing` process snippet | Extension model-scope enforcement against `enabledModels` is opt-in; without it, pins are best-effort |
191
-
192
- For the hosts without rendered role files, `planr prompt routing` prints ready process-dispatch snippets pre-filled from the registry. Whatever the host does, the [run audit](#run-audit) catches silent overrides after the fact.
193
-
194
- ## Command Summary
195
-
196
- The registry surface end to end: `planr agents init [--force]` scaffolds, `planr agents list|check` inspect and validate, `planr pick --json` carries the `routing` block, `planr item route [--set|--clear]` pins per item, the MCP tools (`planr_agents_list`, `planr_item_route`, `planr_item_route_set`, `planr_item_route_clear`) return identical JSON shapes, `planr install <client> [--force]` renders host role files from the registry, `planr prompt routing` prints the driver dispatch block, `planr log add`/`done --profile` (or `PLANR_PROFILE`) feed the run audit, `planr trace item` and `planr doctor` surface mismatches and drift, and `planr export`/`import` carry the registry preview-first.
39
+ Bundle application is restricted to the repository. Planr never edits user configuration such as `~/.codex/config.toml`. See [Routing Bundles](ROUTING_BUNDLES.md).
@@ -0,0 +1,32 @@
1
+ # Routing bundles
2
+
3
+ Planr Core is provider-neutral. It parses `.planr/agents.toml`, resolves routes into pick packets, records declared-versus-observed evidence, and safely previews or applies a strict RoutingBundle v1:
4
+
5
+ ```bash
6
+ planr routing bundle inspect routing-bundle.json
7
+ planr routing bundle preview routing-bundle.json
8
+ planr routing bundle apply routing-bundle.json
9
+ ```
10
+
11
+ Core accepts only allowlisted repository-local targets, verifies payload hashes, rejects absolute paths, traversal, symlinks, parent/child target collisions, conflicts, unsupported versions, and invalid payloads, and applies the validated set atomically. It never writes user configuration or files outside the repository.
12
+
13
+ A signed bundle is accepted only with an independent trust anchor supplied to every inspect, preview, or apply call:
14
+
15
+ ```bash
16
+ planr routing bundle inspect signed-bundle.json \
17
+ --trusted-signer planr-maintainers \
18
+ --trusted-public-key-file /absolute/path/to/maintainer.pub
19
+ ```
20
+
21
+ The bundle contains the signer id and signature, not a self-trusted public key. Both trust flags are required together; unsigned bundles require neither. An unsigned bundle also cannot label its evidence `verified` or `recommended`.
22
+
23
+ The `planr-routing` workspace package owns all volatile opinions: named policies, exact model ids, host bindings, generated role and skill files, capability probes, evaluation scenarios, signing, registry data, and the website catalog. A normal flow is:
24
+
25
+ ```bash
26
+ planr-routing policy list
27
+ planr-routing compile balanced --host codex-openai --output routing-bundle.json
28
+ planr routing bundle preview routing-bundle.json
29
+ planr routing bundle apply routing-bundle.json
30
+ ```
31
+
32
+ The package emits the same provider-neutral bundle contract for Codex, Claude Code, Cursor, and mixed-host configurations. Offline evaluation remains experimental; a recommendation requires complete authenticated live-host evidence. Missing authentication or missing effective model, effort, role, or context-fork evidence cannot pass.
package/docs/SKILLS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Planr ships agent-facing skill templates under `plugins/planr/skills/`.
4
4
 
5
- The repository ships an installable plugin under `plugins/planr` for Codex, Claude Code, and Cursor, so the skills can be installed as one package instead of copied by hand. Marketplace manifests at the repo root (`.agents/plugins/marketplace.json`, `.claude-plugin/marketplace.json`) point at that subdirectory — Codex silently ignores marketplaces whose plugin source is the repo root itself. The plugin only carries skills and agent roles; the `planr` CLI must be installed separately (`brew install instructa/tap/planr`).
5
+ The repository ships an installable plugin under `plugins/planr` for Codex and Claude Code, while Cursor receives the same skills through `planr install cursor`. Marketplace manifests at the repo root (`.agents/plugins/marketplace.json`, `.claude-plugin/marketplace.json`) point at that subdirectory — Codex silently ignores marketplaces whose plugin source is the repo root itself. The shared package carries skills and Claude's independent workflow roles; optional model-specific host roles come only from a repository-local routing bundle, never from Planr Core or static fallbacks. The `planr` CLI must be installed separately (`brew install instructa/tap/planr`).
6
6
 
7
7
  ## Install As Plugin (preferred)
8
8
 
@@ -165,10 +165,12 @@ The CLI provisions the role files automatically — no manual copying:
165
165
 
166
166
  ```bash
167
167
  planr project init "My Product" --client all # writes .codex/agents/*.toml, .claude/agents/*.md, and .cursor/agents/*.md
168
- planr install codex # provisions roles for an existing project (same for claude and cursor)
168
+ planr agents init # generates canonical native Codex roles in an existing project
169
+ planr install claude # provisions Claude's independent roles
170
+ planr install cursor # provisions Cursor's independent roles and skills
169
171
  ```
170
172
 
171
- Codex needs these project-scoped files because its plugin system carries skills only; the Claude Code and Cursor plugins register the same roles automatically, so the provisioned `.claude/agents/` and `.cursor/agents/` copies only matter for standalone (non-plugin) installs. Existing role files are never overwritten.
173
+ Optional project-scoped model-routing files are generated by `planr-routing` bundles. Core workflow skills remain host-neutral, and bundle application never overwrites conflicts or writes user configuration.
172
174
 
173
175
  Dispatches stay one line: `Use $planr-work on item <id>` and `Use $planr-review on item <id>`. The map and logs are the loop memory, so any iteration can resume from zero context.
174
176
 
@@ -20,12 +20,6 @@
20
20
  "planr_item_amend",
21
21
  "planr_item_replan",
22
22
  "planr_agents_list",
23
- "planr_presets_list",
24
- "planr_preset_evaluate",
25
- "planr_preset_apply",
26
- "planr_preset_registry_verify",
27
- "planr_preset_registry_import",
28
- "planr_preset_registry_list",
29
23
  "planr_policy_show",
30
24
  "planr_policy_check",
31
25
  "planr_policy_admit",
@@ -109,11 +103,9 @@
109
103
  "planr recover sweep",
110
104
  "planr agents list",
111
105
  "planr agents check",
112
- "planr agents preset list",
113
- "planr agents preset apply",
114
- "planr agents preset registry verify",
115
- "planr agents preset registry import",
116
- "planr agents preset registry list",
106
+ "planr routing bundle inspect",
107
+ "planr routing bundle preview",
108
+ "planr routing bundle apply",
117
109
  "planr item route",
118
110
  "planr trace item",
119
111
  "planr serve --port"
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planr",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Local-first planning and execution coordination for coding agents.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,27 +15,15 @@
15
15
  "npm/bin/planr.js",
16
16
  "npm/native",
17
17
  "plugins",
18
- "presets",
19
- "evaluations",
20
- "website",
21
18
  "README.md",
22
19
  "LICENSE.md",
23
20
  "docs"
24
21
  ],
25
22
  "scripts": {
26
- "alchemy:check-runtime": "node scripts/check-alchemy-runtime.mjs",
27
23
  "build:native": "cargo build --release",
28
24
  "test": "cargo test",
29
25
  "pack:check": "npm pack --dry-run",
30
- "deploy:test": "node scripts/cloudflare-test.mjs deploy",
31
- "destroy:test": "node scripts/cloudflare-test.mjs destroy",
32
- "site:build": "node scripts/build-site.mjs",
33
- "site:check": "pnpm site:test && pnpm site:build",
34
- "site:test": "node --test website/*.test.mjs",
35
- "site:serve": "node website/serve.mjs"
36
- },
37
- "devDependencies": {
38
- "alchemy": "0.93.7"
26
+ "routing": "cargo run --manifest-path planr-routing/Cargo.toml --"
39
27
  },
40
28
  "engines": {
41
29
  "node": ">=18"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "planr",
3
3
  "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.",
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "author": {
6
6
  "name": "instructa"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planr",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "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
5
  "author": {
6
6
  "name": "instructa",
@@ -67,7 +67,7 @@ A worker subagent may take several items sequentially instead of being respawned
67
67
 
68
68
  Host wiring:
69
69
 
70
- - Codex: project agents in `.codex/agents/*.toml` preload the skill via `[[skills.config]]` (TOML templates in `agents/` next to this skill). Spawn explicitly: "spawn the planr_worker agent for item X". Keep `[agents] max_depth = 1`.
70
+ - When a repository routing bundle supplies host roles, follow its generated routing skill exactly. Otherwise treat the profile in the pick packet as advisory and use the host's current dispatch contract. Never infer effective model or effort from a declaration alone.
71
71
  - Claude Code: subagents preload via the `skills:` frontmatter field. The Planr plugin registers `planr-worker` and `planr-reviewer` automatically from its `agents/` directory; standalone installs copy them to `.claude/agents/`. The reviewer subagent is read-only except for `planr review` commands.
72
72
  - Cursor: project subagents in `.cursor/agents/*.md` (Markdown templates in `agents/` next to this skill; `planr install cursor` provisions them). Dispatch explicitly: `/planr-worker implement item X`, `/planr-reviewer review item X`. Parallel dispatches are safe — the map's pick lease keeps one owner per item.
73
73
  - Single-agent hosts: run worker and checker as separate sequential dispatches with a fresh read of map state in between; never carry the worker's self-assessment into the review step. The mode is recorded automatically: `review close` derives `review_mode` (`single_agent`/`independent`) from worker identity.
@@ -1,109 +0,0 @@
1
- # Preset Composition
2
-
3
- Planr composes two independent inputs:
4
-
5
- - a provider-neutral Usage Policy v1 TOML file;
6
- - a versioned host binding containing exact client/model/effort and dispatch capabilities.
7
-
8
- Preview is the default:
9
-
10
- ```bash
11
- planr agents preset apply ./policy.toml --binding ./codex-binding.toml --preview --json
12
- planr agents preset apply ./policy.toml --binding ./codex-binding.toml --confirm --json
13
- ```
14
-
15
- 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.
16
-
17
- ## Built-in Catalog and Safe Packs
18
-
19
- `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:
20
-
21
- ```bash
22
- planr agents preset apply balanced --binding codex-openai --preview --json
23
- planr agents preset apply read-only-audit --binding mixed-host --confirm --json
24
- ```
25
-
26
- 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.
27
-
28
- 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.
29
-
30
- ## Host Binding v1
31
-
32
- 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.
33
-
34
- ```toml
35
- schema_version = 1
36
- id = "codex-team"
37
- version = "1.0.0"
38
- host = "codex"
39
- driver_role = "driver"
40
- default_role = "driver"
41
- capability_evidence = ["codex-0.138-cross-tier-smoke"]
42
- billing_assumptions = ["local subscription"]
43
- known_limitations = ["effective model requires host evidence"]
44
-
45
- [capabilities]
46
- model_override = true
47
- effort_override = true
48
- fork_none = true
49
- fork_all = true
50
- max_partial_fork_turns = 4
51
-
52
- [profiles.driver]
53
- profile = "sol"
54
- client = "codex"
55
- model = "gpt-5.5"
56
- effort = "xhigh"
57
-
58
- [profiles.worker]
59
- profile = "luna"
60
- client = "codex"
61
- model = "gpt-5.4-mini"
62
- effort = "high"
63
- skill = "planr-work"
64
- # Omit for the deterministic `none` default.
65
- fork_turns = { mode = "partial", turns = 2 }
66
-
67
- [[routes]]
68
- work_type = "code"
69
- role = "worker"
70
- fallback_roles = ["driver"]
71
-
72
- [verification]
73
- id = "verify-codex-team"
74
- verified_at_unix = 1900000000
75
- max_age_seconds = 2592000
76
-
77
- [[artifacts]]
78
- path = ".codex/agents/luna.toml"
79
- kind = "codex_agent"
80
- content = '''model = "gpt-5.4-mini"
81
- model_reasoning_effort = "high"
82
- '''
83
- ```
84
-
85
- 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.
86
-
87
- ## Preview and Lock
88
-
89
- The preview includes:
90
-
91
- - composed abstract-role to registry-profile and dispatch-context mappings;
92
- - compatibility errors and host/billing limitations;
93
- - execution-permission additions relative to the active policy;
94
- - verification id, age, maximum age, and fresh/stale state;
95
- - source ids, versions, SHA-256 hashes, Planr version, and applied-at value;
96
- - 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]`.
97
-
98
- 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.
99
-
100
- ## Repository Boundary
101
-
102
- Only these repository-relative targets are valid:
103
-
104
- - `.planr/policy.toml`
105
- - `.planr/agents.toml`
106
- - `.planr/preset.lock.toml`
107
- - files below `.codex/{agents,skills}/`, `.claude/{agents,skills}/`, or `.cursor/{agents,skills}/`
108
-
109
- 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.
@@ -1,118 +0,0 @@
1
- # Preset Evaluation
2
-
3
- Planr provides three explicit evidence levels over the same versioned fixture and lifecycle engine:
4
-
5
- - the default offline policy simulation performs no external execution and can reach `verified` but never `recommended`;
6
- - 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.
7
- - 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.
8
-
9
- `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.
10
-
11
- ```sh
12
- planr agents preset evaluate
13
- planr --json agents preset evaluate --at-unix 1783987200
14
- planr agents preset evaluate --report-dir reports/preset-v1
15
- planr agents preset evaluate \
16
- --live-host-command /absolute/path/to/host-adapter \
17
- --live-host-arg optional-argument \
18
- --trusted-telemetry-signer production-collector \
19
- --trusted-telemetry-collector /absolute/path/to/collector
20
- ```
21
-
22
- 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.
23
-
24
- ## Offline simulation
25
-
26
- 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"`.
27
-
28
- 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.
29
-
30
- ## Explicit live-host runner
31
-
32
- `--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.
33
-
34
- 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:
35
-
36
- ```json
37
- {
38
- "schema_version": 1,
39
- "host_id": "example-host",
40
- "host_version": "1.0.0",
41
- "candidate_id": "balanced-codex-openai",
42
- "task_id": "browser-report-smoke",
43
- "input_sha256": "<request input hash>",
44
- "artifact_kind": "browser_trace",
45
- "artifact_sha256": "<SHA-256 of the artifact file>",
46
- "output": "balanced-codex-openai:browser-report-smoke:browser-report-inspected",
47
- "effective_model": "model-id",
48
- "effective_effort": "high",
49
- "effective_context_fork": { "mode": "none" },
50
- "tool_calls": 1,
51
- "tokens": 10,
52
- "credits_micros": 100,
53
- "retries": 1,
54
- "availability_fallbacks": 1,
55
- "quality_escalations": 1,
56
- "corrections": 0,
57
- "violations": 0
58
- }
59
- ```
60
-
61
- The artifact file is separate from stdout and has this strict shape:
62
-
63
- ```json
64
- {
65
- "schema_version": 1,
66
- "candidate_id": "balanced-codex-openai",
67
- "task_id": "browser-report-smoke",
68
- "input_sha256": "<request input hash>",
69
- "artifact_kind": "browser_trace",
70
- "challenge_sha256": "<SHA-256 of Planr's challenge file>",
71
- "output": "balanced-codex-openai:browser-report-smoke:browser-report-inspected"
72
- }
73
- ```
74
-
75
- 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.
76
-
77
- 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.
78
-
79
- 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.
80
-
81
- ## Independently signed telemetry
82
-
83
- 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:
84
-
85
- ```toml
86
- schema_version = 1
87
-
88
- [[signers]]
89
- id = "production-collector"
90
- public_key_hex = "<64-hex-character-ed25519-public-key>"
91
- collector_sha256 = "<64-hex-character-executable-sha256>"
92
- ```
93
-
94
- 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.
95
-
96
- 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.
97
-
98
- 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.
99
-
100
- ## Lifecycle and thresholds
101
-
102
- - `recommended`: every task artifact and oracle passes, every effective route and usage measurement has a valid signed telemetry receipt, and all thresholds pass;
103
- - `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;
104
- - `stale`: evaluation time is later than fixture expiry;
105
- - `incompatible`: requested host is unsupported by the binding;
106
- - `unverified`: required composition, transition, hash, live task execution, challenge-bound artifact, or outcome-oracle evidence is incomplete.
107
-
108
- 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.
109
-
110
- ## Immutable output
111
-
112
- `--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.
113
-
114
- 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.
115
-
116
- ## Sol/Luna Codex contract
117
-
118
- `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.