omp-llm-role 1.0.0 → 1.1.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 (49) hide show
  1. package/README.md +179 -11
  2. package/agents/designer.md +4 -3
  3. package/docs/dev/README.md +44 -7
  4. package/docs/dev/agent-authoring.md +59 -1
  5. package/docs/dev/architecture.md +84 -27
  6. package/docs/dev/ci.md +152 -0
  7. package/docs/dev/current-state.md +327 -24
  8. package/docs/dev/data-sources.md +202 -38
  9. package/docs/dev/explorer.md +157 -46
  10. package/docs/dev/quirks.md +45 -4
  11. package/docs/dev/releasing.md +129 -17
  12. package/docs/dev/scoring.md +789 -28
  13. package/docs/dev/testing.md +206 -0
  14. package/docs/dev/writes-and-state.md +176 -28
  15. package/docs/llm-role-rankings.md +141 -140
  16. package/package.json +754 -80
  17. package/skills/omp-llm-role-create-agent/SKILL.md +7 -1
  18. package/src/agent-architect.ts +148 -19
  19. package/src/agent-create.ts +800 -40
  20. package/src/agent-file.ts +2 -5
  21. package/src/architect-provenance.ts +16 -0
  22. package/src/availability.ts +104 -6
  23. package/src/benchmark-author.ts +9 -4
  24. package/src/benchmark-sources.ts +283 -18
  25. package/src/cli/create-role.ts +29 -1
  26. package/src/cli/llm-role-rank.ts +40 -1
  27. package/src/config-edit.ts +207 -80
  28. package/src/engine.ts +409 -46
  29. package/src/error-report.ts +562 -0
  30. package/src/explorer/boot.ts +36 -24
  31. package/src/explorer/explain.ts +110 -13
  32. package/src/explorer/scopes.ts +110 -0
  33. package/src/explorer/server.ts +112 -32
  34. package/src/extension.ts +407 -34
  35. package/src/features.ts +103 -0
  36. package/src/project-registry.ts +65 -0
  37. package/src/project-setup.ts +382 -0
  38. package/src/prompts/agent-creation-architect.md +2 -0
  39. package/src/prompts/project-profile-architect.md +33 -0
  40. package/src/prompts/project-profile-user.md +8 -0
  41. package/src/role-archetypes.ts +12 -2
  42. package/src/role-settings.ts +22 -0
  43. package/src/settings.ts +205 -24
  44. package/src/state.ts +1 -1
  45. package/src/updater.ts +150 -31
  46. package/web/app.js +411 -12
  47. package/web/index.html +1 -0
  48. package/web/style.css +44 -1
  49. package/docs/dev/spec.md +0 -633
@@ -1,11 +1,311 @@
1
- # Current state (2026-10-02)
1
+ # Current state (2026-10-08)
2
2
 
3
- Dated snapshot of the ranking as of the date in the heading — matched/priced/
4
- eligible counts, per-role leaders, and the open defects. It is refreshed by
3
+ Dated snapshot of the ranking and the landed work as of the date in the heading —
4
+ matched/priced/eligible counts, per-role leaders, the open defects, and the
5
+ per-issue work log below. It is refreshed by
5
6
  regenerating the report (`node src/cli/llm-role-rank.ts --out docs/llm-role-rankings.md`) and
6
7
  copying the new numbers here; release prose belongs in
