@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.
Files changed (97) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +136 -0
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/conformance.md +182 -0
  5. package/dist/agent-src/commands/analyze/inbox.md +33 -2
  6. package/dist/agent-src/commands/review/changes.md +6 -5
  7. package/dist/agent-src/commands/roadmap/next.md +194 -0
  8. package/dist/agent-src/commands/roadmap.md +12 -4
  9. package/dist/agent-src/contexts/execution/subagent-routing.md +4 -1
  10. package/dist/agent-src/rules/decision-revisit-gate.md +2 -0
  11. package/dist/agent-src/rules/delegation-policy.md +3 -2
  12. package/dist/agent-src/rules/design-fidelity.md +17 -1
  13. package/dist/agent-src/rules/design-review-after-ui-write.md +116 -0
  14. package/dist/agent-src/rules/evaluator-independence.md +111 -0
  15. package/dist/agent-src/rules/session-canary.md +15 -1
  16. package/dist/agent-src/rules/settings-ask-protocol.md +176 -0
  17. package/dist/agent-src/rules/token-budget-discipline.md +6 -3
  18. package/dist/agent-src/rules/ui-audit-gate.md +3 -0
  19. package/dist/agent-src/rules/user-interaction.md +23 -0
  20. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +1 -1
  21. package/dist/agent-src/skills/recursive-verification/SKILL.md +13 -0
  22. package/dist/agent-src/skills/rule-writing/SKILL.md +45 -0
  23. package/dist/agent-src/skills/skill-writing/SKILL.md +114 -0
  24. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  25. package/dist/cli/registry.js +1 -0
  26. package/dist/cli/registry.js.map +1 -1
  27. package/dist/cli-delegate/cmd_route_audit.js +4 -1
  28. package/dist/cli-delegate/cmd_route_explain.js +4 -1
  29. package/dist/discovery/deprecation-report.md +1 -1
  30. package/dist/discovery/discovery-manifest.json +146 -28
  31. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  32. package/dist/discovery/discovery-manifest.summary.md +5 -5
  33. package/dist/discovery/orphan-report.md +1 -1
  34. package/dist/discovery/packs.json +16 -11
  35. package/dist/discovery/trust-report.md +3 -3
  36. package/dist/discovery/workspaces.json +12 -7
  37. package/dist/hooks/dispatch.js +1501 -511
  38. package/dist/mcp/registry-manifest.json +3 -3
  39. package/dist/mcp/server.json +2 -2
  40. package/dist/router.json +1 -1
  41. package/dist/server/nicknameResolver.js +116 -0
  42. package/dist/server/nicknameResolver.js.map +1 -0
  43. package/dist/server/routes/ping.js +29 -11
  44. package/dist/server/routes/ping.js.map +1 -1
  45. package/dist/shared/interactiveContext.js +73 -0
  46. package/dist/shared/interactiveContext.js.map +1 -0
  47. package/dist/shared/nicknamePrefill.js +59 -0
  48. package/dist/shared/nicknamePrefill.js.map +1 -0
  49. package/dist/shared/settingsAsks.js +128 -0
  50. package/dist/shared/settingsAsks.js.map +1 -0
  51. package/dist/shared/settingsConsent.js +93 -0
  52. package/dist/shared/settingsConsent.js.map +1 -0
  53. package/package.json +1 -1
  54. package/src/config/agents-paths.yml +15 -0
  55. package/src/config/evaluator-budgets.json +68 -68
  56. package/src/config/gate-coverage.yml +131 -5
  57. package/src/config/gate-violation-baselines.json +11 -0
  58. package/src/scripts/_cli/cmd_route_audit.ts +4 -1
  59. package/src/scripts/_cli/cmd_route_explain.ts +9 -1
  60. package/src/scripts/_dispatch.bash +12 -0
  61. package/src/scripts/ai-video/stitch.sh +27 -9
  62. package/src/scripts/before_complete_hook.ts +146 -5
  63. package/src/scripts/bench_ab_v2_run.ts +272 -16
  64. package/src/scripts/bench_ab_v2_stats.ts +156 -3
  65. package/src/scripts/check_completion_review.ts +80 -3
  66. package/src/scripts/check_corpus_staleness.ts +692 -0
  67. package/src/scripts/check_depth_budget.ts +295 -0
  68. package/src/scripts/check_gate_coverage.ts +67 -0
  69. package/src/scripts/check_hook_bundle_freshness.ts +183 -0
  70. package/src/scripts/conformance_scan.ts +422 -0
  71. package/src/scripts/dispatch_r2_reviewer.ts +31 -2
  72. package/src/scripts/git_authorization_hook.ts +328 -0
  73. package/src/scripts/hook_manifest.yaml +69 -9
  74. package/src/scripts/hooks/block_no_verify.ts +3 -1
  75. package/src/scripts/hooks/block_unauthorized_git.ts +371 -0
  76. package/src/scripts/hooks/concern_registry.ts +8 -0
  77. package/src/scripts/hooks/design_slop_hook.ts +13 -1
  78. package/src/scripts/hooks/dispatch_hook.ts +97 -2
  79. package/src/scripts/hooks/evidence_independence.ts +368 -0
  80. package/src/scripts/language_mirror_hook.ts +456 -0
  81. package/src/scripts/lint_abstraction_thresholds.ts +41 -8
  82. package/src/scripts/lint_agents_layout.ts +2 -0
  83. package/src/scripts/lint_decision_impact.ts +246 -0
  84. package/src/scripts/lint_example_fences.ts +609 -0
  85. package/src/scripts/lint_hook_manifest.ts +29 -0
  86. package/src/scripts/lint_rule_interactions.ts +90 -0
  87. package/src/scripts/lint_rule_migration_ledger.ts +480 -0
  88. package/src/scripts/lint_skill_descriptions.ts +86 -2
  89. package/src/scripts/memory_learn_hook.ts +109 -6
  90. package/src/scripts/pr_url_reminder_hook.ts +17 -2
  91. package/src/scripts/release.ts +38 -1
  92. package/src/scripts/schemas/review-findings.schema.json +99 -0
  93. package/src/scripts/schemas/rule.schema.json +10 -0
  94. package/src/shared/interactiveContext.ts +98 -0
  95. package/src/shared/nicknamePrefill.ts +96 -0
  96. package/src/shared/settingsAsks.ts +176 -0
  97. package/src/shared/settingsConsent.ts +136 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What to put in the nickname field before the user types anything.
3
+ *
4
+ * Phase 4 step 2 of `road-to-zero-ceremony-settings`: *"Ask the nickname once,
5
+ * prefilled from git user name then `$USER`, so accepting is one keypress."*
6
+ * `src/rules/settings-ask-protocol.md` states the chain and closes it with a
7
+ * negation — **"Never `$USER` alone."**
8
+ *
9
+ * WHY THIS MODULE EXISTS. The rule shipped that chain; the one surface that
10
+ * actually prefills shipped a different one. `src/server/routes/ping.ts` read
11
+ * `userInfo().username` and nothing else — precisely the `$USER`-alone shape the
12
+ * rule forbids. A rule and the code it describes disagreeing is drift, not a
13
+ * preference, so the chain now has exactly one implementation and both the
14
+ * server route and the tests read it from here.
15
+ *
16
+ * WHY GIT COMES FIRST. `$USER` is a login handle (`mathiasberg`); `git config
17
+ * user.name` is the name the human chose to be called by (`Mathias Berg`). The
18
+ * whole point of the prefill is that accepting costs one keypress, and that only
19
+ * holds when the prefilled value is the one they would have typed. Note the
20
+ * lookup is deliberately NOT `--local`: the merged config includes
21
+ * `~/.gitconfig`, so the wizard still finds a name when it runs outside a repo.
22
+ *
23
+ * Pure: the git lookup is injected, so the resolver is a function of its probe
24
+ * alone and a test never depends on the machine it runs on — the same shape as
25
+ * `interactiveContext.ts` and `settingsConsent.ts`. The Node-side half (running
26
+ * git, falling back to `userInfo()`) lives in `src/server/nicknameResolver.ts`,
27
+ * because `src/shared/**` may not import Node built-ins and the lint enforces it.
28
+ *
29
+ * @see src/server/nicknameResolver.ts — the impure half
30
+ * @see src/rules/settings-ask-protocol.md § Worked example — the nickname
31
+ * @see docs/contracts/settings-classes.md
32
+ */
33
+ /** Trim, and treat a whitespace-only value as absent. */
34
+ function _clean(value) {
35
+ return (value ?? '').trim();
36
+ }
37
+ /**
38
+ * The prefill for the nickname ask, following the documented chain.
39
+ *
40
+ * Order is the rule's, and the negation at the end of it is the load-bearing
41
+ * part: `$USER` is a fallback, never the first answer.
42
+ */
43
+ export function nicknamePrefill(probe) {
44
+ const git = _clean(probe.gitUserName);
45
+ if (git !== '') {
46
+ return { name: git, source: 'git-user-name' };
47
+ }
48
+ const user = _clean(probe.env['USER']);
49
+ if (user !== '') {
50
+ return { name: user, source: 'env-user' };
51
+ }
52
+ // Windows spells it differently; the rule names it explicitly.
53
+ const username = _clean(probe.env['USERNAME']);
54
+ if (username !== '') {
55
+ return { name: username, source: 'env-username' };
56
+ }
57
+ return { name: '', source: 'none' };
58
+ }
59
+ //# sourceMappingURL=nicknamePrefill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nicknamePrefill.js","sourceRoot":"","sources":["../../src/shared/nicknamePrefill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAsCH,yDAAyD;AACzD,SAAS,MAAM,CAAC,KAAyB;IACrC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAoB;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAClD,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACvC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9C,CAAC;IACD,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/C,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The one-question budget, as a function rather than as a promise.
3
+ *
4
+ * Phase 5 of `road-to-zero-ceremony-settings`. The rule
5
+ * `src/rules/settings-ask-protocol.md` states the budget — at most ONE settings
6
+ * question per command execution, every further undecided key taking its
7
+ * conservative default silently and being named in the end-summary. Prose is
8
+ * where that obligation has to live, because no gate can count the questions in
9
+ * a chat turn.
10
+ *
11
+ * But the *split* is not a judgement call: given the keys an execution needs
12
+ * decided, which one is asked and what the others resolve to is fully
13
+ * determined. Leaving that determined part in prose too would mean the roadmap's
14
+ * own exit criterion — "a planted fixture needing three B decisions asks once,
15
+ * assumes two conservatively, and lists both in the summary" — had nothing to
16
+ * run against. So the split is computed here and the rule points at it.
17
+ *
18
+ * What this module does NOT do, stated so nobody mistakes it for the fence:
19
+ * it does not ask, it does not write, and it cannot stop an agent that ignores
20
+ * it. The write fence is one layer down and is real —
21
+ * `src/scripts/_cli/cmd_settings_set.ts` and the Fastify settings route both
22
+ * refuse every C-class key and fail closed on an unreadable class contract.
23
+ *
24
+ * Pure — no I/O, no clock, no environment.
25
+ *
26
+ * @see src/rules/settings-ask-protocol.md
27
+ * @see docs/contracts/settings-classes.md
28
+ */
29
+ import { classOfPath, isConservativeDefault } from './settingsClasses.js';
30
+ /**
31
+ * The end-summary line for a silently defaulted key.
32
+ *
33
+ * `--source manual` and not `jit-answer`: the user did not answer a question, so
34
+ * stamping the write as a just-in-time answer would record a consent nobody
35
+ * gave. The provenance vocabulary exists to keep exactly that distinction.
36
+ */
37
+ export function changeCommand(key) {
38
+ return `agent-config settings:set ${key} <value> --source manual`;
39
+ }
40
+ /**
41
+ * Plan one execution's settings asks.
42
+ *
43
+ * `needed` is the keys this execution genuinely cannot proceed without, in the
44
+ * order they became needed — the caller's own ordering is respected because the
45
+ * first key to actually block the run is the one that earned the question.
46
+ *
47
+ * `decided` is every key already settled on any resolution layer. A decided key
48
+ * is not a candidate: re-asking a settled question is the cheap question the
49
+ * ask-discipline rules forbid, and it is the single most likely way this budget
50
+ * gets spent on nothing.
51
+ *
52
+ * `defaultOf` supplies the template default. It returns `undefined` for a key
53
+ * the template does not carry, which is treated as unclassified rather than as
54
+ * a default of `undefined` — a missing template entry is a defect, not a value.
55
+ */
56
+ export function planSettingsAsks(needed, classes, defaultOf, decided = new Set(), options = {}) {
57
+ const plan = { ask: null, silent: [], skipped: [] };
58
+ const seen = new Set();
59
+ // Non-TTY / CI / headless: nobody can answer, so the budget is ZERO rather
60
+ // than one. Every candidate takes its conservative default and the plan
61
+ // carries no ask — "no questions, ever", as a computed fact rather than as a
62
+ // promise the caller has to remember. Nothing is written either: this
63
+ // function never persists, and with no ask there is no `jit-answer` write.
64
+ const interactive = options.interactive ?? true;
65
+ for (const key of needed) {
66
+ // A duplicate in the caller's list must not consume the budget twice.
67
+ if (seen.has(key)) {
68
+ continue;
69
+ }
70
+ seen.add(key);
71
+ if (decided.has(key)) {
72
+ plan.skipped.push({ key, reason: 'already-decided' });
73
+ continue;
74
+ }
75
+ const cls = classOfPath(classes, key);
76
+ if (cls === undefined) {
77
+ // Unclassified is guarded everywhere else in this system; it is
78
+ // guarded here too. Never fall back to "probably fine".
79
+ plan.skipped.push({ key, reason: 'unclassified' });
80
+ continue;
81
+ }
82
+ if (cls === 'A') {
83
+ plan.skipped.push({ key, reason: 'class-a-never-asked' });
84
+ continue;
85
+ }
86
+ if (cls === 'C') {
87
+ // A C key set to `ask` still produces a RUNTIME question, but that
88
+ // answer lasts one run and is never persisted, so it is not this
89
+ // budget's business. Persisting it stays on the human path.
90
+ plan.skipped.push({ key, reason: 'class-c-guarded' });
91
+ continue;
92
+ }
93
+ const fallback = defaultOf(key);
94
+ if (fallback === undefined) {
95
+ plan.skipped.push({ key, reason: 'unclassified' });
96
+ continue;
97
+ }
98
+ if (interactive && plan.ask === null) {
99
+ plan.ask = key;
100
+ continue;
101
+ }
102
+ if (!isConservativeDefault(fallback)) {
103
+ // Taking a permissive value silently would be a decision made in
104
+ // the user's name — the one outcome the Iron Law names outright.
105
+ // Surfacing it as skipped forces the caller to deal with it instead
106
+ // of inheriting a permission nobody granted.
107
+ plan.skipped.push({ key, reason: 'non-conservative-default' });
108
+ continue;
109
+ }
110
+ plan.silent.push({ key, value: fallback, command: changeCommand(key) });
111
+ }
112
+ return plan;
113
+ }
114
+ /**
115
+ * The end-summary block for a plan, or `null` when nothing was defaulted.
116
+ *
117
+ * `null` rather than an empty string so a caller cannot append a heading with no
118
+ * rows under it. This text rides INSIDE the single end-summary that
119
+ * `direct-answers` already requires — it is never a second summary.
120
+ */
121
+ export function silentDefaultsSummary(plan) {
122
+ if (plan.silent.length === 0) {
123
+ return null;
124
+ }
125
+ const lines = plan.silent.map((s) => `- \`${s.key}\` → ${JSON.stringify(s.value)} (default) · change: \`${s.command}\``);
126
+ return `Settings resolved without asking this run:\n${lines.join('\n')}`;
127
+ }
128
+ //# sourceMappingURL=settingsAsks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settingsAsks.js","sourceRoot":"","sources":["../../src/shared/settingsAsks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Is a permissive B-class value a RECORDED CONSENT, or just a value?
3
+ *
4
+ * Phase 5 of `road-to-zero-ceremony-settings`, and the answer to that
5
+ * roadmap's Risk 3 — *"the provenance stamp becomes decoration; an unread stamp
6
+ * is cost with no return"*. Before this module the sidecar
7
+ * `settings/.agent-settings.provenance.json` was written by `settings:set` and
8
+ * read by exactly one caller, the GUI, to **display** it. Nothing decided
9
+ * anything on it. This is the reader that makes it load-bearing.
10
+ *
11
+ * The distinction it draws, which a bare value cannot: `true` that a human chose
12
+ * and `true` that the machine inferred are the same byte and are not the same
13
+ * permission. `auto-detected` is the provenance a machine writes about itself —
14
+ * a locale, an installed binary, a detected host. Letting it grant a consent
15
+ * would mean the agent could arrive at its own permission by observing the
16
+ * world, which is the confused-deputy shape in miniature.
17
+ *
18
+ * Modelled on `src/scripts/hooks/block_unauthorized_git.ts`: answer from a
19
+ * recorded fact rather than from the model's recollection, and treat a missing
20
+ * record as NOT granted rather than as probably-fine.
21
+ *
22
+ * WHAT THIS IS NOT. It is not a hook and it does not intercept anything. No
23
+ * B-class key currently gates an action through a guard — the two that gate
24
+ * behaviour at all (`personal.open_edited_files`, `memory.learn_on_session_end`)
25
+ * are read as values by the installer, the wizard and one session_end concern,
26
+ * and the *action* they govern is prose. So the enforcement gradient is honest
27
+ * and steep: this function is deterministic, the obligation to call it is
28
+ * model-carried, and on a prose-only host ask-once can degrade to ask-never.
29
+ *
30
+ * Pure — no I/O. Callers supply the value and the sidecar entry.
31
+ *
32
+ * @see src/rules/settings-ask-protocol.md
33
+ * @see docs/contracts/settings-classes.md
34
+ */
35
+ import { isConservativeDefault } from './settingsClasses.js';
36
+ /**
37
+ * The sources that can carry a human decision.
38
+ *
39
+ * `jit-answer` is a question the user answered. `gui` is a human at the settings
40
+ * editor. `manual` is a hand-edit or an explicit CLI write on the user's
41
+ * instruction. `auto-detected` is excluded on purpose — see the module header.
42
+ */
43
+ const HUMAN_SOURCES = ['jit-answer', 'gui', 'manual'];
44
+ /**
45
+ * The verdict for one consent-gated action.
46
+ *
47
+ * Order matters: class first (a C key never reaches a consent question at all),
48
+ * then the value (conservative means no, regardless of provenance), then the
49
+ * record.
50
+ */
51
+ export function consentVerdict(query) {
52
+ if (query.cls !== 'B') {
53
+ return 'not-a-consent-key';
54
+ }
55
+ if (isConservativeDefault(query.value)) {
56
+ return 'withheld-default';
57
+ }
58
+ if (query.handEdited === true) {
59
+ return 'granted';
60
+ }
61
+ if (query.source === undefined) {
62
+ return 'withheld-unrecorded';
63
+ }
64
+ if (!HUMAN_SOURCES.includes(query.source)) {
65
+ return 'withheld-machine-inferred';
66
+ }
67
+ return 'granted';
68
+ }
69
+ /** `true` only for `granted` — every other verdict withholds. */
70
+ export function consentGranted(query) {
71
+ return consentVerdict(query) === 'granted';
72
+ }
73
+ /**
74
+ * One line explaining a withheld verdict, for the surface that refused.
75
+ *
76
+ * `null` for `granted`, so a caller cannot accidentally print a refusal reason
77
+ * on the allow path.
78
+ */
79
+ export function withheldReason(verdict, key) {
80
+ switch (verdict) {
81
+ case 'granted':
82
+ return null;
83
+ case 'withheld-default':
84
+ return `${key} is at its conservative default — absent and no are the same answer.`;
85
+ case 'withheld-unrecorded':
86
+ return `${key} permits this, but nothing records who decided it. Ask, or set it via the GUI.`;
87
+ case 'withheld-machine-inferred':
88
+ return `${key} was auto-detected, not decided. A machine inference is not a consent.`;
89
+ case 'not-a-consent-key':
90
+ return `${key} is not a class-B consent key — this gate does not apply to it.`;
91
+ }
92
+ }
93
+ //# sourceMappingURL=settingsConsent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settingsConsent.js","sourceRoot":"","sources":["../../src/shared/settingsConsent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,qBAAqB,EAAsB,MAAM,sBAAsB,CAAC;AAKjF;;;;;;GAMG;AACH,MAAM,aAAa,GAA6B,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAsChF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB;IAC9C,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;QACpB,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IACD,IAAI,qBAAqB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO,kBAAkB,CAAC;IAC9B,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,qBAAqB,CAAC;IACjC,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,OAAO,2BAA2B,CAAC;IACvC,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,KAAmB;IAC9C,OAAO,cAAc,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,GAAW;IAC/D,QAAQ,OAAO,EAAE,CAAC;QACd,KAAK,SAAS;YACV,OAAO,IAAI,CAAC;QAChB,KAAK,kBAAkB;YACnB,OAAO,GAAG,GAAG,sEAAsE,CAAC;QACxF,KAAK,qBAAqB;YACtB,OAAO,GAAG,GAAG,gFAAgF,CAAC;QAClG,KAAK,2BAA2B;YAC5B,OAAO,GAAG,GAAG,wEAAwE,CAAC;QAC1F,KAAK,mBAAmB;YACpB,OAAO,GAAG,GAAG,iEAAiE,CAAC;IACvF,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
3
  "mcpName": "io.github.event4u-app/agent-config",
