@event4u/agent-config 14.17.0 → 14.19.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 +207 -77
- package/README.md +72 -150
- package/dist/agent-src/README.md +1 -1
- package/dist/agent-src/commands/agent-handoff.md +2 -2
- package/dist/agent-src/commands/pr/merge.md +35 -44
- package/dist/agent-src/commands/review/changes.md +19 -19
- package/dist/agent-src/commands/review.md +2 -2
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +30 -3
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +1 -1
- package/dist/agent-src/rules/session-canary.md +2 -2
- package/dist/agent-src/rules/token-efficiency.md +1 -1
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +2 -0
- package/dist/agent-src/skills/adversarial-review/SKILL.md +0 -8
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +27 -1
- package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +60 -12
- package/dist/agent-src/skills/complexity-first-planning/SKILL.md +27 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
- package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
- package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
- package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
- package/dist/agent-src/skills/design-review/SKILL.md +3 -1
- package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
- package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
- package/dist/agent-src/skills/feature-planning/SKILL.md +34 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +37 -12
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +176 -0
- package/dist/agent-src/skills/laravel-mail/SKILL.md +44 -0
- package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
- package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +40 -0
- package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +29 -2
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +53 -8
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/commands/recordTriggerEval.js +2 -2
- package/dist/cli/main.js +1 -1
- package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-GZTMRYCN.js} +405 -341
- package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-IDHUQIXP.js} +283 -132
- package/dist/cli-delegate/cmd_adr_effective.js +318 -81
- package/dist/cli-delegate/cmd_conformance.js +749 -669
- package/dist/cli-delegate/cmd_doctor.js +1 -1
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +39 -38
- 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 +2764 -2439
- package/dist/install/install.mjs +588 -527
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1381 -1138
- package/dist/scripts/_lib/model_tier.js +1 -1
- package/dist/shared/settingsConsent.js +1 -1
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/design-antipatterns.md +87 -64
- package/package.json +1 -1
- package/src/agent-src/README.md +1 -1
- package/src/config/ci-local-parity.yml +6 -0
- package/src/config/conformance-claim-baseline.json +9 -0
- package/src/config/gate-coverage.yml +170 -0
- package/src/config/gate-violation-baselines.json +4 -4
- package/src/config/release-gate-locality.yml +141 -0
- package/src/scripts/_cli/cmd_doctor.ts +2 -2
- package/src/scripts/_cli/cmd_handoff.ts +1 -1
- package/src/scripts/_cli/cmd_update.ts +1 -1
- package/src/scripts/_cli/cmd_versions.ts +1 -1
- package/src/scripts/_lib/adr_frontmatter.ts +44 -0
- package/src/scripts/_lib/consequence_objects.ts +152 -0
- package/src/scripts/_lib/exec_evidence.ts +7 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
- package/src/scripts/_lib/host_permission_checks.ts +251 -0
- package/src/scripts/_lib/model_tier.ts +1 -1
- package/src/scripts/_lib/obligation_frequency.ts +2 -2
- package/src/scripts/_lib/release_highlights.ts +265 -1
- package/src/scripts/_lib/release_material.ts +129 -0
- package/src/scripts/_lib/repeated_run.ts +77 -0
- package/src/scripts/_lib/runtime_journal.ts +26 -3
- package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
- package/src/scripts/_lib/session_register.ts +11 -6
- package/src/scripts/_lib/sqlite_schema.ts +22 -0
- package/src/scripts/_lib/surface.ts +86 -0
- package/src/scripts/_lib/tamper_vocabulary.ts +242 -0
- package/src/scripts/_lib/ui_authority.ts +116 -0
- package/src/scripts/_lib/unified_diff.ts +121 -0
- package/src/scripts/adr/regenerate_index.ts +54 -1
- package/src/scripts/adr_cite_check.ts +71 -15
- package/src/scripts/ai_council/clients.ts +1 -1
- package/src/scripts/autonomy_friction_corpus.ts +229 -0
- package/src/scripts/build_proof.ts +15 -2
- package/src/scripts/check_beta_review_markers.ts +141 -17
- package/src/scripts/check_enforcement_coverage.ts +1 -1
- package/src/scripts/check_enforcement_denominator.ts +121 -11
- package/src/scripts/check_finding_dispositions.ts +239 -6
- package/src/scripts/check_gate_coverage.ts +96 -0
- package/src/scripts/check_installed_hooks_fresh.ts +381 -0
- package/src/scripts/check_release_highlights.ts +99 -14
- package/src/scripts/check_skill_activation_claim.ts +179 -0
- package/src/scripts/check_suppression_hygiene.ts +11 -0
- package/src/scripts/check_trigger_evals.ts +1 -1
- package/src/scripts/code_graph/build.ts +230 -33
- package/src/scripts/code_graph/extract.ts +168 -33
- package/src/scripts/code_graph/types.ts +37 -2
- package/src/scripts/code_graph/validate.ts +12 -1
- package/src/scripts/conformance_scan.ts +45 -4
- package/src/scripts/detect_silent_catch.ts +403 -0
- package/src/scripts/detect_verification_tampering.ts +538 -0
- package/src/scripts/explain_run.ts +1 -1
- package/src/scripts/generate_capability_matrix.ts +1 -1
- package/src/scripts/git_authorization_hook.ts +228 -18
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +47 -30
- package/src/scripts/hooks/augment-dispatcher.sh +1 -1
- package/src/scripts/hooks/block_no_verify.ts +3 -3
- package/src/scripts/hooks/category_a.ts +291 -0
- package/src/scripts/hooks/cline-dispatcher.sh +1 -1
- package/src/scripts/hooks/concern_registry.ts +2 -2
- package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
- package/src/scripts/hooks/dispatch_hook.ts +17 -22
- package/src/scripts/hooks/evidence_independence.ts +21 -2
- package/src/scripts/hooks/{block_unauthorized_git.ts → git_command_classifier.ts} +175 -364
- package/src/scripts/hooks/host_lowering.ts +215 -0
- package/src/scripts/hooks/host_lowering.yaml +185 -0
- package/src/scripts/hooks/host_semantics.ts +105 -37
- package/src/scripts/hooks/journal_record_hook.ts +10 -0
- package/src/scripts/hooks/permission_gate.ts +117 -0
- package/src/scripts/hooks/push_settle_hook.ts +250 -0
- package/src/scripts/hooks/ship_diff_volume_hook.ts +1 -1
- package/src/scripts/hooks_status.ts +12 -10
- package/src/scripts/install-hooks.sh +134 -1
- package/src/scripts/install.ts +53 -82
- package/src/scripts/install_anthropic_key.sh +2 -2
- package/src/scripts/language_mirror_hook.ts +27 -5
- package/src/scripts/lint_design_antipattern_parity.ts +80 -0
- package/src/scripts/lint_evidence_artifacts.ts +45 -1
- package/src/scripts/lint_hook_manifest.ts +87 -3
- package/src/scripts/lint_motion_authority_drift.ts +731 -0
- package/src/scripts/lint_readme_size.ts +158 -12
- package/src/scripts/lint_reasoning_fixtures.ts +216 -0
- package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
- package/src/scripts/measure_sibling_naming.ts +150 -0
- package/src/scripts/redact_hook_capture.ts +1 -1
- package/src/scripts/release.ts +31 -143
- package/src/scripts/release_drill.ts +71 -1
- package/src/scripts/release_publication.ts +243 -3
- package/src/scripts/release_verify.ts +220 -0
- package/src/scripts/report_skill_activation.ts +319 -7
- package/src/scripts/schemas/review-findings.schema.json +6 -1
- package/src/scripts/schemas/skill.schema.json +12 -2
- package/src/scripts/self_review_gate.ts +10 -1
- package/src/scripts/session_canary_hook.ts +1 -1
- package/src/scripts/session_register_hook.ts +4 -3
- package/src/scripts/trigger_eval_grandfather.json +5 -1
- package/src/scripts/validate_agent_settings.ts +1 -1
- package/src/shared/settingsConsent.ts +1 -1
|
@@ -44,7 +44,19 @@
|
|
|
44
44
|
* count descriptions against bare names. Until that runs, "most skills reach the
|
|
45
45
|
* model without a description" is a suspicion.
|
|
46
46
|
*
|
|
47
|
-
* Exit: 0 always, except a usage/IO error (1). Deliberate
|
|
47
|
+
* Exit: the REPORT is 0 always, except a usage/IO error (1). Deliberate — it
|
|
48
|
+
* gates on nothing and must not acquire a threshold.
|
|
49
|
+
*
|
|
50
|
+
* One later mode DOES carry a verdict, and it is not a threshold on the census:
|
|
51
|
+
* `--emit` exits 1 rather than writing a record from an empty store, because a
|
|
52
|
+
* zero-session reading is "no transcripts at this path" and publishing it as an
|
|
53
|
+
* activation figure is the defect this file exists to end.
|
|
54
|
+
*
|
|
55
|
+
* The assertion that the PUBLISHED figure still matches that record is a gate
|
|
56
|
+
* and lives in `check_skill_activation_claim.ts`. It is a separate script on
|
|
57
|
+
* purpose: `src/config/gate-coverage.yml` is a manifest of gates and
|
|
58
|
+
* `_lib/gate_population.ts` classifies one by prefix, so a `report_*` id
|
|
59
|
+
* registered there forks that population.
|
|
48
60
|
*/
|
|
49
61
|
|
|
50
62
|
import * as fs from 'node:fs';
|
|
@@ -53,6 +65,7 @@ import * as path from 'node:path';
|
|
|
53
65
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
54
66
|
|
|
55
67
|
import { projectStoreSlug } from './_lib/cc_transcript.js';
|
|
68
|
+
import { reportScanned } from './_lib/scan_scope.js';
|
|
56
69
|
|
|
57
70
|
const _HERE = fileURLToPath(import.meta.url);
|
|
58
71
|
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
@@ -73,11 +86,17 @@ export const DETERMINISTIC_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?(MUST|NEVER|ALWAYS)\b/
|
|
|
73
86
|
export interface SkillCensus {
|
|
74
87
|
total: number;
|
|
75
88
|
withTriggerKey: string[];
|
|
89
|
+
withTriggerCorpus: string[];
|
|
76
90
|
withDeterministicObligation: string[];
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
export function censusSkills(root: string): SkillCensus {
|
|
80
|
-
const out: SkillCensus = {
|
|
94
|
+
const out: SkillCensus = {
|
|
95
|
+
total: 0,
|
|
96
|
+
withTriggerKey: [],
|
|
97
|
+
withTriggerCorpus: [],
|
|
98
|
+
withDeterministicObligation: [],
|
|
99
|
+
};
|
|
81
100
|
if (!fs.existsSync(root)) {
|
|
82
101
|
return out;
|
|
83
102
|
}
|
|
@@ -98,6 +117,12 @@ export function censusSkills(root: string): SkillCensus {
|
|
|
98
117
|
if (TRIGGER_KEYS.some((k) => new RegExp(`^${k}`, 'm').test(fm))) {
|
|
99
118
|
out.withTriggerKey.push(entry.name);
|
|
100
119
|
}
|
|
120
|
+
// A test fixture, not a routing input: no host reads it at selection time.
|
|
121
|
+
// Counted because it is the second of the three populations the surface
|
|
122
|
+
// splits into, and it is the one most often mistaken for the first.
|
|
123
|
+
if (fs.existsSync(path.join(root, entry.name, 'evals', 'triggers.json'))) {
|
|
124
|
+
out.withTriggerCorpus.push(entry.name);
|
|
125
|
+
}
|
|
101
126
|
const body = fmEnd === -1 ? text : text.slice(fmEnd + 4);
|
|
102
127
|
if (DETERMINISTIC_RE.test(body)) {
|
|
103
128
|
out.withDeterministicObligation.push(entry.name);
|
|
@@ -178,7 +203,185 @@ function _pct(n: number, d: number): string {
|
|
|
178
203
|
return d === 0 ? 'n/a' : `${((100 * n) / d).toFixed(1)}%`;
|
|
179
204
|
}
|
|
180
205
|
|
|
181
|
-
|
|
206
|
+
/** Repo-relative path of the record the published claim is derived from. */
|
|
207
|
+
export const RECORD_PATH = 'agents/evidence/metrics/skill-activation-census.json';
|
|
208
|
+
export const CLAIMS_PATH = 'docs/CLAIMS.md';
|
|
209
|
+
/** The one ledger entry this census backs. */
|
|
210
|
+
export const CLAIM_ID = 'skill-activation-census-zero';
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The three populations the shipped surface splits into, plus their overlap.
|
|
214
|
+
*
|
|
215
|
+
* They are computed from one pass rather than three greps because the whole
|
|
216
|
+
* point of 2.1 is that they reconcile: `humanNamedOnly` is a remainder, so it
|
|
217
|
+
* cannot be stated independently and then found to disagree.
|
|
218
|
+
*/
|
|
219
|
+
export interface Populations {
|
|
220
|
+
shipped: number;
|
|
221
|
+
triggerKey: number;
|
|
222
|
+
triggerCorpus: number;
|
|
223
|
+
bothKeyAndCorpus: number;
|
|
224
|
+
humanNamedOnly: number;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function populations(census: SkillCensus): Populations {
|
|
228
|
+
const corpus = new Set(census.withTriggerCorpus);
|
|
229
|
+
const both = census.withTriggerKey.filter((s) => corpus.has(s));
|
|
230
|
+
const eitherOne = new Set([...census.withTriggerKey, ...census.withTriggerCorpus]);
|
|
231
|
+
return {
|
|
232
|
+
shipped: census.total,
|
|
233
|
+
triggerKey: census.withTriggerKey.length,
|
|
234
|
+
triggerCorpus: census.withTriggerCorpus.length,
|
|
235
|
+
bothKeyAndCorpus: both.length,
|
|
236
|
+
humanNamedOnly: census.total - eitherOne.size,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface CensusRecord {
|
|
241
|
+
schema_version: 1;
|
|
242
|
+
measured_at: string;
|
|
243
|
+
skills_shipped: number;
|
|
244
|
+
with_trigger_key: number;
|
|
245
|
+
with_trigger_corpus: number;
|
|
246
|
+
trigger_key_and_corpus: number;
|
|
247
|
+
human_named_only: number;
|
|
248
|
+
sessions: number;
|
|
249
|
+
assistant_turns: number;
|
|
250
|
+
invocations: number;
|
|
251
|
+
distinct_skills_invoked: number;
|
|
252
|
+
stores: string[];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function buildRecord(census: SkillCensus, usage: UsageReport[], today: string): CensusRecord {
|
|
256
|
+
const pop = populations(census);
|
|
257
|
+
const distinct = new Set<string>();
|
|
258
|
+
for (const u of usage) {
|
|
259
|
+
for (const k of Object.keys(u.bySkill)) {
|
|
260
|
+
distinct.add(k);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
schema_version: 1,
|
|
265
|
+
measured_at: today,
|
|
266
|
+
skills_shipped: pop.shipped,
|
|
267
|
+
with_trigger_key: pop.triggerKey,
|
|
268
|
+
with_trigger_corpus: pop.triggerCorpus,
|
|
269
|
+
trigger_key_and_corpus: pop.bothKeyAndCorpus,
|
|
270
|
+
human_named_only: pop.humanNamedOnly,
|
|
271
|
+
sessions: usage.reduce((a, u) => a + u.sessions, 0),
|
|
272
|
+
assistant_turns: usage.reduce((a, u) => a + u.assistantTurns, 0),
|
|
273
|
+
invocations: usage.reduce((a, u) => a + u.invocations, 0),
|
|
274
|
+
distinct_skills_invoked: distinct.size,
|
|
275
|
+
stores: usage.map((u) => path.basename(u.store)),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function readRecord(repoRoot: string): CensusRecord | null {
|
|
280
|
+
const file = path.join(repoRoot, RECORD_PATH);
|
|
281
|
+
if (!fs.existsSync(file)) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
return JSON.parse(fs.readFileSync(file, 'utf8')) as CensusRecord;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Numbers the published claim asserts, pulled back out of the claim text.
|
|
289
|
+
*
|
|
290
|
+
* Read from prose on purpose: the claim is the surface a reader believes, so
|
|
291
|
+
* comparing the record against a separate machine field would leave the
|
|
292
|
+
* sentence free to drift from both. `null` for any field the claim no longer
|
|
293
|
+
* states in the expected shape — which `checkClaim` reports as a defect rather
|
|
294
|
+
* than passing over, because a claim that stopped stating its figures is not a
|
|
295
|
+
* claim that agrees with the record.
|
|
296
|
+
*/
|
|
297
|
+
export interface ClaimFigures {
|
|
298
|
+
sessions: number | null;
|
|
299
|
+
assistantTurns: number | null;
|
|
300
|
+
invocations: number | null;
|
|
301
|
+
distinct: number | null;
|
|
302
|
+
shipped: number | null;
|
|
303
|
+
triggerKey: number | null;
|
|
304
|
+
triggerCorpus: number | null;
|
|
305
|
+
bothKeyAndCorpus: number | null;
|
|
306
|
+
humanNamedOnly: number | null;
|
|
307
|
+
measuredAt: string | null;
|
|
308
|
+
lastVerified: string | null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function _num(text: string, re: RegExp): number | null {
|
|
312
|
+
const m = re.exec(text);
|
|
313
|
+
return m?.[1] === undefined ? null : Number.parseInt(m[1].replace(/,/gu, ''), 10);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function claimBlock(claimsText: string, id: string): string | null {
|
|
317
|
+
const start = claimsText.indexOf(`### claim: ${id}\n`);
|
|
318
|
+
if (start === -1) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
const rest = claimsText.slice(start + 1);
|
|
322
|
+
const end = rest.indexOf('\n### claim: ');
|
|
323
|
+
return end === -1 ? claimsText.slice(start) : claimsText.slice(start, start + 1 + end);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function parseClaimFigures(block: string): ClaimFigures {
|
|
327
|
+
return {
|
|
328
|
+
sessions: _num(block, /over ([\d,]+) sessions and/u),
|
|
329
|
+
assistantTurns: _num(block, /sessions and ([\d,]+) assistant turns/u),
|
|
330
|
+
invocations: _num(block, /records ([\d,]+) Skill invocations/u),
|
|
331
|
+
distinct: _num(block, /Skill invocations and ([\d,]+) of [\d,]+ distinct skills/u),
|
|
332
|
+
shipped: _num(block, /Skill invocations and [\d,]+ of ([\d,]+) distinct skills/u),
|
|
333
|
+
triggerKey: _num(block, /([\d,]+) declare a machine-matchable trigger key/u),
|
|
334
|
+
triggerCorpus: _num(block, /([\d,]+) carry an `evals\/triggers\.json` corpus/u),
|
|
335
|
+
bothKeyAndCorpus: _num(block, /([\d,]+) do both/u),
|
|
336
|
+
humanNamedOnly: _num(block, /([\d,]+) are reachable only by a human naming them/u),
|
|
337
|
+
measuredAt: /measured (\d{4}-\d{2}-\d{2})/u.exec(block)?.[1] ?? null,
|
|
338
|
+
lastVerified: /^- last_verified: (\d{4}-\d{2}-\d{2})/mu.exec(block)?.[1] ?? null,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Disagreements between the committed record and the published claim.
|
|
344
|
+
*
|
|
345
|
+
* This is the whole of what 1.3 buys, so it is worth being exact about what it
|
|
346
|
+
* does NOT buy: it cannot tell whether the RECORD still describes the world,
|
|
347
|
+
* because the transcript store it was taken from is one machine's and is absent
|
|
348
|
+
* from CI. Re-taking the measurement is `--emit`; this only forbids the
|
|
349
|
+
* published sentence and the record from telling two different stories.
|
|
350
|
+
*/
|
|
351
|
+
export function claimProblems(rec: CensusRecord, fig: ClaimFigures): string[] {
|
|
352
|
+
const out: string[] = [];
|
|
353
|
+
const pairs: Array<[string, number, number | null]> = [
|
|
354
|
+
['sessions', rec.sessions, fig.sessions],
|
|
355
|
+
['assistant turns', rec.assistant_turns, fig.assistantTurns],
|
|
356
|
+
['Skill invocations', rec.invocations, fig.invocations],
|
|
357
|
+
['distinct skills invoked', rec.distinct_skills_invoked, fig.distinct],
|
|
358
|
+
['skills shipped', rec.skills_shipped, fig.shipped],
|
|
359
|
+
['skills with a trigger key', rec.with_trigger_key, fig.triggerKey],
|
|
360
|
+
['skills with a triggers.json corpus', rec.with_trigger_corpus, fig.triggerCorpus],
|
|
361
|
+
['skills in both populations', rec.trigger_key_and_corpus, fig.bothKeyAndCorpus],
|
|
362
|
+
['human-named-only skills', rec.human_named_only, fig.humanNamedOnly],
|
|
363
|
+
];
|
|
364
|
+
for (const [label, recorded, claimed] of pairs) {
|
|
365
|
+
if (claimed === null) {
|
|
366
|
+
out.push(`${label}: the claim no longer states this figure; the record says ${String(recorded)}`);
|
|
367
|
+
} else if (claimed !== recorded) {
|
|
368
|
+
out.push(`${label}: claim says ${String(claimed)}, record says ${String(recorded)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const [label, claimed] of [
|
|
372
|
+
['measurement date', fig.measuredAt],
|
|
373
|
+
['last_verified', fig.lastVerified],
|
|
374
|
+
] as Array<[string, string | null]>) {
|
|
375
|
+
if (claimed === null) {
|
|
376
|
+
out.push(`${label}: the claim no longer states it; the record was measured ${rec.measured_at}`);
|
|
377
|
+
} else if (claimed !== rec.measured_at) {
|
|
378
|
+
out.push(`${label}: claim says ${claimed}, record was measured ${rec.measured_at}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function render(census: SkillCensus, usage: UsageReport[], published: CensusRecord | null = null): string {
|
|
182
385
|
const lines: string[] = [];
|
|
183
386
|
lines.push('skill activation census — advisory, gates on nothing');
|
|
184
387
|
lines.push('');
|
|
@@ -193,6 +396,16 @@ export function render(census: SkillCensus, usage: UsageReport[]): string {
|
|
|
193
396
|
lines.push(` ${census.withDeterministicObligation.join(', ')}`);
|
|
194
397
|
}
|
|
195
398
|
lines.push('');
|
|
399
|
+
const pop = populations(census);
|
|
400
|
+
lines.push(' populations — the three ways a skill can be reached, reconciled');
|
|
401
|
+
lines.push(` machine-matchable trigger key ${pop.triggerKey}`);
|
|
402
|
+
lines.push(` evals/triggers.json corpus ${pop.triggerCorpus} (a test fixture; no host reads it at routing time)`);
|
|
403
|
+
lines.push(` both of the above ${pop.bothKeyAndCorpus}`);
|
|
404
|
+
lines.push(` human-named only (remainder) ${pop.humanNamedOnly}`);
|
|
405
|
+
lines.push(
|
|
406
|
+
` reconciles ${pop.triggerKey} + ${pop.triggerCorpus} - ${pop.bothKeyAndCorpus} + ${pop.humanNamedOnly} = ${String(pop.triggerKey + pop.triggerCorpus - pop.bothKeyAndCorpus + pop.humanNamedOnly)} of ${pop.shipped}`,
|
|
407
|
+
);
|
|
408
|
+
lines.push('');
|
|
196
409
|
let totalInv = 0;
|
|
197
410
|
const distinct = new Set<string>();
|
|
198
411
|
for (const u of usage) {
|
|
@@ -213,6 +426,65 @@ export function render(census: SkillCensus, usage: UsageReport[]): string {
|
|
|
213
426
|
lines.push(` ${[...distinct].sort().join(', ')}`);
|
|
214
427
|
}
|
|
215
428
|
lines.push('');
|
|
429
|
+
const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
|
|
430
|
+
if (sessionsRead === 0) {
|
|
431
|
+
// The store is keyed on the directory the process runs in, so a checkout
|
|
432
|
+
// that never hosted a session — a fresh worktree, a CI runner — yields a
|
|
433
|
+
// zero that means "no transcripts at this path". Publishing that as the
|
|
434
|
+
// activation reading would be the defect this census exists to end, so the
|
|
435
|
+
// two zeros are separated here rather than left to the reader.
|
|
436
|
+
lines.push(' EMPTY STORE: 0 sessions were read, so the zero above is "no transcripts at');
|
|
437
|
+
lines.push(' this path", NOT "no activations". It is not a measurement of the surface and');
|
|
438
|
+
lines.push(' must not be published as one. Run this where the store lives, or pass --store.');
|
|
439
|
+
lines.push('');
|
|
440
|
+
}
|
|
441
|
+
if (published !== null) {
|
|
442
|
+
// Staleness has to be readable from the run itself. Without this block the
|
|
443
|
+
// only way to tell a fresh published figure from a rotted one is to open
|
|
444
|
+
// the git log of `docs/CLAIMS.md`, which is exactly the thing 1.3 forbids.
|
|
445
|
+
const live = buildRecord(census, usage, published.measured_at);
|
|
446
|
+
// The two halves drift for different reasons and a single verdict over both
|
|
447
|
+
// would be worthless: the store is rolling, so its counts move between any
|
|
448
|
+
// two runs — including a run in the session that is adding to it. A reader
|
|
449
|
+
// who is told STALE every time stops reading the line. Only the repository
|
|
450
|
+
// half is a defect when it moves, because the published population split
|
|
451
|
+
// then no longer describes the tree it claims to count.
|
|
452
|
+
const REPO_FIELDS: Array<keyof CensusRecord> = [
|
|
453
|
+
'skills_shipped',
|
|
454
|
+
'with_trigger_key',
|
|
455
|
+
'with_trigger_corpus',
|
|
456
|
+
'trigger_key_and_corpus',
|
|
457
|
+
'human_named_only',
|
|
458
|
+
];
|
|
459
|
+
const STORE_FIELDS: Array<keyof CensusRecord> = [
|
|
460
|
+
'sessions',
|
|
461
|
+
'assistant_turns',
|
|
462
|
+
'invocations',
|
|
463
|
+
'distinct_skills_invoked',
|
|
464
|
+
];
|
|
465
|
+
const repoDrift = REPO_FIELDS.filter((k) => live[k] !== published[k]);
|
|
466
|
+
const storeDrift = STORE_FIELDS.filter((k) => live[k] !== published[k]);
|
|
467
|
+
lines.push(` PUBLISHED RECORD (${RECORD_PATH}), measured ${published.measured_at}:`);
|
|
468
|
+
lines.push(
|
|
469
|
+
` sessions=${String(published.sessions)} asst=${String(published.assistant_turns)} invocations=${String(published.invocations)} distinct=${String(published.distinct_skills_invoked)}`,
|
|
470
|
+
);
|
|
471
|
+
if (repoDrift.length > 0) {
|
|
472
|
+
lines.push(` STALE: the published population split no longer describes this tree —`);
|
|
473
|
+
lines.push(` ${repoDrift.join(', ')} moved. Re-take with --emit.`);
|
|
474
|
+
} else {
|
|
475
|
+
lines.push(' population split still describes this tree.');
|
|
476
|
+
}
|
|
477
|
+
if (sessionsRead === 0) {
|
|
478
|
+
lines.push(' Store reading: UNDECIDED — this run read no store, so it cannot say whether');
|
|
479
|
+
lines.push(' the published figures still hold. Re-run where the store lives.');
|
|
480
|
+
} else if (storeDrift.length === 0) {
|
|
481
|
+
lines.push(' Store reading: reproduces the published figures exactly.');
|
|
482
|
+
} else {
|
|
483
|
+
lines.push(` Store reading: moved since the record (${storeDrift.join(', ')}). Expected —`);
|
|
484
|
+
lines.push(' the store is rolling, not append-only, and a later reading can be lower.');
|
|
485
|
+
}
|
|
486
|
+
lines.push('');
|
|
487
|
+
}
|
|
216
488
|
if (census.withTriggerKey.length === 0) {
|
|
217
489
|
lines.push(' VERDICT: activation is not measurable as a rate. No skill declares a');
|
|
218
490
|
lines.push(' machine-matchable trigger, so a missed-activation detector has nothing to');
|
|
@@ -227,19 +499,32 @@ export function render(census: SkillCensus, usage: UsageReport[]): string {
|
|
|
227
499
|
return lines.join('\n');
|
|
228
500
|
}
|
|
229
501
|
|
|
502
|
+
const USAGE =
|
|
503
|
+
'usage: report_skill_activation [--root DIR] [--limit N] [--store PATH]... [--emit] [--allow-empty-store]\n';
|
|
504
|
+
|
|
230
505
|
function main(argv: string[]): number {
|
|
231
506
|
let limit = 30;
|
|
507
|
+
let emit = false;
|
|
508
|
+
let allowEmpty = false;
|
|
509
|
+
const rootIdx = argv.indexOf('--root');
|
|
510
|
+
const root = rootIdx === -1 ? REPO_ROOT : (argv[rootIdx + 1] ?? REPO_ROOT);
|
|
232
511
|
const stores: string[] = [];
|
|
233
512
|
for (let i = 0; i < argv.length; i += 1) {
|
|
234
513
|
const a = argv[i];
|
|
235
|
-
if (a === '--
|
|
514
|
+
if (a === '--root' && argv[i + 1] !== undefined) {
|
|
515
|
+
i += 1;
|
|
516
|
+
} else if (a === '--limit' && argv[i + 1] !== undefined) {
|
|
236
517
|
limit = Number.parseInt(argv[i + 1] as string, 10);
|
|
237
518
|
i += 1;
|
|
238
519
|
} else if (a === '--store' && argv[i + 1] !== undefined) {
|
|
239
520
|
stores.push(argv[i + 1] as string);
|
|
240
521
|
i += 1;
|
|
522
|
+
} else if (a === '--emit') {
|
|
523
|
+
emit = true;
|
|
524
|
+
} else if (a === '--allow-empty-store') {
|
|
525
|
+
allowEmpty = true;
|
|
241
526
|
} else if (a === '--help' || a === '-h') {
|
|
242
|
-
process.stdout.write(
|
|
527
|
+
process.stdout.write(USAGE);
|
|
243
528
|
return 0;
|
|
244
529
|
}
|
|
245
530
|
}
|
|
@@ -250,9 +535,36 @@ function main(argv: string[]): number {
|
|
|
250
535
|
if (stores.length === 0) {
|
|
251
536
|
stores.push(defaultStore(REPO_ROOT));
|
|
252
537
|
}
|
|
253
|
-
const census = censusSkills(path.join(
|
|
538
|
+
const census = censusSkills(path.join(root, SKILLS_ROOT));
|
|
254
539
|
const usage = stores.map((s) => measureUsage(s, limit));
|
|
255
|
-
process.stdout.write(`${render(census, usage)}\n`);
|
|
540
|
+
process.stdout.write(`${render(census, usage, readRecord(root))}\n`);
|
|
541
|
+
// The skills are the corpus this floor can hold: they are in the repository,
|
|
542
|
+
// so the count is the same everywhere. The transcript half is path-scoped and
|
|
543
|
+
// legitimately zero in CI, which is why it is reported above and not here — a
|
|
544
|
+
// floor over it would either red every CI run or be vacuous.
|
|
545
|
+
reportScanned({
|
|
546
|
+
gate: 'report_skill_activation',
|
|
547
|
+
scanned: census.total,
|
|
548
|
+
units: 'SKILL.md file(s)',
|
|
549
|
+
roots: [SKILLS_ROOT],
|
|
550
|
+
});
|
|
551
|
+
if (!emit) {
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
|
|
555
|
+
if (sessionsRead === 0 && !allowEmpty) {
|
|
556
|
+
process.stderr.write(
|
|
557
|
+
'report_skill_activation --emit: refusing to write a record from 0 sessions — that zero\n' +
|
|
558
|
+
'is "no transcripts at this path", not an activation reading. Run where the store lives,\n' +
|
|
559
|
+
'pass --store, or pass --allow-empty-store if an empty store is genuinely the finding.\n',
|
|
560
|
+
);
|
|
561
|
+
return 1;
|
|
562
|
+
}
|
|
563
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
564
|
+
const file = path.join(root, RECORD_PATH);
|
|
565
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
566
|
+
fs.writeFileSync(file, `${JSON.stringify(buildRecord(census, usage, today), null, 2)}\n`);
|
|
567
|
+
process.stdout.write(`\nwrote ${RECORD_PATH} (measured ${today})\n`);
|
|
256
568
|
return 0;
|
|
257
569
|
}
|
|
258
570
|
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"type": "string"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"no_findings_reason": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"description": "Why the finding set is empty. REQUIRED whenever a ledger file exists and `findings` is empty — enforced in check_finding_dispositions.empty_ledger_problem rather than here, because the requirement is conditional on the array length and this repo's Draft-07 subset validator resolves no `if`/`then`. The field exists because `findings: []` and a missing file made the same claim to every reader while collapsing two states that differ entirely: a review that ran and reported nothing, and a review that never ran. The reason must pick one and say how a reader can check it."
|
|
58
|
+
},
|
|
54
59
|
"findings": {
|
|
55
60
|
"type": "array",
|
|
56
61
|
"description": "May be empty. An empty array is an honest null, not a missing field — the distinction the Markdown track spells with its own §2.3 line. The item shape is INLINED rather than `$ref`-ed for the same measured reason: the subset validator does not resolve `$ref`, so a referenced definition would have validated nothing.",
|
|
@@ -66,7 +71,7 @@
|
|
|
66
71
|
"finding_id": {
|
|
67
72
|
"type": "string",
|
|
68
73
|
"pattern": "^[0-9a-f]{12}$",
|
|
69
|
-
"description": "First 12 hex of sha256(`kind|title|file`), per findingId() in self_review_gate.ts. Stable across runs, which is what lets the disposition ledger key on it. NOT required: an in-flight finding may not have been keyed yet, and requiring it would make the schema reject the shape the model returns before the gate stamps it."
|
|
74
|
+
"description": "First 12 hex of sha256(`kind|title|file`), per findingId() in self_review_gate.ts. Stable across runs, which is what lets the disposition ledger key on it. NOT required: an in-flight finding may not have been keyed yet, and requiring it would make the schema reject the shape the model returns before the gate stamps it. It is a FINDING id, NOT a commit SHA — a 12-hex string looks exactly like a short SHA, and searching the commit log for it finds nothing by design; the disposition lives in agents/evidence/release-findings/<version>.json."
|
|
70
75
|
},
|
|
71
76
|
"severity": {
|
|
72
77
|
"enum": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://github.com/event4u-app/agent-config/scripts/schemas/skill.schema.json",
|
|
4
|
-
"x-schemaVersion": "1.
|
|
4
|
+
"x-schemaVersion": "1.1.1",
|
|
5
5
|
"title": "Skill frontmatter",
|
|
6
6
|
"$comment": "Source: agents/reference/docs/frontmatter-contract.md#skills. Keep in sync with inventory_frontmatter.py and the linter's lint_skill().",
|
|
7
7
|
"type": "object",
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
},
|
|
277
277
|
"requires_confirmation": {
|
|
278
278
|
"type": "boolean",
|
|
279
|
-
"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: only claude both binds pre_tool_use and honours a deny (docs/contracts/hook-architecture-v1.md, section Which hosts carry pre_tool_use — 2 further hosts bind it and ignore the verdict, 3 alias it unbound, 2
|
|
279
|
+
"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: only claude both binds pre_tool_use and honours a deny (docs/contracts/hook-architecture-v1.md, section Which hosts carry pre_tool_use — 2 further hosts bind it and ignore the verdict, 3 alias it unbound, 2 carry no pre-tool alias row), so a guard would degrade to prose on 7 of 8 hosts while the flag reads like a control — that decision is deferred behind blocker confirmation-degraded-host-semantics."
|
|
280
280
|
},
|
|
281
281
|
"command": {
|
|
282
282
|
"type": "array",
|
|
@@ -472,6 +472,16 @@
|
|
|
472
472
|
],
|
|
473
473
|
"description": "Classifier signal for auto-dispatch. 'steps' = ordered multi-step plan → do-in-steps. 'files' = N independent files of the same shape → do-in-parallel. 'independent' = N independent same-shape targets (not files) → do-in-parallel. Omit for skills that are always single-pass. Read by auto-dispatch-classification.md classifier signal 1."
|
|
474
474
|
},
|
|
475
|
+
"enforced_by": {
|
|
476
|
+
"type": "array",
|
|
477
|
+
"minItems": 1,
|
|
478
|
+
"uniqueItems": true,
|
|
479
|
+
"items": {
|
|
480
|
+
"type": "string",
|
|
481
|
+
"pattern": "^(hook:[a-z0-9_-]+|validator:[A-Za-z0-9_./-]+|test:[A-Za-z0-9_./-]+|observer:[a-z0-9 _-]+|instruction-only: *[^ ].*|none)$"
|
|
482
|
+
},
|
|
483
|
+
"description": "What resolves a CHECKABLE CLAIM this skill makes about itself \u2014 a standard-conformance claim of the form `<standard> <version> <level>` (WCAG 2.2 AA). Deliberately the SAME key and the same value vocabulary as rule.schema.json's `enforced_by`, per council 2026-09-04 (anthropic + openai, 2/2 for frontmatter over body markers; the residual split between reusing this key and minting a new `claim_resolvers:` was resolved on measurement \u2014 reuse costs ONE schema addition where a new key costs two, and adds no second vocabulary for one idea). `test:<path>` names the resolver; `instruction-only: <reason>` declares an honest gap, and the reason is MANDATORY because a bare marker is a claim with no triage record behind it. Resolved by tests/contracts/conformance_claim_declaration.test.ts. NOT read by check_enforcement_coverage.ts or lint_rule_enforcement_declaration.ts \u2014 both scan `src/rules/` only, so a skill carrying this key does not join the rule-enforcement ratchet."
|
|
484
|
+
},
|
|
475
485
|
"gaps": {
|
|
476
486
|
"type": "array",
|
|
477
487
|
"description": "B6 — honest-null known limitations. Human-curated (NOT auto-discovered), not all skills. Each entry documents a real limitation and cites a witness test that reproduces it. Rendered on the proof page's 'Known Limits'; check_skill_gaps enforces every witness path exists. The offensive-transparency surface a headline-benchmark cannot ship.",
|
|
@@ -356,6 +356,15 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
|
|
|
356
356
|
? `❌ ${blocking.length} merge-blocking finding(s) (security/claim × high+).`
|
|
357
357
|
: `⚠️ ${blocking.length} finding(s) WOULD block merge under an enforced gate (advisory now).`
|
|
358
358
|
: '✅ No merge-blocking findings (style/correctness advise only).';
|
|
359
|
+
// Three reviewers in the 2026-09 round searched the commit log for a finding
|
|
360
|
+
// id's first eight characters, found nothing, and reported "no fix found". A
|
|
361
|
+
// 12-hex id looks exactly like a short SHA, so the rendered comment now says
|
|
362
|
+
// what it is and where its disposition lives. This is the only place a reader
|
|
363
|
+
// of the comment can learn it without opening the source.
|
|
364
|
+
const idNote =
|
|
365
|
+
'\n\n`id` above is a FINDING id — the first 12 hex of sha256(kind|title|file), '
|
|
366
|
+
+ '**not a commit SHA**. Searching the git log for it finds nothing, by design. '
|
|
367
|
+
+ 'Its disposition is recorded in `agents/evidence/release-findings/<version>.json`.';
|
|
359
368
|
// Machine-readable block (release-truth Phase 3): invisible in the rendered
|
|
360
369
|
// comment; `check_finding_dispositions --pr` reads it as the TRIGGER that
|
|
361
370
|
// demands committed dispositions. The comment is transport, never the
|
|
@@ -363,7 +372,7 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
|
|
|
363
372
|
const machine = `\n\n<!-- release-findings-json: ${JSON.stringify(
|
|
364
373
|
findings.map((f) => ({ finding_id: findingId(f), severity: f.severity, kind: f.kind, title: f.title, file: f.file ?? null })),
|
|
365
374
|
)} -->`;
|
|
366
|
-
return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${esc}${machine}`;
|
|
375
|
+
return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${idNote}${esc}${machine}`;
|
|
367
376
|
}
|
|
368
377
|
|
|
369
378
|
function postReview(body: string): void {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* rule alone is exactly the kind of surface that is dropped in fresh
|
|
17
17
|
* conversations (found RED baseline on the reply-close PR-URL contract,
|
|
18
18
|
* 2026-07-27); the session_start injection makes the contract present in every
|
|
19
|
-
* new conversation on hook-capable hosts. Copilot (
|
|
19
|
+
* new conversation on hook-capable hosts. Copilot (nothing bound here) falls back
|
|
20
20
|
* to the rule alone.
|
|
21
21
|
*
|
|
22
22
|
* Gate + name resolution (first non-empty wins — the canary is a PERSONAL,
|
|
@@ -37,9 +37,10 @@
|
|
|
37
37
|
*
|
|
38
38
|
* ## Honest coverage
|
|
39
39
|
*
|
|
40
|
-
* The heartbeat is a per-turn obligation and does not reach every host.
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
* The heartbeat is a per-turn obligation and does not reach every host. This
|
|
41
|
+
* package binds nothing on `copilot`; `cursor`'s per-turn reachability is not
|
|
42
|
+
* established here, so it is excluded rather than assumed; `cowork`'s lifecycle
|
|
43
|
+
* events are wired but inert. Those hosts
|
|
43
44
|
* are named in `HEARTBEAT_REACHABLE_PLATFORMS` by their absence, and a session
|
|
44
45
|
* there simply stops being visible after its TTL rather than being reported as
|
|
45
46
|
* covered.
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_note": "Shrink-only grandfather allowlist for check_trigger_eval_presence (road-to-composition-ratchet). Skills listed here predate the trigger-eval presence gate and may lack evals/triggers.json. Entries may ONLY be removed (when the skill gains a triggers.json or is deleted)
|
|
2
|
+
"_note": "Shrink-only grandfather allowlist for check_trigger_eval_presence (road-to-composition-ratchet). Skills listed here predate the trigger-eval presence gate and may lack evals/triggers.json. Entries may ONLY be removed (when the skill gains a triggers.json or is deleted) — never added. Frozen 2026-07-08 at 221 of 264 skills.",
|
|
3
|
+
"_no_obligation_note": "Entries in `_no_obligation` are on the `skills` allowlist for a DIFFERENT reason from the rest. The list above records skills that predate the gate and owe a triggers.json; these owe none, because their description says explicit-request-only and they publish no auto-trigger keywords — there is no trigger to evaluate. Listing them without this distinction reads as a waived obligation when the obligation is absent. Read by humans only: check_trigger_eval_presence parses `skills` and ignores every other key, so this changes no verdict. Removing one from `skills` WOULD change a verdict — the gate would then demand a triggers.json for a skill that has nothing to trigger on.",
|
|
4
|
+
"_no_obligation": [
|
|
5
|
+
"sequential-thinking"
|
|
6
|
+
],
|
|
3
7
|
"skills": [
|
|
4
8
|
"accessibility-auditor",
|
|
5
9
|
"activation-design",
|
|
@@ -42,7 +42,7 @@ const SCHEMA_PATH = path.join(REPO_ROOT, 'src', 'scripts', 'schemas', 'agent-set
|
|
|
42
42
|
const TEMPLATE_PATH = path.join(REPO_ROOT, 'src', 'config', 'agent-settings.template.yml');
|
|
43
43
|
const LOCAL_PATHS = [project_settings_path(REPO_ROOT)];
|
|
44
44
|
|
|
45
|
-
// Installer-default substitutions, mirroring
|
|
45
|
+
// Installer-default substitutions, mirroring the installer so the
|
|
46
46
|
// template validates as it would after a fresh `balanced` install.
|
|
47
47
|
const PLACEHOLDERS: Record<string, string> = {
|
|
48
48
|
__RULE_LOADING_TIER__: 'balanced',
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* would mean the agent could arrive at its own permission by observing the
|
|
16
16
|
* world, which is the confused-deputy shape in miniature.
|
|
17
17
|
*
|
|
18
|
-
* Modelled on `src/scripts/hooks/
|
|
18
|
+
* Modelled on `src/scripts/hooks/git_command_classifier.ts`: answer from a
|
|
19
19
|
* recorded fact rather than from the model's recollection, and treat a missing
|
|
20
20
|
* record as NOT granted rather than as probably-fine.
|
|
21
21
|
*
|