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
package/README.md CHANGED
@@ -62,7 +62,7 @@ omp plugin install omp-llm-role@gott50-plugins
62
62
  | `smol` | Fast lightweight model: cheap and quick, still competent |
63
63
  | `slow` | Most capable model for hard problems; cost and speed as tiebreakers |
64
64
  | `vision` | Image understanding: vision index dominates |
65
- | `plan` | Planning: reasoning, math, long-context coherence |
65
+ | `plan` | Planning: reasoning and long-context coherence, priced so the cheapest capable planner wins |
66
66
  | `commit` | Commit messages: cheap and fast with decent general quality |
67
67
  | `tiny` | Background tasks (titles, memory): cheapest and fastest wins |
68
68
  | `task` | Subagents: agentic + tool calling, moderate cost sensitivity |
@@ -110,6 +110,7 @@ Global knobs:
110
110
  | `writeFallbackChains` | `true` | Write per-role fallback chains into `config.yml` |
111
111
  | `fallbackChainDepth` | `2` | Fallback models per role chain |
112
112
  | `activateDefaultOnEmptySession` | `true` | Reapply the new `default` selector to an empty session's active model |
113
+ | `errorReporting` | `off` | Opt-in reporting of **unexpected** plugin errors as GitHub issues on `Gott50/omp-llm-role`; one of `off`, `ask`, `auto` (see below) |
113
114
 
114
115
  Per-role knobs (`roles.<name>.*`):
115
116
 
@@ -120,10 +121,97 @@ Per-role knobs (`roles.<name>.*`):
120
121
  | `weights` | shipped set | Metric weights; must sum to 1.0 ± 0.01 |
121
122
  | `required` | shipped set | Eligibility gate (metrics a model must have); independent of weights |
122
123
  | `filters.image` | `false` (`vision`/`designer`: `true`) | Restrict the pool to image-capable models |
124
+ | `filters.tools` | `false` | Restrict the pool to models with a tool-capable route |
125
+ | `filters.minContextTokens` | `0` (off) | Drop routes whose endpoint context is below this (tokens) |
126
+ | `filters.minOutputTokens` | `0` (off) | Drop routes whose endpoint output ceiling is below this (tokens) |
127
+ | `filters.maxPriceUsdPerM` | `0` (off) | Drop a model whose thinking-adjusted blend exceeds this ($/M) |
123
128
  | `thinking` | shipped level | Thinking level appended to the selector (`off`…`max`, `auto`) |
