@event4u/agent-config 16.1.0 → 16.2.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +237 -248
- package/dist/agent-src/commands/roadmap/process-full.md +1 -1
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +16 -1
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-7XGBX6ZH.js} +471 -387
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-FP36SWJD.js} +432 -144
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-IGD6ERX6.js} +31 -0
- package/dist/cli-delegate/{chunk-FXYUYU4N.js → chunk-JNMVLWLR.js} +42 -1
- package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
- package/dist/cli-delegate/cmd_conformance.js +902 -782
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +4 -4
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +10 -10
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +6666 -2581
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/install.mjs +848 -733
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +2747 -1510
- package/dist/server/schemas/settings.js +23 -0
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +6 -4
- package/package.json +4 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +29 -0
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +2 -2
- package/src/config/gate-coverage.yml +234 -3
- package/src/config/gate-violation-baselines.json +7 -3
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +2 -2
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +18 -4
- package/src/scripts/_cli/cmd_doctor.ts +5 -19
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/turn_end_refusals.ts +525 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/before_complete_hook.ts +340 -23
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +245 -3
- package/src/scripts/check_finding_dispositions.ts +22 -3
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +47 -5
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -1
- package/src/scripts/compile_hook_manifest.ts +60 -16
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +42 -5
- package/src/scripts/hooks/block_config_weakening.ts +229 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +25 -19
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +128 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +128 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +8 -5
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +49 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/install.ts +3 -4
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +55 -0
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_standing_payload_by_host.ts +1 -1
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +359 -10
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/trigger_eval_grandfather.json +0 -2
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +31 -0
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
- /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
|
@@ -7,35 +7,49 @@
|
|
|
7
7
|
* the corpus's expected skill per line". Building it surfaced two defects in
|
|
8
8
|
* that premise, and this file is written around them rather than over them.
|
|
9
9
|
*
|
|
10
|
-
* DEFECT A — the
|
|
11
|
-
* is keyed by `rule:` and every prompt
|
|
12
|
-
* activate. Scoring a skill ranker against it would need a skill label that
|
|
13
|
-
*
|
|
10
|
+
* DEFECT A — the routing matrix had no expected SKILL. `tests/eval/routing-matrix/`
|
|
11
|
+
* is keyed by `rule:` and every prompt was labelled with the RULE it should
|
|
12
|
+
* activate. Scoring a skill ranker against it would need a skill label that did
|
|
13
|
+
* not exist there, so a "hit rate" over it would be invented.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* **Defect A is CLOSED as of `road-to-a-menu-whose-precision-is-measured` 1.1.**
|
|
16
|
+
* Every case in the matrix now carries an `expected_skills` list, hand-written
|
|
17
|
+
* by seats that had not read this ranker's scoring (the protocol is in
|
|
18
|
+
* `tests/eval/routing-matrix/README.md`). An EMPTY list is a first-class value
|
|
19
|
+
* meaning "deliberately no skill expectation", and such a row is excluded from
|
|
20
|
+
* the accuracy denominator rather than counted as a miss. So the matrix arm now
|
|
21
|
+
* has ground truth; `--corpus routing-matrix` scores against it.
|
|
18
22
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* confident) with no ground truth and therefore no accuracy.
|
|
23
|
+
* DEFECT B — the published size is stale, repeatedly. 496 was published while
|
|
24
|
+
* the matrix held 499, and 499 is still published in several sites while the
|
|
25
|
+
* matrix holds more than that again. The size is therefore MEASURED on every run
|
|
26
|
+
* and printed, never asserted from a constant.
|
|
24
27
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
28
|
+
* THE INTERVAL, AND THE REFUSAL TO PRINT A VERDICT UNDER n = 100.
|
|
29
|
+
* A hit rate over 26 prompts
|
|
30
|
+
* moves ~4 points per prompt; quoting it as "61.5 %" invites a comparison the
|
|
31
|
+
* sample cannot carry. Both arms therefore report a 95 % Wilson score interval
|
|
32
|
+
* (`_lib/capture_rate.ts` — Wilson rather than the normal approximation for the
|
|
33
|
+
* same reason stated there: near p = 1 the normal one produces bounds above 1).
|
|
27
34
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
35
|
+
* Below {@link MIN_POWERED_N} labelled rows the arm's `verdict` is
|
|
36
|
+
* `underpowered`. That is not a failure and not an exit code — the point
|
|
37
|
+
* estimate and the interval are still printed, because suppressing them would
|
|
38
|
+
* replace a wide measurement with no measurement. It is a label that travels
|
|
39
|
+
* with the number so a later reader cannot quote the point estimate alone.
|
|
30
40
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
41
|
+
* Usage:
|
|
42
|
+
* ./scripts-run src/scripts/measure_skill_ranker_baseline
|
|
43
|
+
* ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus routing-matrix
|
|
44
|
+
* ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus all
|
|
45
|
+
* ./scripts-run src/scripts/measure_skill_ranker_baseline --ranker keyword-v2
|
|
33
46
|
*/
|
|
34
47
|
|
|
35
48
|
import * as fs from 'node:fs';
|
|
36
49
|
import * as path from 'node:path';
|
|
37
50
|
import { fileURLToPath } from 'node:url';
|
|
38
51
|
|
|
52
|
+
import { wilsonInterval } from './_lib/capture_rate.js';
|
|
39
53
|
import { rank } from './skill_tools/score_skill_relevance.js';
|
|
40
54
|
import type { RankOptions } from '../shared/skillRanking.js';
|
|
41
55
|
|
|
@@ -45,6 +59,20 @@ const MATRIX_DIR = 'tests/eval/routing-matrix';
|
|
|
45
59
|
/** Where the projected skills live in this checkout. */
|
|
46
60
|
export const SKILLS_DIR = path.join(REPO, 'src', 'skills');
|
|
47
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The smallest labelled corpus this report will call powered.
|
|
64
|
+
*
|
|
65
|
+
* 100 is the roadmap's number, not a derived optimum, and it is stated as such:
|
|
66
|
+
* at n = 100 a point estimate near 0.7 carries a 95 % Wilson half-width of
|
|
67
|
+
* roughly ±9 points, which is wide but no longer moves on a single prompt the
|
|
68
|
+
* way a 26-row denominator does. Raise it with a dated reason if a decision
|
|
69
|
+
* ever needs a tighter bound than that.
|
|
70
|
+
*/
|
|
71
|
+
export const MIN_POWERED_N = 100;
|
|
72
|
+
|
|
73
|
+
export type CorpusName = 'labelled' | 'routing-matrix' | 'all';
|
|
74
|
+
export const CORPUS_NAMES: readonly CorpusName[] = ['labelled', 'routing-matrix', 'all'];
|
|
75
|
+
|
|
48
76
|
export interface LabelledPrompt {
|
|
49
77
|
id: string;
|
|
50
78
|
corpus: string;
|
|
@@ -94,38 +122,193 @@ export function readLabelledPrompts(repo = REPO): LabelledPrompt[] {
|
|
|
94
122
|
return out;
|
|
95
123
|
}
|
|
96
124
|
|
|
97
|
-
|
|
98
|
-
|
|
125
|
+
export interface MatrixCase {
|
|
126
|
+
rule: string;
|
|
127
|
+
section: 'positives' | 'near_misses';
|
|
128
|
+
ordinal: number;
|
|
129
|
+
prompt: string;
|
|
130
|
+
/** `undefined` = the key is missing (a defect the lint catches); `[]` = deliberately unlabelled. */
|
|
131
|
+
expected?: string[];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Every case in the rule routing matrix, with its `expected_skills` label when
|
|
136
|
+
* one is present.
|
|
137
|
+
*
|
|
138
|
+
* Line-oriented for the same reason as the reader above, and because a YAML
|
|
139
|
+
* round-trip through this directory would reformat 100+ hand-kept fixtures.
|
|
140
|
+
* A case is `prompt:` plus the keys indented under it up to the next `- `.
|
|
141
|
+
*/
|
|
142
|
+
export function readMatrixCases(repo = REPO): MatrixCase[] {
|
|
99
143
|
const dir = path.join(repo, MATRIX_DIR);
|
|
100
144
|
if (!fs.existsSync(dir)) return [];
|
|
101
|
-
const out:
|
|
145
|
+
const out: MatrixCase[] = [];
|
|
102
146
|
for (const name of fs.readdirSync(dir).sort()) {
|
|
103
147
|
if (!name.endsWith('.yaml')) continue;
|
|
148
|
+
const rule = name.replace(/\.yaml$/, '');
|
|
149
|
+
let section: MatrixCase['section'] = 'positives';
|
|
150
|
+
let ordinal = 0;
|
|
151
|
+
let current: MatrixCase | undefined;
|
|
104
152
|
for (const raw of fs.readFileSync(path.join(dir, name), 'utf8').split('\n')) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
153
|
+
const line = raw.replace(/\r$/, '');
|
|
154
|
+
if (/^positives:\s*$/.test(line)) {
|
|
155
|
+
section = 'positives';
|
|
156
|
+
ordinal = 0;
|
|
157
|
+
current = undefined;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (/^near_misses:\s*$/.test(line)) {
|
|
161
|
+
section = 'near_misses';
|
|
162
|
+
ordinal = 0;
|
|
163
|
+
current = undefined;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const mPrompt = /^\s*-?\s*prompt:\s*"(.*)"\s*$/.exec(line);
|
|
167
|
+
if (mPrompt && mPrompt[1] !== undefined) {
|
|
168
|
+
current = { rule, section, ordinal: ordinal++, prompt: mPrompt[1] };
|
|
169
|
+
out.push(current);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const mExp = /^\s*expected_skills:\s*\[(.*)\]\s*$/.exec(line);
|
|
173
|
+
if (mExp && current) {
|
|
174
|
+
current.expected = mExp[1]!
|
|
175
|
+
.split(',')
|
|
176
|
+
.map((s) => s.trim())
|
|
177
|
+
.filter(Boolean);
|
|
178
|
+
}
|
|
107
179
|
}
|
|
108
180
|
}
|
|
109
181
|
return out;
|
|
110
182
|
}
|
|
111
183
|
|
|
184
|
+
/** Every `- prompt:` string in the rule routing matrix, label or no label. */
|
|
185
|
+
export function readMatrixPrompts(repo = REPO): string[] {
|
|
186
|
+
return readMatrixCases(repo).map((c) => c.prompt);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The matrix rows that carry a NON-EMPTY label — the accuracy denominator. */
|
|
190
|
+
export function readMatrixLabelledPrompts(repo = REPO): LabelledPrompt[] {
|
|
191
|
+
return readMatrixCases(repo)
|
|
192
|
+
.filter((c) => (c.expected?.length ?? 0) > 0)
|
|
193
|
+
.map((c) => ({
|
|
194
|
+
id: `${c.rule}#${c.section}[${c.ordinal}]`,
|
|
195
|
+
corpus: 'routing-matrix',
|
|
196
|
+
prompt: c.prompt,
|
|
197
|
+
expected: c.expected!,
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function promptsForCorpus(corpus: CorpusName, repo = REPO): LabelledPrompt[] {
|
|
202
|
+
if (corpus === 'labelled') return readLabelledPrompts(repo);
|
|
203
|
+
if (corpus === 'routing-matrix') return readMatrixLabelledPrompts(repo);
|
|
204
|
+
return [...readLabelledPrompts(repo), ...readMatrixLabelledPrompts(repo)];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The packs a skill declares, read from its own `packs:` frontmatter block.
|
|
209
|
+
*
|
|
210
|
+
* Line-oriented, and deliberately NOT read from `src/packs/*\/pack.yaml`: those
|
|
211
|
+
* manifests are generated FROM this frontmatter, and four of them
|
|
212
|
+
* (`analytics`, `core`, `memory`, `product-reasoning`) carry no skill at all.
|
|
213
|
+
* A pack with no skill can never own a labelled prompt, so a census over the
|
|
214
|
+
* manifest set would state a floor nothing could ever meet.
|
|
215
|
+
*/
|
|
216
|
+
export function packsForSkill(skillsDir: string, name: string): string[] {
|
|
217
|
+
const file = path.join(skillsDir, name, 'SKILL.md');
|
|
218
|
+
if (!fs.existsSync(file)) return [];
|
|
219
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
220
|
+
if (!text.startsWith('---')) return [];
|
|
221
|
+
const end = text.indexOf('\n---', 3);
|
|
222
|
+
if (end === -1) return [];
|
|
223
|
+
const lines = text.slice(3, end).split('\n');
|
|
224
|
+
const at = lines.findIndex((l) => /^packs:/u.test(l));
|
|
225
|
+
if (at === -1) return [];
|
|
226
|
+
const out: string[] = [];
|
|
227
|
+
for (let i = at + 1; i < lines.length; i += 1) {
|
|
228
|
+
const m = /^\s+-\s+(\S+)\s*$/u.exec(lines[i] as string);
|
|
229
|
+
if (!m) break;
|
|
230
|
+
out.push(m[1] as string);
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Every pack id that at least one shipped skill declares. The census domain. */
|
|
236
|
+
export function allPacks(skillsDir: string): string[] {
|
|
237
|
+
const packs = new Set<string>();
|
|
238
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
239
|
+
if (!entry.isDirectory()) continue;
|
|
240
|
+
for (const p of packsForSkill(skillsDir, entry.name)) packs.add(p);
|
|
241
|
+
}
|
|
242
|
+
return [...packs].sort();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* How many labelled prompts each pack owns, via the packs of its expected skills.
|
|
247
|
+
*
|
|
248
|
+
* A prompt counts ONCE per pack however many of that pack's skills it expects —
|
|
249
|
+
* otherwise a single prompt naming three `laravel` skills would satisfy a
|
|
250
|
+
* three-prompt floor on its own, which is the shape of coverage this census
|
|
251
|
+
* exists to refuse.
|
|
252
|
+
*/
|
|
253
|
+
export function packCensus(prompts: readonly LabelledPrompt[], skillsDir: string): Record<string, number> {
|
|
254
|
+
const out: Record<string, number> = {};
|
|
255
|
+
for (const p of allPacks(skillsDir)) out[p] = 0;
|
|
256
|
+
for (const prompt of prompts) {
|
|
257
|
+
const packs = new Set<string>();
|
|
258
|
+
for (const skill of prompt.expected) for (const pack of packsForSkill(skillsDir, skill)) packs.add(pack);
|
|
259
|
+
for (const pack of packs) out[pack] = (out[pack] ?? 0) + 1;
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Packs owning fewer than `floor` labelled prompts, sorted by pack id. */
|
|
265
|
+
export function packsBelow(census: Record<string, number>, floor: number): string[] {
|
|
266
|
+
return Object.keys(census)
|
|
267
|
+
.filter((p) => (census[p] ?? 0) < floor)
|
|
268
|
+
.sort();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** The per-pack floor `road-to-a-menu-whose-precision-is-measured` 1.1 states. */
|
|
272
|
+
export const MIN_PROMPTS_PER_PACK = 3;
|
|
273
|
+
|
|
274
|
+
export interface Interval {
|
|
275
|
+
lower: number;
|
|
276
|
+
upper: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
112
279
|
export interface AccuracyArm {
|
|
280
|
+
corpus: CorpusName;
|
|
113
281
|
corpus_prompts: number;
|
|
114
282
|
top1: number;
|
|
283
|
+
top1_ci95: Interval;
|
|
115
284
|
top3: number;
|
|
285
|
+
top3_ci95: Interval;
|
|
286
|
+
/** `underpowered` below MIN_POWERED_N labelled rows; `measured` at or above. */
|
|
287
|
+
verdict: 'underpowered' | 'measured';
|
|
288
|
+
min_powered_n: number;
|
|
116
289
|
misses: string[];
|
|
117
290
|
denominator_note: string;
|
|
291
|
+
/** Pack ids at least one shipped skill declares — the census domain. */
|
|
292
|
+
packs_total: number;
|
|
293
|
+
min_prompts_per_pack: number;
|
|
294
|
+
/** Packs this corpus leaves under the floor. Empty is the passing shape. */
|
|
295
|
+
packs_below_floor: string[];
|
|
296
|
+
/** Full census, so a reader can see the distribution and not just the failures. */
|
|
297
|
+
pack_census: Record<string, number>;
|
|
118
298
|
}
|
|
119
299
|
|
|
120
300
|
export interface CoverageArm {
|
|
121
301
|
corpus_prompts: number;
|
|
302
|
+
labelled_prompts: number;
|
|
303
|
+
unlabelled_prompts: number;
|
|
304
|
+
missing_label_key: number;
|
|
122
305
|
prompts_with_any_result: number;
|
|
123
306
|
mean_top_score: number;
|
|
124
307
|
note: string;
|
|
125
308
|
}
|
|
126
309
|
|
|
127
310
|
export interface RankerBaseline {
|
|
128
|
-
schema:
|
|
311
|
+
schema: 2;
|
|
129
312
|
ranker: string;
|
|
130
313
|
commit: string;
|
|
131
314
|
skills_dir: string;
|
|
@@ -139,28 +322,85 @@ function hitAt(rows: readonly (readonly [string, number, string[]])[], n: number
|
|
|
139
322
|
return expected.some((e) => top.includes(e));
|
|
140
323
|
}
|
|
141
324
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
325
|
+
function round3(n: number): number {
|
|
326
|
+
return Math.round(n * 1000) / 1000;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function ci(successes: number, trials: number): Interval {
|
|
330
|
+
const raw = wilsonInterval(successes, trials);
|
|
331
|
+
return { lower: round3(raw.lower), upper: round3(raw.upper) };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const DENOMINATOR_NOTES: Record<CorpusName, string> = {
|
|
335
|
+
labelled:
|
|
336
|
+
'tests/eval/corpus-dev.yaml + corpus-non-dev.yaml — the two hand-written eval corpora. ' +
|
|
337
|
+
'Small; a single prompt moves each rate by ~4 points, which is why the interval is ' +
|
|
338
|
+
'printed beside the point estimate and not instead of it.',
|
|
339
|
+
'routing-matrix':
|
|
340
|
+
'tests/eval/routing-matrix/ rows whose expected_skills list is NON-EMPTY. A row with an ' +
|
|
341
|
+
'empty list is a deliberate "no skill expectation" and is excluded from the ' +
|
|
342
|
+
'denominator — never counted as a miss.',
|
|
343
|
+
all:
|
|
344
|
+
'Both labelled sources pooled. The two were written under different protocols, so a ' +
|
|
345
|
+
'pooled rate is a convenience reading and the per-corpus arms are the citable ones.',
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export function measureAccuracy(opts: {
|
|
349
|
+
corpus: CorpusName;
|
|
350
|
+
repo: string;
|
|
351
|
+
skillsDir: string;
|
|
352
|
+
rankOpts: RankOptions;
|
|
353
|
+
}): AccuracyArm {
|
|
354
|
+
const labelled = promptsForCorpus(opts.corpus, opts.repo);
|
|
149
355
|
let top1 = 0;
|
|
150
356
|
let top3 = 0;
|
|
151
357
|
const misses: string[] = [];
|
|
152
358
|
for (const p of labelled) {
|
|
153
|
-
const rows = rank(p.prompt, skillsDir, rankOpts);
|
|
359
|
+
const rows = rank(p.prompt, opts.skillsDir, opts.rankOpts);
|
|
154
360
|
if (hitAt(rows, 1, p.expected)) top1++;
|
|
155
361
|
if (hitAt(rows, 3, p.expected)) top3++;
|
|
156
362
|
else misses.push(p.id);
|
|
157
363
|
}
|
|
364
|
+
const n = labelled.length;
|
|
365
|
+
const census = packCensus(labelled, opts.skillsDir);
|
|
366
|
+
return {
|
|
367
|
+
corpus: opts.corpus,
|
|
368
|
+
corpus_prompts: n,
|
|
369
|
+
top1: n ? round3(top1 / n) : 0,
|
|
370
|
+
top1_ci95: ci(top1, n),
|
|
371
|
+
top3: n ? round3(top3 / n) : 0,
|
|
372
|
+
top3_ci95: ci(top3, n),
|
|
373
|
+
verdict: n >= MIN_POWERED_N ? 'measured' : 'underpowered',
|
|
374
|
+
min_powered_n: MIN_POWERED_N,
|
|
375
|
+
misses,
|
|
376
|
+
denominator_note: DENOMINATOR_NOTES[opts.corpus],
|
|
377
|
+
packs_total: Object.keys(census).length,
|
|
378
|
+
min_prompts_per_pack: MIN_PROMPTS_PER_PACK,
|
|
379
|
+
packs_below_floor: packsBelow(census, MIN_PROMPTS_PER_PACK),
|
|
380
|
+
pack_census: census,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function measure(opts: {
|
|
385
|
+
ranker: string;
|
|
386
|
+
commit: string;
|
|
387
|
+
corpus?: CorpusName;
|
|
388
|
+
skillsDir?: string;
|
|
389
|
+
repo?: string;
|
|
390
|
+
}): RankerBaseline {
|
|
391
|
+
const repo = opts.repo ?? REPO;
|
|
392
|
+
const skillsDir = opts.skillsDir ?? SKILLS_DIR;
|
|
393
|
+
// `keyword-v2` is Phase 3.1: the same formula with `triggers:` prose folded
|
|
394
|
+
// into each skill's term source. Any other label measures v1.
|
|
395
|
+
const rankOpts: RankOptions = opts.ranker === 'keyword-v2' ? { includeTriggers: true } : {};
|
|
396
|
+
const corpus = opts.corpus ?? 'labelled';
|
|
397
|
+
const accuracy = measureAccuracy({ corpus, repo, skillsDir, rankOpts });
|
|
158
398
|
|
|
159
|
-
const
|
|
399
|
+
const cases = readMatrixCases(repo);
|
|
160
400
|
let withResult = 0;
|
|
161
401
|
let scoreSum = 0;
|
|
162
|
-
for (const
|
|
163
|
-
const rows = rank(prompt, skillsDir, rankOpts);
|
|
402
|
+
for (const c of cases) {
|
|
403
|
+
const rows = rank(c.prompt, skillsDir, rankOpts);
|
|
164
404
|
if (rows.length > 0) {
|
|
165
405
|
withResult++;
|
|
166
406
|
scoreSum += rows[0]![1];
|
|
@@ -172,37 +412,51 @@ export function measure(opts: { ranker: string; commit: string; skillsDir?: stri
|
|
|
172
412
|
: 0;
|
|
173
413
|
|
|
174
414
|
return {
|
|
175
|
-
schema:
|
|
415
|
+
schema: 2,
|
|
176
416
|
ranker: opts.ranker,
|
|
177
417
|
commit: opts.commit,
|
|
178
418
|
skills_dir: path.relative(repo, skillsDir),
|
|
179
419
|
skills_indexed: skillsIndexed,
|
|
180
|
-
accuracy
|
|
181
|
-
corpus_prompts: labelled.length,
|
|
182
|
-
top1: labelled.length ? Math.round((top1 / labelled.length) * 1000) / 1000 : 0,
|
|
183
|
-
top3: labelled.length ? Math.round((top3 / labelled.length) * 1000) / 1000 : 0,
|
|
184
|
-
misses,
|
|
185
|
-
denominator_note:
|
|
186
|
-
'tests/eval/corpus-dev.yaml + corpus-non-dev.yaml — the only expected-skill ground ' +
|
|
187
|
-
'truth in this tree. Small; a single prompt moves each rate by ~4 points.',
|
|
188
|
-
},
|
|
420
|
+
accuracy,
|
|
189
421
|
matrix_coverage: {
|
|
190
|
-
corpus_prompts:
|
|
422
|
+
corpus_prompts: cases.length,
|
|
423
|
+
labelled_prompts: cases.filter((c) => (c.expected?.length ?? 0) > 0).length,
|
|
424
|
+
unlabelled_prompts: cases.filter((c) => c.expected?.length === 0).length,
|
|
425
|
+
missing_label_key: cases.filter((c) => c.expected === undefined).length,
|
|
191
426
|
prompts_with_any_result: withResult,
|
|
192
427
|
mean_top_score: withResult ? Math.round((scoreSum / withResult) * 100) / 100 : 0,
|
|
193
428
|
note:
|
|
194
|
-
'
|
|
195
|
-
'
|
|
196
|
-
'
|
|
197
|
-
'and
|
|
429
|
+
'Coverage over EVERY matrix case, labelled or not: whether the ranker answers at ' +
|
|
430
|
+
'all and how confident its top answer is. Accuracy over the labelled subset is ' +
|
|
431
|
+
'the `accuracy` arm under `--corpus routing-matrix`. Size is measured here, never ' +
|
|
432
|
+
'asserted: 496 and 499 are both published elsewhere and both stale.',
|
|
198
433
|
},
|
|
199
434
|
};
|
|
200
435
|
}
|
|
201
436
|
|
|
437
|
+
function parseCorpus(argv: readonly string[]): CorpusName {
|
|
438
|
+
const i = argv.indexOf('--corpus');
|
|
439
|
+
if (i === -1) return 'labelled';
|
|
440
|
+
const raw = argv[i + 1];
|
|
441
|
+
if (!raw || !CORPUS_NAMES.includes(raw as CorpusName)) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`measure_skill_ranker_baseline: --corpus expects one of ${CORPUS_NAMES.join(' | ')}, got ${raw ?? '(nothing)'}`,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
return raw as CorpusName;
|
|
447
|
+
}
|
|
448
|
+
|
|
202
449
|
export function main(argv: readonly string[]): number {
|
|
203
450
|
const ranker = argv.includes('--ranker') ? (argv[argv.indexOf('--ranker') + 1] ?? 'keyword-v1') : 'keyword-v1';
|
|
204
451
|
const commit = argv.includes('--commit') ? (argv[argv.indexOf('--commit') + 1] ?? 'unknown') : 'unknown';
|
|
205
|
-
|
|
452
|
+
let corpus: CorpusName;
|
|
453
|
+
try {
|
|
454
|
+
corpus = parseCorpus(argv);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
process.stderr.write(`${(err as Error).message}\n`);
|
|
457
|
+
return 2;
|
|
458
|
+
}
|
|
459
|
+
const out = measure({ ranker, commit, corpus });
|
|
206
460
|
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
207
461
|
return 0;
|
|
208
462
|
}
|