@memnexus-ai/mx-agent-cli 0.1.208 → 0.1.210

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 (56) hide show
  1. package/dist/__tests__/claude-sync.test.js +77 -5
  2. package/dist/__tests__/claude-sync.test.js.map +1 -1
  3. package/dist/__tests__/config-generation-gate.test.js +18 -9
  4. package/dist/__tests__/config-generation-gate.test.js.map +1 -1
  5. package/dist/__tests__/generate-claude.test.js +90 -6
  6. package/dist/__tests__/generate-claude.test.js.map +1 -1
  7. package/dist/__tests__/security-posture.test.d.ts +8 -0
  8. package/dist/__tests__/security-posture.test.d.ts.map +1 -0
  9. package/dist/__tests__/security-posture.test.js +82 -0
  10. package/dist/__tests__/security-posture.test.js.map +1 -0
  11. package/dist/__tests__/start-report-generation-capture.test.d.ts +17 -0
  12. package/dist/__tests__/start-report-generation-capture.test.d.ts.map +1 -0
  13. package/dist/__tests__/start-report-generation-capture.test.js +127 -0
  14. package/dist/__tests__/start-report-generation-capture.test.js.map +1 -0
  15. package/dist/__tests__/startup-health-render.test.d.ts +11 -0
  16. package/dist/__tests__/startup-health-render.test.d.ts.map +1 -0
  17. package/dist/__tests__/startup-health-render.test.js +60 -0
  18. package/dist/__tests__/startup-health-render.test.js.map +1 -0
  19. package/dist/__tests__/startup-report.test.js +34 -1
  20. package/dist/__tests__/startup-report.test.js.map +1 -1
  21. package/dist/__tests__/startup-shipper.test.d.ts +13 -0
  22. package/dist/__tests__/startup-shipper.test.d.ts.map +1 -0
  23. package/dist/__tests__/startup-shipper.test.js +149 -0
  24. package/dist/__tests__/startup-shipper.test.js.map +1 -0
  25. package/dist/agent-config/settings.json +3 -3
  26. package/dist/commands/dashboard.d.ts +21 -0
  27. package/dist/commands/dashboard.d.ts.map +1 -1
  28. package/dist/commands/dashboard.js +72 -1
  29. package/dist/commands/dashboard.js.map +1 -1
  30. package/dist/commands/start.d.ts.map +1 -1
  31. package/dist/commands/start.js +16 -0
  32. package/dist/commands/start.js.map +1 -1
  33. package/dist/commands/sync.d.ts.map +1 -1
  34. package/dist/commands/sync.js +17 -0
  35. package/dist/commands/sync.js.map +1 -1
  36. package/dist/lib/claude.d.ts.map +1 -1
  37. package/dist/lib/claude.js +31 -6
  38. package/dist/lib/claude.js.map +1 -1
  39. package/dist/lib/security-posture.d.ts +69 -0
  40. package/dist/lib/security-posture.d.ts.map +1 -0
  41. package/dist/lib/security-posture.js +102 -0
  42. package/dist/lib/security-posture.js.map +1 -0
  43. package/dist/lib/startup-report.d.ts +19 -3
  44. package/dist/lib/startup-report.d.ts.map +1 -1
  45. package/dist/lib/startup-report.js +66 -8
  46. package/dist/lib/startup-report.js.map +1 -1
  47. package/dist/lib/startup-shipper.d.ts +55 -0
  48. package/dist/lib/startup-shipper.d.ts.map +1 -0
  49. package/dist/lib/startup-shipper.js +177 -0
  50. package/dist/lib/startup-shipper.js.map +1 -0
  51. package/package.json +1 -1
  52. package/scripts/generate-claude.mjs +45 -1
  53. package/dist/__tests__/config-mirror-integrity.test.d.ts +0 -32
  54. package/dist/__tests__/config-mirror-integrity.test.d.ts.map +0 -1
  55. package/dist/__tests__/config-mirror-integrity.test.js +0 -248
  56. package/dist/__tests__/config-mirror-integrity.test.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-shipper.d.ts","sourceRoot":"","sources":["../../src/lib/startup-shipper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAcH;oEACoE;AACpE,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY7D;AAwCD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA8BnF;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAgCjG"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * startup-shipper.ts — ships StartupReports to the central observability
