@lifeaitools/clauth 2.15.7 → 2.15.8

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.
@@ -354,15 +354,26 @@ class ClauthSurfacesPanel extends window.ClauthPanelElement {
354
354
  const needsHelpLine = needsHelp
355
355
  ? "Stopped auto-restarting after " + (surface.consecutive_reconcile_failures || "several") + " failed attempts — check the log, fix the cause, then Start/Restart to resume auto-repair."
356
356
  : null;
357
+ // Command-set drift (2026-09-06): the surface's OWN code (read live via
358
+ // its health response) disagrees with what its manifest declares --
359
+ // exactly the "note says X, code doesn't enforce X" defect class. Only
360
+ // rendered when TRUE -- a surface that hasn't been retrofitted with the
361
+ // live commands field yet (the common case today) reports no drift, not
362
+ // a false one.
363
+ const driftLine = surface.command_drift
364
+ ? "Command-set drift: manifest declares " + (surface.declared_commands || []).join(", ") + "; surface's own code reports " + (surface.last_health_commands || []).join(", ") + "."
365
+ : null;
357
366
  return '<div class="' + rowClass + (needsHelp ? " needs-help" : "") + '" data-surface-id="' + htmlEscape(compositeId) + '"' + rowAction + '>' +
358
367
  '<div class="supervisor-row-top"><span class="supervisor-name">' + htmlEscape(surface.name || compositeId) + '</span>' +
359
368
  healthPill +
360
369
  supervisorBadge(surface.lifecycle_owner || "unknown", ownerKind) + supervisorBadge(stateLabel, stateKind) +
370
+ (surface.command_drift ? supervisorBadge("COMMAND DRIFT", "warn") : "") +
361
371
  openBtn +
362
372
  '</div>' +
363
373
  '<div class="supervisor-meta">' + htmlEscape(surface.destination || "—") + ' · port ' + htmlEscape(surface.port || "—") + '</div>' +
364
374
  '<div class="supervisor-meta">' + htmlEscape(healthLine) + '</div>' +
365
375
  (needsHelpLine ? '<div class="supervisor-meta critical">' + htmlEscape(needsHelpLine) + '</div>' : '') +
376
+ (driftLine ? '<div class="supervisor-meta critical">' + htmlEscape(driftLine) + '</div>' : '') +
366
377
  '</div>';
367
378
  }
368
379
 
@@ -163,20 +163,56 @@ function appendSupervisorEvent(event) {
163
163
  appendJsonl(file("events.jsonl"), { ts: now(), ...event });
164
164
  }
165
165
 
166
+ // The command set a surface's OWN manifest actually declares — derived from
167
+ // which of the real action fields carry a non-empty command array. "reconcile"
168
+ // and "test" are structural (reconcile is start-or-restart chosen by clauth;
169
+ // test is the candidate-testing path), not something a manifest declares, so
170
+ // they are excluded here on purpose.
171
+ const DECLARABLE_ACTIONS = ["start", "stop", "restart", "promote", "rollback"];
172
+ function declaredCommands(surface) {
173
+ return DECLARABLE_ACTIONS.filter((action) => Array.isArray(surface?.[action]) && surface[action].length > 0);
174
+ }
175
+
176
+ function sameCommandSet(a, b) {
177
+ if (!Array.isArray(a) || !Array.isArray(b)) return false;
178
+ if (a.length !== b.length) return false;
179
+ const setA = new Set(a);
180
+ return b.every((item) => setA.has(item));
181
+ }
182
+
166
183
  async function probeSurfaceHealth(url, fetchImpl, timeoutMs) {
167
184
  try {
168
185
  const controller = new AbortController();
169
186
  const timer = setTimeout(() => controller.abort(), timeoutMs);
170
187
  try {
171
188
  const response = await fetchImpl(url, { signal: controller.signal });
172
- return response?.ok
173
- ? { healthy: true, error: null }
174
- : { healthy: false, error: `HTTP ${response?.status ?? "unknown"}` };
189
+ if (!response?.ok) return { healthy: false, error: `HTTP ${response?.status ?? "unknown"}`, liveCommands: null };
190
+ // Command-set contract (2026-09-06): a surface MAY include a
191
+ // `commands` array in its health response the set of actions ITS
192
+ // OWN code actually supports, read live rather than trusted from the
193
+ // manifest's static declaration ("from code not text so we know there
194
+ // is no drift" — Dave, 2026-09-06). Piggybacked on the health probe
195
+ // clauth already runs every reconcile tick, so retrofitting a surface
196
+ // costs it nothing extra: no new endpoint, no new round trip. A
197
+ // surface that doesn't (yet) include this field is unaffected —
198
+ // liveCommands stays null and callers fall back to the manifest's
199
+ // declared set, exactly as before this contract existed.
200
+ let liveCommands = null;
201
+ try {
202
+ // No .clone() needed — this is the only read of this response, real
203
+ // or mocked. Test fixtures across this suite mock fetchImpl with
204
+ // plain { ok, status } objects with no .json() at all; the try/catch
205
+ // already treats that identically to "not JSON" — both normal, not
206
+ // an error — so existing tests are unaffected without special-casing.
207
+ const body = typeof response.json === "function" ? await response.json() : null;
208
+ if (Array.isArray(body?.commands)) liveCommands = body.commands.filter((c) => typeof c === "string");
209
+ } catch { /* not JSON, or no commands field — both normal, not an error */ }
210
+ return { healthy: true, error: null, liveCommands };
175
211
  } finally {
176
212
  clearTimeout(timer);
177
213
  }
178
214
  } catch (err) {
179
- return { healthy: false, error: err?.name === "AbortError" ? "health timeout" : String(err?.message || err) };
215
+ return { healthy: false, error: err?.name === "AbortError" ? "health timeout" : String(err?.message || err), liveCommands: null };
180
216
  }
181
217
  }
