@event4u/agent-config 9.24.0 → 9.26.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 +136 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/conformance.md +182 -0
- package/dist/agent-src/commands/analyze/inbox.md +33 -2
- package/dist/agent-src/commands/review/changes.md +6 -5
- package/dist/agent-src/commands/roadmap/next.md +194 -0
- package/dist/agent-src/commands/roadmap.md +12 -4
- package/dist/agent-src/contexts/execution/subagent-routing.md +4 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +2 -0
- package/dist/agent-src/rules/delegation-policy.md +3 -2
- package/dist/agent-src/rules/design-fidelity.md +17 -1
- package/dist/agent-src/rules/design-review-after-ui-write.md +116 -0
- package/dist/agent-src/rules/evaluator-independence.md +111 -0
- package/dist/agent-src/rules/session-canary.md +15 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +176 -0
- package/dist/agent-src/rules/token-budget-discipline.md +6 -3
- package/dist/agent-src/rules/ui-audit-gate.md +3 -0
- package/dist/agent-src/rules/user-interaction.md +23 -0
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +1 -1
- package/dist/agent-src/skills/recursive-verification/SKILL.md +13 -0
- package/dist/agent-src/skills/rule-writing/SKILL.md +45 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +114 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +4 -1
- package/dist/cli-delegate/cmd_route_explain.js +4 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +146 -28
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +5 -5
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +16 -11
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +12 -7
- package/dist/hooks/dispatch.js +1501 -511
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/router.json +1 -1
- package/dist/server/nicknameResolver.js +116 -0
- package/dist/server/nicknameResolver.js.map +1 -0
- package/dist/server/routes/ping.js +29 -11
- package/dist/server/routes/ping.js.map +1 -1
- package/dist/shared/interactiveContext.js +73 -0
- package/dist/shared/interactiveContext.js.map +1 -0
- package/dist/shared/nicknamePrefill.js +59 -0
- package/dist/shared/nicknamePrefill.js.map +1 -0
- package/dist/shared/settingsAsks.js +128 -0
- package/dist/shared/settingsAsks.js.map +1 -0
- package/dist/shared/settingsConsent.js +93 -0
- package/dist/shared/settingsConsent.js.map +1 -0
- package/package.json +1 -1
- package/src/config/agents-paths.yml +15 -0
- package/src/config/evaluator-budgets.json +68 -68
- package/src/config/gate-coverage.yml +131 -5
- package/src/config/gate-violation-baselines.json +11 -0
- package/src/scripts/_cli/cmd_route_audit.ts +4 -1
- package/src/scripts/_cli/cmd_route_explain.ts +9 -1
- package/src/scripts/_dispatch.bash +12 -0
- package/src/scripts/ai-video/stitch.sh +27 -9
- package/src/scripts/before_complete_hook.ts +146 -5
- package/src/scripts/bench_ab_v2_run.ts +272 -16
- package/src/scripts/bench_ab_v2_stats.ts +156 -3
- package/src/scripts/check_completion_review.ts +80 -3
- package/src/scripts/check_corpus_staleness.ts +692 -0
- package/src/scripts/check_depth_budget.ts +295 -0
- package/src/scripts/check_gate_coverage.ts +67 -0
- package/src/scripts/check_hook_bundle_freshness.ts +183 -0
- package/src/scripts/conformance_scan.ts +422 -0
- package/src/scripts/dispatch_r2_reviewer.ts +31 -2
- package/src/scripts/git_authorization_hook.ts +328 -0
- package/src/scripts/hook_manifest.yaml +69 -9
- package/src/scripts/hooks/block_no_verify.ts +3 -1
- package/src/scripts/hooks/block_unauthorized_git.ts +371 -0
- package/src/scripts/hooks/concern_registry.ts +8 -0
- package/src/scripts/hooks/design_slop_hook.ts +13 -1
- package/src/scripts/hooks/dispatch_hook.ts +97 -2
- package/src/scripts/hooks/evidence_independence.ts +368 -0
- package/src/scripts/language_mirror_hook.ts +456 -0
- package/src/scripts/lint_abstraction_thresholds.ts +41 -8
- package/src/scripts/lint_agents_layout.ts +2 -0
- package/src/scripts/lint_decision_impact.ts +246 -0
- package/src/scripts/lint_example_fences.ts +609 -0
- package/src/scripts/lint_hook_manifest.ts +29 -0
- package/src/scripts/lint_rule_interactions.ts +90 -0
- package/src/scripts/lint_rule_migration_ledger.ts +480 -0
- package/src/scripts/lint_skill_descriptions.ts +86 -2
- package/src/scripts/memory_learn_hook.ts +109 -6
- package/src/scripts/pr_url_reminder_hook.ts +17 -2
- package/src/scripts/release.ts +38 -1
- package/src/scripts/schemas/review-findings.schema.json +99 -0
- package/src/scripts/schemas/rule.schema.json +10 -0
- package/src/shared/interactiveContext.ts +98 -0
- package/src/shared/nicknamePrefill.ts +96 -0
- package/src/shared/settingsAsks.ts +176 -0
- package/src/shared/settingsConsent.ts +136 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* lint_decision_impact — the evidence bar on the one classification that permits removal.
|
|
4
|
+
*
|
|
5
|
+
* `rule.schema.json` gained a `decision_impact` class: what is at stake when a
|
|
6
|
+
* rule fires, as distinct from `type`, which is a *delivery* class (how the rule
|
|
7
|
+
* reaches the model). Kernel-membership and always-loaded-budget arguments were
|
|
8
|
+
* being made with no stated impact class at all — a rule was argued into the
|
|
9
|
+
* always-set on prose.
|
|
10
|
+
*
|
|
11
|
+
* The schema can express the enum. It cannot express the part that matters:
|
|
12
|
+
* **`already-complied-with` is the value that permits deleting a rule**, and a
|
|
13
|
+
* classification that cheap needs something behind it. "The model probably does
|
|
14
|
+
* this anyway" is the least effortful possible way to remove a floor that was
|
|
15
|
+
* working precisely because nothing had crossed it — the failure
|
|
16
|
+
* `active-remediation` names as removing a rule that is merely quiet.
|
|
17
|
+
*
|
|
18
|
+
* So this gate enforces the pairing the schema cannot:
|
|
19
|
+
*
|
|
20
|
+
* - `decision_impact: already-complied-with` REQUIRES `decision_impact_evidence`
|
|
21
|
+
* — a pointer a reviewer can follow to the observation.
|
|
22
|
+
* - Any other value FORBIDS it, so the field cannot become decoration attached
|
|
23
|
+
* to classifications it says nothing about.
|
|
24
|
+
*
|
|
25
|
+
* ## Why it currently finds nothing, and why that is the intended state
|
|
26
|
+
*
|
|
27
|
+
* The field is OPTIONAL and backfill is opportunistic, because a batch edit
|
|
28
|
+
* across the rule set trips `check_kernel_prefix_stability` and that gate is
|
|
29
|
+
* right to fire. So today every rule is unclassified and this gate is
|
|
30
|
+
* verified-empty over 111 rules: it reads a real corpus, finds no violation
|
|
31
|
+
* because there is no adopter yet, and fires the moment the first
|
|
32
|
+
* removal-permitting classification lands without evidence. That is a gate
|
|
33
|
+
* waiting at the right door, not a gate scanning nothing — the distinction this
|
|
34
|
+
* repository pays for elsewhere.
|
|
35
|
+
*
|
|
36
|
+
* Exit codes: 0 = clean, 1 = violations, 2 = usage.
|
|
37
|
+
*/
|
|
38
|
+
import * as fs from 'node:fs';
|
|
39
|
+
import * as os from 'node:os';
|
|
40
|
+
import * as path from 'node:path';
|
|
41
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
42
|
+
import { parse as parseYaml } from 'yaml';
|
|
43
|
+
|
|
44
|
+
import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
|
|
45
|
+
import { reportScanned } from './_lib/scan_scope.js';
|
|
46
|
+
|
|
47
|
+
const SCRIPTS_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
const REAL_REPO_ROOT = path.dirname(path.dirname(SCRIPTS_DIR));
|
|
49
|
+
|
|
50
|
+
/** The one value that permits removal, and therefore the one carrying a bar. */
|
|
51
|
+
export const REMOVAL_PERMITTING = 'already-complied-with';
|
|
52
|
+
|
|
53
|
+
export interface Violation {
|
|
54
|
+
readonly rule: string;
|
|
55
|
+
readonly message: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function frontmatterOf(text: string): Record<string, unknown> | null {
|
|
59
|
+
const m = /^---[ \t]*\n([\s\S]*?)\n---/.exec(text);
|
|
60
|
+
if (!m?.[1]) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const doc = parseYaml(m[1]) as unknown;
|
|
65
|
+
return typeof doc === 'object' && doc !== null ? (doc as Record<string, unknown>) : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function checkRule(rule: string, fm: Record<string, unknown>): Violation[] {
|
|
72
|
+
const impact = fm['decision_impact'];
|
|
73
|
+
const evidence = fm['decision_impact_evidence'];
|
|
74
|
+
if (impact === undefined) {
|
|
75
|
+
if (evidence !== undefined) {
|
|
76
|
+
return [{
|
|
77
|
+
rule,
|
|
78
|
+
message:
|
|
79
|
+
'`decision_impact_evidence` is set with no `decision_impact` — evidence for a ' +
|
|
80
|
+
'classification that was never made says nothing a reviewer can use',
|
|
81
|
+
}];
|
|
82
|
+
}
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
if (impact === REMOVAL_PERMITTING) {
|
|
86
|
+
if (typeof evidence !== 'string' || evidence.trim() === '') {
|
|
87
|
+
return [{
|
|
88
|
+
rule,
|
|
89
|
+
message:
|
|
90
|
+
`\`decision_impact: ${REMOVAL_PERMITTING}\` is the classification that permits ` +
|
|
91
|
+
'deleting this rule, so it requires `decision_impact_evidence` — a pointer a ' +
|
|
92
|
+
'reviewer can follow to the observation that the model complies without it',
|
|
93
|
+
}];
|
|
94
|
+
}
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
if (evidence !== undefined) {
|
|
98
|
+
return [{
|
|
99
|
+
rule,
|
|
100
|
+
message:
|
|
101
|
+
`\`decision_impact_evidence\` is only meaningful for \`${REMOVAL_PERMITTING}\`; ` +
|
|
102
|
+
`this rule is \`${String(impact)}\`, so the evidence field is decoration`,
|
|
103
|
+
}];
|
|
104
|
+
}
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseArgs(argv: readonly string[]): { quiet: boolean } {
|
|
109
|
+
let quiet = false;
|
|
110
|
+
for (const a of argv) {
|
|
111
|
+
if (a === '--quiet') { quiet = true; }
|
|
112
|
+
else if (a === '-h' || a === '--help') {
|
|
113
|
+
process.stdout.write('usage: lint_decision_impact [--quiet]\n');
|
|
114
|
+
process.exit(0);
|
|
115
|
+
} else {
|
|
116
|
+
process.stderr.write(`lint_decision_impact: unrecognized argument: ${a}\n`);
|
|
117
|
+
process.exit(2);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { quiet };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function selfTest(): number {
|
|
124
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ldi-'));
|
|
125
|
+
const mk = (fmLines: string): string => {
|
|
126
|
+
const root = fs.mkdtempSync(path.join(tmp, 'repo-'));
|
|
127
|
+
const p = path.join(root, 'src', 'rules', 'r.md');
|
|
128
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
129
|
+
fs.writeFileSync(p, `---\ntype: "auto"\n${fmLines}---\n\n# R\n`, 'utf-8');
|
|
130
|
+
return root;
|
|
131
|
+
};
|
|
132
|
+
const run = (root: string): number => {
|
|
133
|
+
process.env['LINT_DECISION_IMPACT_ROOT'] = root;
|
|
134
|
+
try {
|
|
135
|
+
return runGateCli(REAL_REPO_ROOT, 'src/scripts/lint_decision_impact.ts', ['--quiet'], root);
|
|
136
|
+
} finally {
|
|
137
|
+
delete process.env['LINT_DECISION_IMPACT_ROOT'];
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
return runSelfTest({
|
|
142
|
+
gate: 'lint_decision_impact',
|
|
143
|
+
minCases: 3,
|
|
144
|
+
minRejectCases: 2,
|
|
145
|
+
cases: [
|
|
146
|
+
{
|
|
147
|
+
name: 'a rule with no classification passes — the field is optional by design',
|
|
148
|
+
expect: 'accept',
|
|
149
|
+
run: () => run(mk('')),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'the removal-permitting class without evidence is rejected',
|
|
153
|
+
expect: 'reject',
|
|
154
|
+
run: () => run(mk('decision_impact: already-complied-with\n')),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'the removal-permitting class with evidence passes',
|
|
158
|
+
expect: 'accept',
|
|
159
|
+
run: () =>
|
|
160
|
+
run(mk('decision_impact: already-complied-with\ndecision_impact_evidence: "PR #123 — 20 trajectories with the rule removed, no regression"\n')),
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'evidence attached to a non-removal class is rejected as decoration',
|
|
164
|
+
expect: 'reject',
|
|
165
|
+
run: () =>
|
|
166
|
+
run(mk('decision_impact: overrides-model-default\ndecision_impact_evidence: "PR #123"\n')),
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
} finally {
|
|
171
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function main(argv?: readonly string[]): number {
|
|
176
|
+
const raw = argv ?? process.argv.slice(2);
|
|
177
|
+
if (raw.includes('--self-test')) {
|
|
178
|
+
return selfTest();
|
|
179
|
+
}
|
|
180
|
+
const args = parseArgs(raw);
|
|
181
|
+
const root = process.env['LINT_DECISION_IMPACT_ROOT'] ?? REAL_REPO_ROOT;
|
|
182
|
+
const dir = path.join(root, 'src', 'rules');
|
|
183
|
+
|
|
184
|
+
let names: string[] = [];
|
|
185
|
+
try {
|
|
186
|
+
names = fs.readdirSync(dir).filter((n) => n.endsWith('.md')).sort();
|
|
187
|
+
} catch {
|
|
188
|
+
names = [];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const violations: Violation[] = [];
|
|
192
|
+
let scanned = 0;
|
|
193
|
+
for (const n of names) {
|
|
194
|
+
const fm = frontmatterOf(fs.readFileSync(path.join(dir, n), 'utf-8'));
|
|
195
|
+
if (fm === null) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
scanned += 1;
|
|
199
|
+
violations.push(...checkRule(n.slice(0, -3), fm));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (violations.length > 0) {
|
|
203
|
+
process.stderr.write(`❌ lint_decision_impact: ${String(violations.length)} issue(s):\n`);
|
|
204
|
+
for (const v of violations) {
|
|
205
|
+
process.stderr.write(` • ${v.rule}: ${v.message}\n`);
|
|
206
|
+
}
|
|
207
|
+
} else if (!args.quiet) {
|
|
208
|
+
const classified = names.filter((n) => {
|
|
209
|
+
const fm = frontmatterOf(fs.readFileSync(path.join(dir, n), 'utf-8'));
|
|
210
|
+
return fm !== null && fm['decision_impact'] !== undefined;
|
|
211
|
+
}).length;
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
`✅ decision-impact clean — ${String(scanned)} rule(s), ${String(classified)} classified.\n`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
reportScanned({
|
|
218
|
+
gate: 'lint_decision_impact',
|
|
219
|
+
scanned,
|
|
220
|
+
units: 'rule(s)',
|
|
221
|
+
roots: ['src/rules'],
|
|
222
|
+
});
|
|
223
|
+
return violations.length > 0 ? 1 : 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
227
|
+
function _isCliEntry(): boolean {
|
|
228
|
+
if (process.argv[1] === undefined) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
return (
|
|
236
|
+
fs.realpathSync(fileURLToPath(import.meta.url)) ===
|
|
237
|
+
fs.realpathSync(path.resolve(process.argv[1]))
|
|
238
|
+
);
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (_isCliEntry() || process.argv[1] === _HERE) {
|
|
245
|
+
process.exit(main());
|
|
246
|
+
}
|