@event4u/agent-config 17.0.0 → 17.1.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 (119) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +104 -485
  5. package/README.md +2 -1
  6. package/dist/agent-src/contexts/execution/host-capability-manifest.md +5 -3
  7. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  8. package/dist/agent-src/templates/roadmaps.md +5 -1
  9. package/dist/cli-delegate/{chunk-IROAQ77A.js → chunk-3PK3VZHB.js} +107 -9
  10. package/dist/cli-delegate/{chunk-2SFOOFJJ.js → chunk-BZJ4Y3CI.js} +1 -1
  11. package/dist/cli-delegate/{chunk-CAL76ADI.js → chunk-CMZEKSNN.js} +52 -10
  12. package/dist/cli-delegate/{chunk-ESUNQ5XR.js → chunk-E3CFULNX.js} +22 -4
  13. package/dist/cli-delegate/{chunk-GOMILC3O.js → chunk-QBHEI5N4.js} +4 -3
  14. package/dist/cli-delegate/{chunk-SIOMDAKL.js → chunk-VYZGJLCA.js} +29 -39
  15. package/dist/cli-delegate/chunk-W5JXMF2T.js +77 -0
  16. package/dist/cli-delegate/{chunk-4SHNERBL.js → chunk-XIAPY44N.js} +1 -1
  17. package/dist/cli-delegate/{chunk-JHMMWNJ2.js → chunk-YHAPONWV.js} +1 -1
  18. package/dist/cli-delegate/cmd_conformance.js +9 -8
  19. package/dist/cli-delegate/cmd_doctor.js +4 -4
  20. package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
  21. package/dist/cli-delegate/cmd_explain.js +1 -1
  22. package/dist/cli-delegate/cmd_export.js +6 -6
  23. package/dist/cli-delegate/cmd_fleet.js +3 -3
  24. package/dist/cli-delegate/cmd_preflight.js +3 -3
  25. package/dist/cli-delegate/cmd_prune.js +6 -6
  26. package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
  27. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  28. package/dist/cli-delegate/cmd_sync.js +6 -6
  29. package/dist/cli-delegate/cmd_uninstall.js +6 -6
  30. package/dist/cli-delegate/cmd_update.js +9 -13
  31. package/dist/cli-delegate/cmd_validate.js +6 -6
  32. package/dist/discovery/deprecation-report.md +1 -1
  33. package/dist/discovery/discovery-manifest.json +4 -4
  34. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  35. package/dist/discovery/discovery-manifest.summary.md +1 -1
  36. package/dist/discovery/dormancy-report.md +1 -1
  37. package/dist/discovery/orphan-report.md +1 -1
  38. package/dist/discovery/packs.json +2 -2
  39. package/dist/discovery/trust-report.md +1 -1
  40. package/dist/discovery/workspaces.json +2 -2
  41. package/dist/hooks/dispatch.js +98 -74
  42. package/dist/hooks/dispatch.sha256 +1 -1
  43. package/dist/install/claudeRuleRewrite.js +10 -3
  44. package/dist/install/conflict.js +24 -2
  45. package/dist/install/conflictTracker.js +14 -3
  46. package/dist/install/install.mjs +222 -117
  47. package/dist/install/installThinLayer.js +1 -0
  48. package/dist/install/loggedWrites.js +142 -0
  49. package/dist/install/paths.js +12 -2
  50. package/dist/install/preserve.js +25 -4
  51. package/dist/install/txlog.js +10 -6
  52. package/dist/mcp/registry-manifest.json +2 -2
  53. package/dist/mcp/server.json +2 -2
  54. package/dist/mcp/server.mjs +845 -294
  55. package/dist/scripts/_lib/egress_rules.js +4 -1
  56. package/dist/scripts/_lib/identifier_redaction.js +35 -2
  57. package/dist/scripts/ai_council/seat_policy.js +7 -1
  58. package/dist/server/schemas/settings.js +1 -1
  59. package/package.json +1 -1
  60. package/src/config/ci-local-parity.yml +1 -1
  61. package/src/config/gate-coverage.yml +27 -7
  62. package/src/config/gate-violation-baselines.json +8 -2
  63. package/src/config/hook-bundle-budget.json +2 -2
  64. package/src/config/production-entries.json +18 -0
  65. package/src/config/release-gate-locality.yml +0 -7
  66. package/src/install/claudeRuleRewrite.ts +10 -1
  67. package/src/install/conflict.ts +24 -1
  68. package/src/install/conflictTracker.ts +18 -2
  69. package/src/install/installThinLayer.ts +6 -0
  70. package/src/install/loggedWrites.ts +148 -0
  71. package/src/install/paths.ts +11 -2
  72. package/src/install/preserve.ts +40 -4
  73. package/src/install/txlog.ts +10 -6
  74. package/src/scripts/_cli/cmd_conformance.ts +3 -2
  75. package/src/scripts/_cli/cmd_update.ts +15 -17
  76. package/src/scripts/_lib/council_settings_block.ts +9 -2
  77. package/src/scripts/_lib/egress_rules.ts +4 -1
  78. package/src/scripts/_lib/host_capability.ts +10 -5
  79. package/src/scripts/_lib/identifier_redaction.ts +38 -2
  80. package/src/scripts/_lib/price_freshness.ts +112 -0
  81. package/src/scripts/_lib/production_reach.ts +194 -0
  82. package/src/scripts/_lib/quota_parked.ts +17 -28
  83. package/src/scripts/_lib/release_findings_ingest.ts +13 -335
  84. package/src/scripts/_lib/tier_downshift_verdict.ts +81 -35
  85. package/src/scripts/ai_council/chairman.ts +5 -2
  86. package/src/scripts/ai_council/clients.ts +4 -3
  87. package/src/scripts/ai_council/free_hand_shadow.ts +24 -12
  88. package/src/scripts/ai_council/free_route_catalog.ts +142 -5
  89. package/src/scripts/ai_council/free_route_client.ts +4 -0
  90. package/src/scripts/ai_council/hand_payload.ts +115 -7
  91. package/src/scripts/ai_council/orchestrator.ts +7 -10
  92. package/src/scripts/ai_council/route_chooser.ts +1 -1
  93. package/src/scripts/ai_council/route_member.ts +66 -4
  94. package/src/scripts/ai_council/seat_policy.ts +8 -1
  95. package/src/scripts/ai_council/self_hosted_probe.ts +327 -0
  96. package/src/scripts/ai_council/status_surface.ts +2 -0
  97. package/src/scripts/ask_transport.ts +137 -15
  98. package/src/scripts/check_finding_dispositions.ts +25 -71
  99. package/src/scripts/check_release_pr_shape.ts +3 -3
  100. package/src/scripts/check_step_production_reach.ts +257 -0
  101. package/src/scripts/council_cli.ts +10 -8
  102. package/src/scripts/hook_manifest.json +1 -1
  103. package/src/scripts/hook_manifest.yaml +3 -0
  104. package/src/scripts/hooks/dispatch_hook.ts +2 -2
  105. package/src/scripts/hooks/payload_stub.ts +27 -0
  106. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +29 -70
  107. package/src/scripts/hooks/subagent_ledger_hook.ts +80 -0
  108. package/src/scripts/install.ts +2 -2
  109. package/src/scripts/lint_decision_classes.ts +34 -0
  110. package/src/scripts/lint_hook_manifest.ts +24 -0
  111. package/src/scripts/lint_roadmap_blockers.ts +52 -3
  112. package/src/scripts/release.ts +5 -9
  113. package/src/scripts/release_drill.ts +11 -165
  114. package/src/scripts/release_publication.ts +0 -9
  115. package/src/scripts/report_production_reach.ts +35 -0
  116. package/src/scripts/worker_reuse_shadow_report.ts +399 -1
  117. package/src/server/schemas/settings.ts +1 -1
  118. package/dist/cli-delegate/chunk-A4QJ6ENT.js +0 -50
  119. /package/dist/cli-delegate/{chunk-E6XNBVE4.js → chunk-KR5ZIFPO.js} +0 -0
@@ -157,6 +157,7 @@ export function thinInstalledRuleLayer(opts) {
157
157
  try {
158
158
  if (next !== installed) {
159
159
  fs.writeFileSync(target, next, 'utf-8');
160
+ opts.onWrite?.(target);
160
161
  rewritten += 1;
161
162
  }
162
163
  thinned += 1;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The install transaction log read as an ownership record.
3
+ *
4
+ * WHY. The writer preserves any managed file it cannot prove is still the bytes
5
+ * it wrote. For a file deployed before the ownership ledger recorded digests,
6
+ * the ledger has nothing to compare against, so EVERY such file was preserved
7
+ * and a sidecar staged beside it — thousands of them on one upgrade, all
8
+ * untouched. Worse, the ledger then records the staged digest for each target,
9
+ * so the next run calls the same untouched file "user-modified".
10
+ *
11
+ * The proof was on disk the whole time. `src/scripts/install.ts` appends a
12
+ * `write` entry carrying the SHA-256 of the target's bytes immediately after
13
+ * every copy. A file whose bytes equal a digest the log records this installer
14
+ * writing to that exact path is the installer's own output, unedited. A path
15
+ * with no matching record keeps the existing answer. WHEN that proof may decide
16
+ * is narrower than "whenever it matches" — see `decideDeployWrite`.
17
+ *
18
+ * WHAT IS READ. The active log and every rotated sibling, plain or gzipped
19
+ * (`txlog.ts` rotates at 10 MB or 30 days, and the gzip step can leave both
20
+ * forms behind). An unreadable or truncated file contributes nothing — the
21
+ * failure direction is "no proof", which preserves.
22
+ *
23
+ * COST. Parsed per FILE and cached on that file's size and mtime. A rotated
24
+ * archive never changes, so only the active log is re-read when it grows — and
25
+ * an install appends to it after every file it touches, so a cache keyed on the
26
+ * whole set would re-parse the entire history once per candidate.
27
+ */
28
+ import { gunzipSync } from 'node:zlib';
29
+ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
30
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
31
+ import { getLogPath } from './paths.js';
32
+ const WRITE_MARKER = '"kind":"write"';
33
+ const fileCache = new Map();
34
+ const dirRealpaths = new Map();
35
+ /**
36
+ * One canonical spelling for a path: its directory realpath'd, its basename
37
+ * kept. Both the log keys and the queries go through it, so `/var/…` and its
38
+ * macOS realpath twin meet. The directory is realpath'd rather than the file
39
+ * because the file may not exist, and is memoised because a log names a few
40
+ * hundred directories across tens of thousands of entries.
41
+ */
42
+ export function canonicalPath(p) {
43
+ const abs = resolve(p);
44
+ const dir = dirname(abs);
45
+ let real = dirRealpaths.get(dir);
46
+ if (real === undefined) {
47
+ try {
48
+ real = realpathSync(dir);
49
+ }
50
+ catch {
51
+ real = dir;
52
+ }
53
+ dirRealpaths.set(dir, real);
54
+ }
55
+ return join(real, basename(abs));
56
+ }
57
+ /** The active log plus its rotated siblings, sorted by name. */
58
+ function logFiles(logPath) {
59
+ const dir = dirname(logPath);
60
+ if (!existsSync(dir))
61
+ return [];
62
+ const stem = basename(logPath).replace(/\.jsonl$/, '');
63
+ try {
64
+ return readdirSync(dir)
65
+ .filter((n) => n.startsWith(stem) && (n.endsWith('.jsonl') || n.endsWith('.jsonl.gz')))
66
+ .map((n) => join(dir, n))
67
+ .sort();
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ function writesIn(file) {
74
+ let stamp;
75
+ try {
76
+ const st = statSync(file);
77
+ stamp = `${st.size}:${Math.trunc(st.mtimeMs)}`;
78
+ }
79
+ catch {
80
+ return [];
81
+ }
82
+ const cached = fileCache.get(file);
83
+ if (cached !== undefined && cached.stamp === stamp)
84
+ return cached.entries;
85
+ let text = '';
86
+ try {
87
+ const raw = readFileSync(file);
88
+ text = (file.endsWith('.gz') ? gunzipSync(raw) : raw).toString('utf8');
89
+ }
90
+ catch {
91
+ text = '';
92
+ }
93
+ const entries = [];
94
+ for (const line of text.split('\n')) {
95
+ if (!line.includes(WRITE_MARKER))
96
+ continue;
97
+ let entry;
98
+ try {
99
+ entry = JSON.parse(line);
100
+ }
101
+ catch {
102
+ continue;
103
+ }
104
+ if (typeof entry.path !== 'string' || typeof entry.sha256 !== 'string' || entry.sha256 === '')
105
+ continue;
106
+ entries.push([canonicalPath(entry.path), entry.sha256]);
107
+ }
108
+ fileCache.set(file, { stamp, entries });
109
+ return entries;
110
+ }
111
+ /** Every `write` the transaction log records, keyed by canonical path. */
112
+ export function loggedWriteDigests(logPath = getLogPath()) {
113
+ const writes = new Map();
114
+ for (const file of logFiles(logPath)) {
115
+ for (const [target, sha] of writesIn(file)) {
116
+ let set = writes.get(target);
117
+ if (set === undefined)
118
+ writes.set(target, (set = new Set()));
119
+ set.add(sha);
120
+ }
121
+ }
122
+ return writes;
123
+ }
124
+ /**
125
+ * Wrap a log writer so it records only paths this run's copy deploys.
126
+ *
127
+ * The claude-code rewrite and thinning passes walk the whole rule directory,
128
+ * including rules the user authored there; logging those rewrites would make
129
+ * the log vouch for content that was never the package's.
130
+ */
131
+ export function onlyDeployed(anchor, currentFiles, log) {
132
+ return (target) => {
133
+ const rel = relative(resolve(anchor), resolve(target)).split(sep).join('/');
134
+ if (currentFiles.has(rel))
135
+ log(target);
136
+ };
137
+ }
138
+ /** Did this installer write exactly `onDiskSha256` to `target`? */
139
+ export function wroteTheseBytes(target, onDiskSha256, writes) {
140
+ return writes.get(canonicalPath(target))?.has(onDiskSha256) === true;
141
+ }
142
+ //# sourceMappingURL=loggedWrites.js.map
@@ -61,12 +61,22 @@ export function getInstallRoot(home) {
61
61
  return join(resolveHome(home), INSTALL_ROOT_SUBPATH);
62
62
  }
63
63
  /**
64
- * Absolute path to the active transaction log:
65
- * `~/.event4u/agent-config/install-log.jsonl`.
64
+ * Absolute path to the active transaction log: `install-log.jsonl` in
65
+ * `$EVENT4U_CONFIG_HOME` when that is set and no explicit `home` is given,
66
+ * otherwise in `~/.event4u/agent-config/`.
66
67
  *
67
68
  * Phase A4 writers append here on every successful atomic rename.
69
+ *
70
+ * Without the variable, the hermetic test environment — which sets only that
71
+ * variable — appended every test install to the real user's log, and the log
72
+ * is now read back as ownership proof. A history written under the other root
73
+ * before a user switched roots is not read; the failure direction is "no
74
+ * proof", which preserves.
68
75
  */
69
76
  export function getLogPath(home) {
77
+ const configHome = home ? undefined : process.env['EVENT4U_CONFIG_HOME'];
78
+ if (configHome)
79
+ return join(configHome, INSTALL_LOG_FILENAME);
70
80
  return join(getInstallRoot(home), INSTALL_LOG_FILENAME);
71
81
  }
72
82
  /**
@@ -80,7 +80,7 @@ export const EXIT_COMPLETED_WITH_CONFLICTS = 3;
80
80
  * behaviour stands rather than being changed on a case nobody reasoned about.
81
81
  */
82
82
  export function isLedgerMigration(inputs) {
83
- if (!inputs.exists || inputs.force)
83
+ if (!inputs.exists || inputs.force || inputs.loggedWrite === true)
84
84
  return false;
85
85
  if (inputs.ledgerListed !== true)
86
86
  return false;
@@ -88,12 +88,31 @@ export function isLedgerMigration(inputs) {
88
88
  return false;
89
89
  return classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256) === 'unknown';
90
90
  }
91
+ /**
92
+ * May the transaction-log proof decide this target? Only where the verdict
93
+ * would otherwise be a preservation the record cannot justify: a ledger-listed
94
+ * file with no digest (the migration branch), or a `recorded-modified` file
95
+ * whose recorded digest is a staged one. Everywhere else the log is not read —
96
+ * the ledger alone already decides, and a genuine edit must stay preserved.
97
+ */
98
+ export function logProofApplies(inputs) {
99
+ if (!inputs.exists || inputs.force || inputs.onDiskSha256 === null)
100
+ return false;
101
+ const ownership = classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256);
102
+ if (ownership === 'recorded-modified')
103
+ return inputs.recordedIsStaged === true;
104
+ // `loggedWrite` is cleared because `isLedgerMigration` excludes a proven
105
+ // target, and this asks whether the target is a candidate BEFORE the proof.
106
+ return ownership === 'unknown' && isLedgerMigration({ ...inputs, loggedWrite: false });
107
+ }
91
108
  /**
92
109
  * Decide whether the writer may replace one managed file.
93
110
  *
94
- * A target that does not exist is written. A forced run writes. Otherwise the
95
- * recorded-ownership verdict decides: `recorded-modified` preserves, and
96
- * `recorded-unchanged` writes.
111
+ * A target that does not exist is written. A forced run writes. A target whose
112
+ * bytes the transaction log records this installer writing there is written
113
+ * where {@link logProofApplies} — there is no edit to lose. Otherwise the
114
+ * recorded-ownership verdict decides:
115
+ * `recorded-modified` preserves, and `recorded-unchanged` writes.
97
116
  *
98
117
  * The `unknown` arm is the one worth stating out loud. No recorded digest
99
118
  * means no divergence to measure — a tree with no manifest, a global anchor
@@ -118,6 +137,8 @@ export function decideDeployWrite(inputs) {
118
137
  return 'write';
119
138
  if (inputs.force)
120
139
  return 'write';
140
+ if (inputs.loggedWrite === true && logProofApplies(inputs))
141
+ return 'write';
121
142
  const ownership = classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256);
122
143
  if (ownership === 'recorded-modified')
123
144
  return 'preserve';
@@ -6,14 +6,18 @@
6
6
  * nothing is un-written automatically. No PID lockfile (per Phase-A4
7
7
  * decision 8).
8
8
  *
9
- * WHAT ACTUALLY WRITES THIS FILE, as of 2026-09-15: two call sites.
10
- * `src/server/routes/install.ts`'s recovery-dismiss handler appends a
11
- * `rollback` marker with an empty path and a null hash; `src/scripts/install.ts`
12
- * — the surviving headless writer, per-file, through `_log_tx_entry` — appends
13
- * a `write` or `skip` entry per copied/skipped path during
14
- * `_copy_dir_dereferencing_symlinks`. Both call `appendTxLog` directly, so the
9
+ * WHAT ACTUALLY WRITES THIS FILE: `src/server/routes/install.ts`'s
10
+ * recovery-dismiss handler appends a `rollback` marker with an empty path and a
11
+ * null hash; `src/scripts/install.ts`, through `_log_tx_entry`, appends a
12
+ * `write` or `skip` entry per path it copies, preserves or stages a sidecar
13
+ * for, and a `write` entry per deployed Claude rule the host-form rewrite and
14
+ * the thinning pass rewrite after the copy. All call `appendTxLog`, so the
15
15
  * entry shape is identical by construction rather than by convention.
16
16
  *
17
+ * WHAT READS IT: recovery (the active log only) and `./loggedWrites.ts`, which
18
+ * reads every `write` digest in the active log AND its rotated siblings as
19
+ * proof that bytes on disk are the installer's own output.
20
+ *
17
21
  * Rotation: 10 MB OR 30 days, whichever first. Rotated copies move to
18
22
  * `install-log.<ISO>.jsonl.gz` siblings; recovery never scans them.
19
23
  *
@@ -3,13 +3,13 @@
3
3
  "artefact_count": 636,
4
4
  "scanner_version": "de0a22b4ac4c"
5
5
  },
6
- "generated_at": "2026-10-22",
6
+ "generated_at": "2026-10-23",
7
7
  "package": {
8
8
  "description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) \u2014 every claim machine-checked, including the artifact counts in these badges.",
9
9
  "homepage": "https://event4u-app.github.io/agent-config",
10
10
  "name": "@event4u/agent-config",
11
11
  "repository": "https://github.com/event4u-app/agent-config",
12
- "version": "17.0.0"
12
+ "version": "17.1.0"
13
13
  },
14
14
  "registries": [
15
15
  {
@@ -9,12 +9,12 @@
9
9
  "transport": {
10
10
  "type": "stdio"
11
11
  },
12
- "version": "17.0.0"
12
+ "version": "17.1.0"
13
13
  }
14
14
  ],
15
15
  "repository": {
16
16
  "source": "github",
17
17
  "url": "https://github.com/event4u-app/agent-config"
18
18
  },
19
- "version": "17.0.0"
19
+ "version": "17.1.0"
20
20
  }