@event4u/agent-config 8.8.0 → 8.9.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +109 -274
- package/README.md +2 -2
- package/dist/agent-src/commands/agent-handoff.md +39 -2
- package/dist/agent-src/commands/research/deep.md +8 -0
- package/dist/agent-src/commands/research/report.md +7 -0
- package/dist/agent-src/commands/roadmap/create.md +12 -0
- package/dist/agent-src/commands/roadmap/process-full.md +31 -5
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +30 -1
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +21 -5
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +37 -0
- package/dist/agent-src/contexts/execution/subagent-topologies.md +34 -0
- package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -9
- package/dist/agent-src/rules/agent-authority.md +1 -1
- package/dist/agent-src/rules/communication-through-line.md +1 -1
- package/dist/agent-src/rules/delegation-policy.md +5 -0
- package/dist/agent-src/rules/direct-answers.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +65 -0
- package/dist/agent-src/rules/git-history-discipline.md +18 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +6 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +58 -10
- package/dist/agent-src/rules/non-destructive-by-default.md +3 -0
- package/dist/agent-src/rules/question-not-instruction.md +76 -0
- package/dist/agent-src/rules/senior-engineering-discipline.md +1 -1
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +8 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +87 -22
- package/dist/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +10 -3
- package/dist/agent-src/skills/code-review/SKILL.md +2 -0
- package/dist/agent-src/skills/command-writing/SKILL.md +53 -40
- package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +49 -12
- package/dist/agent-src/skills/design-review/SKILL.md +29 -15
- package/dist/agent-src/skills/design-system-capture/SKILL.md +63 -24
- package/dist/agent-src/skills/forecasting/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -0
- package/dist/agent-src/skills/mcp/SKILL.md +24 -0
- package/dist/agent-src/skills/mcp-builder/SKILL.md +9 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +55 -4
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +39 -5
- package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +7 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +12 -3
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +27 -33
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +29 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
- package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +40 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +36 -0
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +90 -48
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +11 -9
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +9 -7
- package/dist/install/install.mjs +136 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_builtin_names.js +145 -0
- package/dist/scripts/_lib/claude_builtin_names.js.map +1 -0
- package/dist/scripts/condense.js +14 -0
- package/dist/scripts/condense.js.map +1 -1
- package/docs/CLAIMS.md +34 -6
- package/docs/architecture.md +2 -2
- package/docs/archive/CHANGELOG-pre-8.9.0.md +306 -0
- package/docs/benchmark.md +113 -0
- package/docs/catalog.md +20 -7
- package/docs/contracts/command-clusters.md +18 -0
- package/docs/contracts/design-artifact-verification.md +124 -0
- package/docs/contracts/injected-block-authenticity-model.md +66 -0
- package/docs/contracts/mcp-tool-inventory.md +28 -28
- package/docs/decisions/ADR-061-corpus-grounding-layer.md +13 -0
- package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +9 -0
- package/docs/getting-started-by-role.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/governance-advantage.md +1 -1
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -0
- package/docs/guidelines/agent-infra/emphasis-budget.md +45 -0
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +151 -0
- package/docs/guidelines/agent-infra/tool-description-as-policy.md +35 -0
- package/docs/guidelines/design-canon.md +1 -0
- package/docs/proof.md +36 -21
- package/docs/second-brain-scope.md +69 -26
- package/package.json +1 -1
- package/src/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/src/scripts/_dispatch.bash +27 -0
- package/src/scripts/_lib/claude_builtin_names.ts +148 -0
- package/src/scripts/_lib/file_slicer.ts +117 -0
- package/src/scripts/_lib/lexical_index.ts +155 -0
- package/src/scripts/_lib/orchestration_record.ts +170 -0
- package/src/scripts/_lib/orchestration_savings.ts +62 -9
- package/src/scripts/_lib/retrieval_sanitize.ts +71 -0
- package/src/scripts/_lib/security_lint.ts +1 -0
- package/src/scripts/_lib/stat_index.ts +98 -0
- package/src/scripts/benchmark.ts +122 -0
- package/src/scripts/build_proof.ts +30 -17
- package/src/scripts/check_memory.ts +2 -0
- package/src/scripts/check_quality_regression.ts +51 -0
- package/src/scripts/check_roadmap_trackable.ts +3 -2
- package/src/scripts/condense.ts +14 -0
- package/src/scripts/discovery_graph.ts +0 -0
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/generate_index.ts +5 -2
- package/src/scripts/install-hooks.sh +25 -0
- package/src/scripts/install.ts +35 -2
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_agent_skill_names.ts +29 -0
- package/src/scripts/lint_knowledge_scale.ts +4 -3
- package/src/scripts/lint_roadmap_complexity.ts +124 -0
- package/src/scripts/lint_versioned_cache.ts +195 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +2 -1
- package/src/scripts/mcp_server/tools.ts +24 -1
- package/src/scripts/measure_lexical_ranking.ts +173 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/orchestration_record.ts +140 -0
- package/src/scripts/orchestration_savings_report.ts +29 -5
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/score_domain_truth.ts +192 -0
- package/src/scripts/second_brain_retrieval.ts +278 -0
- package/src/scripts/second_brain_run.ts +341 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* `agent-config benchmark` — self-measuring token-reduction report
|
|
4
|
+
* (road-to-retrieval-substrate-hardening B7a).
|
|
5
|
+
*
|
|
6
|
+
* Reports the context-token reduction of the retrieval economy against the
|
|
7
|
+
* REAL alternative: the FULL always-loaded projection (council Q4) — the
|
|
8
|
+
* honest "what the user pays if everything loads eagerly + full descriptions".
|
|
9
|
+
* NOT a synthetic full-corpus strawman (explicitly rejected).
|
|
10
|
+
*
|
|
11
|
+
* Baseline components (from the pinned internal/bench/reports/token-baseline.json):
|
|
12
|
+
* eager_rule_load + skill_descriptions + command_descriptions + mcp_schemas
|
|
13
|
+
* Reduced: eager→thin rule projection, descriptions/schemas unchanged.
|
|
14
|
+
*
|
|
15
|
+
* Deterministic (reads the pinned baseline). Every emitted number binds to
|
|
16
|
+
* docs/CLAIMS.md with a method line (council Q4 hard condition).
|
|
17
|
+
*
|
|
18
|
+
* Usage: benchmark.ts [--baseline PATH] [--format text|json]
|
|
19
|
+
* Exit codes: 0 ok, 1 baseline missing/malformed, 2 usage error.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
import process from 'node:process';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
|
|
26
|
+
const PROG = 'benchmark.ts';
|
|
27
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
28
|
+
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
29
|
+
export const BASELINE = path.join(REPO_ROOT, 'internal', 'bench', 'reports', 'token-baseline.json');
|
|
30
|
+
|
|
31
|
+
interface Metrics {
|
|
32
|
+
eager_rule_load: number;
|
|
33
|
+
thin_rule_load: number;
|
|
34
|
+
skill_descriptions: number;
|
|
35
|
+
command_descriptions: number;
|
|
36
|
+
mcp_schemas: number;
|
|
37
|
+
}
|
|
38
|
+
export interface BenchmarkReport {
|
|
39
|
+
baseline_full_projection: number;
|
|
40
|
+
reduced_projection: number;
|
|
41
|
+
saved: number;
|
|
42
|
+
reduction_ratio: number; // 0..1
|
|
43
|
+
method: string;
|
|
44
|
+
components: Metrics;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _num(v: unknown): number {
|
|
48
|
+
return typeof v === 'number' && Number.isFinite(v) ? v : 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Compute the reduction report from a token-baseline metrics object. */
|
|
52
|
+
export function computeReport(metrics: Metrics): BenchmarkReport {
|
|
53
|
+
const shared = metrics.skill_descriptions + metrics.command_descriptions + metrics.mcp_schemas;
|
|
54
|
+
const full = metrics.eager_rule_load + shared;
|
|
55
|
+
const reduced = metrics.thin_rule_load + shared;
|
|
56
|
+
const saved = full - reduced;
|
|
57
|
+
const ratio = full > 0 ? Math.round((saved / full) * 10000) / 10000 : 0;
|
|
58
|
+
return {
|
|
59
|
+
baseline_full_projection: full,
|
|
60
|
+
reduced_projection: reduced,
|
|
61
|
+
saved,
|
|
62
|
+
reduction_ratio: ratio,
|
|
63
|
+
method:
|
|
64
|
+
'Baseline = FULL always-loaded projection (eager rule load + skill/command ' +
|
|
65
|
+
'descriptions + MCP schemas) from token-baseline.json; reduced = thin rule ' +
|
|
66
|
+
'projection, shared surfaces unchanged. Not a synthetic strawman (council Q4).',
|
|
67
|
+
components: metrics,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function loadMetrics(baselinePath: string): Metrics {
|
|
72
|
+
const parsed = JSON.parse(fs.readFileSync(baselinePath, 'utf-8')) as { metrics?: Record<string, unknown> };
|
|
73
|
+
const m = parsed.metrics ?? {};
|
|
74
|
+
return {
|
|
75
|
+
eager_rule_load: _num(m['eager_rule_load']),
|
|
76
|
+
thin_rule_load: _num(m['thin_rule_load']),
|
|
77
|
+
skill_descriptions: _num(m['skill_descriptions']),
|
|
78
|
+
command_descriptions: _num(m['command_descriptions']),
|
|
79
|
+
mcp_schemas: _num(m['mcp_schemas']),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function main(argv: string[]): number {
|
|
84
|
+
let baselinePath = BASELINE;
|
|
85
|
+
let format: 'text' | 'json' = 'text';
|
|
86
|
+
for (let i = 0; i < argv.length; i++) {
|
|
87
|
+
const a = argv[i] as string;
|
|
88
|
+
if (a === '--baseline') baselinePath = argv[++i] ?? baselinePath;
|
|
89
|
+
else if (a === '--format') format = (argv[++i] as 'text' | 'json') ?? 'text';
|
|
90
|
+
else if (a === '-h' || a === '--help') {
|
|
91
|
+
process.stdout.write(`usage: ${PROG} [--baseline PATH] [--format text|json]\n`);
|
|
92
|
+
return 0;
|
|
93
|
+
} else {
|
|
94
|
+
process.stderr.write(`${PROG}: error: unknown argument ${a}\n`);
|
|
95
|
+
return 2;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let report: BenchmarkReport;
|
|
99
|
+
try {
|
|
100
|
+
report = computeReport(loadMetrics(baselinePath));
|
|
101
|
+
} catch (exc) {
|
|
102
|
+
process.stderr.write(`${PROG}: cannot read baseline ${baselinePath}: ${String(exc)}\n`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
if (format === 'json') {
|
|
106
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
const pct = (report.reduction_ratio * 100).toFixed(1);
|
|
110
|
+
process.stdout.write(
|
|
111
|
+
`context token reduction (vs full always-loaded projection):\n` +
|
|
112
|
+
` full projection ${report.baseline_full_projection} tokens\n` +
|
|
113
|
+
` reduced (thin) ${report.reduced_projection} tokens\n` +
|
|
114
|
+
` saved ${report.saved} tokens (${pct}%)\n` +
|
|
115
|
+
` method ${report.method}\n`,
|
|
116
|
+
);
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1] ?? '')) {
|
|
121
|
+
process.exit(main(process.argv.slice(2)));
|
|
122
|
+
}
|
|
@@ -26,6 +26,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
26
26
|
import { REPO, LEDGER_REL, load_ledger, pointer_unresolved } from './check_claims.js';
|
|
27
27
|
import { collectSkillGaps } from './check_skill_gaps.js';
|
|
28
28
|
import { loadRows as loadComparisonRows } from './check_comparison.js';
|
|
29
|
+
import { computeStatus as domainSoundnessStatus } from './domain_soundness_status.js';
|
|
29
30
|
|
|
30
31
|
const _FILE = fileURLToPath(import.meta.url);
|
|
31
32
|
const OUT_REL = 'docs/proof.md';
|
|
@@ -118,30 +119,42 @@ function render(): string {
|
|
|
118
119
|
L.push('checks the wrong property is worse than none), so the number grows');
|
|
119
120
|
L.push('deliberately, not overnight.');
|
|
120
121
|
L.push('');
|
|
122
|
+
const ds = domainSoundnessStatus();
|
|
121
123
|
L.push('**Non-coding domain soundness — scoped, not proven.** The `finance` /');
|
|
122
124
|
L.push('`founder` / `ops` / `content` profiles sell concrete domain value (DCF,');
|
|
123
125
|
L.push('runway, RICE, incident command, messaging), but the skills are forged on');
|
|
124
126
|
L.push('TS/PHP — "promising, not proven" off those stacks. A disclaimer floor');
|
|
125
127
|
L.push('bounds *liability*, not *correctness*: a skill can be format-correct,');
|
|
126
|
-
L.push(
|
|
128
|
+
L.push(`disclaimered, and still embed a wrong domain assumption. Today **${ds.validated} of ${ds.total}**`);
|
|
127
129
|
L.push('default-surface domain skills carry a sourced `domain-truth` fixture');
|
|
128
|
-
L.push('(`./scripts-run src/scripts/domain_soundness_status`);
|
|
129
|
-
L.push('`unvalidated` and the validated count is CI-ratcheted.
|
|
130
|
-
L.push('
|
|
131
|
-
L.push('
|
|
132
|
-
L.push('
|
|
130
|
+
L.push('(`./scripts-run src/scripts/domain_soundness_status`); the rest are labeled');
|
|
131
|
+
L.push('`unvalidated` and the validated count is CI-ratcheted. The fixtures landed');
|
|
132
|
+
L.push('so far are the **deterministic** targets (runway, unit-economics, DCF,');
|
|
133
|
+
L.push('forecasting, scenario band/sensitivity), whose answer keys are computed');
|
|
134
|
+
L.push('from cited standard formulas — never the skill\'s own output; the');
|
|
135
|
+
L.push('**rubric** targets (incident command, messaging, fundraising, editorial)');
|
|
136
|
+
L.push('need domain-competent grounding and remain unvalidated, so validation');
|
|
137
|
+
L.push('lands deliberately — the gap is published, never implied away.');
|
|
133
138
|
L.push('');
|
|
134
|
-
L.push('**Second-brain substrate —
|
|
135
|
-
L.push('
|
|
136
|
-
L.push('
|
|
137
|
-
L.push('
|
|
138
|
-
L.push('
|
|
139
|
-
L.push('
|
|
140
|
-
L.push('
|
|
141
|
-
L.push('
|
|
142
|
-
L.push('
|
|
143
|
-
L.push('
|
|
144
|
-
L.push('
|
|
139
|
+
L.push('**Second-brain substrate — measured recall lift, honestly bounded.** On a');
|
|
140
|
+
L.push('deterministic multi-session recall corpus, the memory substrate beats a');
|
|
141
|
+
L.push('no-memory baseline AND an equal-byte placebo: memory-on **27/27** vs');
|
|
142
|
+
L.push('no-memory **10/27** vs placebo **9/27** (claude-haiku-4-5, 9 tasks × 3');
|
|
143
|
+
L.push('seeds, sign test **p = 0.031** for both pairings) — a real,');
|
|
144
|
+
L.push('placebo-controlled lift (`internal/bench/reports/second-brain-delta.json`).');
|
|
145
|
+
L.push('Scoped, not oversold: this is the context-value upper bound (perfect');
|
|
146
|
+
L.push('retrieval on a one-fact-per-task corpus), NOT retrieval precision under a');
|
|
147
|
+
L.push('large store; the lift concentrates exactly where memory is the only source');
|
|
148
|
+
L.push('and ties where the prompt self-contains the fact. Boundary vs a human PKM,');
|
|
149
|
+
L.push('and why the Obsidian export stays declined, are in');
|
|
150
|
+
L.push('[`docs/second-brain-scope.md`](second-brain-scope.md).');
|
|
151
|
+
L.push('');
|
|
152
|
+
L.push('A follow-up removed the perfect-retrieval assumption: against a store of');
|
|
153
|
+
L.push('keyword-overlapping confusers the REAL retrieval recalls the needed');
|
|
154
|
+
L.push('decision into the top-5 (9/9) and the model disambiguates it — retrieval-on');
|
|
155
|
+
L.push('**27/27** vs no-memory **5/27** and vs placebo **5/27** (p=0.008 both). The');
|
|
156
|
+
L.push('honest limit: the keyword scorer recalls but does not rank (mean tie-set');
|
|
157
|
+
L.push('3.3), which is the SQLite-FTS5 activation signal (ADR-116) at scale.');
|
|
145
158
|
L.push('');
|
|
146
159
|
L.push('## 3. Known limits (published, witness-tested)');
|
|
147
160
|
L.push('');
|
|
@@ -108,6 +108,57 @@ export function evaluatePair(
|
|
|
108
108
|
return { id: task.id, winner, length_delta, winner_is_longer };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Cohen's Kappa — chance-corrected agreement between two raters over the same
|
|
113
|
+
* categorical labels (road-to-retrieval-substrate-hardening B7b). 1 = perfect,
|
|
114
|
+
* 0 = chance-level, <0 = worse than chance. `NaN` when there is no data.
|
|
115
|
+
*/
|
|
116
|
+
export function cohensKappa(labelsA: readonly string[], labelsB: readonly string[]): number {
|
|
117
|
+
const n = Math.min(labelsA.length, labelsB.length);
|
|
118
|
+
if (n === 0) return Number.NaN;
|
|
119
|
+
const cats = new Set<string>();
|
|
120
|
+
for (let i = 0; i < n; i++) {
|
|
121
|
+
cats.add(labelsA[i] as string);
|
|
122
|
+
cats.add(labelsB[i] as string);
|
|
123
|
+
}
|
|
124
|
+
let observedAgree = 0;
|
|
125
|
+
const countA = new Map<string, number>();
|
|
126
|
+
const countB = new Map<string, number>();
|
|
127
|
+
for (let i = 0; i < n; i++) {
|
|
128
|
+
const a = labelsA[i] as string;
|
|
129
|
+
const b = labelsB[i] as string;
|
|
130
|
+
if (a === b) observedAgree += 1;
|
|
131
|
+
countA.set(a, (countA.get(a) ?? 0) + 1);
|
|
132
|
+
countB.set(b, (countB.get(b) ?? 0) + 1);
|
|
133
|
+
}
|
|
134
|
+
const po = observedAgree / n;
|
|
135
|
+
let pe = 0;
|
|
136
|
+
for (const c of cats) pe += ((countA.get(c) ?? 0) / n) * ((countB.get(c) ?? 0) / n);
|
|
137
|
+
if (pe === 1) return 1; // both raters unanimous on one label → perfect by convention
|
|
138
|
+
return Math.round(((po - pe) / (1 - pe)) * 10000) / 10000;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Second-independent-judge validation: Cohen's Kappa over the two judges'
|
|
143
|
+
* per-pair winner labels (aligned by task id). A LOW kappa means the win-rate
|
|
144
|
+
* verdict rests on an unreliable grader — the trustworthiness gap the
|
|
145
|
+
* token-saving live run surfaced (33% judge-inconsistency), distinct from the
|
|
146
|
+
* effect-significance the Wilcoxon/McNemar gates already test.
|
|
147
|
+
*/
|
|
148
|
+
export function judgeKappa(resultsA: readonly PairResult[], resultsB: readonly PairResult[]): number {
|
|
149
|
+
const byIdB = new Map(resultsB.map((r) => [r.id, r.winner]));
|
|
150
|
+
const a: string[] = [];
|
|
151
|
+
const b: string[] = [];
|
|
152
|
+
for (const r of resultsA) {
|
|
153
|
+
const bw = byIdB.get(r.id);
|
|
154
|
+
if (bw !== undefined) {
|
|
155
|
+
a.push(r.winner);
|
|
156
|
+
b.push(bw);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return cohensKappa(a, b);
|
|
160
|
+
}
|
|
161
|
+
|
|
111
162
|
export interface QualityAggregate {
|
|
112
163
|
total: number;
|
|
113
164
|
thin_wins: number;
|
|
@@ -37,10 +37,11 @@ const ROADMAP_ROOT = 'agents/roadmaps';
|
|
|
37
37
|
// CHECKBOX_RE = re.compile(r"^\s*[-*]\s+\[([ xX~\-])\]\s", re.MULTILINE)
|
|
38
38
|
const CHECKBOX_RE = /^[ \t]*[-*][ \t]+\[([ xX~\-])\][ \t\f\v]/m;
|
|
39
39
|
// PHASE_RE = re.compile(
|
|
40
|
-
// r"^(#{2,3})\s+Phase\s+(\d+[a-z]?|[IVX]+|[A-Z](?:\d+)?)"
|
|
40
|
+
// r"^(#{2,3})\s+Phase\s+(\d+(?:\.\d+)*[a-z]?|[IVX]+|[A-Z](?:\d+)?)"
|
|
41
41
|
// r"(?:[\s:—\-]+(.*?))?\s*$", re.MULTILINE)
|
|
42
|
+
// Numeric ids accept dotted sub-phases ("1.0", "4.1") — mirrors the dashboard's PHASE_RE.
|
|
42
43
|
const PHASE_RE =
|
|
43
|
-
/^(#{2,3})[ \t\f\v]+Phase[ \t\f\v]+(\d+[a-z]?|[IVX]+|[A-Z](?:\d+)?)(?:[\s:—\-]+(.*?))?[ \t\f\v]*$/gm;
|
|
44
|
+
/^(#{2,3})[ \t\f\v]+Phase[ \t\f\v]+(\d+(?:\.\d+)*[a-z]?|[IVX]+|[A-Z](?:\d+)?)(?:[\s:—\-]+(.*?))?[ \t\f\v]*$/gm;
|
|
44
45
|
// FRONTMATTER_RE = re.compile(r"\A---\n(.*?)\n---\s*\n", re.DOTALL)
|
|
45
46
|
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\s*\n/;
|
|
46
47
|
const DRAFT_VALUES: ReadonlySet<string> = new Set(['draft']);
|
package/src/scripts/condense.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
strip_source_prefix,
|
|
41
41
|
} from './_lib/agent_src.js';
|
|
42
42
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
43
|
+
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
43
44
|
import { project_settings_path, load_agent_settings } from './_lib/agent_settings.js';
|
|
44
45
|
import { info, success, flush_summary, resolve_level } from './_lib/script_output.js';
|
|
45
46
|
import {
|
|
@@ -1529,6 +1530,7 @@ export function generate_claude_commands(active_command_slugs: ReadonlySet<strin
|
|
|
1529
1530
|
const current_slugs = new Set<string>();
|
|
1530
1531
|
let count = 0;
|
|
1531
1532
|
let skipped = 0;
|
|
1533
|
+
let reserved = 0;
|
|
1532
1534
|
let rendered = 0;
|
|
1533
1535
|
const auto = _read_model_auto_switch() === 'auto';
|
|
1534
1536
|
for (const [source_file, slug] of iterCommands()) {
|
|
@@ -1539,6 +1541,15 @@ export function generate_claude_commands(active_command_slugs: ReadonlySet<strin
|
|
|
1539
1541
|
skipped += 1;
|
|
1540
1542
|
continue;
|
|
1541
1543
|
}
|
|
1544
|
+
// Never claim a `/name` that is a Claude Code built-in command or
|
|
1545
|
+
// bundled skill — a same-name entry shadows the built-in (e.g. a
|
|
1546
|
+
// `review` entry hides Claude Code's own /review). Nested
|
|
1547
|
+
// `/cluster:sub` slugs are hyphen-joined and cannot collide.
|
|
1548
|
+
// See src/scripts/_lib/claude_builtin_names.ts.
|
|
1549
|
+
if (is_claude_builtin_name(slug)) {
|
|
1550
|
+
reserved += 1;
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1542
1553
|
current_slugs.add(slug);
|
|
1543
1554
|
|
|
1544
1555
|
const skill_dir = path.join(MODULE_STATE.CLAUDE_SKILLS_DIR, slug);
|
|
@@ -1588,6 +1599,9 @@ export function generate_claude_commands(active_command_slugs: ReadonlySet<strin
|
|
|
1588
1599
|
if (skipped) {
|
|
1589
1600
|
msg += ` (${skipped} skipped — same-name skill exists)`;
|
|
1590
1601
|
}
|
|
1602
|
+
if (reserved) {
|
|
1603
|
+
msg += ` (${reserved} withheld — Claude Code built-in name)`;
|
|
1604
|
+
}
|
|
1591
1605
|
if (removed_dirs) {
|
|
1592
1606
|
msg += ` (${removed_dirs} stale dirs removed)`;
|
|
1593
1607
|
}
|
|
Binary file
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
iter_commands,
|
|
25
25
|
strip_source_prefix,
|
|
26
26
|
} from './_lib/agent_src.js';
|
|
27
|
+
import { computeStatus as domainSoundnessStatus } from './domain_soundness_status.js';
|
|
27
28
|
|
|
28
29
|
const _HERE = fileURLToPath(import.meta.url);
|
|
29
30
|
|
|
@@ -398,6 +399,7 @@ export function _render_catalog(
|
|
|
398
399
|
const publicRules = rules.filter((r) => !INTERNAL_RULES.has(r.name));
|
|
399
400
|
const publicCommands = commands.filter((c) => c.kind === 'command');
|
|
400
401
|
const total = skills.length + publicRules.length + publicCommands.length + guidelines.length;
|
|
402
|
+
const ds = domainSoundnessStatus();
|
|
401
403
|
const parts = [
|
|
402
404
|
'# agent-config — Public Catalog',
|
|
403
405
|
'',
|
|
@@ -419,8 +421,9 @@ export function _render_catalog(
|
|
|
419
421
|
'',
|
|
420
422
|
'> **Non-coding domain correctness is scoped, not proven.** The',
|
|
421
423
|
'> `finance` / `founder` / `ops` / `content` skills are forged on TS/PHP',
|
|
422
|
-
|
|
423
|
-
'>
|
|
424
|
+
`> codebases; ${ds.validated} of ${ds.total} default-surface domain skills carry a sourced`,
|
|
425
|
+
'> `evals/domain-truth.json` fixture (deterministic targets, keys from',
|
|
426
|
+
'> cited formulas); the rest are labeled `unvalidated` until they pass one',
|
|
424
427
|
'> (`./scripts-run src/scripts/domain_soundness_status`). A disclaimer',
|
|
425
428
|
'> floor bounds liability, not correctness — see [the proof page](proof.md).',
|
|
426
429
|
'',
|
|
@@ -42,6 +42,31 @@ if ! ./scripts-run src/scripts/check_no_conflict_markers --quiet; then
|
|
|
42
42
|
fail=1
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
|
+
# Changed-files static pass (typecheck + lint) — the deterministic backstop to
|
|
46
|
+
# the behavioural verify-before-complete rule (#818). Catches the compile/lint
|
|
47
|
+
# errors that reach remote CI "Static Checks" (e.g. TS18048). Runs only when
|
|
48
|
+
# the push touches .ts, so docs-only pushes stay fast. Skip a genuine WIP push
|
|
49
|
+
# with AGENT_CONFIG_SKIP_PREPUSH_STATIC=1 (the agent cannot use --no-verify).
|
|
50
|
+
echo "🔍 Static check on changed TypeScript (typecheck + lint)..."
|
|
51
|
+
if [ "${AGENT_CONFIG_SKIP_PREPUSH_STATIC:-}" = "1" ]; then
|
|
52
|
+
echo "⏭️ skipped via AGENT_CONFIG_SKIP_PREPUSH_STATIC=1"
|
|
53
|
+
else
|
|
54
|
+
base="$(git merge-base HEAD origin/main 2>/dev/null || echo HEAD~1)"
|
|
55
|
+
changed_ts="$(git diff --name-only --diff-filter=d "$base"...HEAD -- '*.ts' 2>/dev/null || true)"
|
|
56
|
+
if [ -n "$changed_ts" ]; then
|
|
57
|
+
if ! npx eslint $changed_ts; then
|
|
58
|
+
echo "❌ ESLint failed on changed TypeScript. Fix before pushing (or AGENT_CONFIG_SKIP_PREPUSH_STATIC=1 for a WIP push)."
|
|
59
|
+
fail=1
|
|
60
|
+
fi
|
|
61
|
+
if ! npm run --silent typecheck; then
|
|
62
|
+
echo "❌ Typecheck (tsc) failed — this is the class of error remote CI 'Static Checks' catches (#818). Fix before pushing."
|
|
63
|
+
fail=1
|
|
64
|
+
fi
|
|
65
|
+
else
|
|
66
|
+
echo "⏭️ no changed .ts vs origin/main — skipping."
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
45
70
|
if [ $fail -ne 0 ]; then
|
|
46
71
|
echo ""
|
|
47
72
|
echo " Push blocked — fix the failures above and re-push."
|
package/src/scripts/install.ts
CHANGED
|
@@ -72,6 +72,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
72
72
|
import type * as YamlModule from 'yaml';
|
|
73
73
|
|
|
74
74
|
import { build_merge_entries } from './_lib/json_pointers.js';
|
|
75
|
+
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
75
76
|
import * as installed_lock from './_lib/installed_lock.js';
|
|
76
77
|
import * as surface_tiers from './_lib/surface_tiers.js';
|
|
77
78
|
import * as global_deploy_inventory from './_lib/global_deploy_inventory.js';
|
|
@@ -2847,15 +2848,40 @@ function _deploy_claude_desktop(
|
|
|
2847
2848
|
* discovery (revert = one package release, no per-machine gating).
|
|
2848
2849
|
* Tier-2/internal flat commands stay command files. A command whose name
|
|
2849
2850
|
* collides with a real skill is skipped (the skill already owns `/name`).
|
|
2851
|
+
*
|
|
2852
|
+
* Reserved-name floor: a flat `/name` that equals a Claude Code BUILT-IN
|
|
2853
|
+
* command or bundled skill (see `_lib/claude_builtin_names.ts`) would shadow
|
|
2854
|
+
* the built-in — e.g. a `review` wrapper hides Claude Code's own `/review`.
|
|
2855
|
+
* Such slugs get NEITHER a skill wrapper NOR a flat command file on this
|
|
2856
|
+
* anchor (their nested `/cluster:sub` commands are untouched); the flat file
|
|
2857
|
+
* is removed so a future upstream flat-discovery fix cannot resurrect the
|
|
2858
|
+
* shadowing. The suite complements the host — it never overlays built-ins.
|
|
2850
2859
|
*/
|
|
2851
2860
|
const _CLAUDE_FLAT_WRAPPER_EXTRA = new Set(['commit']); // essential; absent from the manifest
|
|
2852
2861
|
export function _apply_claude_flat_command_wrappers(
|
|
2853
2862
|
anchor: string,
|
|
2854
2863
|
package_root: string,
|
|
2855
2864
|
current_files: Set<string>,
|
|
2856
|
-
): { wrapped: string[]; collisions: string[] } {
|
|
2865
|
+
): { wrapped: string[]; collisions: string[]; reserved: string[] } {
|
|
2857
2866
|
const wrapped: string[] = [];
|
|
2858
2867
|
const collisions: string[] = [];
|
|
2868
|
+
const reserved: string[] = [];
|
|
2869
|
+
// Reserved-name sweep over ALL flat command files (any tier): a name
|
|
2870
|
+
// shadowing a Claude Code built-in never ships on the claude-code anchor.
|
|
2871
|
+
const commands_dir = path.join(anchor, 'commands');
|
|
2872
|
+
let flat_entries: string[] = [];
|
|
2873
|
+
try {
|
|
2874
|
+
flat_entries = fs.readdirSync(commands_dir).filter((f) => f.endsWith('.md'));
|
|
2875
|
+
} catch {
|
|
2876
|
+
// No commands dir → nothing to sweep.
|
|
2877
|
+
}
|
|
2878
|
+
for (const fname of flat_entries.sort()) {
|
|
2879
|
+
const slug = fname.slice(0, -'.md'.length);
|
|
2880
|
+
if (!is_claude_builtin_name(slug)) continue;
|
|
2881
|
+
fs.rmSync(path.join(commands_dir, fname), { force: true });
|
|
2882
|
+
current_files.delete(`commands/${fname}`);
|
|
2883
|
+
reserved.push(slug);
|
|
2884
|
+
}
|
|
2859
2885
|
// Tier-0/1 visible command slugs from the locked discovery manifest.
|
|
2860
2886
|
const eligible = new Set<string>(_CLAUDE_FLAT_WRAPPER_EXTRA);
|
|
2861
2887
|
try {
|
|
@@ -2892,7 +2918,7 @@ export function _apply_claude_flat_command_wrappers(
|
|
|
2892
2918
|
current_files.add(`skills/${slug}/SKILL.md`);
|
|
2893
2919
|
wrapped.push(slug);
|
|
2894
2920
|
}
|
|
2895
|
-
return { wrapped, collisions };
|
|
2921
|
+
return { wrapped, collisions, reserved };
|
|
2896
2922
|
}
|
|
2897
2923
|
|
|
2898
2924
|
function _deploy_global_content(
|
|
@@ -2949,6 +2975,13 @@ function _deploy_global_content(
|
|
|
2949
2975
|
'skill wrappers (Claude Code flat-command discovery workaround)',
|
|
2950
2976
|
);
|
|
2951
2977
|
}
|
|
2978
|
+
if (res.reserved.length > 0 && !state.QUIET) {
|
|
2979
|
+
info(
|
|
2980
|
+
` claude-code: ${res.reserved.length} flat command(s) withheld — name is a ` +
|
|
2981
|
+
`Claude Code built-in (${res.reserved.join(', ')}); nested /cluster:sub ` +
|
|
2982
|
+
'commands remain available',
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2952
2985
|
}
|
|
2953
2986
|
|
|
2954
2987
|
const missing_targets = _verify_deploy_targets(anchor, plan);
|
|
Binary file
|
|
@@ -14,6 +14,7 @@ import * as path from 'node:path';
|
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
15
15
|
|
|
16
16
|
import { command_slug } from './_lib/agent_src.js';
|
|
17
|
+
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
17
18
|
|
|
18
19
|
const SCRIPTS_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
const ROOT = path.dirname(path.dirname(SCRIPTS_DIR));
|
|
@@ -97,6 +98,18 @@ function frontmatterNameFromFile(p: string): string | null {
|
|
|
97
98
|
return _frontmatter_name(fs.readFileSync(p, 'utf-8'));
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
const USER_INVOCABLE_FALSE_RE = /^user-invocable:\s*false\s*$/m;
|
|
102
|
+
|
|
103
|
+
/** True when the SKILL.md opts out of slash registration (`user-invocable: false`). */
|
|
104
|
+
export function _opts_out_of_slash(text: string): boolean {
|
|
105
|
+
if (!text.startsWith('---')) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const end = text.indexOf('\n---', 3);
|
|
109
|
+
const block = end !== -1 ? text.slice(3, end) : text;
|
|
110
|
+
return USER_INVOCABLE_FALSE_RE.test(block);
|
|
111
|
+
}
|
|
112
|
+
|
|
100
113
|
export function _spec_violation(name: string): string | null {
|
|
101
114
|
if (name.length > MAX_NAME_LEN) {
|
|
102
115
|
return `longer than ${MAX_NAME_LEN} chars`;
|
|
@@ -178,6 +191,22 @@ export function check_skills(): string[] {
|
|
|
178
191
|
'requires the folder name to match the `name:` field',
|
|
179
192
|
);
|
|
180
193
|
}
|
|
194
|
+
// Reserved-name floor: a skill named after a Claude Code built-in
|
|
195
|
+
// command / bundled skill shadows the built-in when projected as
|
|
196
|
+
// `/name` (e.g. a `mcp` skill hides Claude Code's own /mcp dialog).
|
|
197
|
+
// Opting out of slash registration (`user-invocable: false`) keeps
|
|
198
|
+
// the skill model-loadable while the host retains the /name.
|
|
199
|
+
// Reserved set: src/scripts/_lib/claude_builtin_names.ts.
|
|
200
|
+
if (
|
|
201
|
+
is_claude_builtin_name(dirname) &&
|
|
202
|
+
!_opts_out_of_slash(fs.readFileSync(md, 'utf-8'))
|
|
203
|
+
) {
|
|
204
|
+
violations.push(
|
|
205
|
+
`${rel}: \`${dirname}\` is a Claude Code built-in command name — ` +
|
|
206
|
+
'add `user-invocable: false` (model-only invocation) or rename ' +
|
|
207
|
+
'the skill; the suite must never shadow a host built-in',
|
|
208
|
+
);
|
|
209
|
+
}
|
|
181
210
|
}
|
|
182
211
|
return violations;
|
|
183
212
|
}
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
* - sessions scale — `agents/knowledge/sessions/` > 50 pages
|
|
15
15
|
* → design fold-through-consolidate-gate for tracked pages
|
|
16
16
|
* - type scale — any single memory/knowledge type > 200 files
|
|
17
|
-
* →
|
|
17
|
+
* → rank via `_lib/lexical_index.ts` (hand-rolled BM25 +
|
|
18
|
+
* trigram, no engine fork / no FTS5; ADR-061 honoured)
|
|
18
19
|
* - corpus scale — > 500 files across all types
|
|
19
|
-
* → same
|
|
20
|
+
* → same lexical-index activation path
|
|
20
21
|
* - hot-context size — `agents/runtime/state/hot-context.md` > 600 tokens
|
|
21
22
|
* (estimated at 4 chars/token) → trim schema / fix the
|
|
22
23
|
* deterministic writer
|
|
@@ -151,7 +152,7 @@ export function runChecks(root: string): Warning[] {
|
|
|
151
152
|
metric: `${n}/${TYPE_FILES_MAX}`,
|
|
152
153
|
message:
|
|
153
154
|
`type '${type}' holds ${n} files (> ${TYPE_FILES_MAX}). ` +
|
|
154
|
-
'Activation path (
|
|
155
|
+
'Activation path (resolved, road-to-retrieval-substrate-hardening B2): rank via the hand-rolled BM25 + trigram index in `_lib/lexical_index.ts` (pure stdlib, NO engine fork / NO SQLite-FTS5, ADR-061 honoured). Measured lift: mean tie-set 3.333 → 1.0 (internal/bench/reports/lexical-ranking.json). Wire it lazily at first lookup + a stat-index, no vectors, no service.',
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
}
|