182
218
 
@@ -198,8 +234,21 @@ export async function reconcileSurfaceHealth({ fetchImpl = globalThis.fetch, tim
198
234
  const error = health.error;
199
235
 
200
236
  if (healthy) {
201
- updateSurfaceState(id, { state: "current", last_health_at: observedAt, last_health_ok: true, last_health_error: null, consecutive_reconcile_failures: 0 });
202
- inspected.push({ surface_id: id, state: "healthy", observed_at: observedAt });
237
+ const declared = declaredCommands(surface);
238
+ const commandDrift = Array.isArray(health.liveCommands) && !sameCommandSet(health.liveCommands, declared);
239
+ updateSurfaceState(id, {
240
+ state: "current",
241
+ last_health_at: observedAt,
242
+ last_health_ok: true,
243
+ last_health_error: null,
244
+ consecutive_reconcile_failures: 0,
245
+ last_health_commands: health.liveCommands,
246
+ command_drift: commandDrift,
247
+ });
248
+ if (commandDrift) {
249
+ appendSupervisorEvent({ kind: "surface_command_drift", surface_id: id, declared, live: health.liveCommands });
250
+ }
251
+ inspected.push({ surface_id: id, state: "healthy", observed_at: observedAt, command_drift: commandDrift });
203
252
  continue;
204
253
  }
205
254
 
@@ -365,6 +414,15 @@ export async function probeAllSurfaceHealth({ fetchImpl = globalThis.fetch, time
365
414
  url ? probeSurfaceHealth(url, fetchImpl, timeoutMs) : null,
366
415
  openUrl ? probeSurfaceHealth(openUrl, fetchImpl, timeoutMs) : null,
367
416
  ]);
417
+ // Command-set drift (2026-09-06): computed here too, not just in
418
+ // reconcileSurfaceHealth, because THIS sweep is the only one that ever
419
+ // probes external/remote surfaces — reconcile only looks at enabled
420
+ // clauth-owned local pm2 surfaces. null when the surface hasn't declared
421
+ // liveCommands (not retrofitted yet, or down) — that is a normal,
422
+ // unretrofitted surface, not a drift finding.
423
+ const declared = declaredCommands(surface);
424
+ const liveCommands = health?.healthy ? health.liveCommands : null;
425
+ const commandDrift = Array.isArray(liveCommands) ? !sameCommandSet(liveCommands, declared) : null;
368
426
  return {
369
427
  surface_id,
370
428
  health: !url ? "no_probe" : (health.healthy ? "healthy" : "down"),
@@ -372,6 +430,8 @@ export async function probeAllSurfaceHealth({ fetchImpl = globalThis.fetch, time
372
430
  error: url && !health.healthy ? (health.error || null) : null,
373
431
  open_url: openUrl || null,
374
432
  open_ok: open ? open.healthy : null,
433
+ live_commands: liveCommands,
434
+ command_drift: commandDrift,
375
435
  observed_at: now(),
376
436
  };
377
437
  }));
@@ -396,6 +456,8 @@ export async function probeAllSurfaceHealth({ fetchImpl = globalThis.fetch, time
396
456
  last_health_at: entry.observed_at,
397
457
  last_health_ok: entry.health === "healthy",
398
458
  last_health_error: entry.health === "healthy" ? null : entry.error,
459
+ last_health_commands: entry.live_commands,
460
+ command_drift: entry.command_drift,
399
461
  };
400
462
  });