4
- "version": "9.24.0",
4
+ "version": "9.26.0",
5
5
  "description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) — every claim machine-checked, including \"zero runtime daemon\".",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -114,6 +114,21 @@
114
114
  writer: /team-knowledge:bootstrap skill
115
115
  rationale: Deterministic template pages; review-then-commit flow (nothing here is the artifact)
116
116
 
117
+ # ---------------------------------------------------------------------------
118
+ # Self-improvement proposals (TRACKED — listed for classification, not ignored)
119
+ # ---------------------------------------------------------------------------
120
+
121
+ - path: /agents/proposals/
122
+ scope: both
123
+ policy: tracked
124
+ writer: learning-to-rule-or-skill skill (drafts) → upstream-contribute (PR)
125
+ rationale: |
126
+ Proposal drafts must be committed — the proposal-drift workflow reads their
127
+ stage: frontmatter in CI, and upstream-contribute opens a PR from them. Listed
128
+ here because agents-layout.md requires every new top-level agents/ entry to
129
+ appear in this manifest; the entry is inert for the gitignore gate, which reads
130
+ policy: ignored only.
131
+
117
132
  # ---------------------------------------------------------------------------
118
133
  # Ghostwriter profiles
119
134
  # ---------------------------------------------------------------------------
@@ -1,74 +1,74 @@
1
1
  {
2
- "_comment": "Outside-in evaluator budgets (road-to-credible-install Phase 6). VALUES ARE FROZEN FROM MEASURED ACCEPTANCE NUMBERS of Phases 0-3 (2026-07-27, packed tarball @ 9.8.0 tree, --omit=dev consumer install), never invented. The umbrella harness (src/scripts/evaluator_umbrella.sh) measures the packed artifact and check_evaluator_budgets.ts enforces: absolute budget AND >regression_pct growth vs last_measured fails even under budget. Owner + review date are lint-enforced (lint_budget_ownership). Timing metrics carry a per-entry regression_pct override (200) — wall-clock noise would flap a 10% creep rule; their absolute max is the binding gate. Sizes/counts keep the tight 10% creep.",
3
- "schema_version": 1,
4
- "owner": "maintainer",
5
- "review_by": "2027-07-27",
6
- "regression_pct": 10,
7
- "posture": {
8
- "on_release": "fail",
9
- "on_main_and_prs": "warn",
10
- "on_local": "fail",
11
- "always_blocking": [
12
- "a metric with no measurement supplied"
13
- ],
14
- "rationale": "road-to-gates-that-can-fail Phase 5. A hard fail on main turns every legitimate command addition into a blocked merge, which is how budgets get quietly raised with a cushion instead of consciously — so budget / regression / drift findings are ADVISORY on main, the nightly and ordinary PRs, and BLOCKING on a release ref. Not a weakening: before the Phase-4 path triggers, this gate ran on release PRs and the nightly only, so the surfaces that now warn never saw it at all. A LOCAL run (no CI env) is strict, because a maintainer running it by hand — including release.ts — wants the strict answer and blocks nobody. A metric with NO measurement is always blocking in either posture: that is a broken harness reporting green on nothing. Implemented in src/scripts/check_evaluator_budgets.ts::detectPosture; override with --posture warn|fail.",
15
- "delivery": "This posture IS the nightly-visibility route (council 2026-08-02, blocker nightly-visibility-owner), not a second mechanism beside it: the nightly publishes measurement_record below, a later run re-measures and compares, and the contradiction warns on main / fails on release. No notification channel, no auto-managed issue — a one-maintainer channel producing routine noise gets muted, and a muted channel reproduces the original failure."
2
+ "_comment": "Outside-in evaluator budgets (road-to-credible-install Phase 6). VALUES ARE FROZEN FROM MEASURED ACCEPTANCE NUMBERS of Phases 0-3 (2026-07-27, packed tarball @ 9.8.0 tree, --omit=dev consumer install), never invented. The umbrella harness (src/scripts/evaluator_umbrella.sh) measures the packed artifact and check_evaluator_budgets.ts enforces: absolute budget AND >regression_pct growth vs last_measured fails even under budget. Owner + review date are lint-enforced (lint_budget_ownership). Timing metrics carry a per-entry regression_pct override (200) — wall-clock noise would flap a 10% creep rule; their absolute max is the binding gate. Sizes/counts keep the tight 10% creep.",
3
+ "schema_version": 1,
4
+ "owner": "maintainer",
5
+ "review_by": "2027-07-27",
6
+ "regression_pct": 10,
7
+ "posture": {
8
+ "on_release": "fail",
9
+ "on_main_and_prs": "warn",
10
+ "on_local": "fail",
11
+ "always_blocking": [
12
+ "a metric with no measurement supplied"
13
+ ],
14
+ "rationale": "road-to-gates-that-can-fail Phase 5. A hard fail on main turns every legitimate command addition into a blocked merge, which is how budgets get quietly raised with a cushion instead of consciously — so budget / regression / drift findings are ADVISORY on main, the nightly and ordinary PRs, and BLOCKING on a release ref. Not a weakening: before the Phase-4 path triggers, this gate ran on release PRs and the nightly only, so the surfaces that now warn never saw it at all. A LOCAL run (no CI env) is strict, because a maintainer running it by hand — including release.ts — wants the strict answer and blocks nobody. A metric with NO measurement is always blocking in either posture: that is a broken harness reporting green on nothing. Implemented in src/scripts/check_evaluator_budgets.ts::detectPosture; override with --posture warn|fail.",
15
+ "delivery": "This posture IS the nightly-visibility route (council 2026-08-02, blocker nightly-visibility-owner), not a second mechanism beside it: the nightly publishes measurement_record below, a later run re-measures and compares, and the contradiction warns on main / fails on release. No notification channel, no auto-managed issue — a one-maintainer channel producing routine noise gets muted, and a muted channel reproduces the original failure."
16
+ },
17
+ "measurement_record": {
18
+ "path": "agents/evidence/metrics/evaluator-measurements.json",
19
+ "written_by": "src/scripts/evaluator_umbrella.sh (UMBRELLA_RECORD=1 — the nightly sets it)",
20
+ "compared_by": "src/scripts/check_evaluator_budgets.ts --recorded",
21
+ "note": "Only entries WITHOUT `deterministic: false` participate in the drift comparison. The council's recorded objection was that a non-deterministic comparison produces noise and gets ignored; determinism is therefore a precondition, verified per metric, not an assumption."
22
+ },
23
+ "remeasurement_2026_08_02": {
24
+ "verified_against_a_fresh_run": {
25
+ "cli_help_command_count": "80 — three consecutive runs of the pinned method on an unchanged tree returned 80/80/80. Matches last_measured; deterministic confirmed.",
26
+ "mcp_public_tool_count": "19 — two consecutive tools/list runs on a freshly built dist/mcp/server.mjs returned 19/19. Matches last_measured; deterministic confirmed."
16
27
  },
17
- "measurement_record": {
18
- "path": "agents/evidence/metrics/evaluator-measurements.json",
19
- "written_by": "src/scripts/evaluator_umbrella.sh (UMBRELLA_RECORD=1 — the nightly sets it)",
20
- "compared_by": "src/scripts/check_evaluator_budgets.ts --recorded",
21
- "note": "Only entries WITHOUT `deterministic: false` participate in the drift comparison. The council's recorded objection was that a non-deterministic comparison produces noise and gets ignored; determinism is therefore a precondition, verified per metric, not an assumption."
28
+ "not_re_measured": "unpacked_size_mb, node_modules_mb, runtime_dep_count, cli_version_cold_ms, mcp_boot_to_initialize_ms. All five require the packed-tarball harness (npm pack of a fully built tree, then a clean `npm install --omit=dev <tarball>` into a scratch project) that evaluator_umbrella.sh runs in CI. They were NOT re-measured in this pass and their values remain the 2026-07-27 acceptance numbers — stated rather than invented. The next nightly writes the real numbers into measurement_record, and the drift check then reports any contradiction with last_measured on the PR that carries it."
29
+ },
30
+ "removed_2026_08_04": "unpacked_size_mb (max 28) — removed by maintainer decision at the 9.17.0 release: the gate turned every legitimate payload addition into a blocked release (measured 28.22 vs max 28 with all functional gates green), and the maintainer judged tarball size not worth gating. evaluator_umbrella.sh still MEASURES and records unpacked_size_mb as evidence; only the budget enforcement is gone. ADR-204 review_trigger (a) fired and was resolved this way.",
31
+ "budgets": {
32
+ "node_modules_mb": {
33
+ "max": 140,
34
+ "last_measured": 127,
35
+ "last_measured_at": "2026-07-27",
36
+ "method": "du -sm node_modules after `npm install --omit=dev <tarball>` in a clean project"
22
37
  },
23
- "remeasurement_2026_08_02": {
24
- "verified_against_a_fresh_run": {
25
- "cli_help_command_count": "80 — three consecutive runs of the pinned method on an unchanged tree returned 80/80/80. Matches last_measured; deterministic confirmed.",
26
- "mcp_public_tool_count": "19 — two consecutive tools/list runs on a freshly built dist/mcp/server.mjs returned 19/19. Matches last_measured; deterministic confirmed."
27
- },
28
- "not_re_measured": "unpacked_size_mb, node_modules_mb, runtime_dep_count, cli_version_cold_ms, mcp_boot_to_initialize_ms. All five require the packed-tarball harness (npm pack of a fully built tree, then a clean `npm install --omit=dev <tarball>` into a scratch project) that evaluator_umbrella.sh runs in CI. They were NOT re-measured in this pass and their values remain the 2026-07-27 acceptance numbers — stated rather than invented. The next nightly writes the real numbers into measurement_record, and the drift check then reports any contradiction with last_measured on the PR that carries it."
38
+ "runtime_dep_count": {
39
+ "max": 200,
40
+ "last_measured": 187,
41
+ "last_measured_at": "2026-07-27",
42
+ "method": "package-lock.json packages count (minus root) of the clean --omit=dev install"
29
43
  },
30
- "removed_2026_08_04": "unpacked_size_mb (max 28) — removed by maintainer decision at the 9.17.0 release: the gate turned every legitimate payload addition into a blocked release (measured 28.22 vs max 28 with all functional gates green), and the maintainer judged tarball size not worth gating. evaluator_umbrella.sh still MEASURES and records unpacked_size_mb as evidence; only the budget enforcement is gone. ADR-204 review_trigger (a) fired and was resolved this way.",
31
- "budgets": {
32
- "node_modules_mb": {
33
- "max": 140,
34
- "last_measured": 127,
35
- "last_measured_at": "2026-07-27",
36
- "method": "du -sm node_modules after `npm install --omit=dev <tarball>` in a clean project"
37
- },
38
- "runtime_dep_count": {
39
- "max": 200,
40
- "last_measured": 187,
41
- "last_measured_at": "2026-07-27",
42
- "method": "package-lock.json packages count (minus root) of the clean --omit=dev install"
43
- },
44
- "cli_version_cold_ms": {
45
- "max": 1500,
46
- "last_measured": 370,
47
- "last_measured_at": "2026-07-27",
48
- "method": "wall-clock of the FIRST `agent-config --version` after install (cold fs cache run counts)",
49
- "regression_pct": 200,
50
- "deterministic": false
51
- },
52
- "mcp_boot_to_initialize_ms": {
53
- "max": 2000,
54
- "last_measured": 264,
55
- "last_measured_at": "2026-07-27",
56
- "method": "initialize request piped to `node dist/mcp/server.mjs`, wall-clock to response",
57
- "regression_pct": 200,
58
- "deterministic": false
59
- },
60
- "cli_help_command_count": {
61
- "max": 85,
62
- "last_measured": 85,
63
- "last_measured_at": "2026-08-05",
64
- "method": "registry enumeration — count of `{ name: '...', disposition` entries in src/cli/registry.ts (NOT --help prose parsing; methods historically disagreed 74 vs 76 vs 79)",
65
- "baseline_note": "80 → 81 on 2026-08-03 (release 9.15.0 gate). This RECORDS growth that already shipped through its own reviewed PR; it does not authorize new surface. The +1 is f02db54c2 (fix(cli): register routing:doctor in the CLI registry) — the command existed, only its registry entry was missing, and the registering PR did not move this budget. The gate did its job again: breach WARNS on main and BLOCKS on release, which is the conscious-decision point rather than a silently raised cap. Re-measured 2026-08-03 via the pinned method: 81. Headroom stays zero by design — the next registry addition must move this number in its own PR. 81 → 82 on 2026-08-04: the `handoff` subcommand (road-to-agent-handoff-resume Phase 4) registers in the same PR that adds the surface, per this record's own contract. Re-measured via the pinned method: 82. 82 → 84 on 2026-08-04: the `route:explain` + `route:audit` verbs (routing-correctness validation surface) register in the same PR that adds the surface. Re-measured via the pinned method: 84. 84 → 85 on 2026-08-05: the `settings:set` verb (road-to-zero-ceremony-settings Phase 2 — the one agent-reachable settings writer) registers in the same PR that adds the surface, per this record's own contract. Re-measured via the pinned method: 85."
66
- },
67
- "mcp_public_tool_count": {
68
- "max": 19,
69
- "last_measured": 19,
70
- "last_measured_at": "2026-08-02",
71
- "method": "tools/list length on the stdio server (post-ADR-132: implemented tools only)"
72
- }
44
+ "cli_version_cold_ms": {
45
+ "max": 1500,
46
+ "last_measured": 370,
47
+ "last_measured_at": "2026-07-27",
48
+ "method": "wall-clock of the FIRST `agent-config --version` after install (cold fs cache run counts)",
49
+ "regression_pct": 200,
50
+ "deterministic": false
51
+ },
52
+ "mcp_boot_to_initialize_ms": {
53
+ "max": 2000,
54
+ "last_measured": 264,
55
+ "last_measured_at": "2026-07-27",
56
+ "method": "initialize request piped to `node dist/mcp/server.mjs`, wall-clock to response",
57
+ "regression_pct": 200,
58
+ "deterministic": false
59
+ },
60
+ "cli_help_command_count": {
61
+ "max": 86,
62
+ "last_measured": 86,
63
+ "last_measured_at": "2026-08-06",
64
+ "method": "registry enumeration — count of `{ name: '...', disposition` entries in src/cli/registry.ts (NOT --help prose parsing; methods historically disagreed 74 vs 76 vs 79)",
65
+ "baseline_note": "80 → 81 on 2026-08-03 (release 9.15.0 gate). This RECORDS growth that already shipped through its own reviewed PR; it does not authorize new surface. The +1 is f02db54c2 (fix(cli): register routing:doctor in the CLI registry) — the command existed, only its registry entry was missing, and the registering PR did not move this budget. The gate did its job again: breach WARNS on main and BLOCKS on release, which is the conscious-decision point rather than a silently raised cap. Re-measured 2026-08-03 via the pinned method: 81. Headroom stays zero by design — the next registry addition must move this number in its own PR. 81 → 82 on 2026-08-04: the `handoff` subcommand (road-to-agent-handoff-resume Phase 4) registers in the same PR that adds the surface, per this record's own contract. Re-measured via the pinned method: 82. 82 → 84 on 2026-08-04: the `route:explain` + `route:audit` verbs (routing-correctness validation surface) register in the same PR that adds the surface. Re-measured via the pinned method: 84. 84 → 85 on 2026-08-05: the `settings:set` verb (road-to-zero-ceremony-settings Phase 2 — the one agent-reachable settings writer) registers in the same PR that adds the surface, per this record's own contract. Re-measured via the pinned method: 85. 85 → 86 on 2026-08-06: the `conformance:behavior` verb (road-to-agent-behavior-conformance Phase 5 — replays the local transcript store through the four mechanised conformance checks) registers in the same PR that adds the surface, per this record's own contract. Re-measured via the pinned method: 86."
66
+ },
67
+ "mcp_public_tool_count": {
68
+ "max": 19,
69
+ "last_measured": 19,
70
+ "last_measured_at": "2026-08-02",
71
+ "method": "tools/list length on the stdio server (post-ADR-132: implemented tools only)"
73
72
  }
73
+ }
74
74
  }