@event4u/agent-config 9.33.0 → 9.35.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 +154 -145
- package/CONTRIBUTING.md +23 -14
- package/dist/agent-src/commands/agent-handoff.md +15 -3
- package/dist/agent-src/commands/roadmap/next.md +37 -5
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
- package/dist/agent-src/scripts/roadmap_gates.ts +119 -4
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +21 -12
- package/dist/agent-src/skills/design-tokens/SKILL.md +15 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +24 -0
- package/dist/agent-src/skills/rule-writing/SKILL.md +23 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +80 -2
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +5 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +24 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +25 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/dist/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/dist/cli/registry.js +2 -2
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-PMQ6Z3T3.js → chunk-2TGMGLBL.js} +11 -4
- package/dist/cli-delegate/{chunk-YPAWXXXR.js → chunk-55RI5UXK.js} +17 -0
- package/dist/cli-delegate/{chunk-MA4GFWDY.js → chunk-H6F2CQFI.js} +1 -1
- package/dist/cli-delegate/{chunk-2WELHO3P.js → chunk-I5EAFRII.js} +35 -0
- package/dist/cli-delegate/{chunk-3EWYC3YK.js → chunk-UEZ7D35A.js} +4 -3
- package/dist/cli-delegate/{chunk-UVKNIJH2.js → chunk-ZAXH3ZWU.js} +3 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- 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 +3 -3
- 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 +7 -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 +13 -13
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +2 -2
- package/dist/discovery/dormancy-report.md +17 -0
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +3 -3
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +3 -3
- package/dist/hooks/dispatch.js +355 -85
- package/dist/install/install.mjs +3 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +192 -12
- package/dist/server/schemas/settings.js +1 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +3 -2
- package/docs/guidelines/agent-infra/failure-signatures.md +47 -12
- package/package.json +2 -2
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
- package/src/agent-src/scripts/roadmap_gates.ts +119 -4
- package/src/agent-src/templates/scripts/memory_lookup.ts +1 -2
- package/src/agent-src/templates/scripts/memory_signal.ts +2 -4
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
- package/src/config/agent-settings.template.yml +11 -2
- package/src/config/quorum-attendance-budget.json +11 -3
- package/src/scripts/_cli/cmd_session_recycle.ts +6 -1
- package/src/scripts/_dispatch.bash +6 -2
- package/src/scripts/_lib/env_kill_switch.ts +37 -0
- package/src/scripts/_lib/security_lint.ts +18 -4
- package/src/scripts/_lib/self_repair.ts +60 -0
- package/src/scripts/_lib/self_repair_store.ts +65 -5
- package/src/scripts/_lib/session_register.ts +67 -1
- package/src/scripts/_lib/subagent_capsule.ts +78 -1
- package/src/scripts/ai_council/config.ts +60 -0
- package/src/scripts/ai_council/events_log.ts +105 -6
- package/src/scripts/ai_council/orchestrator.ts +38 -0
- package/src/scripts/ai_council/prompts.ts +165 -3
- package/src/scripts/ai_council/quorum.ts +87 -0
- package/src/scripts/ai_team/review_gate.ts +2 -2
- package/src/scripts/build_discovery_manifest.ts +165 -0
- package/src/scripts/check_claims.ts +51 -2
- package/src/scripts/ci_time_ratio.ts +3 -1
- package/src/scripts/council_cli.ts +36 -1
- package/src/scripts/discovery_graph.ts +201 -27
- package/src/scripts/dispatch_r2_reviewer.ts +102 -1
- package/src/scripts/hooks/turn_end_gate_hook.ts +172 -11
- package/src/scripts/lint_framework_leakage.ts +195 -28
- package/src/scripts/lint_framework_leakage_allowlist.json +94 -86
- package/src/scripts/lint_handoffs.ts +92 -2
- package/src/scripts/lint_hedge_words.ts +317 -0
- package/src/scripts/lint_mcp_config_security.ts +1 -1
- package/src/scripts/lint_skill_frontmatter_safety.ts +1 -1
- package/src/scripts/memory_lookup.ts +1 -2
- package/src/scripts/schemas/command.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/self_repair_cli.ts +26 -1
- package/src/scripts/session_register_hook.ts +238 -18
- package/src/scripts/sessions_cli.ts +377 -12
- package/src/scripts/skill_linter.ts +58 -2
- package/src/server/schemas/settings.ts +3 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Hedge-word lint — diff-scoped, ADVISORY, with a declared escalation trigger.
|
|
4
|
+
*
|
|
5
|
+
* `bench_honesty_score.ts` has measured hedging in agent OUTPUT since it was
|
|
6
|
+
* written (`HEDGE_WORDS`, `countHedgeWords`). Nothing has ever looked at the
|
|
7
|
+
* hedging in this package's own authored prose, which is where a hedge does the
|
|
8
|
+
* most damage: a rule that says a gate "should probably" fire is a rule nobody
|
|
9
|
+
* can be held to. This linter closes that half, reusing the same lexicon rather
|
|
10
|
+
* than restating a second list that would drift from it.
|
|
11
|
+
*
|
|
12
|
+
* ── Why diff-scoped, not corpus-wide ────────────────────────────────────────
|
|
13
|
+
* ADR-218:93 settled the question for prose-shape tooling outright: the wanted
|
|
14
|
+
* instrument is "one that watches diffs, not one that counts the corpus". A
|
|
15
|
+
* corpus count over 116 rules and 289 skills produces a number nobody can act
|
|
16
|
+
* on — every reading is dominated by prose written years apart under different
|
|
17
|
+
* conventions, and no single commit can move it.
|
|
18
|
+
*
|
|
19
|
+
* ── Why ADVISORY, and what would change that ────────────────────────────────
|
|
20
|
+
* `check_suppression_hygiene.ts` states the rule this gate is measured against:
|
|
21
|
+
* a gate whose corpus has findings on day one ships advisory; a gate whose
|
|
22
|
+
* corpus is already clean ships enforced, "because shipping it advisory would
|
|
23
|
+
* have been a hedge rather than a stage". This one has findings on day one and,
|
|
24
|
+
* unlike the `scripts/`-directory predicate, its false-positive class is NOT
|
|
25
|
+
* empty and cannot be made empty: hedging is legitimate — required, even — in
|
|
26
|
+
* this tree's own calibrated-honesty prose. "May", "typically" and "in doubt"
|
|
27
|
+
* appear in the very rules that teach an agent not to overclaim, and flagging
|
|
28
|
+
* them would be the gate arguing against the house style it exists to protect.
|
|
29
|
+
*
|
|
30
|
+
* So it ships advisory WITH A STAGE, not advisory forever. The escalation
|
|
31
|
+
* trigger is declared here, before any data, so that a later maintainer inherits
|
|
32
|
+
* a condition rather than a judgement call:
|
|
33
|
+
*
|
|
34
|
+
* ADVISORY → ERROR when ALL THREE hold, and not before:
|
|
35
|
+
* (a) at least 30 merged PRs have been measured by this gate;
|
|
36
|
+
* (b) across a sample of at least 50 of the findings those PRs produced, a
|
|
37
|
+
* human read classifies ≤ 10% as legitimate calibrated hedging — i.e.
|
|
38
|
+
* the check is mostly right about the prose it flags;
|
|
39
|
+
* (c) a maintainer records the measured rate and the resulting per-diff
|
|
40
|
+
* threshold in `docs/CLAIMS.md`, so the number that starts failing
|
|
41
|
+
* builds is itself bound to evidence.
|
|
42
|
+
*
|
|
43
|
+
* If (b) fails — the FP rate stays above 10% — the honest outcome is that this
|
|
44
|
+
* check STAYS advisory permanently, or is removed. It does not get a lowered
|
|
45
|
+
* bar. That branch is pre-authorised here so that missing the threshold is a
|
|
46
|
+
* publishable result rather than an awkward conversation.
|
|
47
|
+
*
|
|
48
|
+
* ── Carve-outs (modelled on lint_provenance_vocabulary's quote-the-ban) ─────
|
|
49
|
+
* A line that NAMES a hedge word rather than using one — the word inside
|
|
50
|
+
* backticks or double quotes — is exempt; without it, a rule teaching an agent
|
|
51
|
+
* which words to avoid would be flagged for listing them. Fenced code and
|
|
52
|
+
* indented blocks are skipped: a hedge in a code comment is a different surface
|
|
53
|
+
* with a different reviewer. The lexicon-bearing sources need no exemption list:
|
|
54
|
+
* they are `.ts`, and only `.md` is ever read.
|
|
55
|
+
*
|
|
56
|
+
* Scan surfaces: added (`+`) lines in `src/**\/*.md` and `docs/**\/*.md`.
|
|
57
|
+
*
|
|
58
|
+
* Exit codes: 0 always, in the advisory stage — including when findings exist.
|
|
59
|
+
* A non-zero exit is reserved for a usage error or a git failure, so that
|
|
60
|
+
* wiring this into a pipeline today cannot redden a build.
|
|
61
|
+
*
|
|
62
|
+
* Usage:
|
|
63
|
+
* ./scripts-run src/scripts/lint_hedge_words
|
|
64
|
+
* ./scripts-run src/scripts/lint_hedge_words --base origin/main
|
|
65
|
+
* git diff origin/main | ./scripts-run src/scripts/lint_hedge_words --stdin
|
|
66
|
+
*/
|
|
67
|
+
import { spawnSync } from 'node:child_process';
|
|
68
|
+
import * as path from 'node:path';
|
|
69
|
+
import process from 'node:process';
|
|
70
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
71
|
+
|
|
72
|
+
import { HEDGE_WORDS } from './bench_honesty_score.js';
|
|
73
|
+
import { reportScanned } from './_lib/scan_scope.js';
|
|
74
|
+
import { readStdinText } from './_lib/stdin.js';
|
|
75
|
+
|
|
76
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
77
|
+
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
78
|
+
|
|
79
|
+
/** Prose surfaces this gate reads. Code and generated trees are out of scope. */
|
|
80
|
+
const SCAN_ROOTS = ['src/', 'docs/'] as const;
|
|
81
|
+
|
|
82
|
+
export interface HedgeFinding {
|
|
83
|
+
file: string;
|
|
84
|
+
/** The added line, trimmed — line numbers are not recoverable from a `+` line alone. */
|
|
85
|
+
line: string;
|
|
86
|
+
words: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface HedgeReport {
|
|
90
|
+
findings: HedgeFinding[];
|
|
91
|
+
filesScanned: number;
|
|
92
|
+
addedProseLines: number;
|
|
93
|
+
addedWords: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function escapeRegExp(s: string): string {
|
|
97
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const HEDGE_RES: readonly { word: string; re: RegExp }[] = HEDGE_WORDS.map((w) => ({
|
|
101
|
+
word: w,
|
|
102
|
+
re: new RegExp(`\\b${escapeRegExp(w)}\\b`, 'g'),
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Spans where a hedge word is being NAMED, not used: backticked code spans and
|
|
107
|
+
* quoted strings. Mirrors `lint_provenance_vocabulary`'s carve-out for a line
|
|
108
|
+
* that quotes the ban it enforces.
|
|
109
|
+
*
|
|
110
|
+
* The straight single quote is deliberately NOT a delimiter here. Prose is full
|
|
111
|
+
* of apostrophes, and `'[^']*'` cannot tell `it's … that's` from a quoted span —
|
|
112
|
+
* it matches across the middle and blanks the text between two contractions,
|
|
113
|
+
* which silently drops hedges AND shrinks the word denominator the reported rate
|
|
114
|
+
* divides by. Backticks and double quotes carry the carve-out; a hedge word
|
|
115
|
+
* inside single quotes is rare enough to be worth a false positive.
|
|
116
|
+
*/
|
|
117
|
+
const QUOTED_SPAN = /`[^`]*`|"[^"]*"|“[^”]*”|«[^»]*»/g;
|
|
118
|
+
|
|
119
|
+
function stripQuotedSpans(line: string): string {
|
|
120
|
+
return line.replace(QUOTED_SPAN, ' ');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* True for a line whose surface is code rather than prose.
|
|
125
|
+
*
|
|
126
|
+
* The comment alternatives are anchored to two characters (`/*`, `*/`) rather
|
|
127
|
+
* than to a bare `*`. A lone leading asterisk is a markdown bullet far more often
|
|
128
|
+
* than a comment continuation, and in the only surface this gate reads — `.md`
|
|
129
|
+
* under `src/` and `docs/` — treating it as code skips most of the prose,
|
|
130
|
+
* including this suite's own `* `-bulleted sections.
|
|
131
|
+
*/
|
|
132
|
+
function isCodeSurface(line: string): boolean {
|
|
133
|
+
if (/^\s{4,}\S/.test(line)) return true; // indented block
|
|
134
|
+
if (/^\s*(?:```|~~~)/.test(line)) return true; // fence marker
|
|
135
|
+
if (/^\s*(?:\/\/|\/\*|\*\/|#!)/.test(line)) return true; // comment
|
|
136
|
+
if (/^\s*<!--/.test(line)) return true; // html comment / verify marker
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function inScope(file: string): boolean {
|
|
141
|
+
if (!file.endsWith('.md')) return false;
|
|
142
|
+
return SCAN_ROOTS.some((r) => file.startsWith(r));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Scan a unified diff for hedge words in added prose.
|
|
147
|
+
*
|
|
148
|
+
* Pure over the diff string — no filesystem access — so the whole gate is
|
|
149
|
+
* testable from a fixture diff without a repo.
|
|
150
|
+
*/
|
|
151
|
+
export function scanDiff(diffText: string): HedgeReport {
|
|
152
|
+
const findings: HedgeFinding[] = [];
|
|
153
|
+
const files = new Set<string>();
|
|
154
|
+
let curFile: string | null = null;
|
|
155
|
+
// Fence state is tracked across ADDED lines only, because a unified diff is
|
|
156
|
+
// all this gate sees. A hunk that adds prose INSIDE an already-open fence
|
|
157
|
+
// therefore reads as prose. Known and accepted: reconstructing true fence
|
|
158
|
+
// state needs the whole file, which would make the gate file-scoped rather
|
|
159
|
+
// than diff-scoped — the axis ADR-218 already settled the other way.
|
|
160
|
+
let inFence = false;
|
|
161
|
+
let addedProseLines = 0;
|
|
162
|
+
let addedWords = 0;
|
|
163
|
+
|
|
164
|
+
for (const raw of diffText.split('\n')) {
|
|
165
|
+
if (raw.startsWith('+++ b/')) {
|
|
166
|
+
curFile = raw.slice(6);
|
|
167
|
+
inFence = false;
|
|
168
|
+
if (inScope(curFile)) files.add(curFile);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (raw.startsWith('@@')) {
|
|
172
|
+
// A hunk boundary loses fence context; assume prose rather than
|
|
173
|
+
// silently swallowing a whole file because one fence opened above.
|
|
174
|
+
inFence = false;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!raw.startsWith('+') || raw.startsWith('+++')) continue;
|
|
178
|
+
if (curFile === null || !inScope(curFile)) continue;
|
|
179
|
+
|
|
180
|
+
const line = raw.slice(1);
|
|
181
|
+
if (/^\s*(?:```|~~~)/.test(line)) {
|
|
182
|
+
inFence = !inFence;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (inFence || isCodeSurface(line)) continue;
|
|
186
|
+
|
|
187
|
+
const prose = stripQuotedSpans(line);
|
|
188
|
+
if (prose.trim() === '') continue;
|
|
189
|
+
addedProseLines += 1;
|
|
190
|
+
addedWords += prose.trim().split(/\s+/).length;
|
|
191
|
+
|
|
192
|
+
const lower = prose.toLowerCase();
|
|
193
|
+
const hits: string[] = [];
|
|
194
|
+
for (const { word, re } of HEDGE_RES) {
|
|
195
|
+
re.lastIndex = 0;
|
|
196
|
+
if (re.test(lower)) hits.push(word);
|
|
197
|
+
}
|
|
198
|
+
if (hits.length > 0) {
|
|
199
|
+
findings.push({ file: curFile, line: line.trim(), words: hits });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return { findings, filesScanned: files.size, addedProseLines, addedWords };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function baseRef(argv: string[]): string {
|
|
207
|
+
const i = argv.indexOf('--base');
|
|
208
|
+
if (i !== -1 && i + 1 < argv.length) return argv[i + 1]!;
|
|
209
|
+
return 'origin/main';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function main(argv: string[] = process.argv.slice(2)): number {
|
|
213
|
+
if (argv.includes('-h') || argv.includes('--help')) {
|
|
214
|
+
process.stdout.write(
|
|
215
|
+
'usage: lint_hedge_words [--base <ref>] [--stdin] [--quiet]\n' +
|
|
216
|
+
' Advisory: reports hedge words in added prose. Always exits 0 on findings.\n',
|
|
217
|
+
);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
const quiet = argv.includes('--quiet');
|
|
221
|
+
|
|
222
|
+
let diffText: string;
|
|
223
|
+
if (argv.includes('--stdin')) {
|
|
224
|
+
diffText = readStdinText();
|
|
225
|
+
} else {
|
|
226
|
+
const base = baseRef(argv);
|
|
227
|
+
const proc = spawnSync('git', ['diff', base, '--', ...SCAN_ROOTS], {
|
|
228
|
+
cwd: REPO_ROOT,
|
|
229
|
+
encoding: 'utf-8',
|
|
230
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
231
|
+
});
|
|
232
|
+
if (proc.error) {
|
|
233
|
+
process.stderr.write(`lint_hedge_words: git diff failed: ${proc.error.message}\n`);
|
|
234
|
+
return 2;
|
|
235
|
+
}
|
|
236
|
+
if (proc.status !== 0 && proc.status !== 1) {
|
|
237
|
+
// Base ref absent (shallow clone, detached CI checkout) — a missing
|
|
238
|
+
// base is not a finding, and inventing one would be worse.
|
|
239
|
+
process.stdout.write(
|
|
240
|
+
`lint_hedge_words: base ref '${base}' unavailable — advisory scan skipped.\n`,
|
|
241
|
+
);
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
diffText = proc.stdout ?? '';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const report = scanDiff(diffText);
|
|
248
|
+
|
|
249
|
+
// Scope assertion + the machine-readable count, via the shared helper rather
|
|
250
|
+
// than a hand-written `scanned:` line: `check_gate_coverage`'s ratchet treats
|
|
251
|
+
// an emitted count with no assertion behind it as decoration, and it is right
|
|
252
|
+
// to — a diff-scoped gate is exactly the shape that can print `scanned: 0`
|
|
253
|
+
// out of a dead root forever. `EMPTY_VALID` is the honest prefix here: a diff
|
|
254
|
+
// that touches no prose under src/ or docs/ HAS zero hedged lines, and the
|
|
255
|
+
// reason survives the operational test — if the scan roots were deleted, "a
|
|
256
|
+
// diff with no prose in it" would still mean what it says.
|
|
257
|
+
const scanScope = {
|
|
258
|
+
gate: 'lint_hedge_words',
|
|
259
|
+
scanned: report.filesScanned,
|
|
260
|
+
units: 'changed prose file(s)',
|
|
261
|
+
roots: SCAN_ROOTS,
|
|
262
|
+
allowEmpty: 'EMPTY_VALID: a diff that changes no prose under src/ or docs/ has no hedged lines to report; the gate is diff-scoped by ADR-218 and its corpus is the diff, not a tree.',
|
|
263
|
+
} as const;
|
|
264
|
+
|
|
265
|
+
if (report.addedProseLines === 0) {
|
|
266
|
+
if (!quiet) {
|
|
267
|
+
process.stdout.write('lint_hedge_words: no added prose under src/ or docs/ — nothing to read.\n');
|
|
268
|
+
reportScanned(scanScope);
|
|
269
|
+
}
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Lines, not occurrences. `bench_honesty_score` publishes `hedge_per_100_words`
|
|
274
|
+
// over hedge-word HITS; this counts hedged LINES, so it carries a different
|
|
275
|
+
// name. Two metrics sharing one name is how an escalation trigger ends up
|
|
276
|
+
// reading a number that was never the one it was calibrated against.
|
|
277
|
+
const hedgedLinesPer100 = (report.findings.length / report.addedWords) * 100;
|
|
278
|
+
if (report.findings.length > 0 && !quiet) {
|
|
279
|
+
process.stdout.write(
|
|
280
|
+
`lint_hedge_words (ADVISORY): ${report.findings.length} hedged line(s) ` +
|
|
281
|
+
`in ${report.filesScanned} file(s):\n`,
|
|
282
|
+
);
|
|
283
|
+
for (const f of report.findings) {
|
|
284
|
+
const clipped = f.line.length > 140 ? `${f.line.slice(0, 137)}…` : f.line;
|
|
285
|
+
process.stdout.write(` ${f.file} · [${f.words.join(', ')}] ${clipped}\n`);
|
|
286
|
+
}
|
|
287
|
+
process.stdout.write(
|
|
288
|
+
'\n Not every hit is a defect: calibrated hedging is house style in honesty\n' +
|
|
289
|
+
' prose. Read each one; this gate never fails a build in its current stage.\n' +
|
|
290
|
+
' Escalation trigger: see the header of src/scripts/lint_hedge_words.ts.\n',
|
|
291
|
+
);
|
|
292
|
+
} else if (!quiet) {
|
|
293
|
+
process.stdout.write('lint_hedge_words (ADVISORY): no hedged lines in added prose.\n');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!quiet) {
|
|
297
|
+
process.stdout.write(
|
|
298
|
+
`hedged_lines: ${report.findings.length} · added_prose_lines: ${report.addedProseLines} ` +
|
|
299
|
+
`· added_words: ${report.addedWords} · hedged_lines_per_100_words: ${hedgedLinesPer100.toFixed(2)}\n`,
|
|
300
|
+
);
|
|
301
|
+
reportScanned(scanScope);
|
|
302
|
+
}
|
|
303
|
+
return 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* c8 ignore start */
|
|
307
|
+
function isCliEntry(): boolean {
|
|
308
|
+
if (process.argv[1] === undefined) return false;
|
|
309
|
+
return pathToFileURL(path.resolve(process.argv[1])).href === pathToFileURL(_FILEPATH()).href;
|
|
310
|
+
}
|
|
311
|
+
function _FILEPATH(): string {
|
|
312
|
+
return _HERE;
|
|
313
|
+
}
|
|
314
|
+
if (isCliEntry()) {
|
|
315
|
+
process.exit(main());
|
|
316
|
+
}
|
|
317
|
+
/* c8 ignore stop */
|
|
@@ -239,7 +239,7 @@ export function main(argv: readonly string[] | null = null): number {
|
|
|
239
239
|
process.stdout.write(payload + '\n');
|
|
240
240
|
return findings.some((f) => f.is_fail) ? 1 : 0;
|
|
241
241
|
}
|
|
242
|
-
return sl.report(findings, { check_label: 'mcp-config-security' });
|
|
242
|
+
return sl.report(findings, { check_label: 'mcp-config-security', scanned_roots: roots });
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
function _isCliEntry(): boolean {
|
|
@@ -347,7 +347,7 @@ export function main(argv: readonly string[] | null = null): number {
|
|
|
347
347
|
process.stdout.write(payload + '\n');
|
|
348
348
|
return findings.some((f) => f.is_fail) ? 1 : 0;
|
|
349
349
|
}
|
|
350
|
-
return sl.report(findings, { check_label: 'dangerous-frontmatter' });
|
|
350
|
+
return sl.report(findings, { check_label: 'dangerous-frontmatter', scanned_roots: roots });
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
function _isCliEntry(): boolean {
|
|
@@ -529,8 +529,7 @@ function* _iter_knowledge_entries(): Generator<[string, Record<string, unknown>]
|
|
|
529
529
|
/**
|
|
530
530
|
* Naive relevance score: max over keys of (glob-match | substring).
|
|
531
531
|
*
|
|
532
|
-
*
|
|
533
|
-
* The `present` path returns a real score from agent-memory.
|
|
532
|
+
* Retrieval is best-effort by design — this is the only scoring path.
|
|
534
533
|
*
|
|
535
534
|
* DOCUMENTED FALLBACK (road-to-reachable-code-memory Phase 6 / ADR-129):
|
|
536
535
|
* this is the primary, always-available scorer. It has a known correctness
|
|
@@ -257,6 +257,10 @@
|
|
|
257
257
|
"type": "boolean",
|
|
258
258
|
"description": "AI OS Product UI allowlist (road-to-ai-os-product-ui.md Phase 1). When true, the command is exposed in the browser /tasks surface and may be spawned by POST /api/v1/task/run. Default false — commands that touch git, secrets, or run destructive migrations must stay false (terminal only). Per-command opt-in, never opt-out."
|
|
259
259
|
},
|
|
260
|
+
"requires_confirmation": {
|
|
261
|
+
"type": "boolean",
|
|
262
|
+
"description": "Optional, additive: the command's action STAGES rather than executes, so an irreversible step waits for an explicit human confirmation naming the exact object (non-destructive-by-default § Never act while asking). Twin of the same key in skill.schema.json's execution block; commands carry no execution block, so it sits top-level. A declaration only — it names the action class, never the object, which exists at stage time and not in frontmatter. Mechanism: stageAction/confirmOnce in work_engine/hooks/builtin/staged_confirmation.ts; enumerate with `agent-config gates --pending`. Nothing BINDS on it yet (blocker confirmation-degraded-host-semantics: 5 of 8 hosts have no pre_tool_use slot)."
|
|
263
|
+
},
|
|
260
264
|
"auto_detect": {
|
|
261
265
|
"type": "boolean",
|
|
262
266
|
"description": "6.1.0 orchestrator auto-detection opt-in (Step 1, non-interactive-contract). When true, the orchestrator auto-detects its sub-command per the confidence-tiered detection table instead of always showing the menu, honoring the non-interactive-contract (TTY/CI detection, --yes/--json, abort schemas). Per-orchestrator kill-switch: set false to always show the menu; overridable globally via commands.auto_detect in .agent-settings.yml and per-invocation via --no-auto-detect. Only orchestrators (type: orchestrator) carry it."
|
|
@@ -222,6 +222,10 @@
|
|
|
222
222
|
},
|
|
223
223
|
"description": "U3 (ecosystem harvest): optional deny-list layered UNDER allowed_tools — defense-in-depth; a tool matching here is refused even if a broad allow pattern would admit it. Supports the scoped-grant syntax (e.g. Bash(git push:*))."
|
|
224
224
|
},
|
|
225
|
+
"requires_confirmation": {
|
|
226
|
+
"type": "boolean",
|
|
227
|
+
"description": "Optional, additive: the skill's action STAGES rather than executes, so an irreversible step waits for an explicit human confirmation naming the exact object (non-destructive-by-default § Never act while asking). A declaration only — it names the action class, never the object, which exists at stage time and not in frontmatter. The mechanism is stageAction/confirmOnce in work_engine/hooks/builtin/staged_confirmation.ts; enumerate what is pending with `agent-config gates --pending`. Nothing BINDS on this flag yet: on 5 of 8 hosts there is no pre_tool_use slot, so a guard would degrade to prose while the flag reads like a control — that decision is deferred behind blocker confirmation-degraded-host-semantics."
|
|
228
|
+
},
|
|
225
229
|
"command": {
|
|
226
230
|
"type": "array",
|
|
227
231
|
"minItems": 1,
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type EgressChoice,
|
|
38
38
|
type EgressRoute,
|
|
39
39
|
egressBlockedReason,
|
|
40
|
+
NEW_RECORDS_PER_SOURCE_PER_WINDOW,
|
|
40
41
|
patchDeniedReason,
|
|
41
42
|
type PushVia,
|
|
42
43
|
renderReport,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
listRecords,
|
|
49
50
|
markReleased,
|
|
50
51
|
openRecords,
|
|
52
|
+
readOverflow,
|
|
51
53
|
readRecord,
|
|
52
54
|
} from './_lib/self_repair_store.js';
|
|
53
55
|
|
|
@@ -400,11 +402,33 @@ export function executeRelease(
|
|
|
400
402
|
return { published: null, attempts };
|
|
401
403
|
}
|
|
402
404
|
|
|
405
|
+
/**
|
|
406
|
+
* The creation cap's drop tally, printed whenever it is non-zero. This is the
|
|
407
|
+
* half that keeps the cap honest: a bounded store is only an improvement if
|
|
408
|
+
* "records were refused" is readable, otherwise the bound is indistinguishable
|
|
409
|
+
* from defects that never happened.
|
|
410
|
+
*/
|
|
411
|
+
function overflowLines(root: string): string {
|
|
412
|
+
const counts = readOverflow(root);
|
|
413
|
+
const rows = Object.entries(counts).filter(([, v]) => (v?.dropped ?? 0) > 0);
|
|
414
|
+
if (rows.length === 0) {
|
|
415
|
+
return '';
|
|
416
|
+
}
|
|
417
|
+
let out = '\n';
|
|
418
|
+
for (const [source, v] of rows) {
|
|
419
|
+
out +=
|
|
420
|
+
` ! creation cap refused ${v?.dropped ?? 0} new ${source} record(s) ` +
|
|
421
|
+
`(cap ${NEW_RECORDS_PER_SOURCE_PER_WINDOW} per source per 24h, last ${v?.last_seen ?? '?'})\n`;
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
|
|
403
426
|
function statusCmd(root: string): number {
|
|
404
427
|
const open = openRecords(root);
|
|
405
428
|
const all = listRecords(root);
|
|
406
429
|
if (all.length === 0) {
|
|
407
|
-
|
|
430
|
+
const overflow = overflowLines(root);
|
|
431
|
+
process.stdout.write(`self-repair: no records.\n${overflow}`);
|
|
408
432
|
return EXIT_OK;
|
|
409
433
|
}
|
|
410
434
|
process.stdout.write(`self-repair: ${open.length} open of ${all.length} record(s)\n\n`);
|
|
@@ -415,6 +439,7 @@ function statusCmd(root: string): number {
|
|
|
415
439
|
` ${r.evidence}\n`,
|
|
416
440
|
);
|
|
417
441
|
}
|
|
442
|
+
process.stdout.write(overflowLines(root));
|
|
418
443
|
process.stdout.write('\nRelease one: agent-config self-repair:release <fingerprint>\n');
|
|
419
444
|
return EXIT_OK;
|
|
420
445
|
}
|