401
463
  saveSupervisorState(state);
@@ -834,6 +896,7 @@ const PRODUCT_REPO_MANIFESTS = [
834
896
  { repo: "regen-root", manifest: "mcp-servers/regen-media/clauth-plugin.json" },
835
897
  { repo: "regen-root", manifest: "mcp-servers/web-research/clauth-plugin.json" },
836
898
  { repo: "rdc-skills", manifest: "clauth-plugin.json" },
899
+ { repo: "rdc-harness", manifest: "fsm-daemon/clauth-plugin.json" },
837
900
  ];
838
901
 
839
902
  // Sweep outcomes that mean "nothing was there to sync", as distinct from
@@ -853,6 +916,12 @@ export const SYNC_REPO_NAMES = Object.freeze([...new Set(PRODUCT_REPO_MANIFESTS.
853
916
  function defaultRepoRoot(repo) {
854
917
  if (repo === "regen-root") return process.env.REGEN_ROOT || process.env.LIFEAI_REPO_ROOT || "C:/Dev/regen-root";
855
918
  if (repo === "rdc-skills") return process.env.RDC_SKILLS_ROOT || "C:/Dev/rdc-skills";
919
+ // fsm-daemon's clauth-plugin.json lives inside rdc-harness, not published
920
+ // separately -- it was previously reachable only via a one-off manual
921
+ // `clauth plugin register` pinned to whichever machine ran it, invisible to
922
+ // this sweep and to any box that registers fsm-daemon fresh. Same env-var
923
+ // + hardcoded-default shape as the two entries above, not a new pattern.
924
+ if (repo === "rdc-harness") return process.env.RDC_HARNESS_ROOT || "C:/Dev/rdc-harness";
856
925
  return null;
857
926
  }
858
927
 
@@ -1193,6 +1262,17 @@ export function setPluginEnabled(id, enabled, actor = "localhost") {
1193
1262
  const prior = (state.plugins || []).find((plugin) => plugin.id === id);
1194
1263
  if (!prior) return { error: "plugin_not_found" };
1195
1264
  if (prior.state === "manifest_invalid") return { error: "manifest_invalid" };
1265
+ // A core:true plugin auto-enables at discovery (discoverPlugins()) and,
1266
+ // per the same rule, is never allowed BACK into awaiting_enable — "core
1267
+ // surfaces just run" is a guarantee, not a default a dashboard toggle can
1268
+ // quietly undo. Approved: direct operator instruction, 2026-09-06 —
1269
+ // "core: true surfaces only" get the no-gate treatment, applied uniformly
1270
+ // to every destination (local AND remote, same rule). Disabling a core
1271
+ // plugin is rejected outright rather than silently ignored, so a caller
1272
+ // finds out immediately rather than wondering why the toggle didn't stick.
1273
+ if (!enabled && prior.core === true) {
1274
+ return { error: "core_plugin_cannot_be_disabled", plugin_id: id };
1275
+ }
1196
1276
  const nextPlugin = { ...prior, enabled: Boolean(enabled), state: enabled ? "current" : "awaiting_enable" };
1197
1277
  state.plugins = state.plugins.map((plugin) => plugin.id === id ? nextPlugin : plugin);
1198
1278
  state.surfaces = (state.surfaces || []).map((surface) => surface.plugin_id === id ? { ...surface, enabled: Boolean(enabled), state: nextPlugin.state } : surface);
@@ -314,6 +314,29 @@ test("trusted managed core plugins auto-enable while user plugins remain opt-in"
314
314
  assert.equal(userPlugin.state, "awaiting_enable");
315
315
  }));
316
316
 
317
+ test("setPluginEnabled refuses to disable a core plugin — 'core surfaces just run' is a guarantee, not a default", () => withTempSupervisor((root) => {
318
+ const managed = path.join(root, "managed");
319
+ const user = path.join(root, "user");
320
+ process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = managed;
321
+ process.env.CLAUTH_USER_PLUGIN_ROOTS = user;
322
+ writePlugin(root, "managed", "core-mcp", baseManifest("core-mcp", { core: true, enable_default: true }));
323
+ writePlugin(root, "managed", "noncore-mcp", baseManifest("noncore-mcp", { core: false }));
324
+ discoverPlugins();
325
+
326
+ const rejected = setPluginEnabled("core-mcp", false);
327
+ assert.equal(rejected.error, "core_plugin_cannot_be_disabled");
328
+ // Positive control: the plugin's actual state is UNCHANGED by the
329
+ // rejected call, not silently flipped anyway.
330
+ assert.equal(listPlugins().find((p) => p.id === "core-mcp").enabled, true);
331
+ assert.equal(listPlugins().find((p) => p.id === "core-mcp").state, "current");
332
+
333
+ // Regression guard: a NON-core plugin can still be disabled exactly as
334
+ // before -- this fix narrows the gate, it does not remove it.
335
+ const disabled = setPluginEnabled("noncore-mcp", false);
336
+ assert.equal(disabled.resulting_state.enabled, false);
337
+ assert.equal(listPlugins().find((p) => p.id === "noncore-mcp").state, "awaiting_enable");
338
+ }));
339
+
317
340
  test("plugin test marks a private candidate and never creates a public route", () => withTempSupervisor((root) => {
318
341
  const managed = path.join(root, "managed");
319
342
  process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = managed;
@@ -452,6 +475,65 @@ test("health reconciliation marks a failed clauth surface and repairs it through
452
475
  }
453
476
  });
454
477
 
478
+ test("a healthy surface's live-reported commands are recorded, and drift against the manifest is detected", async () => {
479
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-supervisor-commands-"));
480
+ const oldDir = process.env.CLAUTH_SUPERVISOR_DIR;
481
+ const oldManaged = process.env.CLAUTH_MANAGED_PLUGIN_ROOTS;
482
+ const oldUser = process.env.CLAUTH_USER_PLUGIN_ROOTS;
483
+ process.env.CLAUTH_SUPERVISOR_DIR = root;
484
+ process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = path.join(root, "managed");
485
+ process.env.CLAUTH_USER_PLUGIN_ROOTS = path.join(root, "user");
486
+ try {
487
+ // Manifest declares start+stop only (no restart command).
488
+ writePlugin(root, "managed", "commands-demo", baseManifest("commands-demo", {
489
+ core: true,
490
+ enable_default: true,
491
+ surfaces: [{
492
+ id: "primary",
493
+ destination: "local/clauth/pm2",
494
+ lifecycle_owner: "clauth",
495
+ port: 39125,
496
+ health: "/health",
497
+ start: [process.execPath, "--version"],
498
+ stop: [process.execPath, "--version"],
499
+ }],
500
+ }));
501
+ discoverPlugins();
502
+
503
+ // Surface's OWN code reports a DIFFERENT set live (it also implements
504
+ // restart, which the manifest never declared) -- this is the drift case.
505
+ const drifted = await reconcileSurfaceHealth({
506
+ fetchImpl: async () => ({ ok: true, json: async () => ({ status: "ok", commands: ["start", "stop", "restart"] }) }),
507
+ });
508
+ assert.equal(drifted.inspected[0].command_drift, true);
509
+ const afterDrift = listSurfaces().find((s) => s.plugin_id === "commands-demo");
510
+ assert.deepEqual(afterDrift.last_health_commands, ["start", "stop", "restart"]);
511
+ assert.equal(afterDrift.command_drift, true);
512
+
513
+ // Surface's code now matches the manifest exactly -- no drift.
514
+ const matched = await reconcileSurfaceHealth({
515
+ fetchImpl: async () => ({ ok: true, json: async () => ({ status: "ok", commands: ["stop", "start"] }) }),
516
+ });
517
+ assert.equal(matched.inspected[0].command_drift, false);
518
+ assert.equal(listSurfaces().find((s) => s.plugin_id === "commands-demo").command_drift, false);
519
+
520
+ // A surface that hasn't been retrofitted with the commands field at all
521
+ // must NOT be flagged as drifted -- no live evidence is never treated as
522
+ // disagreeing evidence.
523
+ const unretrofitted = await reconcileSurfaceHealth({ fetchImpl: async () => ({ ok: true }) });
524
+ assert.equal(unretrofitted.inspected[0].command_drift, false, "no liveCommands reported must never read as drift");
525
+ assert.equal(listSurfaces().find((s) => s.plugin_id === "commands-demo").last_health_commands, null);
526
+ } finally {
527
+ if (oldDir === undefined) delete process.env.CLAUTH_SUPERVISOR_DIR;
528
+ else process.env.CLAUTH_SUPERVISOR_DIR = oldDir;
529
+ if (oldManaged === undefined) delete process.env.CLAUTH_MANAGED_PLUGIN_ROOTS;
530
+ else process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = oldManaged;
531
+ if (oldUser === undefined) delete process.env.CLAUTH_USER_PLUGIN_ROOTS;
532
+ else process.env.CLAUTH_USER_PLUGIN_ROOTS = oldUser;
533
+ fs.rmSync(root, { recursive: true, force: true });
534
+ }
535
+ });
536
+
455
537
  test("health reconciliation does not claim current when a successful command leaves health down", async () => {
456
538
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-supervisor-post-health-"));
457
539
  const oldDir = process.env.CLAUTH_SUPERVISOR_DIR;
@@ -894,6 +976,7 @@ function withTempProductRepos(fn) {
894
976
  const base = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-sync-repos-"));
895
977
  const regenRoot = path.join(base, "regen-root");
896
978
  const rdcSkills = path.join(base, "rdc-skills");
979
+ const rdcHarness = path.join(base, "rdc-harness");
897
980
  const writeManifest = (repoRoot, relPath, manifest) => {
898
981
  const full = path.join(repoRoot, relPath);
899
982
  fs.mkdirSync(path.dirname(full), { recursive: true });
@@ -905,8 +988,9 @@ function withTempProductRepos(fn) {
905
988
  writeManifest(regenRoot, "mcp-servers/regen-media/clauth-plugin.json", baseManifest("regen-media"));
906
989
  writeManifest(regenRoot, "mcp-servers/web-research/clauth-plugin.json", baseManifest("web-research"));
907
990
  writeManifest(rdcSkills, "clauth-plugin.json", baseManifest("rdc-skills"));
991
+ writeManifest(rdcHarness, "fsm-daemon/clauth-plugin.json", baseManifest("fsm-daemon"));
908
992
  try {
909
- return fn({ base, regenRoot, rdcSkills, writeManifest, roots: { "regen-root": regenRoot, "rdc-skills": rdcSkills } });
993
+ return fn({ base, regenRoot, rdcSkills, rdcHarness, writeManifest, roots: { "regen-root": regenRoot, "rdc-skills": rdcSkills, "rdc-harness": rdcHarness } });
910
994
  } finally {
911
995
  fs.rmSync(base, { recursive: true, force: true });
912
996
  }
@@ -914,7 +998,7 @@ function withTempProductRepos(fn) {
914
998
 
915
999
  test("plugin sync inherits registerPlugin idempotence — a second sweep reports every manifest unchanged", () => withTempSupervisor(() => withTempProductRepos(({ roots }) => {
916
1000
  const first = syncPluginsFromRepos(roots, "test");
917
- assert.equal(first.length, 5, "one receipt per attempted manifest");
1001
+ assert.equal(first.length, 6, "one receipt per attempted manifest");
918
1002
  assert.deepEqual(
919
1003
  first.filter((entry) => !entry.ok).map((entry) => `${entry.repo}:${entry.state}`),
920
1004
  [],
@@ -925,7 +1009,7 @@ test("plugin sync inherits registerPlugin idempotence — a second sweep reports
925
1009
  // registerPlugin sha256-compares before writing; sync must not defeat that by
926
1010
  // re-writing or re-hashing on its own.
927
1011
  const second = syncPluginsFromRepos(roots, "test");
928
- assert.equal(second.length, 5);
1012
+ assert.equal(second.length, 6);
929
1013
  assert.equal(second.every((entry) => entry.ok && entry.state === "unchanged"), true, "re-sweeping identical content must be a no-op");
930
1014
  })));
931
1015
 
@@ -937,7 +1021,7 @@ test("plugin sync warns and continues over a missing repo root instead of throwi
937
1021
  assert.doesNotThrow(() => {
938
1022
  receipts = syncPluginsFromRepos({ "regen-root": absent, "rdc-skills": rdcSkills }, "test");
939
1023
  });
940
- assert.equal(receipts.length, 5, "a skipped repo still yields a receipt per attempted manifest");
1024
+ assert.equal(receipts.length, 6, "a skipped repo still yields a receipt per attempted manifest");
941
1025
  const missing = receipts.filter((entry) => entry.state === "repo_root_missing");
942
1026
  assert.equal(missing.length, 4, "all four regen-root manifests report the missing root");
943
1027
  assert.equal(missing.every((entry) => entry.ok === false), true);
@@ -951,15 +1035,15 @@ test("plugin sync registers the remaining manifests when one is malformed", () =
951
1035
  writeManifest(regenRoot, "apps/dev-center/clauth-plugin.json", "{ this is not valid json");
952
1036
 
953
1037
  const receipts = syncPluginsFromRepos(roots, "test");
954
- assert.equal(receipts.length, 5);
1038
+ assert.equal(receipts.length, 6);
955
1039
  const bad = receipts.find((entry) => entry.path.includes("dev-center"));
956
1040
  assert.equal(bad.ok, false);
957
1041
  assert.equal(bad.state, "manifest_invalid");
958
1042
  const good = receipts.filter((entry) => entry !== bad);
959
- assert.equal(good.length, 4);
1043
+ assert.equal(good.length, 5);
960
1044
  assert.equal(good.every((entry) => entry.ok && entry.state === "registered"), true, "one bad manifest must not abort the sweep");
961
1045
  const ids = new Set(listPlugins().map((plugin) => plugin.id));
962
- for (const id of ["codeflow-mcp", "regen-media", "web-research", "rdc-skills"]) {
1046
+ for (const id of ["codeflow-mcp", "regen-media", "web-research", "rdc-skills", "fsm-daemon"]) {
963
1047
  assert.ok(ids.has(id), `${id} must still be registered`);
964
1048
  }
965
1049
  })));
@@ -1201,7 +1285,7 @@ test("plugin sync reports an unknown repo-root override instead of silently swee
1201
1285
  assert.equal(rejected[0].ok, false);
1202
1286
  assert.match(rejected[0].error, /unknown repo name/);
1203
1287
  assert.equal(SYNC_SKIP_STATES.includes("unknown_repo_name"), false, "a typo'd repo name must fail the sweep, not be skipped");
1204
- assert.deepEqual([...SYNC_REPO_NAMES].sort(), ["rdc-skills", "regen-root"]);
1288
+ assert.deepEqual([...SYNC_REPO_NAMES].sort(), ["rdc-harness", "rdc-skills", "regen-root"]);
1205
1289
  })));
1206
1290
 
1207
1291
  test("plugin sync never throws on a malformed repo root — the contract absence must not break", () => withTempSupervisor(() => withTempProductRepos(({ rdcSkills }) => {
@@ -1212,7 +1296,7 @@ test("plugin sync never throws on a malformed repo root — the contract absence
1212
1296
  assert.doesNotThrow(() => {
1213
1297
  receipts = syncPluginsFromRepos({ "regen-root": badRoot, "rdc-skills": rdcSkills }, "test");
1214
1298
  }, `root ${JSON.stringify(badRoot)} must not throw`);
1215
- assert.equal(receipts.length, 5, "every attempted manifest still yields a receipt");
1299
+ assert.equal(receipts.length, 6, "every attempted manifest still yields a receipt");
1216
1300
  assert.equal(
1217
1301
  receipts.filter((entry) => entry.repo === "regen-root" && entry.state === "repo_root_unknown").length,
1218
1302
  4,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "2.15.7",
3
+ "version": "2.15.8",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {