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.
- package/README.md +179 -11
- package/agents/designer.md +4 -3
- package/docs/dev/README.md +44 -7
- package/docs/dev/agent-authoring.md +59 -1
- package/docs/dev/architecture.md +84 -27
- package/docs/dev/ci.md +152 -0
- package/docs/dev/current-state.md +327 -24
- package/docs/dev/data-sources.md +202 -38
- package/docs/dev/explorer.md +157 -46
- package/docs/dev/quirks.md +45 -4
- package/docs/dev/releasing.md +129 -17
- package/docs/dev/scoring.md +789 -28
- package/docs/dev/testing.md +206 -0
- package/docs/dev/writes-and-state.md +176 -28
- package/docs/llm-role-rankings.md +141 -140
- package/package.json +754 -80
- package/skills/omp-llm-role-create-agent/SKILL.md +7 -1
- package/src/agent-architect.ts +148 -19
- package/src/agent-create.ts +800 -40
- package/src/agent-file.ts +2 -5
- package/src/architect-provenance.ts +16 -0
- package/src/availability.ts +104 -6
- package/src/benchmark-author.ts +9 -4
- package/src/benchmark-sources.ts +283 -18
- package/src/cli/create-role.ts +29 -1
- package/src/cli/llm-role-rank.ts +40 -1
- package/src/config-edit.ts +207 -80
- package/src/engine.ts +409 -46
- package/src/error-report.ts +562 -0
- package/src/explorer/boot.ts +36 -24
- package/src/explorer/explain.ts +110 -13
- package/src/explorer/scopes.ts +110 -0
- package/src/explorer/server.ts +112 -32
- package/src/extension.ts +407 -34
- package/src/features.ts +103 -0
- package/src/project-registry.ts +65 -0
- package/src/project-setup.ts +382 -0
- package/src/prompts/agent-creation-architect.md +2 -0
- package/src/prompts/project-profile-architect.md +33 -0
- package/src/prompts/project-profile-user.md +8 -0
- package/src/role-archetypes.ts +12 -2
- package/src/role-settings.ts +22 -0
- package/src/settings.ts +205 -24
- package/src/state.ts +1 -1
- package/src/updater.ts +150 -31
- package/web/app.js +411 -12
- package/web/index.html +1 -0
- package/web/style.css +44 -1
- package/docs/dev/spec.md +0 -633
package/docs/dev/architecture.md
CHANGED
|
@@ -4,7 +4,7 @@ Maintainer map of the `omp-llm-role` plugin: how the modules fit together, the
|
|
|
4
4
|
runtime contract they must satisfy, and the invariants a change must not break.
|
|
5
5
|
|
|
6
6
|
- User-facing install/usage: [`../../README.md`](../../README.md).
|
|
7
|
-
-
|
|
7
|
+
- How it is verified: [`testing.md`](testing.md).
|
|
8
8
|
- Where the numbers come from: [`data-sources.md`](data-sources.md).
|
|
9
9
|
|
|
10
10
|
## Module map
|
|
@@ -25,32 +25,34 @@ the extension's `/refresh-roles`, `/explore-roles` and `/create-agent`.
|
|
|
25
25
|
|
|
26
26
|
| File | Role / key exports |
|
|
27
27
|
|---|---|
|
|
28
|
-
| `src/engine.ts` | Ranking engine shared by CLI and plugin. Fetch/cache chain per source, cardinal transforms, value scoring. Exports `loadRankData`, `computeRankings`, `buildModels`, `rankRole`, `paretoFrontier`, `cardinalMetric`, `roleLambda`, `thinkingPriceFactor`, `CAPABILITY_FILL`, `SUFFIX_LEVELS`, `META_LEVELS`, `THINKING_TOKEN_OVERHEAD`, `parseFindData`, `parseModelPage`, `buildOpenRouterEnrichment`, `applyOpenRouterData`, `applyDesignPercentiles`, `applyWritingScores`, `extractFlight`, `extractJsonArray`, the cache readers/writers, and the `Model`/`RoleDef`/`Ranked`/`RankData` types. Re-exports `normalizeDesignId` and `parseWritingEvidence` from the benchmark-source registry. |
|
|
29
|
-
| `src/benchmark-sources.ts` | The benchmark-source registry: the single source of truth for which sources exist, what metric each feeds, and how to fetch/parse/join each. Exports `BENCHMARK_SOURCES` (the shipped static sources), `resolveBenchmarkSource`, `declaredSourceForLink`, `sourceForMetric`, `parseBenchmarkPayload`, `loadBenchmarkScores`, `applyBenchmarkScores`, `joinBenchmarkScores`, `normalizeMetricKey`, `externalMetricKey`, `normalizeDesignId`, `parseWritingEvidence`, `parseLlmStatsBenchmark`, `parseBenchmarkCatalog`, `loadBenchmarkCatalog`, `catalogMetric`, `SHIPPED_CATALOG_METRICS`, the declarative-source surface (`SourceDeclaration`, `validateDeclaration`, `parseSourceDeclaration`, `loadDeclaredSources`, `saveDeclaredSource`, `declaredSourcesPath`, `declarationToSource`, `executeDeclaration`, `llmStatsBenchmarkDeclaration`, `genericBenchmarkSource`, `dryRunDeclaration`), the fetch helpers (`fetchJson`, `fetchText`), and the `BenchmarkSource`/`JoinRule`/`BenchmarkScores`/`DeclarationDryRun` types. Imports `engine.ts` for types only, so there is no runtime cycle. |
|
|
30
|
-
| `src/settings.ts` | Shipped defaults and the settings merge/validate path. Exports `DEFAULT_ROLES`, `DEFAULT_SETTINGS`, `SHIPPED_AGENTS`, `KNOWN_METRICS`, `isKnownMetric` (a shipped key or an external `<ns>:<local>`), `PLUGIN_SETTINGS_PATH`, `ACTIVATE_DEFAULT_KEY`, `deriveSettingsSchema`, `readPluginSettingsMap`, `resolveSettings`, `roleUniverse`, `deepMergeInto`, and the `ResolvedSettings`/`PluginSettingSchema`/`UniverseEntry`/`RoleKind` types. |
|
|
28
|
+
| `src/engine.ts` | Ranking engine shared by CLI and plugin. Fetch/cache chain per source, cardinal transforms, value scoring. Exports `loadRankData`, `computeRankings`, `buildModels`, `rankRole`, `paretoFrontier`, `cardinalMetric`, `roleLambda`, `thinkingPriceFactor`, `CAPABILITY_FILL`, `SUFFIX_LEVELS`, `META_LEVELS`, `THINKING_TOKEN_OVERHEAD`, `parseFindData`, `parseModelPage`, `narrowEndpointRecord`, `buildOpenRouterEnrichment`, `applyOpenRouterData`, `hasEndpointFilters`, `routePassesEndpointFilters`, `modelPassesEndpointFilters`, `endpointFilterDrops`, `applyDesignPercentiles`, `applyWritingScores`, `extractFlight`, `extractJsonArray`, the cache readers/writers, and the `Model`/`RoleDef`/`Ranked`/`RankData`/`OpenRouterEndpointRecord` types. Re-exports `normalizeDesignId` and `parseWritingEvidence` from the benchmark-source registry. |
|
|
29
|
+
| `src/benchmark-sources.ts` | The benchmark-source registry: the single source of truth for which sources exist, what metric each feeds, and how to fetch/parse/join each. Exports `BENCHMARK_SOURCES` (the shipped static sources), `resolveBenchmarkSource`, `declaredSourceForLink`, `sourceForMetric`, `parseBenchmarkPayload`, `parseBenchmarkPayloadMeta`, `loadBenchmarkScores`, `applyBenchmarkScores`, `joinBenchmarkScores`, `normalizeMetricKey`, `externalMetricKey`, `normalizeDesignId`, `parseWritingEvidence`, `parseLlmStatsBenchmark`, `parseBenchmarkCatalog`, `loadBenchmarkCatalog`, `cachedSourceInfo`, `catalogMetric`, `SHIPPED_CATALOG_METRICS`, the declarative-source surface (`SourceDeclaration`, `validateDeclaration`, `parseSourceDeclaration`, `loadDeclaredSources`, `saveDeclaredSource`, `declaredSourcesPath`, `declarationToSource`, `executeDeclaration`, `llmStatsBenchmarkDeclaration`, `genericBenchmarkSource`, `dryRunDeclaration`), the fetch helpers (`fetchJson`, `fetchText`), and the `BenchmarkSource`/`JoinRule`/`BenchmarkScores`/`BenchmarkPayloadMeta`/`FocusSourceInfo`/`BenchmarkCatalogEntry`/`DeclarationDryRun` types. Imports `engine.ts` for types only, so there is no runtime cycle. |
|
|
30
|
+
| `src/settings.ts` | Shipped defaults and the settings merge/validate path. Exports `DEFAULT_ROLES`, `DEFAULT_SETTINGS`, `SHIPPED_AGENTS`, `KNOWN_METRICS`, `isKnownMetric` (a shipped key or an external `<ns>:<local>`), `PLUGIN_SETTINGS_PATH`, `ACTIVATE_DEFAULT_KEY`, `deriveSettingsSchema`, `readPluginSettingsMap`, `resolveSettings`, `roleUniverse`, `deepMergeInto`, `findProjectAnchor`, `projectLockPath`, and the `ResolvedSettings`/`PluginSettingSchema`/`UniverseEntry`/`RoleKind` types. `readPluginSettingsMap` merges the project lock file (`projectLockPath(cwd)` = `<cwd>/.omp/plugins/omp-plugins.lock.json`, omp's project dir with **no walk-up**) over the user-level one; `findProjectAnchor` (the walk-up `.omp`/`.git` anchor) is kept only for the `/project-roles` divergence warning. |
|
|
31
31
|
| `src/agent-pins.ts` | Agent → pinned-role derivation. Exports `parseAgentPin` (first `@<role>` in the `model:` frontmatter) and `discoverAgentPins` (scans the shipped, user and project agent dirs; project > user > plugin). Drives the `task.disabledAgents` sync. |
|
|
32
|
-
| `src/availability.ts` | Key tier gate, catalog filter, variant resolution, provider-allowlist probe. Exports `fetchKeyMeta`, `tierGate`, `filterCatalog`, `rankingIdOf`, `currentRankingId`, `resolveVariant`, `enrichThinkingLevels`, `probeModel`, `catalogFromOmpModelsJson`, `THINKING_LEVELS`, and the `CatalogEntry`/`KeyMeta`/`Tier`/`ProbeVerdict` types. |
|
|
32
|
+
| `src/availability.ts` | Key tier gate, catalog filter, variant resolution, provider-allowlist probe, and the keyed-catalog availability primitive. The keyed source is the Bearer-authenticated `GET /api/v1/models` (the account setting "Filter the model catalog for API keys" makes it a per-key allowlist; set membership is the only signal), shared by the explorer and the updater's companion fast path. Exports `fetchKeyMeta`, `tierGate`, `filterCatalog`, `rankingIdOf`, `currentRankingId`, `resolveVariant`, `enrichThinkingLevels`, `probeModel`, `computeKeyAvailability`, `fetchKeyAvailability`, `catalogFromOmpModelsJson`, `THINKING_LEVELS`, and the `CatalogEntry`/`KeyMeta`/`Tier`/`ProbeVerdict`/`KeyAvailability` types. |
|
|
33
33
|
| `src/config-edit.ts` | Surgical line-oriented YAML patch for `modelRoles` + `retry.fallbackChains` + `task.disabledAgents`, plus the atomic writer. Exports `parseConfig`, `patchConfig`, `writeConfigAtomic`, `ConfigEditError`, and the `ConfigPatch` type. No runtime YAML dependency. |
|
|
34
34
|
| `src/state.ts` | State/history/lock files under the agent dir, and agent-dir resolution. Exports `agentDir`, `loadState`, `saveState`, `appendHistory`, `acquireLock`, `releaseLock`, `freshState`, and the `PluginState` type (which carries `managedDisabledAgents` — the `task.disabledAgents` names the plugin added). |
|
|
35
|
-
| `src/
|
|
36
|
-
| `src/
|
|
35
|
+
| `src/error-report.ts` | Opt-in reporting of **unexpected** plugin errors as GitHub issues on `Gott50/omp-llm-role` (the `errorReporting` setting). The plugin's only GitHub client and the only home for a fingerprint, a redaction rule, the agent-dir ledger (`ERROR_REPORT_LEDGER_FILE`) and the issue body. **Entry point:** `reportUnexpectedError(input, deps)` — the feature's one test seam (per-call input + fully injected `deps`; tests supply a fake `fetch`, clock, env, dir and token resolver). Also exports `defaultErrorReportDeps()` (production deps: resolved settings, global `fetch`, real clock, `agentDir`, the shipped manifest, `gh auth token`, the OS opener) and `resetErrorReportGuard()` (the in-process dedupe guard's test hook). Never throws, never rejects; a no-op when `errorReporting` is `off`. |
|
|
36
|
+
| `src/updater.ts` | Orchestration: rank → tier gate → probe → hysteresis → chains → agent-disable sync → config write. Exports `runUpdater`, the `Deps`/`Decision`/`RunResult`/`Trigger`/`DecisionReason` types. Resolves the project scope (`<cwd>/.omp` when it carries an `omp-llm-role` settings entry) and scopes the config/state/history/lock to it. `agentDisablePatch` also removes a previously-managed `task.disabledAgents` name whose agent file is gone. `RunResult.defect` is set on the exception-abort paths only (a `ConfigEditError`/self-check mismatch and the catch-all throw); the enumerated environment aborts leave it unset. |
|
|
37
|
+
| `src/extension.ts` | omp extension entry (default export). Registers `session_start` (awaited day-gated run), `/refresh-roles`, `/explore-roles` (in-process explorer), `/create-agent` (architect + benchmark-link resolution + catalog discovery), `/remove-agent`, `/project-roles` (profile architect + `setupProject` + in-process updater), and `session_shutdown`. Owns `extDeps` (the extension's `Deps`) and the live-session model hook. Every unexpected command-handler throw and every `runUpdater` defect is routed through `src/error-report.ts`: `reportCommandError` for the six command-handler catch sites and `reportDefect` for a `runUpdater` result whose `defect` is set — each keeps the pre-existing one-line warning and adds the reporting module's own lines. |
|
|
37
38
|
| `src/guards.ts` | The package's one type guard: `isRecord`. |
|
|
38
39
|
| `src/role-settings.ts` | The one validated role write path (validate → merge → backup → atomic write) and its removal half. Exports `validateRole`, `mergeExport`, `writeRoleSettings`, `mergeRemove`, `removeRoleSettings`, `timestamp`. Shared by the explorer's Export, `src/cli/create-role.ts`, `/create-agent` and `/remove-agent`. |
|
|
39
40
|
| `src/role-archetypes.ts` | Purpose → weight archetype table (10 sets) + keyword fitting. Exports `ARCHETYPES`, `FALLBACK_ARCHETYPE`, `fitArchetype`, `archetypeById`, and the `Archetype`/`ArchetypeMatch` types. |
|
|
40
41
|
| `src/agent-file.ts` | Agent `.md` rendering/placement. Exports `renderAgentFile`, `writeAgentFile`, `removeAgentFile`, `isReadOnlyTools`, `userAgentsDir`, `projectAgentsDir`, `READ_ONLY_TOOLS`, `RESERVED_AGENT_NAMES`, `AGENT_NAME_RE`, and the `AgentFileSpec`/`AgentWriteResult`/`AgentRemoveResult` types. |
|
|
41
|
-
| `src/agent-architect.ts` | omp's agent-creation architect
|
|
42
|
-
| `src/benchmark-author.ts` | Benchmark-source authoring for an unknown link: fetch the link, run an in-process architect (`src/prompts/benchmark-source-architect.md`) to propose a declarative source spec, and validate it.
|
|
42
|
+
| `src/agent-architect.ts` | omp's in-process architects: the agent-creation architect (`/create-agent`, `/agents` hub) and the project-profile architect (`/project-roles`). Exports `generateAgentSpec`, `generateProjectProfile`, `parseAgentSpec`, `parseProjectProfile`, `extractJsonObject`, `extractAssistantText`, and the `ArchitectOptions`/`ProjectArchitectOptions` types. Imports `@oh-my-pi/pi-coding-agent` **dynamically** (inside the runner), so the module is importable under plain Node. |
|
|
43
|
+
| `src/benchmark-author.ts` | Benchmark-source authoring for an unknown link: fetch the link, run an in-process architect (`src/prompts/benchmark-source-architect.md`) to propose a declarative source spec, and validate it. Imports `@oh-my-pi/pi-coding-agent` **dynamically** (inside the function). Exports `authorBenchmarkSource`, `parseSourceDeclarationJson`, and the `BenchmarkAuthorOptions` type. |
|
|
43
44
|
| `src/benchmark-discovery.ts` | Judge-backed benchmark relevance for `/create-agent` discovery: one `noul` (yes/no probability) question per catalog candidate, batched into a single judgment through omp's configured `judge` role (TypeSafe jev or its fallback chain). Extension-only (imports `@oh-my-pi/pi-coding-agent` and its `./judgment` subpath — the deeper `./judgment/standalone` path does not resolve under omp's SDK injection). The SDK is imported dynamically so a resolution failure degrades to "no discovery" instead of failing the whole extension load. Exports `judgeBenchmarkRelevance`. |
|
|
44
|
-
| `src/agent-create.ts` | `/create-agent` core: purpose → archetype → validated role → agent `.md`, plus the shared input parser and report formatter both hosts use. Exports `createAgent`, `parseCreateAgentInput` (flag form or free text), `parseCreateAgentArgs`, `tokenizeArgs`, `extractBenchmarks`, `extractBenchmarkLinks`, `applyFocusBenchmarks`, `discoverBenchmarks`, `formatBenchmarks`, `formatArchetypes`, `formatCreateAgentReport`, `CREATE_AGENT_USAGE`, and the request/result types. |
|
|
45
|
+
| `src/agent-create.ts` | `/create-agent` core: purpose → archetype → validated role → agent `.md`, plus the shared input parser and report formatter both hosts use. Exports `createAgent`, `resolveRole`, `parseCreateAgentInput` (flag form or free text), `parseCreateAgentArgs`, `tokenizeArgs`, `extractBenchmarks`, `extractBenchmarkLinks`, `applyFocusBenchmarks`, `assessFocusMetric`, `belowBarReason`, `focusCoverageOk`, `countMetricCoverage`, `discoverBenchmarks`, `checkWeightMath`, `formatBenchmarks`, `formatArchetypes`, `formatCreateAgentReport`, `CREATE_AGENT_USAGE`, and the request/result types (incl. `FocusMetricAssessment`/`FocusCoverageEntry`/`FocusAssessor`). |
|
|
45
46
|
| `src/agent-remove.ts` | `/remove-agent` core: delete an agent `.md` and its role, plus the shared flag parser and report formatter. Exports `removeAgent`, `parseRemoveAgentArgs`, `formatRemoveAgentReport`, `REMOVE_AGENT_USAGE`, and the request/result types. Refuses shipped default roles. |
|
|
47
|
+
| `src/project-setup.ts` | `/project-roles` core: a discovered `ProjectProfile` → a project-scoped role set (kept shipped roles, dropped ones, new fitted roles) written to the project plugin settings lock file, plus the project agents for the new roles. All-or-nothing (every check — names, metrics, weight math, the `--force` gate, agent-file collisions — before any write), `--dry-run`. Exports `setupProject`, `parseProjectRolesArgs`, `parseRolesSpec`, `applyProfileOverrides`, `formatProjectRolesReport`, `PROJECT_ROLES_USAGE`, and the `ProjectProfile`/`ProposedRole`/`SetupProjectOpts`/`SetupProjectResult` types (the result carries an `archetypes` map). The plan report prints the capability flags each new role will author (`features: <id>=<on>`). Dual-runtime safe (no SDK import); the extension command does the LLM work and calls it. |
|
|
46
48
|
|
|
47
49
|
### `src/explorer/`
|
|
48
50
|
|
|
49
51
|
| File | Role / key exports |
|
|
50
52
|
|---|---|
|
|
51
53
|
| `src/explorer/boot.ts` | Shared explorer launcher: bind/port fallback, lock-file roles, browser open, close. Exports `startExplorer`, `EXPLORER_DEFAULT_PORT`, and the `ExplorerHandle`/`ExplorerBootOpts` types. Used by `/explore-roles`. |
|
|
52
|
-
| `src/explorer/server.ts` | Zero-dependency HTTP surface (static SPA + JSON API). Exports `createExplorerServer` and the `ExplorerOpts` type. Endpoints: `GET /api/bootstrap
|
|
53
|
-
| `src/explorer/explain.ts` | Pure explanation layer: rank rows with baseline deltas, per-model decomposition, inverse-cardinal targets. Exports `rankRows`, `explainModel`, `inverseCardinal`, `METRIC_META`, `metricMeta`, `metricMetaFor`, `weightableMetrics`, and the `RankRow`/`Explanation`/`Contribution`/`Closing`/`MetricMeta` types. |
|
|
54
|
+
| `src/explorer/server.ts` | Zero-dependency HTTP surface (static SPA + JSON API). Exports `createExplorerServer` and the `ExplorerOpts` type. Endpoints: `GET /api/bootstrap` (carries `focusAssessments`), `POST /api/rank`, `POST /api/explain`, `POST /api/export`, `POST /api/refresh`. |
|
|
55
|
+
| `src/explorer/explain.ts` | Pure explanation layer: rank rows with baseline deltas, per-model decomposition, inverse-cardinal targets, and the nine-axis focus assessment. Exports `rankRows`, `explainModel`, `inverseCardinal`, `focusMetricsOf`, `focusAssessments`, `METRIC_META`, `metricMeta`, `metricMetaFor`, `weightableMetrics`, and the `RankRow`/`Explanation`/`Contribution`/`Closing`/`MetricMeta` types. |
|
|
54
56
|
|
|
55
57
|
### Assets and non-code
|
|
56
58
|
|
|
@@ -58,6 +60,7 @@ the extension's `/refresh-roles`, `/explore-roles` and `/create-agent`.
|
|
|
58
60
|
|---|---|
|
|
59
61
|
| `src/prompts/agent-creation-architect.md`, `src/prompts/agent-creation-user.md` | omp's architect prompts, shipped verbatim; read by `src/agent-architect.ts` via `new URL("./prompts/…", import.meta.url)`. |
|
|
60
62
|
| `src/prompts/benchmark-source-architect.md`, `src/prompts/benchmark-source-user.md` | The benchmark-source architect prompts; read by `src/benchmark-author.ts`. |
|
|
63
|
+
| `src/prompts/project-profile-architect.md`, `src/prompts/project-profile-user.md` | The `/project-roles` profile-discovery architect prompts; read by `src/agent-architect.ts`. |
|
|
61
64
|
| `web/index.html`, `web/app.js`, `web/style.css` | Explorer SPA — no framework, no build step, no external requests. Served by `src/explorer/server.ts`. |
|
|
62
65
|
| `agents/designer.md` | The shipped `designer` subagent, discovered from the plugin's extension root (`<ext>/agents/*.md`). Opt-in: kept in `task.disabledAgents` until `roles.designer.enabled=true`. |
|
|
63
66
|
| `skills/omp-llm-role-create-agent/SKILL.md` | Shipped skill, discovered from the plugin's `skills/` root. Hand-driven equivalent of `/create-agent`. |
|
|
@@ -70,20 +73,35 @@ the extension's `/refresh-roles`, `/explore-roles` and `/create-agent`.
|
|
|
70
73
|
| `skills-lock.json` | Lock for the local `.agents/skills/` collection. |
|
|
71
74
|
| `.agents/skills/` | Local dev-skill collection (Matt Pocock skills); not part of the plugin's shipped surface. |
|
|
72
75
|
| `docs/agents/domain.md`, `docs/agents/issue-tracker.md`, `docs/agents/triage-labels.md` | Repo process docs (domain glossary, issue tracker, triage labels). |
|
|
73
|
-
| `docs/dev/
|
|
76
|
+
| `docs/dev/testing.md` | The test-suite seam map and the live checks. |
|
|
74
77
|
| `docs/dev/architecture.md`, `docs/dev/data-sources.md` | This file and the data-source map. |
|
|
75
|
-
| `tests/` | `node --test tests/` fixtures
|
|
78
|
+
| `tests/` | `node --test tests/` fixtures — the seam map is in [`testing.md`](testing.md). |
|
|
76
79
|
| `docs/llm-role-rankings.md` | Generated report (regenerate with `--out`); the README's worked example. |
|
|
77
80
|
| `cache/*.json` | Daily UTC caches (gitignored; see [`data-sources.md`](data-sources.md)). |
|
|
78
81
|
|
|
82
|
+
## Variant resolution (ranking id → selector)
|
|
83
|
+
|
|
84
|
+
A ranking row (an llm-stats bare id) resolves to an `openrouter/*` catalog id by
|
|
85
|
+
matching the id after the last `/` (org prefix — including `~`-prefixed —
|
|
86
|
+
ignored), or after stripping a trailing `-latest`. Among the matches,
|
|
87
|
+
`resolveVariant` emits in order: **exact id** (bare or dated, whatever equals the
|
|
88
|
+
ranking id) → **newest dated id** (max trailing `-MMDD`/date suffix) → **bare
|
|
89
|
+
id** → `~org/…-latest` alias (last resort — a valid selector beats no update).
|
|
90
|
+
Never `:batch`; `:free` only on the free-tier branch of the tier gate. Ties
|
|
91
|
+
break lexicographically. The emitted selector is always `openrouter/<catalogId>`.
|
|
92
|
+
|
|
79
93
|
## Data flow
|
|
80
94
|
|
|
81
95
|
One pipeline, driven by `runUpdater(trigger, deps, opts)` in `src/updater.ts`.
|
|
82
96
|
The omp extension injects the production `Deps`; tests inject fakes.
|
|
83
97
|
|
|
84
98
|
1. **Trigger.** `session_start` (awaited, day-gated) or `/refresh-roles`
|
|
85
|
-
(forced). `runUpdater`
|
|
86
|
-
|
|
99
|
+
(forced). `runUpdater` resolves the **project scope** first: when
|
|
100
|
+
`<cwd>/.omp/plugins/omp-plugins.lock.json` carries an `omp-llm-role` settings
|
|
101
|
+
entry, the config, the settings read, and the state/history/lock are scoped to
|
|
102
|
+
`<cwd>/.omp` (the global `~/.omp/agent/config.yml` is never written in project
|
|
103
|
+
mode); otherwise the global agent dir is used. It then loads `loadState()` and
|
|
104
|
+
the raw settings (`deps.getSettings?.() ?? readPluginSettingsMap()`), then
|
|
87
105
|
`resolveSettings(raw)`; any validation error aborts with no write.
|
|
88
106
|
2. **Agent-disable sync.** `agentDisablePatch` computes the
|
|
89
107
|
`task.disabledAgents` adds/removes from `roleUniverse` + `discoverAgentPins`,
|
|
@@ -101,7 +119,10 @@ The omp extension injects the production `Deps`; tests inject fakes.
|
|
|
101
119
|
`enrichThinkingLevels(rank.models, catalog)`. Per role, candidates are
|
|
102
120
|
`resolveVariant`-resolved and then verified by a bounded `probeModel` walk
|
|
103
121
|
(current selector first, then rank order; budget `PROBE_BUDGET = 12`; verdicts
|
|
104
|
-
cached per run).
|
|
122
|
+
cached per run). Only the narrow no-allowed-providers 404 disqualifies; every
|
|
123
|
+
other failure (5xx, timeout, unknown model) counts as usable and stays in
|
|
124
|
+
omp's runtime-fallback domain. Blocked candidates are excluded and recorded on
|
|
125
|
+
the decision.
|
|
105
126
|
6. **Hysteresis.** `no-current` → adopt; ineligible current → `adopted`; best
|
|
106
127
|
beats current by `switchMargin` → `switched`; inside the margin but
|
|
107
128
|
`cheaperInsideMargin` → `switched-cost`; else `kept-margin` / `kept-eligible`.
|
|
@@ -154,10 +175,16 @@ Rules every module must satisfy:
|
|
|
154
175
|
`undefined` at runtime, not a compile error. Run the script after edits and
|
|
155
176
|
sanity-check stderr match/eligible counts.
|
|
156
177
|
|
|
157
|
-
The one deliberate exception is `src/agent-architect.ts
|
|
158
|
-
`@oh-my-pi/pi-coding-agent` (the
|
|
159
|
-
resolve in the compiled binary)
|
|
160
|
-
|
|
178
|
+
The one deliberate exception is the SDK import in `src/agent-architect.ts` and
|
|
179
|
+
`src/benchmark-author.ts`: they import `@oh-my-pi/pi-coding-agent` (the
|
|
180
|
+
**package root** only — subpath imports do not resolve in the compiled binary)
|
|
181
|
+
**dynamically, inside the runner function**. A static import would be fatal to
|
|
182
|
+
the whole `extension.ts` load (it would kill every plugin command, not just the
|
|
183
|
+
architect); dynamic, the modules — and `src/extension.ts` — stay importable
|
|
184
|
+
under plain Node (the command registration is testable) and a resolution failure
|
|
185
|
+
degrades to the caller's error path. The architect is still **extension-only in
|
|
186
|
+
practice**: it is only called from the extension. The tests import
|
|
187
|
+
`src/agent-create.ts`, which never imports the architect.
|
|
161
188
|
|
|
162
189
|
## Injected `Deps`
|
|
163
190
|
|
|
@@ -193,7 +220,8 @@ before `catalogFromOmpModelsJson`.
|
|
|
193
220
|
(`runUpdater("manual", …, { force: true })`), `/explore-roles` (in-process
|
|
194
221
|
`startExplorer`), `/create-agent` (architect → `createAgent` → in-process
|
|
195
222
|
`runUpdater`), `/remove-agent` (`removeAgent` → in-process `runUpdater`),
|
|
196
|
-
`
|
|
223
|
+
`/project-roles` (profile architect → `setupProject` → in-process
|
|
224
|
+
`runUpdater`), `session_shutdown` (closes the explorer handle).
|
|
197
225
|
- **CLI**: `src/cli/llm-role-rank.ts` (report), `src/cli/create-role.ts`
|
|
198
226
|
(authoring). The updater and explorer have no CLI shim — they run through
|
|
199
227
|
`/refresh-roles` and `/explore-roles`.
|
|
@@ -202,12 +230,18 @@ before `catalogFromOmpModelsJson`.
|
|
|
202
230
|
|
|
203
231
|
`src/engine.ts` is the shared engine; `src/cli/llm-role-rank.ts` is a thin CLI over it.
|
|
204
232
|
|
|
205
|
-
- `loadRankData(opts?: { refresh?; url?; roles? }): Promise<RankData>`
|
|
206
|
-
per-source cache → fetch → stale-cache chain. `opts.roles` names the roles
|
|
207
|
-
caller will rank: a role-exclusive source is fetched only when some role
|
|
233
|
+
- `loadRankData(opts?: { refresh?; url?; roles?; extraMetrics? }): Promise<RankData>`
|
|
234
|
+
— the per-source cache → fetch → stale-cache chain. `opts.roles` names the roles
|
|
235
|
+
the caller will rank: a role-exclusive source is fetched only when some role
|
|
208
236
|
weights its metric — Design Arena (`website`), the writing leaderboard
|
|
209
237
|
(`writing`), and every external metric (a declared source or the generic
|
|
210
|
-
llm-stats benchmark). `
|
|
238
|
+
llm-stats benchmark). `opts.extraMetrics` adds metrics no role weights and also
|
|
239
|
+
forces the `website`/`writing` fetches. `undefined` roles = fetch every declared
|
|
240
|
+
source. The explorer passes the resolved roles **plus** the shipped keys
|
|
241
|
+
(`Object.keys(KNOWN_METRICS)`), so its dataset is a superset of any def the UI
|
|
242
|
+
can rank — without it a role weighting an undeclared generic benchmark
|
|
243
|
+
(`bench:<id>`) would rank on a dataset missing that metric and disagree with the
|
|
244
|
+
updater.
|
|
211
245
|
- `src/benchmark-sources.ts` is the registry: `resolveBenchmarkSource(link)` maps
|
|
212
246
|
a link to a source, `sourceForMetric(metric)` maps a metric back, and
|
|
213
247
|
`loadBenchmarkScores`/`applyBenchmarkScores` are the shared cache chain and
|
|
@@ -257,7 +291,8 @@ stderr `openrouter: matched N/<pool> models (throughput), M priced`, the
|
|
|
257
291
|
- **One write path per artifact.** Roles go through `writeRoleSettings` (and
|
|
258
292
|
`removeRoleSettings` for deletion); the config through `patchConfig` +
|
|
259
293
|
`writeConfigAtomic`; agent files through `writeAgentFile` (and
|
|
260
|
-
`removeAgentFile` for deletion)
|
|
294
|
+
`removeAgentFile` for deletion); the error-report ledger through `writeLedger`
|
|
295
|
+
in `src/error-report.ts` (temp + rename). Nothing else writes those files.
|
|
261
296
|
- **Surgical config edits.** Only managed role lines, managed chain keys and the
|
|
262
297
|
plugin-managed `task.disabledAgents` names change; comments, blank lines and
|
|
263
298
|
unknown keys stay byte-identical. A no-change run writes nothing.
|
|
@@ -276,3 +311,25 @@ stderr `openrouter: matched N/<pool> models (throughput), M priced`, the
|
|
|
276
311
|
field already sets and prices the effort.
|
|
277
312
|
- **The engine is the only ranking math.** The explorer never reimplements
|
|
278
313
|
`value = q − λ·$/M`; it calls `rankRole`/`explainModel`.
|
|
314
|
+
|
|
315
|
+
## Non-goals
|
|
316
|
+
|
|
317
|
+
- **First-party provider selectors.** The plugin emits `openrouter/*` selectors
|
|
318
|
+
only — the ranking's price/throughput are OpenRouter-derived and the key is an
|
|
319
|
+
OpenRouter key.
|
|
320
|
+
- **Non-OpenRouter scoring sources.**
|
|
321
|
+
- **A `/rollback` command.** `llm-role-state.json` snapshots the previous
|
|
322
|
+
`modelRoles` block as a manual rollback aid; there is no command.
|
|
323
|
+
- **Auto-tuning `switchMargin`.**
|
|
324
|
+
- **A generic benchmark scraper** that auto-detects a payload with no user
|
|
325
|
+
review — the declarative source + authoring step is the mechanism.
|
|
326
|
+
- **A UI for browsing/editing benchmark sources** — the source file is edited by
|
|
327
|
+
hand or by the authoring step; the explorer's role editor gains the external
|
|
328
|
+
metrics already in use.
|
|
329
|
+
- **Changing the cardinal transform classes** — external metrics are normalized
|
|
330
|
+
to 0–1 at parse time.
|
|
331
|
+
- **Re-ranking or re-fetching beyond the daily UTC cache chain.**
|
|
332
|
+
|
|
333
|
+
Weight editing is the explorer's Export, `src/cli/create-role.ts` or
|
|
334
|
+
`/create-agent` (all validate through `resolveSettings`); role/agent removal is
|
|
335
|
+
`/remove-agent` (through `removeRoleSettings`).
|
package/docs/dev/ci.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# CI/CD
|
|
2
|
+
|
|
3
|
+
The GitHub Actions pipeline that gates every change and delivers every release.
|
|
4
|
+
The release *policy* — when to bump, SemVer over the public surface, the
|
|
5
|
+
CHANGELOG rules, the three channels — lives in [`releasing.md`](releasing.md);
|
|
6
|
+
this doc owns the machinery.
|
|
7
|
+
|
|
8
|
+
## The workflow
|
|
9
|
+
|
|
10
|
+
`.github/workflows/ci.yml` — one file, two jobs. One file because the npm
|
|
11
|
+
trusted-publisher registration names the workflow **filename**, so a second file
|
|
12
|
+
would need a second registration.
|
|
13
|
+
|
|
14
|
+
| Job | Runs on | Does |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `test` | every `pull_request`, every push to `main`, every `v*` tag push, and `workflow_dispatch` | `npm ci`, then `node --test tests/` |
|
|
17
|
+
| `release` | `v*` tag pushes (and a `workflow_dispatch` against a tag ref) | verifies the tag, publishes to npm over OIDC, creates the GitHub Release |
|
|
18
|
+
|
|
19
|
+
`release` declares `needs: test`, so a red tree cannot be published: the same
|
|
20
|
+
ref must be green first. `test` runs **exactly** the command the local
|
|
21
|
+
pre-commit hook runs (`.githooks/pre-commit`), and `tests/ci.test.ts` fails when
|
|
22
|
+
the two drift apart — "green locally" and "green in CI" cannot diverge.
|
|
23
|
+
|
|
24
|
+
The Node version comes from `.node-version` at the repo root (the exact local
|
|
25
|
+
toolchain; bumping it is a normal commit). `engines` is deliberately not in
|
|
26
|
+
`package.json`: the plugin executes inside omp's runtime, so an `engines` field
|
|
27
|
+
would only produce install warnings for users.
|
|
28
|
+
|
|
29
|
+
`test` is the whole gate — no lint, no format, no typecheck, no coverage. The
|
|
30
|
+
repo runs Node type-stripping without a typecheck (see [`quirks.md`](quirks.md)).
|
|
31
|
+
|
|
32
|
+
## The tag → publish contract
|
|
33
|
+
|
|
34
|
+
Pushing a `v<version>` tag is the delivery trigger. The `release` job:
|
|
35
|
+
|
|
36
|
+
1. checks out the tag;
|
|
37
|
+
2. runs `node scripts/release.ts notes "$GITHUB_REF_NAME"` as the **guard** —
|
|
38
|
+
tag ↔ package version ↔ lockstep ↔ CHANGELOG section — and writes the release
|
|
39
|
+
notes to a file. A mistyped tag, a half-applied lockstep bump or a missing
|
|
40
|
+
CHANGELOG section fails here, *before* anything is published;
|
|
41
|
+
3. ensures npm ≥ 11.5.1 (the trusted-publishing floor);
|
|
42
|
+
4. `npm publish` — authenticated by GitHub OIDC, with a provenance attestation
|
|
43
|
+
generated automatically. No `NPM_TOKEN`, no `.npmrc` step, no third-party
|
|
44
|
+
publish action;
|
|
45
|
+
5. `gh release create <tag> --notes-file <notes>` — the GitHub Release body is
|
|
46
|
+
the CHANGELOG section, never retyped.
|
|
47
|
+
|
|
48
|
+
Because the publish runs from the tagged commit, npm's `gitHead` equals the tag.
|
|
49
|
+
|
|
50
|
+
### One-time setup: register the trusted publisher
|
|
51
|
+
|
|
52
|
+
Required before the first CD run, and it cannot be done from the repo. On
|
|
53
|
+
npmjs.com → the package → Settings → Trusted publishing → GitHub Actions:
|
|
54
|
+
|
|
55
|
+
- **Organization or user**: `Gott50`
|
|
56
|
+
- **Repository**: `omp-llm-role`
|
|
57
|
+
- **Workflow filename**: `ci.yml` (the filename only, with the extension)
|
|
58
|
+
- **Allowed actions**: tick **direct publishing (`npm publish`)**.
|
|
59
|
+
|
|
60
|
+
That last tick is the trap. `npm stage publish` is always allowed, and a
|
|
61
|
+
configuration created after 2026-09-03 is automatically set to allow *only*
|
|
62
|
+
staged publishing — so a publisher registered today rejects the job's
|
|
63
|
+
`npm publish` **after** the guard has already passed, the worst possible failure
|
|
64
|
+
point. Staged publishing is not a substitute: it needs a separate
|
|
65
|
+
`npm stage approve` with 2FA, which breaks unattended CD. Do **not** tick
|
|
66
|
+
`npm dist-tag`; CD never moves a dist-tag.
|
|
67
|
+
|
|
68
|
+
Floors and constraints (npm's trusted-publishing docs): npm CLI ≥ 11.5.1, Node
|
|
69
|
+
≥ 22.14.0 (the pinned `.node-version` clears both), and GitHub-**hosted** runners
|
|
70
|
+
only — hence `ubuntu-latest`.
|
|
71
|
+
|
|
72
|
+
## The release script
|
|
73
|
+
|
|
74
|
+
`scripts/release.ts` — Node builtins only, no dependencies, outside the `files`
|
|
75
|
+
whitelist so it never ships in the tarball. Three subcommands, each with a
|
|
76
|
+
documented exit code:
|
|
77
|
+
|
|
78
|
+
| Command | Exit 0 | Exit 1 |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `due` | `## [Unreleased]` has at least one entry under an allowed section | it is empty (message on stdout) |
|
|
81
|
+
| `notes <tag>` | prints **only** the CHANGELOG section body, ready for `--notes-file` | the tag is not `v<semver>`, disagrees with the package version, the lockstep fields disagree, the CHANGELOG has no `## [<version>] - <date>` heading, or that section has no entries (message on stderr) |
|
|
82
|
+
| `cut <patch\|minor\|major> [--dry-run]` | the cut ran | a refusal (message on stderr) |
|
|
83
|
+
|
|
84
|
+
`due` is the agent's branch point: "not owed" is not an error, so a script can
|
|
85
|
+
branch on the code without parsing markdown.
|
|
86
|
+
|
|
87
|
+
### The lockstep set
|
|
88
|
+
|
|
89
|
+
Exactly four fields, bumped together:
|
|
90
|
+
|
|
91
|
+
- `package.json` `version`
|
|
92
|
+
- `.omp-plugin/marketplace.json` `plugins[0].version`
|
|
93
|
+
- `package-lock.json` `version`
|
|
94
|
+
- `package-lock.json` `packages[""].version`
|
|
95
|
+
|
|
96
|
+
The marketplace catalog's own `metadata.version` is **deliberately excluded** —
|
|
97
|
+
`releasing.md` locks the plugin entry, not the catalog. The two numbers look
|
|
98
|
+
like they should agree; they do not.
|
|
99
|
+
|
|
100
|
+
### What `cut` does, in order
|
|
101
|
+
|
|
102
|
+
1. refuse a dirty tree;
|
|
103
|
+
2. refuse an existing tag;
|
|
104
|
+
3. refuse an empty `## [Unreleased]`;
|
|
105
|
+
4. refuse when HEAD is not `origin/main` (so the tag always points at a pushed
|
|
106
|
+
commit and npm's `gitHead` resolves);
|
|
107
|
+
5. compute the new version from the bump argument;
|
|
108
|
+
6. write the four version fields;
|
|
109
|
+
7. rename `## [Unreleased]` to `## [<version>] - <YYYY-MM-DD>` (UTC) and insert
|
|
110
|
+
a fresh empty `## [Unreleased]` above it;
|
|
111
|
+
8. rewrite `[Unreleased]: …/compare/v<version>...HEAD` and prepend
|
|
112
|
+
`[<version>]: …/releases/tag/v<version>` to the footer link block;
|
|
113
|
+
9. regenerate `docs/llm-role-rankings.md`
|
|
114
|
+
(`node src/cli/llm-role-rank.ts --all --out docs/llm-role-rankings.md`);
|
|
115
|
+
10. commit `Release <version>`, staging only the touched paths (never `.`);
|
|
116
|
+
11. push `main`, create the annotated tag `v<version>`, push the tag.
|
|
117
|
+
|
|
118
|
+
The tag push is the CD trigger. `--dry-run` writes nothing: it prints the target
|
|
119
|
+
version, the file plan and the changelog diff, and skips the network-dependent
|
|
120
|
+
report regeneration with a note.
|
|
121
|
+
|
|
122
|
+
`cut` does not run the suite itself. The commit it makes goes through
|
|
123
|
+
`.githooks/pre-commit` when `core.hooksPath` is wired, and the `release` job
|
|
124
|
+
re-verifies with `needs: test` on the tag — a second definition of the gate
|
|
125
|
+
inside the script would be a second thing to keep in sync.
|
|
126
|
+
|
|
127
|
+
There is no `--force` and no interactive prompt: every refusal is a message plus
|
|
128
|
+
exit 1, so the command is safe to run unattended.
|
|
129
|
+
|
|
130
|
+
## Verifying the pipeline
|
|
131
|
+
|
|
132
|
+
- **The gate:** push a branch and open a PR, or push to `main`; `gh run watch`
|
|
133
|
+
the `test` job green.
|
|
134
|
+
- **The guard (negative control):** push a tag that disagrees with the package
|
|
135
|
+
version (e.g. `v0.0.1` against `1.1.0`); the `release` job must fail in the
|
|
136
|
+
`notes` guard *before* any publish and leave the registry untouched. Delete
|
|
137
|
+
the tag afterwards.
|
|
138
|
+
- **The publish:** run the release job against an already-cut tag —
|
|
139
|
+
`gh workflow run ci.yml --ref v<version>` — then confirm npm's `gitHead`
|
|
140
|
+
equals the tag commit, `dist-tags.latest` moved, and the GitHub Release body
|
|
141
|
+
is that version's CHANGELOG section. This is also the recovery path for a
|
|
142
|
+
failed publish: the tag exists, so no new version is needed.
|
|
143
|
+
|
|
144
|
+
The dispatch resolves the workflow **from the ref**, so the tag's commit must
|
|
145
|
+
contain `.github/workflows/ci.yml`. A tag cut before this workflow existed
|
|
146
|
+
cannot be dispatched (`HTTP 422: Workflow does not have 'workflow_dispatch'
|
|
147
|
+
trigger`) — the recovery path only works for tags cut after it landed.
|
|
148
|
+
|
|
149
|
+
`tests/ci.test.ts` asserts the offline half: the workflow's structure (jobs,
|
|
150
|
+
triggers, permissions, the command it runs), the hook/workflow command parity,
|
|
151
|
+
the tag-trigger ↔ CHANGELOG-link agreement, the lockstep fields, and the
|
|
152
|
+
script's exit codes and refusals against fixture repositories.
|