pi-mega-compact 0.20.8 → 0.20.9

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 (96) hide show
  1. package/dist/config/vector-cortex.js +13 -0
  2. package/dist/config.js +1 -1
  3. package/dist/extensions/dashboard-server/route-dispatch.js +3 -1
  4. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  5. package/dist/extensions/dashboard-server/routes-vector-cortex-heal.js +42 -3
  6. package/dist/extensions/dashboard-server/routes-vector-cortex.js +3 -1
  7. package/dist/extensions/dashboard-server/routes.js +1 -1
  8. package/dist/src/config/vector-cortex.js +13 -0
  9. package/dist/src/config.js +1 -1
  10. package/dist/src/vector-cortex/heal/_restore-fixture.js +96 -0
  11. package/dist/src/vector-cortex/heal/_vc6b-helpers.js +35 -0
  12. package/dist/src/vector-cortex/heal/restore-emit.js +70 -0
  13. package/dist/src/vector-cortex/heal/restore-readers.js +114 -0
  14. package/dist/src/vector-cortex/heal/restore-types.js +66 -0
  15. package/dist/src/vector-cortex/heal/restore.js +140 -0
  16. package/dist/src/vector-cortex/heal/types.js +1 -0
  17. package/dist/src/vector-cortex/heal/verify.js +75 -0
  18. package/dist/vector-cortex/heal/_restore-fixture.js +96 -0
  19. package/dist/vector-cortex/heal/_vc6b-helpers.js +35 -0
  20. package/dist/vector-cortex/heal/restore-emit.js +70 -0
  21. package/dist/vector-cortex/heal/restore-readers.js +114 -0
  22. package/dist/vector-cortex/heal/restore-types.js +66 -0
  23. package/dist/vector-cortex/heal/restore.js +140 -0
  24. package/dist/vector-cortex/heal/types.js +1 -0
  25. package/dist/vector-cortex/heal/verify.js +75 -0
  26. package/extensions/dashboard-client/dist/assets/{AreaChart-5yxaI8IO.js → AreaChart-fxyq2KaH.js} +2 -2
  27. package/extensions/dashboard-client/dist/assets/{AreaChart-5yxaI8IO.js.map → AreaChart-fxyq2KaH.js.map} +1 -1
  28. package/extensions/dashboard-client/dist/assets/{BarChart-BoHhCqH-.js → BarChart-D5EqAINP.js} +2 -2
  29. package/extensions/dashboard-client/dist/assets/{BarChart-BoHhCqH-.js.map → BarChart-D5EqAINP.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{CacheTab-Di6L3CsF.js → CacheTab-D4UWbzLo.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{CacheTab-Di6L3CsF.js.map → CacheTab-D4UWbzLo.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{EventsTab-Cdff3h6s.js → EventsTab-BiXquGrX.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{EventsTab-Cdff3h6s.js.map → EventsTab-BiXquGrX.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/{HealthTab-MthYYVLw.js → HealthTab-D5QvoTjy.js} +2 -2
  35. package/extensions/dashboard-client/dist/assets/{HealthTab-MthYYVLw.js.map → HealthTab-D5QvoTjy.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D9G4ntsG.js → MaintenanceTab-Cy_-CWgM.js} +2 -2
  37. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D9G4ntsG.js.map → MaintenanceTab-Cy_-CWgM.js.map} +1 -1
  38. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CN8Lr5Qf.js → MemoryMapTab-DrAjgGOR.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CN8Lr5Qf.js.map → MemoryMapTab-DrAjgGOR.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/{MetricsTab-CgO32kSo.js → MetricsTab-BvPHcuFN.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{MetricsTab-CgO32kSo.js.map → MetricsTab-BvPHcuFN.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{OverviewTab-Cb4kR0h1.js → OverviewTab-DsbIigOQ.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{OverviewTab-Cb4kR0h1.js.map → OverviewTab-DsbIigOQ.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{ReposTab-Dt1FGnMR.js → ReposTab-7v_uZX_a.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{ReposTab-Dt1FGnMR.js.map → ReposTab-7v_uZX_a.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{SessionsTab-D7D2RW6U.js → SessionsTab-BXGrv-6C.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{SessionsTab-D7D2RW6U.js.map → SessionsTab-BXGrv-6C.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{SetupTab-CgV5xCzz.js → SetupTab-QRUXa65y.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{SetupTab-CgV5xCzz.js.map → SetupTab-QRUXa65y.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CCNuJjyO.js → TimeSavedCard-p327ZZKT.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CCNuJjyO.js.map → TimeSavedCard-p327ZZKT.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cv1OCttJ.js → TurnsTab-BcmBSlik.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cv1OCttJ.js.map → TurnsTab-BcmBSlik.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js.map +1 -0
  56. package/extensions/dashboard-client/dist/assets/{WikiTab-B48QnQrK.js → WikiTab-DLXFMuNy.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{WikiTab-B48QnQrK.js.map → WikiTab-DLXFMuNy.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/{button-BBQl2MiF.js → button-C9CghmRx.js} +2 -2
  59. package/extensions/dashboard-client/dist/assets/{button-BBQl2MiF.js.map → button-C9CghmRx.js.map} +1 -1
  60. package/extensions/dashboard-client/dist/assets/{card-BYbefUur.js → card-DWU-1y3t.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{card-BYbefUur.js.map → card-DWU-1y3t.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-swm0YZe0.js → generateCategoricalChart-Dayy9dGn.js} +2 -2
  63. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-swm0YZe0.js.map → generateCategoricalChart-Dayy9dGn.js.map} +1 -1
  64. package/extensions/dashboard-client/dist/assets/{index-DeIky7xm.js → index-D9PsW3VZ.js} +3 -3
  65. package/extensions/dashboard-client/dist/assets/{index-DeIky7xm.js.map → index-D9PsW3VZ.js.map} +1 -1
  66. package/extensions/dashboard-client/dist/assets/{switch-CctU78ww.js → switch-CnLFAmVT.js} +2 -2
  67. package/extensions/dashboard-client/dist/assets/{switch-CctU78ww.js.map → switch-CnLFAmVT.js.map} +1 -1
  68. package/extensions/dashboard-client/dist/assets/{toggle-BRCNCoiI.js → toggle-HN5C7_vy.js} +2 -2
  69. package/extensions/dashboard-client/dist/assets/{toggle-BRCNCoiI.js.map → toggle-HN5C7_vy.js.map} +1 -1
  70. package/extensions/dashboard-client/dist/assets/{useSSE-WfOiWDH_.js → useSSE-DoJfk3Ce.js} +2 -2
  71. package/extensions/dashboard-client/dist/assets/{useSSE-WfOiWDH_.js.map → useSSE-DoJfk3Ce.js.map} +1 -1
  72. package/extensions/dashboard-client/dist/index.html +1 -1
  73. package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
  74. package/extensions/dashboard-client/src/tabs/VectorCortexRestoreCard.tsx +42 -0
  75. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +8 -0
  76. package/extensions/dashboard-client/src/types/vector-cortex.ts +16 -0
  77. package/extensions/dashboard-server/api-contracts/vector-cortex-heal.ts +30 -0
  78. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +4 -1
  79. package/extensions/dashboard-server/route-dispatch.ts +2 -0
  80. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  81. package/extensions/dashboard-server/routes-vector-cortex-heal.ts +51 -4
  82. package/extensions/dashboard-server/routes-vector-cortex.ts +6 -1
  83. package/extensions/dashboard-server/routes.ts +1 -0
  84. package/package.json +1 -1
  85. package/src/config/vector-cortex.ts +14 -0
  86. package/src/config.ts +1 -0
  87. package/src/vector-cortex/heal/_restore-fixture.ts +163 -0
  88. package/src/vector-cortex/heal/_vc6b-helpers.ts +55 -0
  89. package/src/vector-cortex/heal/restore-emit.ts +94 -0
  90. package/src/vector-cortex/heal/restore-readers.ts +145 -0
  91. package/src/vector-cortex/heal/restore-types.ts +185 -0
  92. package/src/vector-cortex/heal/restore.ts +165 -0
  93. package/src/vector-cortex/heal/types.ts +22 -0
  94. package/src/vector-cortex/heal/verify.ts +93 -0
  95. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DTabQo6h.js +0 -2
  96. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DTabQo6h.js.map +0 -1
@@ -208,6 +208,19 @@ export const VC5C_ENABLED = () => sprintFlag("MEGACOMPACT_VC5C");
208
208
  * EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C.
209
209
  */
210
210
  export const VC6A_ENABLED = () => sprintFlag("MEGACOMPACT_VC6A");
211
+ /**
212
+ * VC6B — exact source restoration. Default ON. `MEGACOMPACT_VC6B=0` disables and
213
+ * is byte-identical to the predecessor (VC6A): the `restoreSources` /
214
+ * `verifyRestored` arithmetic STILL RUNS (it is PURE — an exact-source read plus
215
+ * a SHA-256 comparison, with no clock, storage, or network), but the
216
+ * `vector_cortex_source_restored` / `vector_cortex_restore_digest_rejected`
217
+ * events are never emitted and the dashboard restoration diagnostics seam is
218
+ * suppressed. Flag OFF gates the reporter + dashboard seam, never the
219
+ * restore/verify arithmetic, so flag-off outbound/predecessor golden bytes match
220
+ * exactly. This flag MUST also be a dashboard SETTINGS toggle (visible in config
221
+ * UI, never in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C/VC6A.
222
+ */
223
+ export const VC6B_ENABLED = () => sprintFlag("MEGACOMPACT_VC6B");
211
224
  // ---------------------------------------------------------------------------
212
225
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
213
226
  // Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
package/dist/config.js CHANGED
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
114
114
  // default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
115
115
  // so root consumers share one source of truth.
116
116
  // ---------------------------------------------------------------------------
117
- export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
117
+ export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
@@ -8,7 +8,7 @@
8
8
  * PREVENT-PI-004: local filesystem read only, no network.
9
9
  * PREVENT-011: no `any` type.
10
10
  */
11
- import { handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerfSamples, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleMemoryStatus, handleCacheStripes, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, handleVectorCortexRollout, handleVectorCortexClosureProof, } from "./routes.js";
11
+ import { handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerfSamples, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleMemoryStatus, handleCacheStripes, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, handleVectorCortexRollout, handleVectorCortexClosureProof, handleVectorCortexRestore, } from "./routes.js";
12
12
  /**
13
13
  * Dispatch a request through every registered route handler.
14
14
  * Returns true if a handler claimed the request (ended the response).
@@ -98,5 +98,7 @@ export function dispatchRoutes(req, res, ctx) {
98
98
  return true;
99
99
  if (handleVectorCortexClosureProof(req, res, ctx))
100
100
  return true;
101
+ if (handleVectorCortexRestore(req, res, ctx))
102
+ return true;
101
103
  return false;
102
104
  }
@@ -40,5 +40,6 @@ export const VECTOR_CORTEX_SETTINGS = {
40
40
  boolDirect("MEGACOMPACT_VC5B", "VC5B Validated Renderer + Provider Profiles", "Validated prompt renderer: replays VC5A's stable Kahn order verbatim, preserves exact tool bytes (PREVENT-PI-002), places compacted context via the host before_agent_start prepend seam — never role:system (PREVENT-PI-003) — and SHA-256 hashes the entire canonical outbound request before provider invocation. Unknown provider/model cleanly bypasses to the predecessor prompt path. OFF = byte-identical predecessor (VC5A).", true),
41
41
  boolDirect("MEGACOMPACT_VC5C", "VC5C Live Graduated Rollout", "Live graduated rollout: deterministically hashes each session into a stable 10,000-bucket cohort and advances the exposure gate (1/5/25/50/100%) only after a 72h monotonic residency, a powered sample, >=10,000 events, and >=200 sessions — advancing ONE gate at a time. A hard causal/tool/anchor/exact failure freezes promotion and selects the pre-VC path. OFF = byte-identical predecessor (VC5B).", true),
42
42
  boolDirect("MEGACOMPACT_VC6A", "VC6A Advanced Closure Optimization", "Advanced closure optimization: deterministically reduces the already-mandatory VC4C closure by transitive reduction over depends edges, emitting a ClosureProofV2 receipt per closure and a verifier that replays reductions against the conservative oracle (HEAL_PROOF_SET_MISMATCH on selected-set divergence). Protected edges (tool-pair / anchor / contradiction / sole-dependency) are never removed. The optimized selected set is byte-identical to the conservative closure. OFF = byte-identical predecessor (VC5C); arithmetic runs, only the reporter + dashboard seam is suppressed.", true),
43
+ boolDirect("MEGACOMPACT_VC6B", "VC6B Exact Source Restoration", "Exact source restoration: restores the original bytes of closure spans ONLY from an exact shard (mode A) or a verified ledger range scan (mode B) — never inferred, reconstructed, or paraphrased from embeddings or semantic text. Requests are hard-bounded at 64 spans / 4MiB (HEAL_RESTORE_LIMIT); every span's SHA-256 is recomputed and bytes are inserted only after all requested span metadata validates, so a single bad span fails the whole request closed (HEAL_RESTORE_DIGEST_MISMATCH, HEAL_RESTORE_RANGE_MISMATCH). When no exact source exists (HEAL_RESTORE_SOURCE_MISSING) the old context is omitted and the loss is disclosed (mode C) rather than filled in. OFF = byte-identical predecessor (VC6A).", true),
43
44
  ],
44
45
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
- * dashboard-server/routes-vector-cortex-heal.ts — VC6A closure-optimization
3
- * dashboard route.
2
+ * dashboard-server/routes-vector-cortex-heal.ts — VC6A closure-optimization and
3
+ * VC6B exact-source-restoration dashboard routes.
4
4
  *
5
5
  * Reader-only GET /api/vector-cortex/closure-proof returning aggregate-only
6
6
  * closure diagnostics: whether the flag is enabled, the runtime triad mode, and
@@ -12,10 +12,14 @@
12
12
  * (the conservative VC4C closure, no reduction) — that is exactly the spec's
13
13
  * fallback rule — so `mode:"B"` + `enabled:false` is the byte-stable OFF view.
14
14
  *
15
+ * Reader-only GET /api/vector-cortex/restore (VC6B) returns restore COUNTS and
16
+ * HEAL_RESTORE_* error codes only. There is NO payload endpoint for restoration,
17
+ * ever — no restored bytes, span ids, node ids, byte ranges, or ledger text.
18
+ *
15
19
  * Guardrails: PREVENT-PI-004 (local in-process state only), PREVENT-011 (no
16
20
  * `any`), reader-only aggregate (counts + mode only).
17
21
  */
18
- import { VC6A_ENABLED } from "../../src/config.js";
22
+ import { VC6A_ENABLED, VC6B_ENABLED } from "../../src/config.js";
19
23
  import { sendJson } from "./routes-vector-cortex-shared.js";
20
24
  /** Reader-only GET /api/vector-cortex/closure-proof (VC6A). */
21
25
  export function handleVectorCortexClosureProof(req, res, _ctx) {
@@ -45,3 +49,38 @@ export function handleVectorCortexClosureProof(req, res, _ctx) {
45
49
  sendJson(res, 200, body);
46
50
  return true;
47
51
  }
52
+ /**
53
+ * Reader-only GET /api/vector-cortex/restore (VC6B).
54
+ *
55
+ * Counts and HEAL_RESTORE_* codes only — this is a static reader-only aggregate
56
+ * seam (same shape as the VC6A handler): there is no payload endpoint and never
57
+ * will be, so no restored bytes/span ids/node ids/ledger text can leak here.
58
+ */
59
+ export function handleVectorCortexRestore(req, res, _ctx) {
60
+ const url = req.url ?? "";
61
+ const path = url.split("?")[0] ?? url;
62
+ if (path !== "/api/vector-cortex/restore")
63
+ return false;
64
+ if (req.method !== "GET") {
65
+ sendJson(res, 405, { error: "method_not_allowed" });
66
+ return true;
67
+ }
68
+ const enabled = VC6B_ENABLED();
69
+ // Flag-off routes to mode C: with VC6B off there is no exact-restoration path
70
+ // at all, so the honest OFF view is the disclose-loss mode (old context is
71
+ // omitted rather than inferred) — mirroring how VC6A's OFF view reports the
72
+ // conservative fallback it actually takes.
73
+ const mode = enabled ? "A" : "C";
74
+ const body = {
75
+ enabled,
76
+ mode,
77
+ restoreAttempts: 0,
78
+ restoredCount: 0,
79
+ missingCount: 0,
80
+ digestRejections: 0,
81
+ lastRejection: null,
82
+ updatedAt: new Date().toISOString(),
83
+ };
84
+ sendJson(res, 200, body);
85
+ return true;
86
+ }
@@ -9,6 +9,8 @@
9
9
  * VC0C health/reset-> routes-vector-cortex-health.ts(GET /health, POST /breakers/reset)
10
10
  * VC1B ledger -> routes-vector-cortex-ledger.ts (GET /ledger)
11
11
  * VC3A topology -> routes-vector-cortex-topology.ts(GET /topology)
12
+ * VC6A closure -> routes-vector-cortex-heal.ts (GET /closure-proof)
13
+ * VC6B restore -> routes-vector-cortex-heal.ts (GET /restore)
12
14
  *
13
15
  * Guardrails: PREVENT-PI-004 (local filesystem read only), PREVENT-011 (no
14
16
  * `any`), reader-only aggregates (never payloads/prompts/ledger text).
@@ -24,4 +26,4 @@ export { handleVectorCortexReconstruct } from "./routes-vector-cortex-reconstruc
24
26
  export { handleVectorCortexPlans } from "./routes-vector-cortex-plans.js";
25
27
  export { handleVectorCortexRender } from "./routes-vector-cortex-render.js";
26
28
  export { handleVectorCortexRollout } from "./routes-vector-cortex-rollout.js";
27
- export { handleVectorCortexClosureProof } from "./routes-vector-cortex-heal.js";
29
+ export { handleVectorCortexClosureProof, handleVectorCortexRestore, } from "./routes-vector-cortex-heal.js";
@@ -23,4 +23,4 @@ export { handleRagSettings } from "./routes-rag-settings.js";
23
23
  export { handleRagMetrics } from "./routes-rag-metrics.js";
24
24
  export { handleModelThresholds } from "./routes-model-thresholds.js";
25
25
  export { handleWiki } from "./routes-wiki.js";
26
- export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, handleVectorCortexRollout, handleVectorCortexClosureProof, } from "./routes-vector-cortex.js";
26
+ export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, handleVectorCortexRollout, handleVectorCortexClosureProof, handleVectorCortexRestore, } from "./routes-vector-cortex.js";
@@ -208,6 +208,19 @@ export const VC5C_ENABLED = () => sprintFlag("MEGACOMPACT_VC5C");
208
208
  * EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C.
209
209
  */
210
210
  export const VC6A_ENABLED = () => sprintFlag("MEGACOMPACT_VC6A");
211
+ /**
212
+ * VC6B — exact source restoration. Default ON. `MEGACOMPACT_VC6B=0` disables and
213
+ * is byte-identical to the predecessor (VC6A): the `restoreSources` /
214
+ * `verifyRestored` arithmetic STILL RUNS (it is PURE — an exact-source read plus
215
+ * a SHA-256 comparison, with no clock, storage, or network), but the
216
+ * `vector_cortex_source_restored` / `vector_cortex_restore_digest_rejected`
217
+ * events are never emitted and the dashboard restoration diagnostics seam is
218
+ * suppressed. Flag OFF gates the reporter + dashboard seam, never the
219
+ * restore/verify arithmetic, so flag-off outbound/predecessor golden bytes match
220
+ * exactly. This flag MUST also be a dashboard SETTINGS toggle (visible in config
221
+ * UI, never in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C/VC6A.
222
+ */
223
+ export const VC6B_ENABLED = () => sprintFlag("MEGACOMPACT_VC6B");
211
224
  // ---------------------------------------------------------------------------
212
225
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
213
226
  // Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
114
114
  // default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
115
115
  // so root consumers share one source of truth.
116
116
  // ---------------------------------------------------------------------------
117
- export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
117
+ export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, VC5C_ENABLED, VC6A_ENABLED, VC6B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
@@ -0,0 +1,96 @@
1
+ /**
2
+ * heal/_restore-fixture.ts — conformance fixture I/O for VC6B restoration rows.
3
+ *
4
+ * Sibling of `_acceptance-fixture.ts` (which owns the VC6A closure-optimization
5
+ * rows); split out so neither file approaches the 300-line soft limit and so the
6
+ * base64/BigInt decoding lives next to the contract it reconstitutes.
7
+ *
8
+ * DECODING IS THE POINT. Fixtures are canonical JSON, which cannot express bytes
9
+ * or bigints, so the corpus stores `originalBytesBase64` + numeric `seq`. These
10
+ * loaders turn those back into the REAL `ExactShardV1` / `EventV2` objects the
11
+ * production readers consume — no mocks, no stubs, no parallel "test shape". If
12
+ * the decode were lossy the digests would not match and the acceptance test would
13
+ * fail loudly, which is exactly the guarantee we want from a byte-identity sprint.
14
+ */
15
+ import { readFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import assert from "node:assert/strict";
18
+ import { V2, readManifest } from "./_acceptance-fixture.js";
19
+ /** Read one registered restoration fixture (asserting it IS registered). */
20
+ export function restorationFixture(id) {
21
+ const m = readManifest();
22
+ const row = m.fixtures.find((f) => f.id === id && f.path.startsWith("restoration/"));
23
+ assert.ok(row, `fixture ${id} registered under restoration/ in manifest`);
24
+ return JSON.parse(readFileSync(join(V2, row.path), "utf8"));
25
+ }
26
+ /** base64 -> exact bytes, byte-for-byte (invalid UTF-8 included). */
27
+ export function decodeBytes(base64) {
28
+ return new Uint8Array(Buffer.from(base64, "base64"));
29
+ }
30
+ /** JSON number seq bounds -> the bigint bounds `ShardRange` declares. */
31
+ export function decodeRange(r) {
32
+ return {
33
+ sessionId: r.sessionId,
34
+ seqStart: BigInt(r.seqStart),
35
+ seqEnd: BigInt(r.seqEnd),
36
+ byteStart: r.byteStart,
37
+ byteEnd: r.byteEnd,
38
+ };
39
+ }
40
+ /** Reconstitute a real `ExactShardV1` from its fixture row. */
41
+ export function decodeShard(s) {
42
+ return {
43
+ schema: "exact-shard-v1",
44
+ sessionId: s.sessionId,
45
+ range: decodeRange(s.range),
46
+ kind: "exact",
47
+ originalBytes: decodeBytes(s.originalBytesBase64),
48
+ digest: s.digest,
49
+ byteCount: s.byteCount,
50
+ case: s.case,
51
+ };
52
+ }
53
+ /**
54
+ * Reconstitute a real `EventV2`. `utf8` is re-derived by strict classification
55
+ * (never lossy replacement) so an invalid-UTF-8 fixture produces the
56
+ * `{valid:false, base64}` discriminant the VC1A contract requires.
57
+ */
58
+ export function decodeEvent(e) {
59
+ const bytes = decodeBytes(e.originalBytesBase64);
60
+ let utf8;
61
+ try {
62
+ utf8 = { valid: true, text: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
63
+ }
64
+ catch {
65
+ utf8 = { valid: false, base64: e.originalBytesBase64 };
66
+ }
67
+ return {
68
+ schema: "event-v2",
69
+ sessionId: e.sessionId,
70
+ seq: BigInt(e.seq),
71
+ eventId: e.eventId,
72
+ role: e.role,
73
+ kind: e.kind,
74
+ originalBytes: bytes,
75
+ bytesDigest: e.bytesDigest,
76
+ utf8,
77
+ ...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}),
78
+ occurredAtMs: BigInt(e.occurredAtMs),
79
+ };
80
+ }
81
+ /** Flag-pinned wrapper: VC6B gated by MEGACOMPACT_VC6B (defaults ON). */
82
+ export function withVc6bFlagsOn(fn) {
83
+ return () => {
84
+ const saved = process.env.MEGACOMPACT_VC6B;
85
+ process.env.MEGACOMPACT_VC6B = "1";
86
+ try {
87
+ fn();
88
+ }
89
+ finally {
90
+ if (saved === undefined)
91
+ delete process.env.MEGACOMPACT_VC6B;
92
+ else
93
+ process.env.MEGACOMPACT_VC6B = saved;
94
+ }
95
+ };
96
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * heal/_vc6b-helpers.ts — shared decode/run helpers for VC6B acceptance tests.
3
+ *
4
+ * Extracted from vc6b-acceptance.test.ts so no single test file crosses the
5
+ * 300-line soft limit (soft-as-hard gate). Each sibling test file imports from
6
+ * here rather than duplicating the decode/runReal logic.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { restoreSources } from "./restore.js";
10
+ import { verifyRestored } from "./verify.js";
11
+ import { decodeRange, decodeShard, decodeEvent } from "./_restore-fixture.js";
12
+ export const enc = (s) => new Uint8Array(Buffer.from(s));
13
+ export const hex = (b) => createHash("sha256").update(b).digest("hex");
14
+ export function decodeFx(fx) {
15
+ return {
16
+ request: {
17
+ schema: "restore-request-v1",
18
+ sessionId: fx.input.sessionId,
19
+ spans: fx.input.request.spans.map((s) => ({
20
+ nodeId: s.nodeId,
21
+ range: decodeRange(s.range),
22
+ digest: s.digest,
23
+ })),
24
+ },
25
+ reader: {
26
+ exactShards: fx.input.exactShards.map(decodeShard),
27
+ ledgerEvents: fx.input.ledgerEvents.map(decodeEvent),
28
+ },
29
+ };
30
+ }
31
+ export function runReal(fx) {
32
+ const { request, reader } = decodeFx(fx);
33
+ const result = restoreSources(request, reader);
34
+ return { request, reader, result, verification: verifyRestored(result, request) };
35
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * vector-cortex/heal/restore-emit.ts — VC6B event reporter seam.
3
+ *
4
+ * Mirrors `./emit.ts`: a thin `safe()` wrapper around an optional injected
5
+ * `emit` (unit tests pass `undefined` and stay pure), and the two event names the
6
+ * sprint spec requires verbatim:
7
+ * - `vector_cortex_source_restored` — a restore batch completed.
8
+ * - `vector_cortex_restore_digest_rejected` — a source failed its digest check.
9
+ *
10
+ * FLAG SEMANTICS. `restoreSources` / `verifyRestored` are PURE arithmetic and run
11
+ * REGARDLESS of `MEGACOMPACT_VC6B`. The flag gates ONLY this reporting +
12
+ * dashboard seam: with the flag off we still restore and still verify, we just do
13
+ * not announce it under the VC6B event namespace. That is what makes flag-off
14
+ * byte-identical to VC6A — the computation is never skipped, only the emission.
15
+ *
16
+ * PAYLOAD DISCIPLINE. These events carry COUNTS and MODES only — never restored
17
+ * bytes, never node text, never a digest of user content. The exact ledger is not
18
+ * diagnostic data (SECURITY_PRIVACY), and a restoration event is exactly the
19
+ * place where an unguarded `payload` field would leak the entire transcript into
20
+ * a log file.
21
+ *
22
+ * No console, no storage, no network (PREVENT-PI-004 / PREVENT-011). Every line
23
+ * is a structured JSON event with `ts` + `event`.
24
+ */
25
+ import { VC6B_ENABLED } from "../../config/vector-cortex.js";
26
+ /** Run `fn` only when an emit exists; a reporting failure is never fatal. */
27
+ function safe(emit, fn) {
28
+ if (emit === undefined)
29
+ return;
30
+ try {
31
+ fn(emit);
32
+ }
33
+ catch {
34
+ // Non-fatal: a reporting failure must never break the agent loop.
35
+ }
36
+ }
37
+ /** The event names VC6B emits, exported for the dashboard seam and tests. */
38
+ export const RESTORE_EVENT_NAMES = [
39
+ "vector_cortex_source_restored",
40
+ "vector_cortex_restore_digest_rejected",
41
+ ];
42
+ /**
43
+ * Report a completed restoration. Counts and mode only — enough to see whether
44
+ * the shard index is serving reads (mode A) or every span is falling through to
45
+ * a ledger scan (mode B), without disclosing what was restored.
46
+ */
47
+ export function reportSourceRestored(emit, payload) {
48
+ if (!VC6B_ENABLED())
49
+ return;
50
+ safe(emit, (e) => e("vector_cortex_source_restored", {
51
+ ts: undefined,
52
+ event: "vector_cortex_source_restored",
53
+ sessionId: payload.sessionId,
54
+ restoredCount: payload.restoredCount,
55
+ missingCount: payload.missingCount,
56
+ mode: payload.mode,
57
+ }));
58
+ }
59
+ /** Report a rejected source (digest/limit/range). Suppressed under flag-off. */
60
+ export function reportRestoreDigestRejected(emit, payload) {
61
+ if (!VC6B_ENABLED())
62
+ return;
63
+ safe(emit, (e) => e("vector_cortex_restore_digest_rejected", {
64
+ ts: undefined,
65
+ event: "vector_cortex_restore_digest_rejected",
66
+ sessionId: payload.sessionId,
67
+ code: payload.code,
68
+ mode: payload.mode,
69
+ }));
70
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * vector-cortex/heal/restore-readers.ts — the two EXACT source readers (VC6B).
3
+ *
4
+ * Split out of `restore.ts` so the orchestrator stays a short, readable policy
5
+ * file and each reader can be reasoned about on its own (PRACTICES: split at the
6
+ * 300-line soft limit, delegate-shell + impl).
7
+ *
8
+ * Both readers share one discipline: they return bytes ONLY after recomputing
9
+ * SHA-256 over the bytes they actually hold and comparing it to the digest the
10
+ * REQUEST pinned. Neither reader trusts the digest recorded alongside its own
11
+ * source — an `ExactShardV1.digest` is metadata that lives in the same file as
12
+ * the bytes, so a swapped file carries a matching (wrong) pair. Only the
13
+ * caller-supplied digest is authoritative, and only a fresh hash of the fetched
14
+ * bytes can be checked against it.
15
+ *
16
+ * Pure/deterministic/local: `node:crypto` only (a Node built-in, not a network
17
+ * call), no storage, no console (PREVENT-PI-004 / PREVENT-011).
18
+ */
19
+ import { createHash } from "node:crypto";
20
+ /**
21
+ * SHA-256 over bytes as LOWERCASE HEX with NO `sha256:` prefix — the
22
+ * `ExactShardV1.digest` / `ReconstructionSpan.digest` / `RestoreSpanRequest`
23
+ * convention. `EventV2.bytesDigest` prefixes this same value with `sha256:`.
24
+ */
25
+ export function sha256Hex(bytes) {
26
+ return createHash("sha256").update(bytes).digest("hex");
27
+ }
28
+ /**
29
+ * Strip the `sha256:` prefix from an `EventV2.bytesDigest` so it can be compared
30
+ * against a bare hex digest. A value that does not carry the prefix is returned
31
+ * unchanged rather than mangled — the comparison then simply fails, which is the
32
+ * correct outcome for a malformed record.
33
+ */
34
+ export function bareHex(digest) {
35
+ return digest.startsWith("sha256:") ? digest.slice("sha256:".length) : digest;
36
+ }
37
+ /**
38
+ * Structural range equality. All five components must match: two spans over the
39
+ * same seq window but different byte offsets are DIFFERENT spans (a re-encoded
40
+ * or re-offset stream is not the stream that was requested).
41
+ */
42
+ export function rangeEquals(a, b) {
43
+ return (a.sessionId === b.sessionId &&
44
+ a.seqStart === b.seqStart &&
45
+ a.seqEnd === b.seqEnd &&
46
+ a.byteStart === b.byteStart &&
47
+ a.byteEnd === b.byteEnd);
48
+ }
49
+ /**
50
+ * Mode A — indexed exact-shard read.
51
+ *
52
+ * Selection is by RANGE identity plus the shard's own recorded digest, which
53
+ * narrows the candidate set the way a real index lookup would. The accepted
54
+ * candidate is then re-hashed from its `originalBytes`: this second check is
55
+ * what catches a shard whose bytes were swapped AFTER the index lookup resolved
56
+ * (the sprint's unique failure injection). A candidate that matches by range but
57
+ * fails either digest check reports `digest-mismatch` — it does NOT silently fall
58
+ * through to the ledger, because a corrupt exact shard is a fact worth surfacing.
59
+ */
60
+ export function readExactShard(shards, range, digest) {
61
+ const byRange = shards.filter((s) => rangeEquals(s.range, range));
62
+ if (byRange.length === 0)
63
+ return { kind: "absent" };
64
+ const candidate = byRange.find((s) => s.digest === digest);
65
+ if (candidate === undefined)
66
+ return { kind: "digest-mismatch" };
67
+ // Defense in depth: the shard's recorded digest is metadata, the bytes are the
68
+ // authority. Re-hash what we actually hold.
69
+ const bytes = candidate.originalBytes;
70
+ if (sha256Hex(bytes) !== digest)
71
+ return { kind: "digest-mismatch" };
72
+ return { kind: "bytes", bytes };
73
+ }
74
+ /**
75
+ * Mode B — ledger range scan.
76
+ *
77
+ * An INDEPENDENT path from mode A: no shard index is consulted. The occurrence
78
+ * records covering `[seqStart..seqEnd]` are selected, sorted ascending by seq
79
+ * (the caller's array order is untrusted — a scan that concatenated records in
80
+ * arrival order would produce plausible-looking but wrong bytes), each record's
81
+ * own `bytesDigest` is verified, and the concatenation is hashed against the
82
+ * span digest.
83
+ *
84
+ * Both checks matter. Per-record verification localizes corruption to a single
85
+ * occurrence; the span-level hash catches a scan that is individually valid but
86
+ * collectively wrong — a missing record in the middle of the range, or a range
87
+ * that covers different content than the requester believed.
88
+ */
89
+ export function readLedgerSpan(events, range, digest) {
90
+ const covering = events
91
+ .filter((e) => e.sessionId === range.sessionId &&
92
+ e.seq >= range.seqStart &&
93
+ e.seq <= range.seqEnd)
94
+ .sort((a, b) => (a.seq < b.seq ? -1 : a.seq > b.seq ? 1 : 0));
95
+ if (covering.length === 0)
96
+ return { kind: "absent" };
97
+ // Per-record byte authority (VC1A): a record whose own digest disagrees with
98
+ // its bytes is corrupt, and a corrupt record can never contribute to a restore.
99
+ for (const e of covering) {
100
+ if (sha256Hex(e.originalBytes) !== bareHex(e.bytesDigest)) {
101
+ return { kind: "digest-mismatch" };
102
+ }
103
+ }
104
+ const total = covering.reduce((sum, e) => sum + e.originalBytes.length, 0);
105
+ const bytes = new Uint8Array(total);
106
+ let offset = 0;
107
+ for (const e of covering) {
108
+ bytes.set(e.originalBytes, offset);
109
+ offset += e.originalBytes.length;
110
+ }
111
+ if (sha256Hex(bytes) !== digest)
112
+ return { kind: "digest-mismatch" };
113
+ return { kind: "bytes", bytes };
114
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * vector-cortex/heal/restore-types.ts — VC6B exact source restoration contract.
3
+ *
4
+ * VC6A optimized WHICH EDGES the closure plan walks. VC6B answers the next
5
+ * question: when the plan needs a node whose bytes are no longer in the live
6
+ * window, WHERE do those bytes come from? The answer is deliberately narrow —
7
+ * an EXACT source, or nothing.
8
+ *
9
+ * THE CARDINAL RULE. Restored bytes are ONLY ever read from an exact source:
10
+ * 1. an `ExactShardV1` whose range and digest both match the request, or
11
+ * 2. a scan of the `EventV2` occurrence ledger over the requested seq range.
12
+ * Bytes are NEVER inferred from an embedding, a semantic shard, a RAPTOR
13
+ * summary, or any other derived/lossy representation. A semantic tier can tell
14
+ * you what a span was ABOUT; it cannot tell you what the span WAS. Attempting to
15
+ * "restore" from a derived source would silently fabricate transcript history,
16
+ * so VC6B has no code path that can do it: `RestoreReader` exposes exactly the
17
+ * two exact sources and nothing else.
18
+ *
19
+ * THE VERIFICATION RULE. Every restored span must hash to the SHA-256 digest the
20
+ * REQUEST pinned, checked immediately before insertion. A source that matches by
21
+ * range but not by hash is rejected (`HEAL_RESTORE_DIGEST_MISMATCH`) — never
22
+ * "close enough". This is what makes a swapped shard file, a truncated read, or a
23
+ * corrupted ledger record fail loudly instead of poisoning the reconstruction.
24
+ *
25
+ * DIGEST PINNING (three fields exist; do not confuse them).
26
+ * - `ReconstructionSpan.digest` and `ExactShardV1.digest` are SHA-256 in
27
+ * LOWERCASE HEX with NO prefix.
28
+ * - `EventV2.bytesDigest` is `sha256:<hex>`, WITH the prefix.
29
+ * `RestoreSpanRequest.digest` works at SPAN level and uses the FORMER: bare
30
+ * lowercase hex, matching `ExactShardV1.digest` / `ReconstructionSpan.digest`.
31
+ * The prefixed `EventV2.bytesDigest` is used ONLY for per-event verification
32
+ * inside the ledger-scan path and is never the request-level digest. Mixing the
33
+ * two would make every ledger restoration fail (or, worse, make a prefix-stripped
34
+ * comparison accidentally succeed against the wrong granularity).
35
+ *
36
+ * BOUNDS. A restore request is attacker-shaped input: it names spans and byte
37
+ * ranges. `RESTORE_LIMIT_SPANS` / `RESTORE_LIMIT_BYTES` bound it BEFORE any
38
+ * reader is consulted, so an oversized request cannot be used to make the
39
+ * restorer walk the whole ledger (HEAL-LIMIT-002).
40
+ *
41
+ * Pure types + registered conformance IDs: no storage, no console, no network
42
+ * (PREVENT-PI-004 / PREVENT-011).
43
+ */
44
+ /**
45
+ * Maximum spans in one request. A request naming more spans than this is
46
+ * rejected outright — the bound exists so a single call cannot be turned into an
47
+ * unbounded traversal of the ledger.
48
+ */
49
+ export const RESTORE_LIMIT_SPANS = 64;
50
+ /**
51
+ * Maximum aggregate requested bytes (4 MiB), summed from the REQUEST's ranges
52
+ * (`byteEnd - byteStart`) so the bound is enforceable without reading anything.
53
+ */
54
+ export const RESTORE_LIMIT_BYTES = 4 * 1024 * 1024;
55
+ /**
56
+ * Registered VC6B conformance ID range (HEAL-016..030), continuing VC6A's
57
+ * HEAL-001..015. The acceptance test reads these rows from the v2 manifest and
58
+ * asserts each returns its manifest `ok`/`code`.
59
+ */
60
+ export const RESTORE_IDS = Array.from({ length: 15 }, (_v, i) => `HEAL-${String(i + 16).padStart(3, "0")}`);
61
+ /** Named VC6B conformance assertions (the sprint's headline rows). */
62
+ export const RESTORE_NAMED_IDS = [
63
+ "HEAL-SPAN-001",
64
+ "HEAL-LIMIT-002",
65
+ "HEAL-DIGEST-003",
66
+ ];