omp-llm-role 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +506 -2
- package/agents/designer.md +76 -0
- package/docs/dev/README.md +91 -0
- package/docs/dev/agent-authoring.md +223 -0
- package/docs/dev/architecture.md +335 -0
- package/docs/dev/ci.md +152 -0
- package/docs/dev/current-state.md +694 -0
- package/docs/dev/data-sources.md +508 -0
- package/docs/dev/explorer.md +317 -0
- package/docs/dev/quirks.md +174 -0
- package/docs/dev/releasing.md +175 -0
- package/docs/dev/scoring.md +1027 -0
- package/docs/dev/testing.md +206 -0
- package/docs/dev/writes-and-state.md +301 -0
- package/docs/llm-role-rankings.md +199 -0
- package/package.json +1496 -4
- package/skills/omp-llm-role-create-agent/SKILL.md +174 -0
- package/src/agent-architect.ts +263 -0
- package/src/agent-create.ts +1493 -0
- package/src/agent-file.ts +119 -0
- package/src/agent-pins.ts +75 -0
- package/src/agent-remove.ts +180 -0
- package/src/architect-provenance.ts +16 -0
- package/src/availability.ts +320 -0
- package/src/benchmark-author.ts +87 -0
- package/src/benchmark-discovery.ts +84 -0
- package/src/benchmark-sources.ts +1023 -0
- package/src/cli/create-role.ts +190 -0
- package/src/cli/llm-role-rank.ts +356 -0
- package/src/config-edit.ts +583 -0
- package/src/engine.ts +1709 -0
- package/src/error-report.ts +562 -0
- package/src/explorer/boot.ts +147 -0
- package/src/explorer/explain.ts +426 -0
- package/src/explorer/scopes.ts +110 -0
- package/src/explorer/server.ts +318 -0
- package/src/extension.ts +891 -0
- package/src/features.ts +103 -0
- package/src/guards.ts +8 -0
- package/src/project-registry.ts +65 -0
- package/src/project-setup.ts +382 -0
- package/src/prompts/agent-creation-architect.md +37 -0
- package/src/prompts/agent-creation-user.md +6 -0
- package/src/prompts/benchmark-source-architect.md +36 -0
- package/src/prompts/benchmark-source-user.md +6 -0
- package/src/prompts/project-profile-architect.md +33 -0
- package/src/prompts/project-profile-user.md +8 -0
- package/src/role-archetypes.ts +258 -0
- package/src/role-settings.ts +209 -0
- package/src/settings.ts +743 -0
- package/src/state.ts +141 -0
- package/src/updater.ts +592 -0
- package/web/app.js +1239 -0
- package/web/index.html +42 -0
- package/web/style.css +287 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gott50
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,507 @@
|
|
|
1
|
-
#
|
|
1
|
+
# omp-llm-role
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ranks today's LLM leaderboard into best-fit picks for each omp model role
|
|
4
|
+
(`default, smol, slow, vision, plan, commit, tiny, task, advisor`, plus the
|
|
5
|
+
opt-in `designer`) and ships an omp plugin that applies those picks to
|
|
6
|
+
`~/.omp/agent/config.yml` daily. Every role weights price and throughput, so
|
|
7
|
+
the pick is the cheapest model that can do the job, not the strongest one.
|
|
8
|
+
|
|
9
|
+
## Example output
|
|
10
|
+
|
|
11
|
+
The report is a full worked example — per-role top-10 tables (value, q, $/M,
|
|
12
|
+
tok/s, ctx, weighted metric contributions, ★ Pareto marker) plus the suggested
|
|
13
|
+
`modelRoles` block. See
|
|
14
|
+
[docs/llm-role-rankings.md](docs/llm-role-rankings.md).
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
Any one of three routes:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
omp plugin install omp-llm-role # npm
|
|
22
|
+
omp plugin install github:Gott50/omp-llm-role # git (public repo)
|
|
23
|
+
omp plugin marketplace add Gott50/omp-llm-role # marketplace
|
|
24
|
+
omp plugin install omp-llm-role@gott50-plugins
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- npm and git installs shell out to `bun install`, so **bun must be on PATH**;
|
|
28
|
+
marketplace installs need no package manager.
|
|
29
|
+
- No runtime dependencies.
|
|
30
|
+
- **Restart the session after install** so the extension module loads.
|
|
31
|
+
- Upgrades: `omp plugin upgrade omp-llm-role` (npm/git); for the marketplace,
|
|
32
|
+
`omp plugin marketplace update gott50-plugins` then
|
|
33
|
+
`omp plugin upgrade omp-llm-role@gott50-plugins`.
|
|
34
|
+
- Dev: `omp plugin link ~/Documents/omp-llm-role`.
|
|
35
|
+
|
|
36
|
+
## Daily behaviour
|
|
37
|
+
|
|
38
|
+
- **UTC-day trigger.** The first omp session of each UTC day rewrites
|
|
39
|
+
`modelRoles` (and `retry.fallbackChains`) to that day's best key-eligible
|
|
40
|
+
models; later same-day sessions no-op. `/refresh-roles` forces a run anytime.
|
|
41
|
+
- **Awaited session-start run.** The session-start run is awaited before the
|
|
42
|
+
session's first prompt is dispatched, so the day's write lands even in a
|
|
43
|
+
short-lived session (`omp -p`, a subagent).
|
|
44
|
+
- **Session model activation.** When the day's run switches `default` and the
|
|
45
|
+
triggering session still has an empty conversation, the plugin also switches
|
|
46
|
+
the live session model to the new selector — the first prompt then runs on
|
|
47
|
+
the freshly ranked pick. Guards: main sessions only, empty conversation, the
|
|
48
|
+
session actually booted on the previous `default` selector (an explicit
|
|
49
|
+
`omp --model X` is never clobbered), and only on a real change. Disable with
|
|
50
|
+
`activateDefaultOnEmptySession=false`.
|
|
51
|
+
- **Surgical writes.** Only managed role lines, managed chain keys, and the
|
|
52
|
+
plugin-managed `task.disabledAgents` names change; comments, blank lines and
|
|
53
|
+
unknown keys stay byte-identical, and a no-change run writes nothing.
|
|
54
|
+
`~/.omp/agent/llm-role-state.json` snapshots the previous `modelRoles` block
|
|
55
|
+
on every write as a rollback aid.
|
|
56
|
+
|
|
57
|
+
## Roles
|
|
58
|
+
|
|
59
|
+
| Role | Purpose |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `default` | Main workhorse: strong general coding-agent quality, sane cost |
|
|
62
|
+
| `smol` | Fast lightweight model: cheap and quick, still competent |
|
|
63
|
+
| `slow` | Most capable model for hard problems; cost and speed as tiebreakers |
|
|
64
|
+
| `vision` | Image understanding: vision index dominates |
|
|
65
|
+
| `plan` | Planning: reasoning and long-context coherence, priced so the cheapest capable planner wins |
|
|
66
|
+
| `commit` | Commit messages: cheap and fast with decent general quality |
|
|
67
|
+
| `tiny` | Background tasks (titles, memory): cheapest and fastest wins |
|
|
68
|
+
| `task` | Subagents: agentic + tool calling, moderate cost sensitivity |
|
|
69
|
+
| `advisor` | Advisor/watchdog: deep reasoning over long context |
|
|
70
|
+
| `designer` | Design work: visual/UX judgement on image-capable models — **opt-in** |
|
|
71
|
+
|
|
72
|
+
`designer` is the only non-built-in shipped role and ships **disabled**: a stock
|
|
73
|
+
run ranks the nine built-in roles and fetches only their sources. Enable it with
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
omp plugin config set omp-llm-role config '{"roles":{"designer":{"enabled":true}}}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The plugin then ranks it and fetches Design Arena. The shipped `designer` agent
|
|
80
|
+
is opt-in too: the plugin keeps it in `task.disabledAgents` until
|
|
81
|
+
`roles.designer.enabled=true`, so it is absent from the roster and refuses
|
|
82
|
+
spawns by default. Its `model: "@designer, @default"` chain only matters if a
|
|
83
|
+
user re-enables the agent by hand — with the role disabled the unresolved first
|
|
84
|
+
entry is skipped and the child runs on `@default`.
|
|
85
|
+
|
|
86
|
+
**Agent pins.** The plugin derives each agent's pinned role from the first
|
|
87
|
+
`@<role>` in its `model:` frontmatter, across the shipped `agents/` dir, the
|
|
88
|
+
user dir (`~/.omp/agent/agents/`) and the project dir (`<project>/.omp/agents/`).
|
|
89
|
+
Disabling a role disables the agents that pin it (they are kept in
|
|
90
|
+
`task.disabledAgents`); enabling re-enables them. This sync is **not**
|
|
91
|
+
day-gated — it runs on every session start, so enabling a role takes effect on
|
|
92
|
+
the next session. An agent pinning a role the plugin does not know is left
|
|
93
|
+
alone, and other `task.disabledAgents` entries and their order are preserved.
|
|
94
|
+
|
|
95
|
+
## Settings
|
|
96
|
+
|
|
97
|
+
Settings live in `~/.omp/plugins/omp-plugins.lock.json` →
|
|
98
|
+
`settings["omp-llm-role"]`, deep-merged over the shipped defaults. The plugin
|
|
99
|
+
also exposes the whole shipped surface in omp's **`/settings` → Plugins →
|
|
100
|
+
*omp-llm-role*** tab (booleans as toggles, `thinking` as a picker, `required` as
|
|
101
|
+
a comma-separated string); an edit there takes effect on the next
|
|
102
|
+
`/refresh-roles` (or the next day-gated session start).
|
|
103
|
+
|
|
104
|
+
Global knobs:
|
|
105
|
+
|
|
106
|
+
| Knob | Default | Meaning |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `switchMargin` | `0.02` | Value margin a challenger must beat the incumbent by before switching; `0` = always take the best |
|
|
109
|
+
| `priceSwitchFraction` | `0.5` | Inside the margin, adopt a challenger at least this fraction cheaper (0.5 = half the $/M); `0` disables |
|
|
110
|
+
| `writeFallbackChains` | `true` | Write per-role fallback chains into `config.yml` |
|
|
111
|
+
| `fallbackChainDepth` | `2` | Fallback models per role chain |
|
|
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) |
|
|
114
|
+
|
|
115
|
+
Per-role knobs (`roles.<name>.*`):
|
|
116
|
+
|
|
117
|
+
| Knob | Default | Meaning |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `enabled` | `true` (`designer`: `false`) | Opt a role in/out; `false` drops it from the resolved set and deletes its `modelRoles.<role>` line |
|
|
120
|
+
| `description` | shipped one-liner | Role description |
|
|
121
|
+
| `weights` | shipped set | Metric weights; must sum to 1.0 ± 0.01 |
|
|
122
|
+
| `required` | shipped set | Eligibility gate (metrics a model must have); independent of weights |
|
|
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) |
|
|
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) |
|
|
131
|
+
| `lambda` | derived | Explicit λ ($ per quality point) override |
|
|
132
|
+
| `locked` | `false` | Leave the role alone: rank it but never rewrite its selector or chain (explorer toggle) |
|
|
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
|
+
|
|
215
|
+
**Typed writes.** `omp plugin config set <plugin> <key> <value>` stores every
|
|
216
|
+
value as a **string**, which the validator rejects for numbers/arrays/booleans,
|
|
217
|
+
so it is only usable for string-valued keys. Write typed role defs with
|
|
218
|
+
`node src/cli/create-role.ts` or the explorer's **Export** (both validate →
|
|
219
|
+
merge → backup → atomic write); for a whole-object override use the `config` escape
|
|
220
|
+
hatch, `omp plugin config set omp-llm-role config '<json>'` (JSON-parsed by the
|
|
221
|
+
plugin). The lock file stores **flat dotted keys** (`roles.slow.weights.code`);
|
|
222
|
+
the reader also accepts nested objects, so an existing nested lock file keeps
|
|
223
|
+
working and normalizes on the next role write. Invalid settings abort the run
|
|
224
|
+
with the offending role/key and no write.
|
|
225
|
+
|
|
226
|
+
## Commands
|
|
227
|
+
|
|
228
|
+
```sh
|
|
229
|
+
# in-session (omp)
|
|
230
|
+
/refresh-roles # force a run now
|
|
231
|
+
/explore-roles [--port N] [--no-open] # interactive ranking UI
|
|
232
|
+
/create-agent <request> [options] # agent + role + wiring (free text or flags)
|
|
233
|
+
/create-agent --name <n> --purpose <text> [options] # agent + role + wiring
|
|
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
|
|
236
|
+
|
|
237
|
+
# CLI
|
|
238
|
+
node src/cli/llm-role-rank.ts [--top N] [--json] [--out FILE] [--refresh] [--all] [--url URL]
|
|
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]
|
|
240
|
+
node --test tests/
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- `/refresh-roles` — force the daily pipeline now (same path as the session-start
|
|
244
|
+
run).
|
|
245
|
+
- `/explore-roles` — boot the explorer in-process (see below); `--port` picks a
|
|
246
|
+
preferred port (default 5177, a busy port falls back to a free one) and
|
|
247
|
+
`--no-open` skips the browser.
|
|
248
|
+
- `/create-agent` — create an agent **and** its role and wire them in one
|
|
249
|
+
command (see *Add a specialist agent*).
|
|
250
|
+
- `/remove-agent` — the inverse of `/create-agent`: deletes the agent `.md` and
|
|
251
|
+
its role, then runs the updater in-process so `modelRoles.<n>` is dropped;
|
|
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*).
|
|
256
|
+
- `src/cli/llm-role-rank.ts` — markdown report to stdout (per-role tables + suggested
|
|
257
|
+
`modelRoles`). `--top N` rows per role (default 10); `--json` machine payload;
|
|
258
|
+
`--out FILE` write instead of stdout; `--refresh` bypass both caches; `--all`
|
|
259
|
+
include opt-in roles (`designer`) — the default ranks the nine built-in roles
|
|
260
|
+
only; `--url` override the llm-stats page URL.
|
|
261
|
+
- `src/cli/create-role.ts` — add/update one role in the settings lock file (validated,
|
|
262
|
+
backup + atomic write); `--dry-run` validates without writing, `--json` prints
|
|
263
|
+
the payload.
|
|
264
|
+
- `node --test tests/` — the unit suite.
|
|
265
|
+
|
|
266
|
+
## Add a specialist agent
|
|
267
|
+
|
|
268
|
+
One command does all of it. Describe the agent in plain language — the architect
|
|
269
|
+
names it, any benchmark you mention is folded into the weights, and any
|
|
270
|
+
benchmark link you point at is resolved:
|
|
271
|
+
|
|
272
|
+
```sh
|
|
273
|
+
/create-agent i want an agent for writing. use the writing related Benchmarks in the Leaderboard https://llm-stats.com/leaderboards/best-ai-for-writing
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
A link to any llm-stats benchmark page (`llm-stats.com/benchmarks/<id>`) resolves
|
|
277
|
+
to that benchmark; a link to a provider the plugin has never seen is authored
|
|
278
|
+
into a source declaration — the plugin fetches it, proposes a declaration, shows
|
|
279
|
+
the coverage and leader, and asks you to confirm before it is saved. A named
|
|
280
|
+
benchmark takes a decisive share of the weights, so it actually drives the
|
|
281
|
+
ranking. You do not have to name every benchmark: when you give a purpose, the
|
|
282
|
+
command also discovers the llm-stats catalog benchmarks relevant to it (a typed
|
|
283
|
+
judgment through your configured `judge` role) and folds them in — so a writing
|
|
284
|
+
agent is ranked on WritingBench, Creative Writing v3, COLLIE and the other
|
|
285
|
+
writing benchmarks, not just the one you named. Discovery is non-fatal and
|
|
286
|
+
skippable with `--no-discover`; an explicit `--benchmarks` list wins over it.
|
|
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
|
+
|
|
307
|
+
The flag form is equivalent and gives you the name explicitly:
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
/create-agent --name review --purpose "review pull requests for correctness and security"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
It fits the weights to the purpose from the archetype table
|
|
314
|
+
(`--list-archetypes` prints the ten sets), authors
|
|
315
|
+
`~/.omp/agent/agents/review.md` with `model: "@review, @default"`, writes the
|
|
316
|
+
validated role into the settings lock file, and runs the updater in-process so
|
|
317
|
+
`modelRoles.review` lands in `config.yml` — nothing to remember. Options:
|
|
318
|
+
`--archetype <id>`, `--weights m=w,...`, `--required`, `--thinking`, `--tools`,
|
|
319
|
+
`--benchmarks m,...` (extra metrics to fold in; `--list-benchmarks` prints the
|
|
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`,
|
|
323
|
+
`--body`/`--body-file`, `--force` (overwrite an existing agent file), `--yes`
|
|
324
|
+
(accept a proposed benchmark source without prompting), `--no-discover` (skip
|
|
325
|
+
catalog discovery), `--dry-run`, `--json`. In
|
|
326
|
+
the free-text form the flags still apply after the request, e.g.
|
|
327
|
+
`/create-agent i want an agent for writing --dry-run`. There is no
|
|
328
|
+
out-of-session CLI for this path — run `/create-agent` in an omp session.
|
|
329
|
+
|
|
330
|
+
By hand, the same three artifacts:
|
|
331
|
+
|
|
332
|
+
1. Define the role with the plugin's validated write path:
|
|
333
|
+
```sh
|
|
334
|
+
node ~/.omp/plugins/node_modules/omp-llm-role/src/cli/create-role.ts --name review \
|
|
335
|
+
--weights reasoning=0.30,general=0.24,code=0.20,agents=0.10,price=0.10,throughput=0.06 \
|
|
336
|
+
--required general,price,throughput --thinking high \
|
|
337
|
+
--description "Code review: agentic depth with cost awareness"
|
|
338
|
+
```
|
|
339
|
+
2. Author the agent that pins `model: "@review, @default"` (the routing), or pin
|
|
340
|
+
an existing agent through `task: { agentModelOverrides: { <agent>: "@review" } }`.
|
|
341
|
+
3. Wire it: `/refresh-roles` in a session.
|
|
342
|
+
|
|
343
|
+
The shipped skill `omp-llm-role-create-agent` is the hand-driven equivalent, for
|
|
344
|
+
when the body needs real authoring rather than the archetype scaffold.
|
|
345
|
+
|
|
346
|
+
## Remove a specialist agent
|
|
347
|
+
|
|
348
|
+
One command deletes an agent **and** its role:
|
|
349
|
+
|
|
350
|
+
```sh
|
|
351
|
+
/remove-agent --name review
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
It deletes the agent `.md` (user and/or project scope) and the role's lock-file
|
|
355
|
+
keys, then runs the updater in-process so `modelRoles.review` is dropped from
|
|
356
|
+
`config.yml` — nothing to remember. Options: `--scope user|project`,
|
|
357
|
+
`--lock PATH`, `--yes` (skip the in-session confirmation), `--dry-run`. Shipped
|
|
358
|
+
default roles (e.g. `designer`) cannot be deleted — the message points at the
|
|
359
|
+
explorer's "Reset to shipped default". A role created any way (the explorer's
|
|
360
|
+
"+ new role", `create-role.ts`, or `/create-agent`) is removable.
|
|
361
|
+
|
|
362
|
+
By hand, the same two artifacts:
|
|
363
|
+
|
|
364
|
+
1. Delete the role from the settings lock file (the explorer's Export, or edit
|
|
365
|
+
the lock file directly).
|
|
366
|
+
2. Delete the agent file: `rm ~/.omp/agent/agents/review.md`.
|
|
367
|
+
3. Wire it: `/refresh-roles` in a session.
|
|
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
|
+
|
|
395
|
+
## Explorer
|
|
396
|
+
|
|
397
|
+
`/explore-roles` in omp boots a loopback-only web UI
|
|
398
|
+
(`http://127.0.0.1:5177`) that answers "why is model X at rank 7 for
|
|
399
|
+
`@slow`?" and "what happens if I care more about price than agents?" without
|
|
400
|
+
editing source and re-running the CLI.
|
|
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.
|
|
411
|
+
- **Role tabs** — every role the plugin knows: the nine omp built-in roles, the
|
|
412
|
+
shipped opt-in `designer`, and any role present only in the lock file. Each
|
|
413
|
+
tab is tinted by provenance — `default` (omp built-in), `plugin` (shipped but
|
|
414
|
+
opt-in), `user` (lock-file only) — and a disabled role is muted and dashed.
|
|
415
|
+
Selecting a disabled role still ranks its models.
|
|
416
|
+
- **Rank table** — every eligible model for the selected role, with `value`,
|
|
417
|
+
`q`, `$/M` (effective, thinking-adjusted), `tok/s`, `ctx`, a `★` Pareto
|
|
418
|
+
marker, and a `Δ` column showing the rank delta against the role's effective
|
|
419
|
+
def (what the plugin does today).
|
|
420
|
+
- **Explain panel** — click a row for the full decomposition: each weighted
|
|
421
|
+
metric's raw value → cardinal transform → renormalized weight → contribution,
|
|
422
|
+
the cost block (`λ`, effective vs billed price, `penalty = λ·price`,
|
|
423
|
+
`value = q − penalty`), "why not higher" (the value gap to the model above
|
|
424
|
+
plus the per-metric target that would close it), and the models that dominate
|
|
425
|
+
it on (price, q).
|
|
426
|
+
- **Weight editor** — edit the role's description, weights, `required` set,
|
|
427
|
+
`filters.image`, `thinking` level, and `λ` override live; the table re-ranks
|
|
428
|
+
on every change. Weights are edited freely (no implicit rescaling): the `Σ`
|
|
429
|
+
readout turns red until `|Σ − 1| ≤ 0.01`, and `Normalize` rescales in one
|
|
430
|
+
click.
|
|
431
|
+
- **Enabled toggle** — opts a role in or out. Exporting writes `enabled` into
|
|
432
|
+
the lock file; the next updater run drops a disabled role from the resolved
|
|
433
|
+
set and deletes its `modelRoles.<role>` line, and re-enabling restores it.
|
|
434
|
+
- **Locked toggle** — tells the plugin to leave the role alone: it is still
|
|
435
|
+
ranked and shown, but the updater never rewrites its selector or fallback
|
|
436
|
+
chain and never removes it. Enable/disable still applies.
|
|
437
|
+
- **+ new role** — creates a role from a template
|
|
438
|
+
(`general/code/price/throughput`, Σ 1.0) that you then tune and Export.
|
|
439
|
+
- **Export** — writes the edited roles into the settings lock file atomically
|
|
440
|
+
and with a `.bak-<timestamp>` sibling, touching only the roles you edited. The
|
|
441
|
+
change takes effect on the next `/refresh-roles` **in any running session**
|
|
442
|
+
(the lock file is re-read from disk on every run). `Copy JSON` / `Download
|
|
443
|
+
JSON` emit the same payload for manual use.
|
|
444
|
+
- **Hover explanations** — every column header, metric name, role tab, and
|
|
445
|
+
control carries a tooltip explaining its semantics.
|
|
446
|
+
|
|
447
|
+
The UI never reimplements the ranking math: the numbers on screen are exactly
|
|
448
|
+
the numbers the plugin would use. The server binds `127.0.0.1` only (no auth)
|
|
449
|
+
and serves the SPA with no build step and no external requests.
|
|
450
|
+
|
|
451
|
+
## Availability
|
|
452
|
+
|
|
453
|
+
The plugin keeps only models the OpenRouter key can actually run:
|
|
454
|
+
|
|
455
|
+
- **Tier/budget gate** — paid keys with budget get billed variants, free or
|
|
456
|
+
exhausted keys get `:free` variants, `:batch` never. Every candidate must
|
|
457
|
+
resolve in omp's catalog.
|
|
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.
|
|
475
|
+
|
|
476
|
+
## Caveats
|
|
477
|
+
|
|
478
|
+
- **Sparse metrics.** A missing weighted metric contributes 0 while still
|
|
479
|
+
occupying its share of the denominator, so weight sparse metrics as
|
|
480
|
+
differentiators, never as `required` gates. `website`, `writing` and
|
|
481
|
+
`long_context` are capability-filled (0.195) instead, so absence is not a
|
|
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)).
|
|
486
|
+
- **Provider whitelist.** The account's allowed-providers whitelist blocks many
|
|
487
|
+
models, so the report's #1 and the written selector can differ — judge weights
|
|
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.
|
|
495
|
+
- **Model concentration.** The cheapest pick concentrates on one vendor; the
|
|
496
|
+
fallback chains mitigate it.
|
|
497
|
+
|
|
498
|
+
## Licence
|
|
499
|
+
|
|
500
|
+
MIT — see [LICENSE](LICENSE).
|
|
501
|
+
|
|
502
|
+
## Documentation
|
|
503
|
+
|
|
504
|
+
Maintainer docs (architecture, data sources, scoring, writes/state, explorer
|
|
505
|
+
internals, releasing, agent authoring, testing) live in
|
|
506
|
+
[docs/dev/README.md](docs/dev/README.md); release history is in
|
|
507
|
+
[CHANGELOG.md](CHANGELOG.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: designer
|
|
3
|
+
description: MUST be used for UI/UX design implementation, review, and visual refinement.
|
|
4
|
+
model: "@designer, @default"
|
|
5
|
+
tools: [read, grep, glob, find, edit, write, bash, ast_grep, ast_edit, eval, lsp, web_search, ask, todo, task, wait, checkpoint, rewind]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Implement/review UI designs; edit files, create components, run commands as needed.
|
|
9
|
+
|
|
10
|
+
<strengths>
|
|
11
|
+
- Design intent → working UI code
|
|
12
|
+
- UX issues: unclear states, missing feedback, poor hierarchy
|
|
13
|
+
- Accessibility: contrast, focus states, semantic markup, screen-reader compatibility
|
|
14
|
+
- Visual consistency: spacing, typography, color, component patterns
|
|
15
|
+
- Responsive design and layout structure
|
|
16
|
+
</strengths>
|
|
17
|
+
|
|
18
|
+
<design-system>
|
|
19
|
+
Design system: foundation; UI without one becomes inconsistent. Four phases, in order:
|
|
20
|
+
1. **Token-first analysis (before CSS/JSX/Svelte).** Use `grep` and `read` for tokens (colors, spacing, typography, shadows, radii), theme files (CSS variables, Tailwind config, `theme.ts`), shared primitives (Button, Card, Input, Layout). Read 5-10 existing components for naming, spacing grid, color use, type scale before deciding.
|
|
21
|
+
2. **No coherent system? Build minimal system first.** Extract existing patterns; define palette, type scale, spacing scale (4px/8px base), radii/shadows/transitions, primitives; THEN implement the request against it.
|
|
22
|
+
3. **Compose with, NEVER around, the system.** Colors: tokens/CSS variables, NEVER hardcoded hex; spacing: scale values, NEVER arbitrary px; type: scale steps; components: extend/compose existing primitives, not one-off div soup. Outside-system need: add token first, then use it; NEVER one-off override.
|
|
23
|
+
4. **Verify before done.** Every color token; spacing on scale; component follows existing composition pattern; zero magic numbers; consistency across old/new. Any no → not done.
|
|
24
|
+
</design-system>
|
|
25
|
+
|
|
26
|
+
<procedure>
|
|
27
|
+
## Implementation
|
|
28
|
+
1. Read existing components, tokens, patterns; reuse before inventing.
|
|
29
|
+
2. Identify aesthetic direction: minimal, bold, editorial, etc.
|
|
30
|
+
3. Implement states: loading, empty, error, disabled, hover, focus.
|
|
31
|
+
4. Verify accessibility: contrast, focus rings, semantic HTML.
|
|
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.
|
|
34
|
+
|
|
35
|
+
## Review
|
|
36
|
+
1. Read reviewed files.
|
|
37
|
+
2. Check UX issues, accessibility gaps, visual inconsistencies.
|
|
38
|
+
3. Cite file, line, concrete issue; no vague feedback.
|
|
39
|
+
4. Suggest specific fixes; code when applicable.
|
|
40
|
+
</procedure>
|
|
41
|
+
|
|
42
|
+
<directives>
|
|
43
|
+
- SHOULD prefer editing existing files to creating new ones.
|
|
44
|
+
- Changes MUST be minimal and match existing code style.
|
|
45
|
+
- NEVER create documentation files (`*.md`) unless explicitly requested.
|
|
46
|
+
</directives>
|
|
47
|
+
|
|
48
|
+
<avoid>
|
|
49
|
+
## AI Slop Patterns
|
|
50
|
+
- Glassmorphism everywhere: decorative blur, glass cards, glow borders
|
|
51
|
+
- Cyan-on-dark with purple gradients: 2024 AI palette
|
|
52
|
+
- Gradient text on metrics/headings: meaningless decoration
|
|
53
|
+
- Identical card grids: repeated icon + heading + text
|
|
54
|
+
- Nested cards: visual noise; flattened hierarchy
|
|
55
|
+
- Large rounded-corner icons above every heading: templated, no value
|
|
56
|
+
- Hero metric layouts: big number, small label, gradient accent; overused
|
|
57
|
+
- Same spacing everywhere: no rhythm; monotony
|
|
58
|
+
- Center-aligning everything: left alignment with asymmetry feels more designed
|
|
59
|
+
- Modals for everything: lazy, rarely best
|
|
60
|
+
- Overused fonts when the project has no type system: Inter, Roboto, Open Sans, system defaults.
|
|
61
|
+
- Pure black (`#000`) or white (`#fff`): ALWAYS tint neutrals
|
|
62
|
+
- Gray text on colored backgrounds: use a background shade instead
|
|
63
|
+
- Bounce/elastic easing: dated, tacky; use exponential easing (`ease-out-quart`/`expo`)
|
|
64
|
+
|
|
65
|
+
## UX Anti-Patterns
|
|
66
|
+
- Missing loading, empty, error states
|
|
67
|
+
- Redundant information: heading restates intro text
|
|
68
|
+
- Every button primary: hierarchy matters
|
|
69
|
+
- Empty states saying "nothing here" rather than guiding users
|
|
70
|
+
</avoid>
|
|
71
|
+
|
|
72
|
+
<critical>
|
|
73
|
+
Every interface: "how was this made?", not "which AI made this?"
|
|
74
|
+
MUST commit to clear aesthetic direction; execute precisely.
|
|
75
|
+
MUST continue until implementation complete.
|
|
76
|
+
</critical>
|