129
+ | `providerPin` | `""` (unset) | Pin the role's requests to one OpenRouter provider route (`@<slug>`, may be tiered like `deepinfra/fp8`). The role is then priced and gated by that route — its billed price and p50 throughput, not the `1/price²` blend — and a model with no matching route is ineligible |
130
+ | `cacheHitRate` | `0` (off) | Assumed cache-hit rate (0–1) for the role's input tokens; each route's effective input price becomes `h·cacheRead + (1−h)·input` before the 3:1 billed blend and the thinking factor. The `1/price²` weight basis stays the **listed** input price (the router's sort key), so a cheap cache on a lightly-weighted route cannot dominate; a route with no cache-read price keeps its full input price. `0`/absent = off (byte-identical ranking) |
124
131
  | `lambda` | derived | Explicit λ ($ per quality point) override |
125
132
  | `locked` | `false` | Leave the role alone: rank it but never rewrite its selector or chain (explorer toggle) |
126
133
 
134
+ **Capability flags** (`features.<id>` global, `roles.<role>.features.<id>` per
135
+ role). One named flag per opt-in capability; turning it on applies the plugin's
136
+ recommended settings for that capability to the role. Every individual knob
137
+ stays settable, and an explicit knob always beats the flag. **Every flag ships
138
+ OFF** — with no flags set, the resolved roles are byte-identical to the shipped
139
+ defaults.
140
+
141
+ | Flag | Recommended settings | What it buys |
142
+ |---|---|---|
143
+ | `endpointCeilings` | `filters.tools: true`, `filters.minOutputTokens: 16384` | Routes that cannot call tools and routes whose output ceiling is below 16 384 tokens leave the pool — a reasoning model is not cut off mid-thought |
144
+ | `cachePricing` | `cacheHitRate: 0.5` | The role is priced on a cache-heavy agent loop's invoice (the endpoint's `cacheReadPrice` blended in) rather than the sticker price |
145
+ | `providerPinning` | `preferOwnProvider: true` | A model is priced **and measured** on its own lab's route where one exists (DeepSeek's own endpoint, 94.5 % cache hit) — the route's billed price and its p50 throughput, so `q` moves too — falling back to the default blend otherwise |
146
+ | `costCap` | `filters.maxPriceUsdPerM: 10` | The priciest tail of the eligible pool leaves it, so the pick cannot be an expensive outlier |
147
+
148
+ Precedence is one rule:
149
+
150
+ ```
151
+ effective def = shipped default ⊕ preset(enabled flags) ⊕ explicit user keys
152
+ ```
153
+
154
+ A flag is on when `roles.<role>.features.<id>` is present (it wins either way)
155
+ and otherwise when the global `features.<id>` is `true` — so a per-role `false`
156
+ beats a global `true`. The preset fills only knobs the role does not already
157
+ set, and an explicit key is merged last, so it always wins. `providerPinning`'s
158
+ recommended setting is a **soft per-model policy** (`preferOwnProvider`), not a
159
+ hard `providerPin`: it re-prices a model on its own lab's route where one exists
160
+ (the route's billed price **and** its p50 throughput, so `q` moves as well as
161
+ `priceEff`) and never makes a model ineligible (the hard `providerPin` stays
162
+ available by hand). `minContextTokens` is deliberately **not** part of `endpointCeilings` —
163
+ the complaint it would answer is an endpoint advertising a *fraction of the
164
+ model's own window*, which an absolute token floor cannot express, so it stays a
165
+ hand-set knob.
166
+
167
+ The flags are settable from every role-authoring surface: `--feature <id,...>`
168
+ (comma-separated) on `node src/cli/create-role.ts`, `/create-agent` and
169
+ `/project-roles` (`--list-features` lists them on the first two), plus the
170
+ explorer's Features panel. An
171
+ unknown flag id or a non-boolean value aborts the run with the offending key and
172
+ no write.
173
+
174
+ **Error reporting (`errorReporting`).** This knob ships **`off`** — installing
175
+ or upgrading the plugin never starts uploading anything. When something
176
+ *genuinely unexpected* happens (a `/refresh-roles` or authoring command throws,
177
+ or the updater hits an internal error), the plugin can file one structured
178
+ report — the plugin version, runtime, platform, the command that failed, the
179
+ error name/message/stack and a timestamp — as a GitHub issue on
180
+ `Gott50/omp-llm-role`. The three values:
181
+
182
+ - **`off`** — never files; behaviour is exactly today's (one warning line).
183
+ - **`ask`** — shows you the exact text and files it only if you confirm. In a
184
+ session with no UI (`omp -p`, RPC, a subagent) it never uploads: it notifies a
185
+ prefilled new-issue URL instead, so you can submit it yourself.
186
+ - **`auto`** — files without asking.
187
+
188
+ The payload is redacted before it leaves the machine (your home directory → `~`,
189
+ token-shaped strings → `<redacted>`) and carries **no** config contents, model
190
+ selectors, prompts or history rows. Filing goes through the GitHub API when a
191
+ token is available (`GITHUB_TOKEN` → `GH_TOKEN` → `gh auth token` — the token is
192
+ never stored in a settings file); a token that cannot write issues here, or no
193
+ token at all, falls through to a prefilled `…/issues/new` URL (opened in your
194
+ browser, or printed). A repeated crash comments on the existing open issue
195
+ instead of opening a new one, and the path is capped (one open issue per
196
+ fingerprint, one creation per fingerprint per UTC day, 3 per day) so a crash
197
+ loop cannot spam the tracker. Reporting is **best-effort**: a failure in it only
198
+ adds one notification line and never affects the run it reports on. Expected
199
+ environment conditions (no key budget, a concurrent refresh holding the lock,
200
+ write conflicts, unavailable ranking data, invalid settings) are **never**
201
+ reported. In project mode the project lock file's value applies.
202
+
203
+ **Validation.** A run aborts (notify, no write) on: a weight ≤ 0, or a role's
204
+ weights not summing to 1.0 ± 0.01; a `required` entry or a weighted metric
205
+ outside the known-metric set; a `thinking` level outside
206
+ `off|minimal|low|medium|high|xhigh|max|auto`; `switchMargin`/`priceSwitchFraction`
207
+ outside `[0, 1]`; a non-boolean `enabled`; a `cacheHitRate` outside `[0, 1]`; a
208
+ `providerPin` that is empty or
209
+ contains `@`/`:` (the selector's own delimiters); an unknown capability id
210
+ (global or per-role) or a non-boolean flag value; a non-boolean
211
+ `preferOwnProvider`. `weights: null` opts a role out;
212
+ the legacy `suffixes.*` keys are rejected with a migration hint (moved into
213
+ `roles.<role>.thinking`).
214
+
127
215
  **Typed writes.** `omp plugin config set <plugin> <key> <value>` stores every
128
216
  value as a **string**, which the validator rejects for numbers/arrays/booleans,
129
217
  so it is only usable for string-valued keys. Write typed role defs with
@@ -144,10 +232,11 @@ with the offending role/key and no write.
144
232
  /create-agent <request> [options] # agent + role + wiring (free text or flags)
145
233
  /create-agent --name <n> --purpose <text> [options] # agent + role + wiring
146
234
  /remove-agent --name <n> [--scope user|project] [--lock PATH] [--yes] [--dry-run] # delete an agent and its role
235
+ /project-roles [--purpose <text>] [--roles <spec>] [--feature <id,...>] [--force] [--yes] [--dry-run] [--json] # project-scoped role set
147
236
 
148
237
  # CLI
149
238
  node src/cli/llm-role-rank.ts [--top N] [--json] [--out FILE] [--refresh] [--all] [--url URL]
150
- node src/cli/create-role.ts --name <role> --weights m=w,... [--required m,...] [--thinking <level>] [--description <text>] [--image] [--lambda N] [--lock PATH] [--dry-run] [--json]
239
+ node src/cli/create-role.ts --name <role> --weights m=w,... [--required m,...] [--thinking <level>] [--description <text>] [--image] [--lambda N] [--feature <id,...>] [--list-features] [--lock PATH] [--dry-run] [--json]
151
240
  node --test tests/
152
241
  ```
153
242
 
@@ -161,6 +250,9 @@ node --test tests/
161
250
  - `/remove-agent` — the inverse of `/create-agent`: deletes the agent `.md` and
162
251
  its role, then runs the updater in-process so `modelRoles.<n>` is dropped;
163
252
  refuses shipped default roles (see *Remove a specialist agent*).
253
+ - `/project-roles` — discover the project's usecase from its own artifacts and
254
+ apply a project-scoped role set to `<cwd>/.omp/config.yml` (see *Project
255
+ roles*).
164
256
  - `src/cli/llm-role-rank.ts` — markdown report to stdout (per-role tables + suggested
165
257
  `modelRoles`). `--top N` rows per role (default 10); `--json` machine payload;
166
258
  `--out FILE` write instead of stdout; `--refresh` bypass both caches; `--all`
@@ -193,6 +285,25 @@ agent is ranked on WritingBench, Creative Writing v3, COLLIE and the other
193
285
  writing benchmarks, not just the one you named. Discovery is non-fatal and
194
286
  skippable with `--no-discover`; an explicit `--benchmarks` list wins over it.
195
287
 
288
+ Discovery is **quality-gated**: each candidate is assessed on nine axes —
289
+ coverage (share of the field carrying the metric), dispersion (does it separate
290
+ models), provider composition, freshness, trust (the share of the payload's
291
+ entries that are self-reported, so a mostly vendor-submitted source is flagged),
292
+ modality (the catalog row's modality and the payload's multimodal share — an
293
+ annotation, never a gate), maintenance (the catalog row's dataset age against
294
+ a 12-month bar — an annotation, never a gate), provenance (the benchmark's
295
+ owner and the source's own per-entry org mix, cross-checked against the
296
+ composition axis — an annotation, never a gate) and cross-source (the zeroeval
297
+ per-entry price/context against the plugin's own, flagging a divergence past a
298
+ tolerance — an annotation, never a gate, and never a change to the `1/price²`
299
+ blend) — and a discovered benchmark that
300
+ fails a gating axis is dropped rather than folded in (a benchmark you name yourself is
301
+ never dropped, only warned). The focus set is capped at three benchmarks so one
302
+ keeps a decisive share. The report prints the nine signals and warns per below-bar
303
+ axis, and warns when the new role's top pick is the same as the `default` role's
304
+ (the role adds nothing). The agent file is written before the role, so a partial
305
+ failure never leaves a dangling role.
306
+
196
307
  The flag form is equivalent and gives you the name explicitly:
197
308
 
198
309
  ```sh
@@ -206,7 +317,9 @@ validated role into the settings lock file, and runs the updater in-process so
206
317
  `modelRoles.review` lands in `config.yml` — nothing to remember. Options:
207
318
  `--archetype <id>`, `--weights m=w,...`, `--required`, `--thinking`, `--tools`,
208
319
  `--benchmarks m,...` (extra metrics to fold in; `--list-benchmarks` prints the
209
- weightable set, including any external benchmark in use), `--scope user|project`,
320
+ weightable set, including any external benchmark in use), `--feature <id,...>`
321
+ (comma-separated capability flags; `--list-features` prints the capability set),
322
+ `--scope user|project`,
210
323
  `--body`/`--body-file`, `--force` (overwrite an existing agent file), `--yes`
211
324
  (accept a proposed benchmark source without prompting), `--no-discover` (skip
212
325
  catalog discovery), `--dry-run`, `--json`. In
@@ -253,6 +366,32 @@ By hand, the same two artifacts:
253
366
  2. Delete the agent file: `rm ~/.omp/agent/agents/review.md`.
254
367
  3. Wire it: `/refresh-roles` in a session.
255
368
 
369
+ ## Project roles
370
+
371
+ `/project-roles` discovers a project's usecase from its own artifacts (README,
372
+ package.json, AGENTS.md, docs listing, git log, file tree) and applies a
373
+ **project-scoped** role set — the roles the project needs, with agents authored
374
+ for the new ones — to `<cwd>/.omp/config.yml`:
375
+
376
+ ```sh
377
+ /project-roles
378
+ /project-roles --purpose "a Python data pipeline repo" --dry-run
379
+ /project-roles --roles "data=build pipelines,-vision,default"
380
+ ```
381
+
382
+ It prints the plan (kept / dropped / added roles, each new role's archetype,
383
+ weights, focus benchmarks and top pick) before writing. `--purpose` overrides the
384
+ discovered usecase; `--roles` overrides the role set (`-name` drops a shipped
385
+ role, `name` keeps or adds one, `name=purpose` adds a new role); `--dry-run`
386
+ writes nothing; without `--force` an existing project role config is refused.
387
+
388
+ When a project carries its own `omp-llm-role` settings entry, the updater scopes
389
+ the whole run to `<cwd>/.omp` — the config, the state/history/lock files and the
390
+ day gate — so the project's `modelRoles` are refreshed independently of the
391
+ global `~/.omp/agent/config.yml`, which is never written in project mode. A
392
+ successful apply registers the project, so the explorer's **scope switcher**
393
+ lists it (see below).
394
+
256
395
  ## Explorer