3
+ * service (platform v2.58 Phase 2, #4350).
4
+ *
5
+ * Durability model mirrors the local JSONL from Phase 1: every report is first
6
+ * written to a spool dir (~/.mx-agent/logs/startup/spool/<uuid>.json), then a
7
+ * best-effort POST deletes it on success. A failed or token-less POST leaves the
8
+ * file spooled; the Stop-hook `mx-agent sync` flushes the backlog on the next
9
+ * session end.
10
+ *
11
+ * Discipline (identical to startup-report.ts): NOTHING here throws. Shipping is
12
+ * additive telemetry — it must never block, slow, or fail a session start. Every
13
+ * filesystem and network call is wrapped; the worst outcome of any failure is a
14
+ * report that stays spooled for a later retry.
15
+ *
16
+ * - spoolReport(json) — write one report to the spool. Drops files older
17
+ * than 14 days at write time (bounded disk/staleness).
18
+ * - flushSpool(cfg) — POST every spooled report, deleting each on 2xx.
19
+ * Enforces a 50-file cap (drops oldest beyond it) and
20
+ * skips entirely when no token is configured.
21
+ * - postSpoolFile(p, cfg) — POST one spool file with a 3 s timeout.
22
+ */
23
+ import { mkdirSync, writeFileSync, readdirSync, statSync, unlinkSync, readFileSync } from 'fs';
24
+ import { homedir } from 'os';
25
+ import { join } from 'path';
26
+ import { randomUUID } from 'crypto';
27
+ /** Drop spooled files older than this at write time (bounded disk / staleness). */
28
+ const MAX_SPOOL_AGE_MS = 14 * 24 * 60 * 60 * 1000; // 14 days
29
+ /** Flush keeps at most this many files; the oldest beyond it are dropped. */
30
+ const MAX_SPOOL_FILES = 50;
31
+ /** Per-POST timeout — a single ship attempt must never stall longer than this. */
32
+ const POST_TIMEOUT_MS = 3000;
33
+ /** Absolute path to the spool directory. Computed at call time so tests that
34
+ * repoint $HOME observe the change (matches startup-report.ts). */
35
+ export function spoolDir() {
36
+ return join(homedir(), '.mx-agent', 'logs', 'startup', 'spool');
37
+ }
38
+ /**
39
+ * Write a report JSON string to the spool. Never throws. Returns the file path,
40
+ * or null if the write could not be performed (e.g. a read-only spool dir).
41
+ * Drops files older than 14 days first.
42
+ */
43
+ export function spoolReport(reportJson) {
44
+ try {
45
+ const dir = spoolDir();
46
+ mkdirSync(dir, { recursive: true });
47
+ dropAgedFiles(dir);
48
+ const path = join(dir, `${randomUUID()}.json`);
49
+ writeFileSync(path, reportJson);
50
+ return path;
51
+ }
52
+ catch {
53
+ // Spooling is best-effort — a failure here is silently tolerated.
54
+ return null;
55
+ }
56
+ }
57
+ /** Remove spool files whose mtime is older than the age cap. Never throws. */
58
+ function dropAgedFiles(dir) {
59
+ try {
60
+ const now = Date.now();
61
+ for (const f of readdirSync(dir)) {
62
+ if (!f.endsWith('.json'))
63
+ continue;
64
+ const p = join(dir, f);
65
+ try {
66
+ if (now - statSync(p).mtimeMs > MAX_SPOOL_AGE_MS)
67
+ unlinkSync(p);
68
+ }
69
+ catch {
70
+ /* individual file error — skip it */
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ /* dir unreadable / absent — nothing to age out */
76
+ }
77
+ }
78
+ /** List spool files (basenames), oldest-first by mtime. Never throws. */
79
+ function listSpoolFilesOldestFirst(dir) {
80
+ let names;
81
+ try {
82
+ names = readdirSync(dir).filter((f) => f.endsWith('.json'));
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ return names
88
+ .map((f) => {
89
+ try {
90
+ return { f, m: statSync(join(dir, f)).mtimeMs };
91
+ }
92
+ catch {
93
+ return { f, m: 0 };
94
+ }
95
+ })
96
+ .sort((a, b) => a.m - b.m)
97
+ .map((x) => x.f);
98
+ }
99
+ /**
100
+ * POST one spool file to the observability service. Returns true and deletes the
101
+ * file on a 2xx response. Any non-2xx, network error, or timeout returns false
102
+ * and leaves the file spooled. Never throws.
103
+ */
104
+ export async function postSpoolFile(path, cfg) {
105
+ if (!cfg.apiToken)
106
+ return false; // no token → cannot ship
107
+ let body;
108
+ try {
109
+ body = readFileSync(path, 'utf-8');
110
+ }
111
+ catch {
112
+ return false; // file vanished (concurrent flush) — nothing to do
113
+ }
114
+ try {
115
+ const res = await fetch(`${cfg.apiUrl}/v1/startup-reports`, {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ Authorization: `Bearer ${cfg.apiToken}`,
120
+ },
121
+ body,
122
+ signal: AbortSignal.timeout(POST_TIMEOUT_MS),
123
+ });
124
+ if (res.ok) {
125
+ try {
126
+ unlinkSync(path);
127
+ }
128
+ catch {
129
+ /* already removed — fine */
130
+ }
131
+ return true;
132
+ }
133
+ return false; // 4xx/5xx — leave spooled for a later retry
134
+ }
135
+ catch {
136
+ return false; // network / timeout / abort — leave spooled
137
+ }
138
+ }
139
+ /**
140
+ * Flush the spool: POST every spooled report, deleting each on success.
141
+ *
142
+ * Enforces the 50-file cap first (drops the oldest beyond it, with a dim note if
143
+ * a logger is provided). If no token is configured the whole flush is skipped —
144
+ * files stay spooled so a later run with a token can retry. Never throws.
145
+ */
146
+ export async function flushSpool(cfg) {
147
+ const dir = spoolDir();
148
+ let files = listSpoolFilesOldestFirst(dir);
149
+ // 50-file cap: drop the oldest beyond the cap so disk stays bounded.
150
+ if (files.length > MAX_SPOOL_FILES) {
151
+ const drop = files.slice(0, files.length - MAX_SPOOL_FILES);
152
+ for (const f of drop) {
153
+ try {
154
+ unlinkSync(join(dir, f));
155
+ }
156
+ catch {
157
+ /* ignore */
158
+ }
159
+ }
160
+ if (cfg.log) {
161
+ cfg.log(`startup-report spool exceeded ${MAX_SPOOL_FILES} files — dropped ${drop.length} oldest`);
162
+ }
163
+ files = files.slice(files.length - MAX_SPOOL_FILES);
164
+ }
165
+ // No token → cannot ship anything. Leave the backlog for a later sync.
166
+ if (!cfg.apiToken) {
167
+ return { shipped: 0, remaining: files.length };
168
+ }
169
+ let shipped = 0;
170
+ for (const f of files) {
171
+ if (await postSpoolFile(join(dir, f), cfg))
172
+ shipped++;
173
+ }
174
+ const remaining = listSpoolFilesOldestFirst(dir).length;
175
+ return { shipped, remaining };
176
+ }
177
+ //# sourceMappingURL=startup-shipper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-shipper.js","sourceRoot":"","sources":["../../src/lib/startup-shipper.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memnexus-ai/mx-agent-cli",
3
- "version": "0.1.208",
3
+ "version": "0.1.210",
4
4
  "description": "CLI for creating and managing AI agent teams",
5
5
  "type": "module",
6
6
  "bin": {
@@ -68,7 +68,7 @@ import {
68
68
  renameSync, rmSync, unlinkSync, writeFileSync,
69
69
  } from 'fs';
70
70
  import { homedir, tmpdir } from 'os';
71
- import { dirname, join, relative, resolve, sep } from 'path';
71
+ import { basename, dirname, join, relative, resolve, sep } from 'path';
72
72
  import { pathToFileURL } from 'url';
73
73
  import { spawnSync } from 'child_process';
74
74
  import { createHash } from 'crypto';
@@ -189,6 +189,46 @@ function pruneExtras(destDir, expected) {
189
189
  }
190
190
  }
191
191
 
192
+ // ── ORPHAN-SIBLING SWEEP (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED) ───────────────
193
+ //
194
+ // A crash mid-generation can only ever leave a `.claude.staging-*` sibling
195
+ // behind, and a failed prev-cleanup can leave a `.claude.prev-*` sibling — stale
196
+ // settings.json + hooks copies at an unguarded path (same-UID residual, not an
197
+ // escalation). Sweep any such orphan siblings of destRoot at generation ENTRY,
198
+ // before a new staging dir is created, so debris never accumulates across runs.
199
+ //
200
+ // Single-writer assumption: generation is serialized per worktree by the leader
201
+ // lock, so any staging/prev sibling present at entry is orphaned from a prior
202
+ // crashed run, never an in-flight concurrent generation. Best-effort per entry
203
+ // (an un-removable orphan never blocks generation). FAILS SAFE on a symlink
204
+ // sibling: unlink the LINK itself, never rmSync THROUGH it (a crafted link could
205
+ // otherwise delete outside the parent).
206
+
207
+ /**
208
+ * Remove stale `.claude.staging-*` / `.claude.prev-*` siblings of `destRoot`.
209
+ * @param destRoot absolute path to the `.claude/` dir about to be generated
210
+ * @returns names of the siblings that were swept (for observability/tests)
211
+ */
212
+ export function sweepOrphanSiblings(destRoot) {
213
+ const dest = resolve(destRoot);
214
+ const parent = dirname(dest);
215
+ const base = basename(dest);
216
+ const stagingPrefix = `${base}.staging-`;
217
+ const prevPrefix = `${base}.prev-`;
218
+ const swept = [];
219
+ for (const ent of listDir(parent)) {
220
+ if (!ent.name.startsWith(stagingPrefix) && !ent.name.startsWith(prevPrefix)) continue;
221
+ const p = join(parent, ent.name);
222
+ if (ent.isSymbolicLink()) {
223
+ // Never follow/delete THROUGH a link — remove only the link entry itself.
224
+ try { unlinkSync(p); swept.push(ent.name); } catch { /* best-effort */ }
225
+ continue;
226
+ }
227
+ try { rmSync(p, { recursive: true, force: true }); swept.push(ent.name); } catch { /* best-effort */ }
228
+ }
229
+ return swept;
230
+ }
231
+
192
232
  // ── GENERATE-TO-STAGING + ATOMIC SWAP (Phase 4 prereq; Red Team finding 1) ────
193
233
  //
194
234
  // Generation writes the WHOLE tree into a staging dir (a sibling of destRoot so
@@ -488,6 +528,10 @@ export function generateClaudeConfig(opts) {
488
528
  // reach the write phase); the existing dest is guarded separately at swap time.
489
529
  const parent = dirname(destRoot);
490
530
  mkdirSync(parent, { recursive: true });
531
+ // Sweep stale staging/prev siblings from a prior crashed run BEFORE creating a
532
+ // fresh staging dir (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED). The just-created
533
+ // staging dir below cannot be a sweep target: it does not exist yet.
534
+ sweepOrphanSiblings(destRoot);
491
535
  const staging = mkdtempSync(join(parent, '.claude.staging-'));
492
536
 
493
537
  try {
@@ -1,32 +0,0 @@
1
- /**
2
- * Mirror-drift guard for the agent-config → .claude config bundle.
3
- *
4
- * The repo keeps two copies of certain hook/skill/agent files:
5
- * - mx-agent-system/agent-config/* — the source of truth that ships in the
6
- * CLI's synced bundle (what `mx-agent` writes into new worktrees).
7
- * - .claude/* — the copy that actually runs in this
8
- * repo's own worktrees.
9
- *
10
- * These were hand-synced three separate times on 2026-07-02 (issue #3728,
11
- * known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
12
- * silently runs stale hook or skill logic. This test asserts byte-identity of
13
- * the known mirror pairs so drift fails locally before it ships.
14
- *
15
- * ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
16
- * This entire file — both the MIRROR_PAIRS byte-identity suite and the
17
- * `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
18
- * only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
19
- * gitignores `.claude/`, deleting the second committed copy; at that point there
20
- * is nothing to compare and these assertions become vacuous (every pair SKIPs on
21
- * the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
22
- * suite short-circuits on `bothPresent`).
23
- *
24
- * DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
25
- * generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
26
- * which proves the single remaining source generates correct, stable output —
27
- * see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
28
- * KEEP these assertions: they are the live guard against a PR editing one side of
29
- * the mirror while `.claude/` is still tracked.
30
- */
31
- export {};
32
- //# sourceMappingURL=config-mirror-integrity.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-mirror-integrity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
@@ -1,248 +0,0 @@
1
- /**
2
- * Mirror-drift guard for the agent-config → .claude config bundle.
3
- *
4
- * The repo keeps two copies of certain hook/skill/agent files:
5
- * - mx-agent-system/agent-config/* — the source of truth that ships in the
6
- * CLI's synced bundle (what `mx-agent` writes into new worktrees).
7
- * - .claude/* — the copy that actually runs in this
8
- * repo's own worktrees.
9
- *
10
- * These were hand-synced three separate times on 2026-07-02 (issue #3728,
11
- * known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
12
- * silently runs stale hook or skill logic. This test asserts byte-identity of
13
- * the known mirror pairs so drift fails locally before it ships.
14
- *
15
- * ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
16
- * This entire file — both the MIRROR_PAIRS byte-identity suite and the
17
- * `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
18
- * only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
19
- * gitignores `.claude/`, deleting the second committed copy; at that point there
20
- * is nothing to compare and these assertions become vacuous (every pair SKIPs on
21
- * the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
22
- * suite short-circuits on `bothPresent`).
23
- *
24
- * DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
25
- * generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
26
- * which proves the single remaining source generates correct, stable output —
27
- * see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
28
- * KEEP these assertions: they are the live guard against a PR editing one side of
29
- * the mirror while `.claude/` is still tracked.
30
- */
31
- import { describe, it, expect } from 'vitest';
32
- import { readFileSync, existsSync } from 'fs';
33
- import { dirname, join } from 'path';
34
- import { fileURLToPath } from 'url';
35
- const __dirname = dirname(fileURLToPath(import.meta.url));
36
- /**
37
- * Ascend from this test file to the repo root — identified by the presence of
38
- * the mx-agent-system/agent-config tree — instead of hard-coding a fixed number
39
- * of `..` segments. Keeps the test correct if the file is ever relocated.
40
- */
41
- function findRepoRoot(start) {
42
- let dir = start;
43
- for (let i = 0; i < 20; i++) {
44
- if (existsSync(join(dir, 'mx-agent-system', 'agent-config')))
45
- return dir;
46
- const parent = dirname(dir);
47
- if (parent === dir)
48
- break;
49
- dir = parent;
50
- }
51
- throw new Error(`config-mirror-integrity: could not locate repo root from ${start}`);
52
- }
53
- const REPO_ROOT = findRepoRoot(__dirname);
54
- // [ .claude runtime copy, agent-config source of truth ] — repo-root-relative.
55
- const MIRROR_PAIRS = [
56
- [
57
- '.claude/hooks/deploy-verification-reminder.sh',
58
- 'mx-agent-system/agent-config/hooks/deploy-verification-reminder.sh',
59
- ],
60
- [
61
- '.claude/hooks/deploy-verification-reminder.test.sh',
62
- 'mx-agent-system/agent-config/hooks/deploy-verification-reminder.test.sh',
63
- ],
64
- // Shared git guards + push validators (#3912). These were mirror-drifting
65
- // silently (git-mutation-guard.sh wasn't even present in the source-of-truth
66
- // copy). Enforce byte-identity so a hook fix lands in both places.
67
- [
68
- '.claude/hooks/git-mutation-guard.sh',
69
- 'mx-agent-system/agent-config/hooks/git-mutation-guard.sh',
70
- ],
71
- [
72
- '.claude/hooks/git-mutation-guard.test.sh',
73
- 'mx-agent-system/agent-config/hooks/git-mutation-guard.test.sh',
74
- ],
75
- [
76
- '.claude/hooks/push-matcher.test.sh',
77
- 'mx-agent-system/agent-config/hooks/push-matcher.test.sh',
78
- ],
79
- [
80
- '.claude/hooks/mx-guard-hook.sh',
81
- 'mx-agent-system/agent-config/hooks/mx-guard-hook.sh',
82
- ],
83
- [
84
- '.claude/hooks/ci-validate-hook.sh',
85
- 'mx-agent-system/agent-config/hooks/ci-validate-hook.sh',
86
- ],
87
- // Package→validate-script routing table extracted from ci-validate-hook.sh
88
- // (Platform v2.35, #4017). Shipped default = the MemNexus map; the hook reads
89
- // it at push time and fails open if absent. Guard it against mirror drift.
90
- [
91
- '.claude/hooks/ci-validate-routes.conf',
92
- 'mx-agent-system/agent-config/hooks/ci-validate-routes.conf',
93
- ],
94
- [
95
- '.claude/hooks/worktree-guard.sh',
96
- 'mx-agent-system/agent-config/hooks/worktree-guard.sh',
97
- ],
98
- [
99
- '.claude/hooks/worktree-guard.test.sh',
100
- 'mx-agent-system/agent-config/hooks/worktree-guard.test.sh',
101
- ],
102
- [
103
- '.claude/hooks/workflow-dispatch-guard.sh',
104
- 'mx-agent-system/agent-config/hooks/workflow-dispatch-guard.sh',
105
- ],
106
- // Named-memory truncating-write guard (Platform v2.33, #3996). Blocks
107
- // full-content `mx memories update|create --name X --content <NEW>` writes
108
- // that shrink a shared named memory past a threshold — two real clobber
109
- // incidents (2026-07-07 known-issues, 2026-07-09 escalations).
110
- [
111
- '.claude/hooks/named-memory-write-guard.sh',
112
- 'mx-agent-system/agent-config/hooks/named-memory-write-guard.sh',
113
- ],
114
- [
115
- '.claude/hooks/named-memory-write-guard.test.sh',
116
- 'mx-agent-system/agent-config/hooks/named-memory-write-guard.test.sh',
117
- ],
118
- [
119
- '.claude/skills/mx-post-merge-sync/SKILL.md',
120
- 'mx-agent-system/agent-config/skills/mx-post-merge-sync/SKILL.md',
121
- ],
122
- [
123
- '.claude/agents/git-expert.md',
124
- 'mx-agent-system/agent-config/agents/git-expert.md',
125
- ],
126
- // Stop hook that warns when a merged session left its iteration log stale
127
- // (Platform v2.35, #4017 — product-slug parameterization). Guard the pair.
128
- [
129
- '.claude/hooks/iteration-log-check.sh',
130
- 'mx-agent-system/agent-config/hooks/iteration-log-check.sh',
131
- ],
132
- [
133
- '.claude/hooks/iteration-log-check.test.sh',
134
- 'mx-agent-system/agent-config/hooks/iteration-log-check.test.sh',
135
- ],
136
- // Agent/skill instruction files carrying the product-slug named-memory
137
- // convention (Platform v2.35, #4017). These had been mirror-unguarded; the
138
- // KI-HOOK-MIRROR-DRIFT-CHECK history (three hand-syncs, #3728) justifies
139
- // enforcing byte-identity now that they are edited in lockstep.
140
- [
141
- '.claude/agents/memory-creator.md',
142
- 'mx-agent-system/agent-config/agents/memory-creator.md',
143
- ],
144
- [
145
- '.claude/agents/status-reporter.md',
146
- 'mx-agent-system/agent-config/agents/status-reporter.md',
147
- ],
148
- [
149
- '.claude/agents/prior-art-researcher.md',
150
- 'mx-agent-system/agent-config/agents/prior-art-researcher.md',
151
- ],
152
- [
153
- '.claude/agents/dogfood-auditor.md',
154
- 'mx-agent-system/agent-config/agents/dogfood-auditor.md',
155
- ],
156
- [
157
- '.claude/skills/mx-session-start/SKILL.md',
158
- 'mx-agent-system/agent-config/skills/mx-session-start/SKILL.md',
159
- ],
160
- [
161
- '.claude/skills/mx-iteration-close/SKILL.md',
162
- 'mx-agent-system/agent-config/skills/mx-iteration-close/SKILL.md',
163
- ],
164
- // Session-start coordination brief hook (PR-D). The .claude runtime copy is
165
- // synced by the normal distribution mechanism; until Gate 3 cutover it may be
166
- // absent, in which case this pair SKIPS (the loop's existsSync guard). Once
167
- // synced it is asserted byte-identical.
168
- [
169
- '.claude/hooks/coordination-brief.sh',
170
- 'mx-agent-system/agent-config/hooks/coordination-brief.sh',
171
- ],
172
- // The co-located hook test suite. Not part of the shipped config surface
173
- // (bundled-config excludes *.test.sh), but both dev copies are edited in
174
- // lockstep — enforce byte-identity so the slug-validation tests can't drift
175
- // (v2.40 QA finding, KI-COORDMT-HOOK-EXPORT-ROBUSTNESS).
176
- [
177
- '.claude/hooks/coordination-brief.test.sh',
178
- 'mx-agent-system/agent-config/hooks/coordination-brief.test.sh',
179
- ],
180
- // NOTE: agent-config/settings.json is intentionally NOT a byte-identity mirror
181
- // pair — the worktree .claude/settings.json has its SessionStart/hook wiring
182
- // COMPOSED/merged from the source at deploy time (see claude.ts), so the two
183
- // are not byte-identical by design. But the SECURITY-SENSITIVE part — the
184
- // `permissions` allow/deny/ask lists and the experimental-teams flag — must
185
- // never drift, because config-sync/`mx-agent new` ship agent-config's copy
186
- // into worktrees verbatim. A prior drift (agent-config/settings.json lagged
187
- // active by the "Monitor" allow entry from #4275, and the git-mutation-guard
188
- // #4289 hardening) shipped a weakened config to consumer worktrees (#4294).
189
- // The `settings.json permissions parity` describe below guards that surface
190
- // semantically rather than by byte-identity.
191
- ];
192
- describe('config mirror integrity', () => {
193
- for (const [claudeRel, agentConfigRel] of MIRROR_PAIRS) {
194
- const claudeAbs = join(REPO_ROOT, claudeRel);
195
- const agentConfigAbs = join(REPO_ROOT, agentConfigRel);
196
- // The .claude copy is not present in every checkout (e.g. a package-only
197
- // export of the CLI). Skip rather than fail spuriously when it's absent.
198
- if (!existsSync(claudeAbs)) {
199
- it.skip(`${claudeRel} ↔ ${agentConfigRel} (SKIPPED: ${claudeRel} absent in this checkout)`, () => { });
200
- continue;
201
- }
202
- it(`${claudeRel} is byte-identical to ${agentConfigRel}`, () => {
203
- const claudeBytes = readFileSync(claudeAbs);
204
- const agentConfigBytes = readFileSync(agentConfigAbs);
205
- expect(claudeBytes.equals(agentConfigBytes)).toBe(true);
206
- });
207
- }
208
- });
209
- /**
210
- * settings.json is composed (SessionStart/hook wiring is merged into the worktree
211
- * copy at deploy time), so it is not a byte-identity mirror pair above. But its
212
- * `permissions` block and the experimental-teams flag are shipped verbatim into
213
- * consumer worktrees by config-sync/`mx-agent new`; drift there ships a weakened
214
- * security posture. This suite asserts semantic parity of exactly those fields
215
- * between the active runtime copy and the agent-config source of truth so a
216
- * regression like #4294 (agent-config lagging the active allow-list) fails
217
- * locally before it ships.
218
- */
219
- describe('settings.json permissions parity', () => {
220
- const claudeSettingsAbs = join(REPO_ROOT, '.claude/settings.json');
221
- const agentConfigSettingsAbs = join(REPO_ROOT, 'mx-agent-system/agent-config/settings.json');
222
- const bothPresent = existsSync(claudeSettingsAbs) && existsSync(agentConfigSettingsAbs);
223
- const loadPerms = (abs) => {
224
- const parsed = JSON.parse(readFileSync(abs, 'utf-8'));
225
- return parsed;
226
- };
227
- (bothPresent ? it : it.skip)('permissions.allow is identical between active and agent-config', () => {
228
- const active = loadPerms(claudeSettingsAbs);
229
- const template = loadPerms(agentConfigSettingsAbs);
230
- expect(template.permissions?.allow).toEqual(active.permissions?.allow);
231
- });
232
- (bothPresent ? it : it.skip)('permissions.deny is identical between active and agent-config', () => {
233
- const active = loadPerms(claudeSettingsAbs);
234
- const template = loadPerms(agentConfigSettingsAbs);
235
- expect(template.permissions?.deny).toEqual(active.permissions?.deny);
236
- });
237
- (bothPresent ? it : it.skip)('permissions.ask is identical between active and agent-config', () => {
238
- const active = loadPerms(claudeSettingsAbs);
239
- const template = loadPerms(agentConfigSettingsAbs);
240
- expect(template.permissions?.ask).toEqual(active.permissions?.ask);
241
- });
242
- (bothPresent ? it : it.skip)('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS flag is identical between active and agent-config', () => {
243
- const active = loadPerms(claudeSettingsAbs);
244
- const template = loadPerms(agentConfigSettingsAbs);
245
- expect(template.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS).toEqual(active.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS);
246
- });
247
- });
248
- //# sourceMappingURL=config-mirror-integrity.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-mirror-integrity.test.js","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"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"}