7
- [`../../CHANGELOG.md`](../../CHANGELOG.md), not in this file. The numbers below
8
- are the 2026-10-02 measurement and are not re-derived on read.
8
+ [`../../CHANGELOG.md`](../../CHANGELOG.md), not in this file. The ranking numbers
9
+ below are the 2026-10-08 measurement (the input-price blend basis #21, the
10
+ #17/#18/#19/#20 wave and the #34–#40 focus-axis/capability-flag work; see the top
11
+ bullets) and are not re-derived on read.
12
+
13
+ - Opt-in error reporting (2026-10-08, issue #48): a new `errorReporting` setting
14
+ (`off` — the default — `ask`, `auto`) turns a genuinely unexpected failure — a
15
+ command handler throwing, or a `runUpdater` abort whose `RunResult.defect` is
16
+ set (a `ConfigEditError`/self-check mismatch or the catch-all throw; the
17
+ enumerated environment aborts stay notify-only) — into one structured report
18
+ (plugin version, runtime, platform, the failing command, error
19
+ name/message/stack, timestamp) filed on `Gott50/omp-llm-role`. `ask` surfaces
20
+ the exact payload and files on confirmation, degrading to a notified prefilled
21
+ `…/issues/new` URL in a session with no UI; `auto` files without asking; `off`
22
+ is byte-identical to the pre-#48 behaviour. A token (`GITHUB_TOKEN` → `GH_TOKEN`
23
+ → `gh auth token`, never stored) uses the API; no token, or a 401/403/404 (a
24
+ fine-grained PAT without Issues:write answers 404), falls through to the
25
+ prefilled URL. The payload is redacted (home dir → `~`, token-shaped strings →
26
+ `<redacted>`) and carries no config contents, model selectors, prompts or
27
+ history rows. A fingerprint marker dedupes a repeat onto the existing open
28
+ issue, and the path is capped (one open issue per fingerprint, one creation per
29
+ fingerprint per UTC day, 3 per UTC day). The ledger of what was filed, offered
30
+ or suppressed is `llm-role-error-reports.json` under the agent dir. The ranking
31
+ numbers are unchanged — reporting is a side channel, not a ranking input.
32
+
33
+ - Empty-collection spellings accepted (2026-10-08, issues #54 + #55): a config
34
+ whose `task.disabledAgents` is spelled `null` — or whose `retry.fallbackChains`
35
+ / `modelRoles` body is the serializer's own indented `{}` / `[]` line — used to
36
+ throw inside `parseConfig`, so **every session start aborted with `config edit
37
+ refused: disabledAgents: inline value is not supported (block sequence
38
+ expected)` and no role was ever refreshed**. omp re-serializes `config.yml`
39
+ through Bun's `YAML.stringify` (`stringifyYamlConfig`), which spells an empty
40
+ collection exactly that way, so those spellings now read as the empty
41
+ collection and the patch path rewrites the placeholder line into the block
42
+ form (`disabledAgents:` + `- "designer"`); any other inline value on a managed
43
+ key line still throws. Verified against a copy of the real
44
+ `~/.omp/agent/config.yml` (which carries `disabledAgents: null`): the run wrote
45
+ and reported 8 decisions instead of aborting — and the real file carries its own
46
+ production confirmation. Its 2026-10-08T16:34Z `session-start` run wrote the
47
+ config while `disabledAgents: null` was present (config, `llm-role-state.json`
48
+ and `llm-role-history.jsonl` are all stamped 18:34:17 local), which pre-fix was
49
+ impossible: the read threw before any write. The write is recognizably the
50
+ plugin's surgical one, not omp's serializer: a *changed* role line is re-quoted
51
+ (`tiny: "…deepseek-v4.1-flash:off"`) while an unchanged one stays byte-identical
52
+ (`smol: …deepseek-v4.1-flash:off`), the previously plugin-written
53
+ `openrouter/inclusionai/ling-3.0-flash` chain key is pruned (a dumper never
54
+ prunes), and the chain keys the plugin does not own (`web:`,
55
+ `openrouter/~deepseek/…`) keep their unquoted items. The placeholder is left in
56
+ place because the agent sync had nothing to add or remove — `patchDisabledAgents`
57
+ early-returns then, by design. The same probe turned up two more defects, one of
58
+ them tracked as its own issue (#55 — a different error-report fingerprint): the
59
+ parent-block variant of this class — an empty `task:` + ` {}` / `retry:` + ` {}`
60
+ used to take the new child key *after* the `{}` line, YAML no parser accepts,
61
+ with the read-back self-check still passing, i.e. **silent corruption** — now
62
+ has the child key replace the `{}` line, and a prune-only patch creates nothing;
63
+ and #55's block-bound bug (`chainEntries` scanned to the end of `retry:`
64
+ instead of to the end of `fallbackChains:`, so any sibling `retry.*` key
65
+ aborted every run with `unexpected line in fallbackChains block`) is fixed by
66
+ bounding the scan at the key's own indent. Ranking numbers unchanged.
67
+
68
+ - Capability presets (2026-10-07, issue #40): one opt-in flag per capability
69
+ (`features.<id>` global, `roles.<role>.features.<id>` per role) applies the
70
+ plugin's recommended settings to a role; every flag ships **off** and no
71
+ shipped role sets one, so the resolved roles are byte-identical to
72
+ `DEFAULT_ROLES` until a user opts in. The registry (`FEATURES` in
73
+ `src/features.ts`) is the single source of truth and `expandFeatures` the
74
+ single precedence implementation (`effective def = shipped default ⊕
75
+ preset(enabled flags) ⊕ explicit user keys`; a per-role `false` beats a global
76
+ `true`; an explicit knob always wins). Grounding on the 2026-10-07 cache
77
+ (`default` role, 142 eligible):
78
+
79
+ | Flag | Recommended settings | Measured effect |
80
+ |---|---|---|
81
+ | `endpointCeilings` | `filters.tools: true`, `filters.minOutputTokens: 16384` | 142 → 132 eligible, leader unchanged (`deepseek-v4.1-flash`, 0.579 $/M) |
82
+ | `cachePricing` | `cacheHitRate: 0.5` | 142 eligible, leader unchanged, its `priceEff` 0.579 → 0.508 (−12 %) |
83
+ | `providerPinning` | `preferOwnProvider: true` | per-model policy, never changes eligibility; 51 of 149 routed models match under a slug-prefix rule (33 exact, 17 prefix, 1 display-org), 10 of 25 orgs match for no model |
84
+ | `costCap` | `filters.maxPriceUsdPerM: 10` | 142 → 135 eligible (the top ≈5 %), leader unchanged; pool p90 7.8, p95 8.9 $/M, every leader 0.58–0.85 $/M |
85
+
86
+ `providerPinning`'s recommended setting is a **soft per-model policy**
87
+ (`preferOwnProvider`), not a hard `providerPin`: a hard pin would make a model
88
+ with no matching route ineligible and empty ~2/3 of a role's pool, so the
89
+ policy re-prices a model on its own lab's route where one exists and falls back
90
+ to the `1/price²` blend otherwise. `minContextTokens` is deliberately not in
91
+ the `endpointCeilings` bundle (an absolute floor cannot express the video's
92
+ fraction-of-the-model's-own-window complaint). The flags are settable from
93
+ `create-role.ts`, `/create-agent`, `/project-roles` (`--feature`/
94
+ `--list-features`) and the explorer's Features panel; the ranking numbers are
95
+ unchanged until a flag is turned on.
96
+
97
+ - Cross-source axis (2026-10-07, issue #38): the focus-metric gate gained the
98
+ cross-source axis — the zeroeval per-benchmark payload's **second, independent**
99
+ set of the price/throughput/context quantities (`input_cost_per_million`/
100
+ `output_cost_per_million`/`speed_rps`/`context_window`, read by
101
+ `parseBenchmarkPayloadMeta` onto `BenchmarkPayloadMeta.crossSource`, keyed by
102
+ the entry's `model_id`) compared against the plugin's own `price`/`context`.
103
+ The join is **direct on the bare id** (the generic llm-stats benchmark's
104
+ `entries[].model_id` is the llm-stats id, the same space as `Model.id`); the
105
+ axis reports the median relative divergence per axis against
106
+ `PRICE_AGREEMENT_TOLERANCE` (0.5, a **chosen, tunable heuristic**).
107
+ `speedAgreement` is **informational only** and excluded from `status` (the
108
+ units differ — rps vs tok/s — and re-deriving throughput from `speed_rps` is
109
+ out of scope). It is an **annotation, never a gate** (`belowBarReason` has no
110
+ cross-source branch) and it never feeds `price`/`weightPrice` or any ranking
111
+ input — the `1/price²` blend is unchanged (a test pins `rankRole`
112
+ byte-identical with the annotation on). It is `unknown` when no model joined or
113
+ the payload carried none of the four fields. The ranking numbers are unchanged
114
+ — the axis is a create-agent/explorer annotation, not a weight.
115
+
116
+ - Provenance axis (2026-10-07, issue #37): the focus-metric gate gained the
117
+ provenance axis — the benchmark's owner (the catalog row's `dataset_org_id`,
118
+ falling back to `dataset_slug`) and the source's own per-entry org mix (the
119
+ dominant lab and its share, read from each row's `organization_id`, falling
120
+ back to `provider_id`), below-bar above `FOCUS_PROVENANCE_DOMINANT_SHARE` (0.5,
121
+ a **chosen, tunable heuristic** — the per-entry org mix is not measurable from
122
+ the repo's caches at implementation time). It is the **source's own claim**
123
+ about who publishes the benchmark and whose scores it carries: an independent
124
+ cross-check on the pool-derived `composition` axis (whose rule is unchanged),
125
+ with `compositionAgreement` recording whether the two concur (`disagree` when
126
+ exactly one flags `below-bar`, `agree` when both or neither, `unknown` when the
127
+ payload carries no org mix). It is an **annotation, never a gate**
128
+ (`belowBarReason` has no provenance branch), so a vendor-populated source is
129
+ warned but never drops a discovered candidate; it is `unknown` when there is
130
+ neither an owner nor a payload org mix. The three catalog fields (`datasetId`/
131
+ `datasetOrgId`/`datasetSlug`) ride `BenchmarkCatalogEntry` behind the cache
132
+ shape guard (a pre-#37 cache is refetched); the per-entry org mix rides
133
+ `BenchmarkPayloadMeta.provenance`. The ranking numbers are unchanged — the axis
134
+ is a create-agent/explorer annotation, not a weight.
135
+
136
+ - Maintenance axis (2026-10-07, issue #36): the focus-metric gate gained the
137
+ maintenance axis — the catalog row's `updated_at` age in months against
138
+ `FOCUS_DATASET_STALENESS_MONTHS` (12), with `version_count`/`star_count`
139
+ carried as supporting detail. This is the **dataset-date** axis, distinct from
140
+ the model-date `freshness` axis: a benchmark whose dataset was last touched
141
+ years ago but whose rows include one recent model passes freshness and is
142
+ caught here. It is an **annotation, never a gate** (`belowBarReason` has no
143
+ maintenance branch), so a stale dataset is warned but never drops a discovered
144
+ candidate; it is `unknown` when the catalog row is absent or its `updated_at`
145
+ is missing/unparseable. The four fields (`updatedAt`/`versionCount`/
146
+ `latestVersionRowCount`/`starCount`) ride `BenchmarkCatalogEntry` behind the
147
+ cache shape guard (a pre-#36 cache is refetched). The ranking numbers are
148
+ unchanged — the axis is a create-agent/explorer annotation, not a weight.
149
+
150
+ - Modality axis (2026-10-07, issue #35): the focus-metric gate gained the
151
+ modality axis — the catalog row's `modality` (text 514, multimodal 194, image 19,
152
+ audio 9, video 2, null 7 of 745) and the payload's per-entry `multimodal`
153
+ share. It is an **annotation, never a gate**: there is no role modality field,
154
+ so the axis is never `below-bar` and never a drop reason; it is `unknown` only
155
+ when neither the catalog row nor the payload carries a modality signal. The
156
+ catalog row's `modality` rides `BenchmarkCatalogEntry` behind the cache shape
157
+ guard (a pre-#35 cache is refetched); the per-entry `multimodal` rides
158
+ `BenchmarkPayloadMeta.modality`. The ranking numbers are unchanged — the axis
159
+ is a create-agent/explorer annotation, not a weight.
160
+
161
+ - Trust axis (2026-10-07, issue #34): the focus-metric gate gained a fifth axis —
162
+ the per-entry `self_reported` share of a benchmark payload, below-bar above
163
+ `FOCUS_SELF_REPORTED_MAX_SHARE` (0.5). Measured live: `verified` is uniformly
164
+ `false`; `self_reported` varies (gpqa 19/20, deepswe-1.1 14/20,
165
+ terminal-bench-4.0 10/20, aa-omniscience-index 2/3, automationbench-aa 0/1, the
166
+ general benchmarks ~20/20), so a strict-majority bar flags the mostly
167
+ vendor-submitted sources while passing an independently measured one. The
168
+ catalog row's `is_community` (20/745 true) rides `BenchmarkCatalogEntry` behind
169
+ a cache shape guard and is surfaced on the trust axis as `community` (issue
170
+ #43), so a community-submitted benchmark is visible even before its scores are
171
+ loaded. The ranking numbers are unchanged — the axis is a
172
+ create-agent discovery gate, not a weight.
173
+
174
+ - Explorer scope switcher (2026-10-06, issue #28): the explorer is no longer
175
+ user-level only. A **scope** is a role-config source — the user-level lock
176
+ file, or one project where `/project-roles` was used. `startExplorer` resolves
177
+ the scope list (the user-level scope, the projects in the new global registry
178
+ `llm-role-projects.json`, and the session's project when its cwd has a project
179
+ role config) and opens on the session's project when present, else user-level.
180
+ A project scope reads the project lock merged over the user-level lock — the
181
+ updater's own read — and Export writes that scope's lock file through the same
182
+ validate → backup → atomic path. The ranking dataset is loaded once as the
183
+ union of every known scope's resolved roles, so a metric only one scope
184
+ weights (a project-only declared `bench:<id>`) is fetched and switching is
185
+ free. The registry is written by the project-aware `runUpdater` immediately
186
+ after `resolveProjectDir()` and before any early return (guarded only by
187
+ `dryRun`), so a day-stamped no-op session start still registers; entries whose
188
+ project lock file is gone are pruned on the next write. New `POST /api/scope`;
189
+ `GET /api/bootstrap` gains `scopes`/`activeScope`. Verified live (headless
190
+ Chromium): the header lists both scopes, the project is selected by default,
191
+ switching to user drops the project role and repoints the lock path, and a
192
+ registry entry with no lock file renders disabled with a reason tooltip.
193
+
194
+ - Measurement wave (2026-10-06, issues #31–#33): three candidate axes were
195
+ measured and **not** landed — guardrail/alignment (#31: the semantically
196
+ relevant catalog entries sit at one model, the best-covered candidate covers
197
+ 5.0% of the field vs the 35% floor, and the safety/security entries that carry
198
+ data are collinear with `general`, r 0.89–1.00), per-task cost (#32: no
199
+ per-task or per-role token profile exists — the only per-model profile the
200
+ plugin already has is OpenRouter's platform-wide traffic, ~10–14× the plugin's
201
+ own input/req, so the $/M price stays the price axis), and agentic multi-agent
202
+ axes (#33: no catalog benchmark measures delegation/teams/handoffs/swarms/
203
+ recovery/a2a, the one multi-agent eval is below the 3-model floor, and the
204
+ 20-entry endpoint cap makes the coverage bar unreachable). Numbers and verdicts
205
+ in `scoring.md`.
206
+
207
+ - Endpoints cache shape guard (2026-10-06, issue #30): `readEndpointsCache` now
208
+ also rejects a cache whose records lack `cacheReadPrice` (alongside
209
+ `weightPrice`/`contextLength`), so a cache written before the cache-read
210
+ pricing landed is refetched instead of leaving every route's cache-read price
211
+ `undefined` and a role's `cacheHitRate` silently inert. The fix forces a
212
+ refetch on the first run after the upgrade; the refreshed measurement is
213
+ 400 models, throughput-matched 152/400, priced 151.
214
+
215
+ - Wave (2026-10-06): the `1/price²` blend now weights routes by the **input
216
+ (prompt) price** — the router's sort key — while reporting the billed 3:1 blend
217
+ under that distribution (issue #21), so prices moved; endpoint capability
218
+ filters (#18), the four-axis benchmark-quality gate (#17), provider pinning
219
+ (#19) and route-aware pricing (#20) landed. The domain-knowledge (#23) and
220
+ truthfulness (#24) axes were measured and **not** landed (`scoring.md`). Fresh
221
+ measurement: 400 models, throughput-matched 152/400, priced 151. Eligible:
222
+ `default`/`smol`/`slow`/`plan`/`commit`/`tiny`/`task`/`advisor` 143, `vision`
223
+ 75, `designer` 88. Leaders: `default`/`task`/`designer` DeepSeek-V4.1-Flash,
224
+ `smol`/`commit` Muse Spark 1.1 (was DeepSeek-V4.1-Flash), `slow`/`plan`/
225
+ `advisor` Muse Spark 1.3, `vision` Qwen3.8 Flash, `tiny` Muse Spark 1.1 (was
226
+ Gemini 3.1 Flash-Lite).
227
+
228
+ - `plan` cost posture (2026-10-03, issue #16): `plan`'s `price` raised 0.12 → 0.25
229
+ (λ 0.00682 → 0.01667) so the posture binds on the **reachable** pool — the pool
230
+ the selector is chosen from. Measured reachable leader-flip w_price 0.2175 (Hy4
231
+ preview → DeepSeek-V4.1-Flash) vs full-pool 0.311 (Muse Spark 1.3 → Qwen3.8
232
+ Flash); 0.25 flips the actionable pick while the full-pool #1 is unchanged.
233
+ Non-price weights rescaled by 0.852273: `general` 0.6, `long_context` 0.116676,
234
+ `throughput` 0.033324 (Σ(non-price) = 0.75 = 1 − price; `q` unchanged). The
235
+ reachable flip rides on `long_context` imputation (39% coverage; DeepSeek's
236
+ index is null → capability fill 0.195, not a measured weakness). Full-pool
237
+ `plan` #1 stays Muse Spark 1.3 (value 0.837 → 0.801); reachable #1 Hy4 preview →
238
+ DeepSeek-V4.1-Flash. The other nine roles are untouched.
239
+
240
+ - Keyed-catalog availability fast path (2026-10-03, issue #15): the updater now consumes
241
+ the shared `KeyAvailability` primitive — when the account's OpenRouter "Filter the
242
+ model catalog for API keys" setting is on, allowed candidates skip the probe and
243
+ key-blocked candidates are pruned from the walk order (the budget counts candidates
244
+ examined, so pre-seeding a blocked verdict would not save it); candidates in neither
245
+ catalog are still probed, and `no-filter`/`unavailable` degrade to the probe walk with
246
+ no abort path. Measured on this account (setting on): keyed `GET /api/v1/models`
247
+ returned 108 rows vs 466 public; after `rankingIdOf` normalization 107 keyed ranking
248
+ ids ⊆ 454 public, 347 blocked. A 6-blocked/6-allowed probe cross-check agreed 6/6
249
+ blocked and 5/6 allowed (the sixth probed `unknown`, which counts as usable).
250
+
251
+ - Role-weight rebalance (2026-10-03, issue #13): `math` dropped from `plan` and
252
+ `slow` (collinear with `reasoning`, r 0.919, and missing for 18% of the pool —
253
+ a coverage penalty, not a quality signal) and its share moved to the
254
+ consolidated `general` axis (capability-preserving, 100% coverage), not
255
+ `throughput` (which would have tripled `slow`'s speed weight and flipped both
256
+ leaders to a cheap/fast model). `general`+`reasoning` merged into `general`
257
+ in `default`/`slow`/`plan`/`advisor` (a pure relabel: `general`~`reasoning`
258
+ r 0.984, so the combined capability share is unchanged). `smol`/`commit`
259
+ `price` raised 0.30/0.35 → 0.45 (the measured binding threshold; non-price
260
+ weights rescaled proportionally, so it is a posture change, not a capability
261
+ change). `plan`'s description drops the now-unweighted `math`. New weights:
262
+ `default` general 0.4926/code 0.1705/agents 0.1137/tool_calling 0.0947/
263
+ throughput 0.0285/price 0.10; `slow` general 0.60/code 0.18/agents 0.13/
264
+ throughput 0.04/price 0.05; `plan` general 0.704/long_context 0.1369/
265
+ throughput 0.0391/price 0.12 (superseded by issue #16, above); `advisor`
266
+ general 0.5947/long_context 0.1004/
267
+ price 0.20/throughput 0.1049; `smol` price 0.45/throughput 0.22/general
268
+ 0.1571/code 0.0943/tool_calling 0.0786; `commit` price 0.45/throughput
269
+ 0.2285/general 0.2369/code 0.0846. Leaders: `default` DeepSeek-V4.1-Flash
270
+ (unchanged), `slow`/`plan` → Muse Spark 1.3, `advisor` Muse Spark 1.3
271
+ (unchanged), `smol`/`commit` → DeepSeek-V4.1-Flash. `required`, `thinking`,
272
+ `filters`, `enabled` and the `vision`/`tiny`/`task`/`designer` weights are
273
+ unchanged.
274
+
275
+ - Designer weight reconciliation (2026-10-03, issue #12): `designer` `code`
276
+ 0.10 → 0.06 (collinear with `website`, r 0.890, so it double-counted one
277
+ capability axis) and the freed 0.04 moved to the independent axes (`price`
278
+ +0.02, `throughput` +0.02), per Scoring rule 5. New weights: general 0.26/
279
+ code 0.06/vision 0.18/throughput 0.15/price 0.17/website 0.18 (Σ = 1.00,
280
+ Σ(non-price) = 0.83 = 1 − 0.17); λ 0.00882 → 0.010241. The `design` archetype
281
+ now derives its `weights`/`required` from `DEFAULT_ROLES.designer` by
282
+ reference (the `general` archetype's pattern), so the two design weightings
283
+ cannot diverge. `required`, `thinking`, `filters.image`, `enabled` and the
284
+ description are unchanged; the leader is unchanged (a refinement, not a
285
+ re-ranking).
286
+
287
+ - Coverage-safe benchmark discovery + agent-write hardening in `/create-agent`
288
+ (2026-10-03, issue #11): discovery now refuses a benchmark that would turn `q`
289
+ into a coverage score — a fill-0 metric whose catalog coverage is below 35% of
290
+ the ranking field is dropped (non-fatal, with a reason), while a capability-
291
+ filled metric (`writing`/`website`/`bench:<id>`, fill 0.195) stays weightable at
292
+ any coverage. The focus set is capped at 3 (priority order preserved) so one
293
+ benchmark keeps a decisive share. The handler loads the ranking universe once
294
+ (the same daily-cached chain the updater/explorer use) for the field size,
295
+ per-metric coverage, and the differentiation comparison; a failed load skips
296
+ the share rule (fill rule still applies, coverage annotated `unknown`). The
297
+ report prints each focus metric's coverage and warns below the bar; `--dry-run`
298
+ and `--json` carry the same signal. Free-text extraction no longer folds
299
+ `general`/`price`/`throughput` (every archetype weights them) but keeps
300
+ `reasoning`. The agent file is written **before** the role, and a failed role
301
+ write rolls it back (restoring the prior file on a `--force` re-create, else
302
+ removing the new one), so a partial failure never leaves a dangling role, a
303
+ retry needs no `--force`, and the user's prior agent is never destroyed. The
304
+ step-3 benchmark prompt previews the request's resolved weights (`resolveRole`),
305
+ so the "already in this role's weights" marker appears live. The vendored
306
+ architect prompt carries a source-version marker (`ARCHITECT_PROMPT_VERSION`)
307
+ asserted by a test. Unit-tested in `tests/create-agent.test.ts` and
308
+ `tests/architect-provenance.test.ts`.
9
309
 
10
310
  - Benchmark discovery in `/create-agent` (2026-10-02, issue #10): when a purpose
11
311
  is given, the command fetches the llm-stats benchmark catalog (745 rows,
@@ -44,13 +344,15 @@ are the 2026-10-02 measurement and are not re-derived on read.
44
344
  explorer's `/api/bootstrap` listed the external metric with its derived
45
345
  metadata. Unit-tested in `tests/benchmark-sources.test.ts`.
46
346
 
47
- - Release (2026-10-02): `Gott50/omp-llm-role` is public and `main` is pushed
48
- (`cc120bb`). The git and marketplace install routes were re-verified against
49
- the real URLs in throwaway HOMEs (omp 18.4.10): both installed 1.0.0, and each
50
- post-install session run wrote `modelRoles` + `retry.fallbackChains` into that
51
- HOME's `config.yml` and landed the three daily caches in the installed copy.
52
- npm is not published yet (name unclaimed, no credential on the workstation) —
53
- see Releasing.
347
+ - Release (2026-10-03): `Gott50/omp-llm-role` is public, `main` is pushed, and
348
+ `omp-llm-role@1.0.0` is published to npm (`npm view omp-llm-role version` →
349
+ `1.0.0`). All three install routes were verified against the real URLs in
350
+ throwaway HOMEs (omp 18.4.12): npm (`omp plugin install omp-llm-role`), git
351
+ (`github:Gott50/omp-llm-role`) and marketplace
352
+ (`omp-llm-role@gott50-plugins`) each installed 1.0.0, and each post-install
353
+ session run wrote `modelRoles` + `retry.fallbackChains` into that HOME's
354
+ `config.yml` and landed the three daily caches in the installed copy. See
355
+ Releasing.
54
356
 
55
357
  - `/create-agent` free-text form (2026-10-02): the command now accepts a
56
358
  plain-language request as well as flags. `parseCreateAgentInput` splits the
@@ -314,18 +616,18 @@ are the 2026-10-02 measurement and are not re-derived on read.
314
616
  0.663, margin 0.039 > `switchMargin`), because the incumbent has no Design
315
617
  Arena data and takes the neutral fill while MiMo carries a measured 0.97.
316
618
  The other nine roles are untouched (only `designer` weights `website`).
317
- - 400 llm-stats models; OpenRouter matched 152/400 (throughput), 151 priced.
318
- - Eligible per role: 143 (vision 75, designer 88, image-input filter).
619
+ - 407 llm-stats models; OpenRouter matched 155/407 (throughput), 153 priced (the
620
+ 2026-10-07 snapshot read 150/400 — the daily cache moved, not the code; the
621
+ #34–#40 axes and flags leave every ranking byte-identical until a flag is
622
+ turned on).
623
+ - Eligible per role: 146 (vision 75, designer 89, image-input filter).
319
624
  - Value-ranking leaders (this report, thinking-adjusted prices; the *ranking*
320
625
  leaders, before the account's provider whitelist drops the blocked ones):
321
- `default` DeepSeek-V4.1-Flash (0.810), `smol` Muse Spark 1.1 (0.740, blocked
322
- → DeepSeek), `slow` GLM-5.3 (0.816), `vision` Qwen3.8 Flash (0.731, blocked
323
- → DeepSeek), `plan` Qwen3.8 Flash (0.751), `commit` Muse Spark 1.1 (0.768,
324
- blocked → DeepSeek), `tiny` Muse Spark 1.1 (0.779, blocked → Ling Fin),
325
- `task` DeepSeek-V4.1-Flash (0.759), `advisor` Muse Spark 1.3 (0.788,
326
- blocked → Hy4), `designer` DeepSeek-V4.1-Flash (0.787). The suffix pass moved
327
- `default` and `advisor` onto their reachable leaders (Muse Spark 1.3 and
328
- GPT-5.6 Sol were blocked).
626
+ `default` DeepSeek-V4.1-Flash (0.808), `smol` Muse Spark 1.1 (0.687),
627
+ `slow` Muse Spark 1.3 (0.832), `vision` Qwen3.8 Flash (0.731), `plan`
628
+ Muse Spark 1.3 (0.795), `commit` Muse Spark 1.1 (0.725), `tiny`
629
+ Mercury 2 (0.797), `task` DeepSeek-V4.1-Flash (0.749), `advisor`
630
+ Muse Spark 1.3 (0.786), `designer` DeepSeek-V4.1-Flash (0.761).
329
631
  - Thinking-adjusted pricing landed (2026-09-27): the suffix table moved into
330
632
  `DEFAULT_ROLES` as a per-role `thinking` field, and the price axis scales by
331
633
  the level's factor for thinking-capable models — `slow` (`:max`, ×7.86)
@@ -373,8 +675,9 @@ are the 2026-10-02 measurement and are not re-derived on read.
373
675
  cannot re-add keys outside `settings.roles` `patchModelRoles` selects. The
374
676
  owner-written `retry.fallbackChains` key
375
677
  `openrouter/~deepseek/deepseek-v4-flash-latest` is now unreferenced by any
376
- role; it is left in place deliberately (SPEC §6.4: unwritten, unreferenced
377
- keys are never pruned), so removing it is a manual call.
678
+ role; it is left in place deliberately (unwritten, unreferenced keys are never
679
+ pruned — see [`writes-and-state.md`](writes-and-state.md)), so removing it is a
680
+ manual call.
378
681
  - Plugin verified live (2026-09-23) with the provider-allowlist probe: the
379
682
  account's allowed-providers whitelist excludes first-party openai/azure/
380
683
  anthropic endpoints, so the probe gate rewrote `slow` → GLM-5.3 (`:max`),