257
396
 
258
397
  `/explore-roles` in omp boots a loopback-only web UI
@@ -260,6 +399,15 @@ By hand, the same two artifacts:
260
399
  `@slow`?" and "what happens if I care more about price than agents?" without
261
400
  editing source and re-running the CLI.
262
401
 
402
+ - **Scope switcher** — the header lists every role-config source: the
403
+ **user-level** scope, plus every project where `/project-roles` was used (the
404
+ plugin records each project root in a small global registry, and the current
405
+ session's project is listed immediately). The explorer opens on the session's
406
+ project when the cwd has a project role config, else user-level. Switching
407
+ re-reads that scope's roles and universe from disk and points **Export** at
408
+ that scope's lock file — a project scope resolves as the project lock merged
409
+ over the user-level lock, exactly what the updater ranks. A project whose lock
410
+ file is gone is shown disabled. Switching with unsaved edits asks first.
263
411
  - **Role tabs** — every role the plugin knows: the nine omp built-in roles, the
264
412
  shipped opt-in `designer`, and any role present only in the lock file. Each
265
413
  tab is tinted by provenance — `default` (omp built-in), `plugin` (shipped but
@@ -307,12 +455,23 @@ The plugin keeps only models the OpenRouter key can actually run:
307
455
  - **Tier/budget gate** — paid keys with budget get billed variants, free or
308
456
  exhausted keys get `:free` variants, `:batch` never. Every candidate must
309
457
  resolve in omp's catalog.
310
- - **Probe verification** — on top of the gate, every candidate is verified with
311
- a one-token completion probe, because the account's OpenRouter
312
- **allowed-providers privacy whitelist** is invisible to the key and catalog
313
- endpoints and a real request's 404 is the only reliable signal. Blocked
314
- candidates are excluded from selection and chains; a role with no clean
315
- candidate is left untouched.
458
+ - **Keyed-catalog fast path** — when the account's OpenRouter setting **"Filter the
459
+ model catalog for API keys"** is on, the key-authenticated `GET /api/v1/models` is a
460
+ per-key allowlist: allowed candidates skip the probe and key-blocked candidates are
461
+ pruned from the walk. With the setting off (or the fetch failing) the plugin falls
462
+ back to the probe walk below — no abort.
463
+ - **Probe verification** — the fallback: every candidate is verified with a one-token
464
+ completion probe, because the account's OpenRouter **allowed-providers privacy
465
+ whitelist** is invisible to the key endpoint and the unauthenticated catalog, and a
466
+ real request's 404 is the only reliable signal. Blocked candidates are excluded from
467
+ selection and chains; a role with no clean candidate is left untouched.
468
+ - **Explorer key marking** — the explorer overlays every ranked model with a
469
+ **usable** / **key-blocked** / **unknown** badge derived from the
470
+ key-authenticated `GET /api/v1/models` (the account setting **"Filter the model
471
+ catalog for API keys"** makes it a per-key allowlist). A **hide key-blocked**
472
+ toggle drops blocked rows, and when the setting is off every badge reads
473
+ *unknown* with a hint naming the OpenRouter setting. The mark is read-only: it
474
+ never edits roles, reorders ranks or changes an Export.
316
475
 
317
476
  ## Caveats
318
477
 
@@ -320,10 +479,19 @@ The plugin keeps only models the OpenRouter key can actually run:
320
479
  occupying its share of the denominator, so weight sparse metrics as
321
480
  differentiators, never as `required` gates. `website`, `writing` and
322
481
  `long_context` are capability-filled (0.195) instead, so absence is not a
323
- penalty for them.
482
+ penalty for them — with one residual inversion: the fill sits above the bottom
483
+ of the covered field, so a covered model in the bottom of the field can still
484
+ score below a model with no data (see
485
+ [docs/dev/quirks.md](docs/dev/quirks.md)).
324
486
  - **Provider whitelist.** The account's allowed-providers whitelist blocks many
325
487
  models, so the report's #1 and the written selector can differ — judge weights
326
488
  on the reachable pool.
489
+ - **Provider pinning opts out of auto-Exacto.** A `providerPin` writes a trailing
490
+ `@<slug>` on the selector, which omp applies as OpenRouter
491
+ `provider: { only: [slug] }` routing. `only` is exclusive, so a pinned role's
492
+ requests bypass OpenRouter's automatic Exacto tool routing (which routes tool
493
+ calls by tool-call correctness) and run on the pinned route. Pin only where you
494
+ know the endpoint you want; leave tool-heavy roles on the default routing.
327
495
  - **Model concentration.** The cheapest pick concentrates on one vendor; the
328
496
  fallback chains mitigate it.
329
497
 
@@ -334,6 +502,6 @@ MIT — see [LICENSE](LICENSE).
334
502
  ## Documentation
335
503
 
336
504
  Maintainer docs (architecture, data sources, scoring, writes/state, explorer
337
- internals, releasing, agent authoring, the normative spec) live in
505
+ internals, releasing, agent authoring, testing) live in
338
506
  [docs/dev/README.md](docs/dev/README.md); release history is in
339
507
  [CHANGELOG.md](CHANGELOG.md).
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: designer
3
- description: UI/UX specialist for design implementation, review, visual refinement
3
+ description: MUST be used for UI/UX design implementation, review, and visual refinement.
4
4
  model: "@designer, @default"
5
- tools: [read, bash, edit, ast_grep, ast_edit, ask, debug, ida, eval, github, glob, grep, find, lsp, checkpoint, rewind, context_notes, new_context, security_scan, task, wait, todo, web_search, write, memory_edit, retain, recall, reflect, learn, manage_skill]
5
+ tools: [read, grep, glob, find, edit, write, bash, ast_grep, ast_edit, eval, lsp, web_search, ask, todo, task, wait, checkpoint, rewind]
6
6
  ---
7
7
 
8
8
  Implement/review UI designs; edit files, create components, run commands as needed.
@@ -30,6 +30,7 @@ Design system: foundation; UI without one becomes inconsistent. Four phases, in
30
30
  3. Implement states: loading, empty, error, disabled, hover, focus.
31
31
  4. Verify accessibility: contrast, focus rings, semantic HTML.
32
32
  5. Test responsive behavior.
33
+ 6. Render and inspect: start the project's dev server (or open the built page), open the route in the browser via `eval` (`browser.open`), `screenshot` it, and check the project's actual breakpoints, both light and dark themes, and focus states. The `browser` facade is an Eval prelude, not an agent tool, so it cannot appear in `tools:` — the instruction lives here in the body.
33
34
 
34
35
  ## Review
35
36
  1. Read reviewed files.
@@ -56,7 +57,7 @@ Design system: foundation; UI without one becomes inconsistent. Four phases, in
56
57
  - Same spacing everywhere: no rhythm; monotony
57
58
  - Center-aligning everything: left alignment with asymmetry feels more designed
58
59
  - Modals for everything: lazy, rarely best
59
- - Overused fonts: Inter, Roboto, Open Sans, system defaults
60
+ - Overused fonts when the project has no type system: Inter, Roboto, Open Sans, system defaults.
60
61
  - Pure black (`#000`) or white (`#fff`): ALWAYS tint neutrals
61
62
  - Gray text on colored backgrounds: use a background shade instead
62
63
  - Bounce/elastic easing: dated, tacky; use exponential easing (`ease-out-quart`/`expo`)
@@ -4,6 +4,18 @@ Maintainer docs for the omp-llm-role plugin. `README.md` is for people *using*
4
4
  the plugin; everything here is for people *changing* it. A fact has exactly one
5
5
  home — if it is here, it is not in the README.
6
6
 
7
+ ## Development setup
8
+
9
+ Clone, then install the dev dependency and wire the commit gate:
10
+
11
+ ```sh
12
+ npm install
13
+ git config core.hooksPath .githooks
14
+ ```
15
+
16
+ `.githooks/pre-commit` refuses staged `cache/` paths (daily UTC caches,
17
+ regenerated on every run) and runs the test suite (`node --test tests/`).
18
+
7
19
  ## Where to start
8
20
 
9
21
  | If you are changing… | Read |
@@ -14,9 +26,10 @@ home — if it is here, it is not in the README.
14
26
  | a config write, the state/history/lock files, or the write gate | [writes-and-state.md](writes-and-state.md) |
15
27
  | the explorer surface (server, explain layer, SPA) | [explorer.md](explorer.md) |
16
28
  | the dated numbers (matched/priced/eligible counts, per-role leaders) | [current-state.md](current-state.md) |
17
- | a release (version bump, npm/git/marketplace channels) | [releasing.md](releasing.md) |
29
+ | a release (version bump, SemVer, CHANGELOG rules, npm/git/marketplace channels) | [releasing.md](releasing.md) |
30
+ | the CI workflow, the release script, or the tag→publish contract | [ci.md](ci.md) |
18
31
  | the agent `.md` contract (frontmatter, routing, read-only rules) | [agent-authoring.md](agent-authoring.md) |
19
- | a normative decision (what the plugin MUST do) | [spec.md](spec.md) |
32
+ | the test suite or a verification scenario | [testing.md](testing.md) |
20
33
  | a debugging-only oddity (a quirk, not a contract) | [quirks.md](quirks.md) |
21
34
 
22
35
  ## The docs
@@ -41,14 +54,38 @@ home — if it is here, it is not in the README.
41
54
  - **[current-state.md](current-state.md)** — the dated snapshot:
42
55
  matched/priced/eligible counts, per-role leaders, and the open defects.
43
56
  Refresh it with each run.
44
- - **[releasing.md](releasing.md)** — the release switch (`package.json`
45
- `version`), the npm/git/marketplace channels, the tarball `files` whitelist,
46
- and the install-route verification.
57
+ - **[releasing.md](releasing.md)** — when and how to bump the version (the
58
+ `package.json` release switch, the SemVer surface), how to cut a release,
59
+ the CHANGELOG entry rules, the npm/git/marketplace channels, the tarball
60
+ `files` whitelist, and the install-route verification.
61
+ - **[ci.md](ci.md)** — the GitHub Actions pipeline: the `test`/`release` jobs
62
+ and their triggers, the tag→publish contract, the one-time trusted-publisher
63
+ registration, the `scripts/release.ts` subcommands and refusals, and how to
64
+ verify the pipeline.
47
65
  - **[agent-authoring.md](agent-authoring.md)** — the agent `.md` contract:
48
66
  frontmatter (`model: "@<role>, @default"`, `tools:`), the routing
49
67
  description, read-only classification, and where agents are discovered.
50
- - **[spec.md](spec.md)** — the normative spec: the confirmed decisions, the
51
- verified ground truth, and the contracts the implementation MUST satisfy.
68
+ - **[testing.md](testing.md)** — the test-suite seam map (which test file covers
69
+ which scenario) and the live checks that need a real omp session.
52
70
  - **[quirks.md](quirks.md)** — debugging-only oddities and gotchas that are not
53
71
  contracts (e.g. Node type-stripping not typechecking, stale in-process plugin
54
72
  code).
73
+
74
+ ## Research notes
75
+
76
+ External material that informs (but does not bind) the design. Not contracts.
77
+
78
+ - **[../research/agentic-engineering-benchmarks.md](../research/agentic-engineering-benchmarks.md)**
79
+ — IndyDevDan's "top 5 agentic-engineering benchmarks" video, mapped onto the
80
+ project's coverage/collinearity rules; records the two axes the project lacks
81
+ (guardrails, hallucination), the missing agentic categories, and the
82
+ per-task-vs-$/M cost gap. Source transcript alongside it
83
+ (`agentic-engineering-benchmarks.transcript.txt`).
84
+ - **[../research/openrouter-endpoint-routing.md](../research/openrouter-endpoint-routing.md)**
85
+ — Kai's "OpenRouter is quietly giving nerfed AI models" video, mapped onto the
86
+ project's OpenRouter data model; confirms the `1/price²` blend, argues
87
+ quantization labels are not a quality signal, and records the per-endpoint
88
+ capability ceilings (context, max output, tool support) the project now uses as
89
+ role filters (issue #18), the opt-in cache-read pricing (issue #30) and
90
+ provider pinning, and the remaining declared-rate-vs-measured-hit gap. Source
91
+ transcript alongside it (`openrouter-endpoint-routing.transcript.txt`).
@@ -128,10 +128,68 @@ effect on the next session.
128
128
  `src/benchmark-discovery.ts`) and folds them in. The resolved benchmark
129
129
  labels are given to the architect (and, on the template path, listed in the
130
130
  body's `<criteria>`), so the agent's rubric names the benchmark its model was
131
- chosen on.
131
+ chosen on. Discovery is **quality-gated**: the pre-fetch catalog gate is
132
+ count-only, and the handler then probe-fetches each selected candidate's source
133
+ and assesses the joined pool on nine axes (`assessFocusMetric`: coverage,
134
+ dispersion, composition, freshness, trust, modality, maintenance, provenance,
135
+ cross-source — see scoring.md). A
136
+ **discovered** candidate below-bar on any gating axis is dropped non-fatally
137
+ with a reason naming the axis; a **user-named** metric is never dropped — it is
138
+ annotated and warned. The
139
+ focus set is capped at `FOCUS_METRIC_CAP` (3) so one benchmark keeps a decisive
140
+ share. The report prints the nine signals and warns per below-bar axis;
141
+ `--dry-run` and `--json` carry the same signal. The agent file is written **before** the role,
142
+ so a partial failure leaves a harmless agent (its `@<name>, @default` chain
143
+ falls back to `@default`) rather than a dangling role; a failed role write
144
+ rolls the agent file back — restoring the prior file on a `--force` re-create,
145
+ else removing the new one — so a retry needs no `--force` and the user's prior
146
+ agent is never destroyed. A malformed architect response is retried once (a
147
+ parse failure only — a `run()` failure propagates immediately, since a second
148
+ session would fail the same way), and the final error carries the raw architect
149
+ output so the failure surfaces what the architect actually said. The step-3
150
+ benchmark prompt previews the request's
151
+ resolved weights (`resolveRole`), so the "already in this role's weights"
152
+ marker appears live.
153
+ - **Capability flags on `/create-agent`.** `--feature <id,...>` (comma-separated)
154
+ sets the new role's `features.<id>` so it starts from the plugin's recommended
155
+ settings for that capability; `--list-features` prints each capability id, the
156
+ settings it applies and what it buys, without creating anything. An unknown id
157
+ fails with a non-zero exit and no write. The archetype table
158
+ (`src/role-archetypes.ts`) carries an optional `features` list the architect
159
+ may *propose*, but **no archetype auto-applies a flag** — a role gets a flag
160
+ only when the user asks for it. `create-role.ts` exposes the same
161
+ `--feature`/`--list-features` surfaces, and `/project-roles` takes `--feature`
162
+ (its help points at `/create-agent --list-features`).
163
+ - **`/project-roles`** (in-session) discovers the project's usecase from its own
164
+ artifacts through the profile architect, then authors an agent per new role
165
+ **into the project scope** (`<cwd>/.omp/agents/`) and writes the project's
166
+ `modelRoles` to `<cwd>/.omp/config.yml`. The agents are ordinary `.md` files
167
+ under the project dir, discovered by omp's nearest-project lookup (project >
168
+ user > plugin) and by `discoverAgentPins`.
132
169
  - **The shipped skill** — the hand-driven workflow, for bodies that need real
133
170
  authoring rather than the archetype scaffold.
134
171
 
172
+ ### The vendored architect prompt
173
+
174
+ `src/prompts/agent-creation-architect.md` is copied verbatim from omp 18.4.8's
175
+ `/agents` hub prompt (the SDK subpath it lives under does not resolve from the
176
+ extension loader, so the plugin ships its own copy). Its first line carries the
177
+ source version, and `ARCHITECT_PROMPT_VERSION` (`src/architect-provenance.ts`,
178
+ re-exported by `src/agent-architect.ts`) holds the same value. `tests/architect-provenance.test.ts`
179
+ asserts the two agree and that this document names the same version, so an omp
180
+ upgrade that changes the prompt surfaces as a test failure rather than silent
181
+ drift.
182
+
183
+ **Re-sync procedure** (after an omp upgrade):
184
+
185
+ 1. Read the installed omp's agent-creation architect prompt (the `/agents` hub's
186
+ system prompt) and diff it against `src/prompts/agent-creation-architect.md`.
187
+ 2. If it changed, replace the body of the asset, keeping the header comment's
188
+ version current.
189
+ 3. Bump `ARCHITECT_PROMPT_VERSION` to the new omp version.
190
+ 4. Update the version named in this section.
191
+ 5. Run `node --test tests/architect-provenance.test.ts`.
192
+
135
193
  ## Removal
136
194
 
137
195
  The `/remove-agent` command deletes an agent **and** its role definition: