pi-mega-compact 0.20.19 → 0.20.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/config/vector-cortex-vc8c.js +31 -0
  2. package/dist/config/vector-cortex.js +5 -0
  3. package/dist/config.js +1 -1
  4. package/dist/extensions/dashboard-server/api-contracts/vector-cortex-platform.js +12 -0
  5. package/dist/extensions/dashboard-server/route-dispatch.js +6 -0
  6. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  7. package/dist/extensions/dashboard-server/routes-vector-cortex-platform.js +42 -0
  8. package/dist/src/config/vector-cortex-vc8c.js +31 -0
  9. package/dist/src/config/vector-cortex.js +5 -0
  10. package/dist/src/config.js +1 -1
  11. package/dist/src/vector-cortex/cache/_diagnostics-fixture.js +79 -5
  12. package/dist/src/vector-cortex/cache/diagnostics.js +17 -8
  13. package/dist/src/vector-cortex/controller/_adaptive-fixture.js +126 -0
  14. package/dist/src/vector-cortex/migrations/request-hash-v2-ops.js +23 -7
  15. package/dist/src/vector-cortex/platform/cross-read.js +98 -0
  16. package/dist/src/vector-cortex/platform/emit.js +54 -0
  17. package/dist/src/vector-cortex/platform/select.js +104 -0
  18. package/dist/src/vector-cortex/platform/types.js +57 -0
  19. package/dist/vector-cortex/cache/_diagnostics-fixture.js +79 -5
  20. package/dist/vector-cortex/cache/diagnostics.js +17 -8
  21. package/dist/vector-cortex/controller/_adaptive-fixture.js +126 -0
  22. package/dist/vector-cortex/migrations/request-hash-v2-ops.js +23 -7
  23. package/dist/vector-cortex/platform/cross-read.js +98 -0
  24. package/dist/vector-cortex/platform/emit.js +54 -0
  25. package/dist/vector-cortex/platform/select.js +104 -0
  26. package/dist/vector-cortex/platform/types.js +57 -0
  27. package/extensions/dashboard-client/dist/assets/{AreaChart-ujtGh2ez.js → AreaChart-BkfB-LSQ.js} +2 -2
  28. package/extensions/dashboard-client/dist/assets/{AreaChart-ujtGh2ez.js.map → AreaChart-BkfB-LSQ.js.map} +1 -1
  29. package/extensions/dashboard-client/dist/assets/{BarChart-DNZqvtqh.js → BarChart-Cs1kogko.js} +2 -2
  30. package/extensions/dashboard-client/dist/assets/{BarChart-DNZqvtqh.js.map → BarChart-Cs1kogko.js.map} +1 -1
  31. package/extensions/dashboard-client/dist/assets/{CacheTab-CeCIW1rD.js → CacheTab-BZgaguGr.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{CacheTab-CeCIW1rD.js.map → CacheTab-BZgaguGr.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/{EventsTab-ChohmqJI.js → EventsTab-BFbkI_MO.js} +2 -2
  34. package/extensions/dashboard-client/dist/assets/{EventsTab-ChohmqJI.js.map → EventsTab-BFbkI_MO.js.map} +1 -1
  35. package/extensions/dashboard-client/dist/assets/{HealthTab-CKfAooiV.js → HealthTab-CvlOENCX.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{HealthTab-CKfAooiV.js.map → HealthTab-CvlOENCX.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D3DxHgM2.js → MaintenanceTab-CtBuz2qL.js} +2 -2
  38. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D3DxHgM2.js.map → MaintenanceTab-CtBuz2qL.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/{MemoryMapTab--c-0PBka.js → MemoryMapTab-K4775S4d.js} +2 -2
  40. package/extensions/dashboard-client/dist/assets/{MemoryMapTab--c-0PBka.js.map → MemoryMapTab-K4775S4d.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/{MetricsTab-9UaEB69R.js → MetricsTab-DVGcFInz.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{MetricsTab-9UaEB69R.js.map → MetricsTab-DVGcFInz.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{OverviewTab-89zNl1qx.js → OverviewTab-4LmW5ZCl.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{OverviewTab-89zNl1qx.js.map → OverviewTab-4LmW5ZCl.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{ReposTab-CkfNrdxF.js → ReposTab-StebchgQ.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{ReposTab-CkfNrdxF.js.map → ReposTab-StebchgQ.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{SessionsTab-DA7wqI05.js → SessionsTab-C3D_XAkX.js} +2 -2
  48. package/extensions/dashboard-client/dist/assets/{SessionsTab-DA7wqI05.js.map → SessionsTab-C3D_XAkX.js.map} +1 -1
  49. package/extensions/dashboard-client/dist/assets/{SetupTab-pG4xMI3P.js → SetupTab-BTBGyppF.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{SetupTab-pG4xMI3P.js.map → SetupTab-BTBGyppF.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DeQnzSkb.js → TimeSavedCard-CTPi_AAW.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DeQnzSkb.js.map → TimeSavedCard-CTPi_AAW.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{TurnsTab-B6ifHMbg.js → TurnsTab-DSOGcY5Q.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{TurnsTab-B6ifHMbg.js.map → TurnsTab-DSOGcY5Q.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/VectorCortexTab-OMW8_RFf.js +2 -0
  56. package/extensions/dashboard-client/dist/assets/VectorCortexTab-OMW8_RFf.js.map +1 -0
  57. package/extensions/dashboard-client/dist/assets/{WikiTab-awMsQNBY.js → WikiTab-Db-qFDdg.js} +2 -2
  58. package/extensions/dashboard-client/dist/assets/{WikiTab-awMsQNBY.js.map → WikiTab-Db-qFDdg.js.map} +1 -1
  59. package/extensions/dashboard-client/dist/assets/{button-DCDwPy_j.js → button-BF4UgBAD.js} +2 -2
  60. package/extensions/dashboard-client/dist/assets/{button-DCDwPy_j.js.map → button-BF4UgBAD.js.map} +1 -1
  61. package/extensions/dashboard-client/dist/assets/{card-D3KAYtck.js → card-XmXoYQqB.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{card-D3KAYtck.js.map → card-XmXoYQqB.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-D_FHNog-.js → generateCategoricalChart-Be8hM1aU.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-D_FHNog-.js.map → generateCategoricalChart-Be8hM1aU.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{index-4MkLsevY.js → index-NXDgILxv.js} +3 -3
  66. package/extensions/dashboard-client/dist/assets/{index-4MkLsevY.js.map → index-NXDgILxv.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/{switch-Cc05xGtg.js → switch-kUZCrbia.js} +2 -2
  68. package/extensions/dashboard-client/dist/assets/{switch-Cc05xGtg.js.map → switch-kUZCrbia.js.map} +1 -1
  69. package/extensions/dashboard-client/dist/assets/{toggle--QUzWk-Y.js → toggle-DEe5mCwF.js} +2 -2
  70. package/extensions/dashboard-client/dist/assets/{toggle--QUzWk-Y.js.map → toggle-DEe5mCwF.js.map} +1 -1
  71. package/extensions/dashboard-client/dist/assets/{useSSE-DcAeX67m.js → useSSE-BlCyLqge.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{useSSE-DcAeX67m.js.map → useSSE-BlCyLqge.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/index.html +1 -1
  74. package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
  75. package/extensions/dashboard-client/src/tabs/VectorCortexPlatformCard.tsx +42 -0
  76. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +2 -0
  77. package/extensions/dashboard-client/src/tabs/useVectorCortexPoll.ts +5 -0
  78. package/extensions/dashboard-client/src/types/vector-cortex-vc8.ts +19 -3
  79. package/extensions/dashboard-client/src/types/vector-cortex.ts +1 -1
  80. package/extensions/dashboard-server/api-contracts/vector-cortex-platform.ts +34 -0
  81. package/extensions/dashboard-server/route-dispatch.ts +6 -0
  82. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  83. package/extensions/dashboard-server/routes-vector-cortex-platform.ts +52 -0
  84. package/package.json +1 -1
  85. package/src/config/vector-cortex-vc8c.ts +33 -0
  86. package/src/config/vector-cortex.ts +6 -0
  87. package/src/config.ts +1 -0
  88. package/src/vector-cortex/cache/_diagnostics-fixture.ts +163 -5
  89. package/src/vector-cortex/cache/diagnostics.ts +18 -9
  90. package/src/vector-cortex/controller/_adaptive-fixture.ts +230 -0
  91. package/src/vector-cortex/migrations/request-hash-v2-ops.ts +21 -6
  92. package/src/vector-cortex/platform/cross-read.ts +122 -0
  93. package/src/vector-cortex/platform/emit.ts +77 -0
  94. package/src/vector-cortex/platform/select.ts +131 -0
  95. package/src/vector-cortex/platform/types.ts +169 -0
  96. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DSz16qtM.js +0 -2
  97. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DSz16qtM.js.map +0 -1
@@ -0,0 +1,54 @@
1
+ /**
2
+ * platform/emit.ts — VC8C engine parity/selection reporter seam (FLAG-GATED).
3
+ *
4
+ * Mirrors the VC8B policy-emit pattern: a thin `safe()` wrapper around an
5
+ * optional injected `emit`, and the two event names the sprint spec requires:
6
+ * - `vector_cortex_engine_parity_checked` — a parity report was resolved.
7
+ * - `vector_cortex_engine_selection_demoted` — the triad dropped from A.
8
+ *
9
+ * FLAG SEMANTICS. The engine selector and cross-conformance runner STILL RUN
10
+ * regardless of `MEGACOMPACT_VC8C`. The flag gates ONLY this reporting +
11
+ * dashboard seam: flag-off is byte-identical to the predecessor (VC8B).
12
+ *
13
+ * PAYLOAD DISCIPLINE. These events carry only ids, the finite mode, the machine
14
+ * code, and counts — never artifact bytes, output bytes, or free-text.
15
+ *
16
+ * PREVENT-PI-004: no network. PREVENT-011: no `any` type.
17
+ */
18
+ import { VC8C_ENABLED } from "../../config/vector-cortex.js";
19
+ /** Run `fn` only when an emit exists; a reporting failure is never fatal. */
20
+ function safe(emit, fn) {
21
+ if (emit === undefined)
22
+ return;
23
+ try {
24
+ fn(emit);
25
+ }
26
+ catch {
27
+ // Non-fatal: a reporting failure must never break the agent loop.
28
+ }
29
+ }
30
+ /** Report a resolved engine parity check. Gated by the flag. */
31
+ export function reportEngineParityChecked(emit, parity) {
32
+ if (!VC8C_ENABLED())
33
+ return;
34
+ safe(emit, (e) => e("vector_cortex_engine_parity_checked", {
35
+ ts: undefined,
36
+ event: "vector_cortex_engine_parity_checked",
37
+ reportId: parity.reportId,
38
+ fixtureCount: parity.fixtureCount,
39
+ passed: parity.passed,
40
+ failed: parity.failed,
41
+ }));
42
+ }
43
+ /** Report an engine selection demotion away from mode A. Gated by the flag. */
44
+ export function reportEngineSelectionDemoted(emit, demotion) {
45
+ if (!VC8C_ENABLED())
46
+ return;
47
+ safe(emit, (e) => e("vector_cortex_engine_selection_demoted", {
48
+ ts: undefined,
49
+ event: "vector_cortex_engine_selection_demoted",
50
+ from: demotion.from,
51
+ to: demotion.to,
52
+ code: demotion.code,
53
+ }));
54
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * platform/select.ts — VC8C canary-selection engine selector (PURE).
3
+ *
4
+ * `selectEngine` decides the failure triad mode from a reviewer-accepted
5
+ * `ParityReportV1` and the current host platform. It admits mode A (qualified
6
+ * external Rust artifact) ONLY when every piece of evidence lines up:
7
+ *
8
+ * 1. The report's ABI version matches ABI_VERSION (RUST-ABI-001).
9
+ * 2. URL metadata is present (artifactUrl is non-empty).
10
+ * 3. A commit hash is present (commit is a non-empty hex-ish string).
11
+ * 4. The artifact's Cargo.lock digest matches the report's evidence digest
12
+ * (RUST-META-003) — a digest mismatch MUST reject the artifact.
13
+ * 5. The platform is in SUPPORTED_PLATFORMS (RUST-PLATFORM-*).
14
+ * 6. EVERY fixture in the report's matrix resolved ok (RUST-001..030).
15
+ *
16
+ * Any failure demotes to B (TS reference). If the TS reference breaker has
17
+ * opened (`allowLegacy` is set by the caller), the selector demotes further to
18
+ * C (legacy path) instead — the two-bytes-of-safety pyramid never runs an
19
+ * unqualified artifact and never silently rides a broken reference.
20
+ *
21
+ * Everything here is PURE: no clock, no storage, no network, no flag read. The
22
+ * flag gates only the reporter seam in emit.ts.
23
+ *
24
+ * PREVENT-002/011/PI-004 honored.
25
+ */
26
+ import { ABI_VERSION, RUST_ABI_MISMATCH, RUST_ARTIFACT_MISSING, RUST_CARGO_DIGEST_MISMATCH, RUST_PARITY_MISMATCH, RUST_PLATFORM_UNSUPPORTED, SUPPORTED_PLATFORMS, } from "./types.js";
27
+ /** Construct a selection failure. */
28
+ function fail(code) {
29
+ return { code };
30
+ }
31
+ /** Type guard: is this a supported host platform? */
32
+ export function isSupportedPlatform(platform) {
33
+ return SUPPORTED_PLATFORMS.includes(platform);
34
+ }
35
+ /** A 40-char lowercase hex string is the canonical bare git SHA-256 commit. */
36
+ const COMMIT_RE = /^[0-9a-f]{40}$/;
37
+ /** Validate the ABI version matches EngineAbiV1 exactly. Throws on mismatch. */
38
+ export function validateAbi(abi) {
39
+ if (abi.version !== ABI_VERSION)
40
+ throw fail(RUST_ABI_MISMATCH);
41
+ }
42
+ /** Validate URL metadata + commit + Cargo.lock evidence are all present. */
43
+ function validateEvidence(report) {
44
+ if (report.artifactUrl.length === 0)
45
+ throw fail(RUST_ARTIFACT_MISSING);
46
+ if (!COMMIT_RE.test(report.commit))
47
+ throw fail(RUST_ARTIFACT_MISSING);
48
+ // SHA-256 hex digests are exactly 64 chars; anything else is not evidence.
49
+ if (report.cargoLockDigest.length !== 64) {
50
+ throw fail(RUST_ARTIFACT_MISSING);
51
+ }
52
+ if (report.artifactCargoLockDigest.length !== 64) {
53
+ throw fail(RUST_ARTIFACT_MISSING);
54
+ }
55
+ }
56
+ /** True when every fixture in the matrix resolved ok. */
57
+ export function matrixAllOk(report) {
58
+ if (report.matrix.length === 0)
59
+ return false;
60
+ return report.matrix.every((f) => f.ok && f.code === null);
61
+ }
62
+ /**
63
+ * Select the engine mode from a parity report and the host platform.
64
+ *
65
+ * Returns mode A only when all six admission checks pass and the digest (the
66
+ * report's own evidence) is internally consistent with the Cargo.lock the
67
+ * artifact claims. Otherwise demotes to B; if the TS reference breaker is open
68
+ * (`allowLegacy`), demotes to C.
69
+ */
70
+ export function selectEngine(abi, report, platform, allowLegacy = false) {
71
+ try {
72
+ validateAbi(abi);
73
+ validateEvidence(report);
74
+ if (!isSupportedPlatform(platform))
75
+ throw fail(RUST_PLATFORM_UNSUPPORTED);
76
+ if (report.platform !== platform)
77
+ throw fail(RUST_PLATFORM_UNSUPPORTED);
78
+ // RUST-META-003: the artifact's own Cargo.lock digest must match the
79
+ // reviewer-accepted evidence; a mismatch rejects the artifact outright.
80
+ if (report.artifactCargoLockDigest !== report.cargoLockDigest) {
81
+ throw fail(RUST_CARGO_DIGEST_MISMATCH);
82
+ }
83
+ if (!matrixAllOk(report)) {
84
+ throw fail(RUST_PARITY_MISMATCH);
85
+ }
86
+ const artifact = {
87
+ url: report.artifactUrl,
88
+ commit: report.commit,
89
+ cargoLockDigest: report.cargoLockDigest,
90
+ platform: report.platform,
91
+ qualified: true,
92
+ };
93
+ return { mode: "A", reason: "qualified", artifact };
94
+ }
95
+ catch (e) {
96
+ const code = typeof e === "object" && e !== null && "code" in e
97
+ ? String(e.code)
98
+ : RUST_PARITY_MISMATCH;
99
+ if (allowLegacy) {
100
+ return { mode: "C", reason: code, artifact: null };
101
+ }
102
+ return { mode: "B", reason: code, artifact: null };
103
+ }
104
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * platform/types.ts — VC8C engine ABI + parity report + selection types.
3
+ *
4
+ * These are the CANARY-SELECTION contracts. This repo has NO Rust workspace and
5
+ * creates none: an external Rad repo supplies the artifact URL, commit, and
6
+ * Cargo.lock digest/evidence as `ParityReportV1`. The selector only ever admits
7
+ * mode A (qualified external Rust) when every piece of that evidence matches;
8
+ * otherwise it demotes to B (TS reference) or C (legacy).
9
+ *
10
+ * EngineAbiV1 is the FINITE, versioned envelope both sides speak: an input
11
+ * envelope in, an output envelope out, and a machine-coded error envelope. There
12
+ * is deliberately no free-text field and no unbounded version string — an ABI
13
+ * whose version or error space can drift at runtime cannot be cross-checked
14
+ * against a foreign binary.
15
+ *
16
+ * Failure triad (private to the runtime; mirrored here for the selector):
17
+ * A = qualified external Rust artifact
18
+ * B = TS reference (forced by a parity or metadata failure)
19
+ * C = legacy path (forced when the TS reference breaker opens)
20
+ *
21
+ * Conformance IDs RUST-001..030 are registered here as the single source of
22
+ * truth for the sprint's conformance rows; the three named fixtures
23
+ * (RUST-ABI-001, RUST-ERR-002, RUST-META-003) are the headline assertions.
24
+ *
25
+ * PREVENT-PI-004: type definitions only, no network code.
26
+ * PREVENT-011: no `any` type.
27
+ */
28
+ /** ABI version string shared by every engine implementation. */
29
+ export const ABI_VERSION = "engine-abi-v1";
30
+ /** Supported host platforms; an artifact for any other platform is rejected. */
31
+ export const SUPPORTED_PLATFORMS = [
32
+ "linux-x64",
33
+ "linux-arm64",
34
+ "darwin-x64",
35
+ "darwin-arm64",
36
+ "win32-x64",
37
+ ];
38
+ /** Failure code: canonical bytes or failure codes disagreed across runners. */
39
+ export const RUST_PARITY_MISMATCH = "RUST_PARITY_MISMATCH";
40
+ /** Failure code: a neutral frame ended mid-record (partial write/read). */
41
+ export const RUST_FRAME_TRUNCATED = "RUST_FRAME_TRUNCATED";
42
+ /** Failure code: the artifact ABI version does not match ABI_VERSION. */
43
+ export const RUST_ABI_MISMATCH = "RUST_ABI_MISMATCH";
44
+ /** Failure code: the artifact Cargo.lock digest does not match the evidence. */
45
+ export const RUST_CARGO_DIGEST_MISMATCH = "RUST_CARGO_DIGEST_MISMATCH";
46
+ /** Failure code: the artifact targets a platform outside SUPPORTED_PLATFORMS. */
47
+ export const RUST_PLATFORM_UNSUPPORTED = "RUST_PLATFORM_UNSUPPORTED";
48
+ /** Failure code: the report names no artifact (URL/commit missing). */
49
+ export const RUST_ARTIFACT_MISSING = "RUST_ARTIFACT_MISSING";
50
+ /** Conformance IDs RUST-001..RUST-030 for the 30 numbered conformance rows. */
51
+ export const RUST_CONFORMANCE_IDS = Array.from({ length: 30 }, (_v, i) => `RUST-${String(i + 1).padStart(3, "0")}`);
52
+ /** Named conformance fixtures for the sprint's headline assertions. */
53
+ export const RUST_NAMED_FIXTURES = [
54
+ "RUST-ABI-001",
55
+ "RUST-ERR-002",
56
+ "RUST-META-003",
57
+ ];
@@ -1,14 +1,33 @@
1
1
  /**
2
- * cache/_diagnostics-fixture.ts — VC7C acceptance-test fixture helpers.
2
+ * cache/_diagnostics-fixture.ts — VC7C conformance fixture I/O + host builders.
3
3
  *
4
- * Reads conformance fixtures from the v2 `cache-diagnostics/` domain and provides
5
- * a flag-toggling wrapper for VC7C parity tests. Mirrors `_economics-fixture.ts`.
4
+ * Reads conformance fixtures from the v2 `cache-diagnostics/` domain, decodes
5
+ * them into the REAL production types (`MissObservation`, `M5Host`) and feeds
6
+ * them verbatim into `classifyMiss` / `migrateRequestHashV2`. Fixtures store
7
+ * bigints as JSON numbers — converted here (lossy conversion would break
8
+ * digests and the acceptance rows would fail loudly).
9
+ *
10
+ * No mocks, no stubs, no parallel "test shape": the decoded objects ARE
11
+ * `MissObservation` / `M5Host` and drive the production functions directly.
12
+ * Mirrors `_economics-fixture.ts`.
6
13
  */
7
- import { readFileSync } from "node:fs";
14
+ import { readFileSync, existsSync } from "node:fs";
8
15
  import { join, dirname } from "node:path";
9
16
  import { fileURLToPath } from "node:url";
10
17
  const here = dirname(fileURLToPath(import.meta.url));
11
- const V2 = join(here, "..", "..", "..", "conformance", "vector-cortex", "v2");
18
+ function repoRoot(from) {
19
+ let dir = from;
20
+ for (let i = 0; i < 8; i++) {
21
+ if (existsSync(join(dir, "conformance", "vector-cortex")))
22
+ return dir;
23
+ const next = dirname(dir);
24
+ if (next === dir)
25
+ break;
26
+ dir = next;
27
+ }
28
+ throw new Error("conformance corpus not found above " + from);
29
+ }
30
+ const V2 = join(repoRoot(here), "conformance", "vector-cortex", "v2");
12
31
  const DIR = join(V2, "cache-diagnostics");
13
32
  /** Read + parse one conformance fixture by ID. */
14
33
  export function diagnosticsFixture(id) {
@@ -29,3 +48,58 @@ export function withVc7cFlag(value, fn) {
29
48
  process.env.MEGACOMPACT_VC7C = prior;
30
49
  }
31
50
  }
51
+ /** Read a cache-diagnostic fixture as a typed CacheFx. */
52
+ export function cacheFx(id) {
53
+ return diagnosticsFixture(id);
54
+ }
55
+ /** Decode the fixture's JSON observe into the production MissObservation. */
56
+ export function toObservation(fx) {
57
+ const o = fx.input.observe;
58
+ return {
59
+ requestProfileId: o.requestProfileId,
60
+ requestProfileVersion: o.requestProfileVersion,
61
+ cachedProfileId: o.cachedProfileId,
62
+ cachedProfileVersion: o.cachedProfileVersion,
63
+ requestCoveredDigest: o.requestCoveredDigest,
64
+ cachedCoveredDigest: o.cachedCoveredDigest,
65
+ requestedRangeCount: o.requestedRangeCount,
66
+ cachedRangeCount: o.cachedRangeCount,
67
+ requestDigest: o.requestDigest,
68
+ cachedRequestDigest: o.cachedRequestDigest,
69
+ requestDependencyHighWater: BigInt(o.requestDependencyHighWater),
70
+ cachedDependencyHighWater: o.cachedDependencyHighWater === null
71
+ ? null
72
+ : BigInt(o.cachedDependencyHighWater),
73
+ generationInvalidated: o.generationInvalidated,
74
+ };
75
+ }
76
+ /** Read an M5 fixture as a typed M5Fx. */
77
+ export function m5Fx(id) {
78
+ return diagnosticsFixture(id);
79
+ }
80
+ /** Build an in-memory M5Host from the fixture input. */
81
+ export function toM5Host(fx) {
82
+ const v1Rows = fx.input.v1Rows.map((r) => ({
83
+ profileId: r.profileId,
84
+ requestDigest: r.requestDigest,
85
+ hash: r.hash,
86
+ }));
87
+ const v2Rows = [];
88
+ let activeVersion = fx.input.activeVersion;
89
+ const host = {
90
+ v1Rows: () => v1Rows,
91
+ economicsVersionOf: (p) => fx.input.econVersionOf[p] ?? "econ-1",
92
+ sessionOf: (p) => fx.input.sessionOf[p] ?? "s1",
93
+ liveGenerationOf: (s) => BigInt(fx.input.liveGenerationOf[s] ?? 0),
94
+ existingV2: () => v2Rows,
95
+ putV2: (rows) => v2Rows.push(...rows),
96
+ activeVersion: () => activeVersion,
97
+ switchToV2: () => {
98
+ activeVersion = 2;
99
+ },
100
+ get activeVersionAfter() {
101
+ return activeVersion;
102
+ },
103
+ };
104
+ return host;
105
+ }
@@ -34,21 +34,30 @@ function advanceDelta(request, cached) {
34
34
  }
35
35
  /** Collect the payload-free evidence from one observation. */
36
36
  export function collectEvidence(o) {
37
+ // Absence is computed first: "absence is not a mismatch" means a cold key
38
+ // (null cached fields) classifies `unknown`, never `profile`/`range`/etc.
39
+ // The cachedRangeCount on a cold key is 0 by convention (not a real count),
40
+ // so it must not trigger a range mismatch even though isPresent(0) is true.
41
+ const absent = !isPresent(o.cachedProfileId) &&
42
+ !isPresent(o.cachedCoveredDigest) &&
43
+ !isPresent(o.cachedRequestDigest) &&
44
+ !isPresent(o.cachedDependencyHighWater);
37
45
  const profileMismatch = isPresent(o.cachedProfileId) &&
38
46
  isPresent(o.cachedProfileVersion) &&
39
47
  (o.cachedProfileId !== o.requestProfileId ||
40
48
  o.cachedProfileVersion !== o.requestProfileVersion);
41
- const rangeMismatch = (isPresent(o.cachedCoveredDigest) &&
42
- o.cachedCoveredDigest !== o.requestCoveredDigest) ||
43
- (isPresent(o.cachedRangeCount) &&
44
- o.cachedRangeCount !== o.requestedRangeCount);
49
+ // Guard with !absent: a cold key's cachedRangeCount is 0 by convention (not
50
+ // a real count), so it must not fire a range mismatch even though
51
+ // isPresent(0) is true. The covered-digest arm is already null-safe but
52
+ // the rangeCount arm is not — !absent covers both uniformly.
53
+ const rangeMismatch = !absent &&
54
+ ((isPresent(o.cachedCoveredDigest) &&
55
+ o.cachedCoveredDigest !== o.requestCoveredDigest) ||
56
+ (isPresent(o.cachedRangeCount) &&
57
+ o.cachedRangeCount !== o.requestedRangeCount));
45
58
  const dep = advanceDelta(o.requestDependencyHighWater, o.cachedDependencyHighWater);
46
59
  const requestMismatch = isPresent(o.cachedRequestDigest) &&
47
60
  o.cachedRequestDigest !== o.requestDigest;
48
- const absent = !isPresent(o.cachedProfileId) &&
49
- !isPresent(o.cachedCoveredDigest) &&
50
- !isPresent(o.cachedRequestDigest) &&
51
- !isPresent(o.cachedDependencyHighWater);
52
61
  return {
53
62
  profileMismatch,
54
63
  rangeMismatch,
@@ -0,0 +1,126 @@
1
+ /**
2
+ * controller/_adaptive-fixture.ts — VC8B conformance fixture I/O + host builders.
3
+ *
4
+ * Reads conformance fixtures from the v2 `adaptive-policy/` domain, decodes
5
+ * them into the REAL production types (`PolicyInput`, `M7Host`) and feeds
6
+ * them verbatim into `evaluatePolicy` / `evaluateShadow` / `migratePressureV2`.
7
+ *
8
+ * No mocks, no stubs, no parallel "test shape": the decoded objects ARE the
9
+ * production types and drive the production functions directly.
10
+ * Mirrors `_diagnostics-fixture.ts` (VC7C) and `_economics-fixture.ts` (VC7B).
11
+ */
12
+ import { readFileSync, existsSync } from "node:fs";
13
+ import { join, dirname } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ function repoRoot(from) {
17
+ let dir = from;
18
+ for (let i = 0; i < 8; i++) {
19
+ if (existsSync(join(dir, "conformance", "vector-cortex")))
20
+ return dir;
21
+ const next = dirname(dir);
22
+ if (next === dir)
23
+ break;
24
+ dir = next;
25
+ }
26
+ throw new Error("conformance corpus not found above " + from);
27
+ }
28
+ const V2 = join(repoRoot(here), "conformance", "vector-cortex", "v2");
29
+ const DIR = join(V2, "adaptive-policy");
30
+ /** Read + parse one conformance fixture by ID from adaptive-policy/. */
31
+ export function adaptiveFixture(id) {
32
+ const raw = readFileSync(join(DIR, `${id}.json`), "utf8");
33
+ return JSON.parse(raw);
34
+ }
35
+ /** Run `fn` with MEGACOMPACT_VC8B set to `value`, restoring the prior value after. */
36
+ export function withVc8bFlag(value, fn) {
37
+ const prior = process.env.MEGACOMPACT_VC8B;
38
+ process.env.MEGACOMPACT_VC8B = value;
39
+ try {
40
+ return fn();
41
+ }
42
+ finally {
43
+ if (prior === undefined)
44
+ delete process.env.MEGACOMPACT_VC8B;
45
+ else
46
+ process.env.MEGACOMPACT_VC8B = prior;
47
+ }
48
+ }
49
+ /** Read a policy-decision fixture as a typed PolFx. */
50
+ export function polFx(id) {
51
+ return adaptiveFixture(id);
52
+ }
53
+ /** Decode the fixture's JSON input into the production PolicyInput. */
54
+ export function toPolicyInput(fx) {
55
+ return {
56
+ decisionId: fx.input.decisionId,
57
+ sessionId: fx.input.sessionId,
58
+ pressure: fx.input.pressure,
59
+ requestedBudget: fx.input.requestedBudget,
60
+ bounds: {
61
+ minBudget: fx.input.bounds.minBudget,
62
+ maxBudget: fx.input.bounds.maxBudget,
63
+ },
64
+ ts: fx.input.ts,
65
+ };
66
+ }
67
+ /** Read a policy-shadow fixture as a typed ShadowFx. */
68
+ export function shadowFx(id) {
69
+ return adaptiveFixture(id);
70
+ }
71
+ /** Decode shadow input rows into production PolicyInput[]. */
72
+ export function toShadowInputs(fx) {
73
+ return fx.input.inputs.map((i) => ({
74
+ decisionId: i.decisionId,
75
+ sessionId: i.sessionId,
76
+ pressure: i.pressure,
77
+ requestedBudget: i.requestedBudget,
78
+ bounds: {
79
+ minBudget: i.bounds.minBudget,
80
+ maxBudget: i.bounds.maxBudget,
81
+ },
82
+ ts: i.ts,
83
+ }));
84
+ }
85
+ /** Read an M7 fixture as a typed M7Fx. */
86
+ export function m7Fx(id) {
87
+ return adaptiveFixture(id);
88
+ }
89
+ /** Build an in-memory M7Host from the fixture input. */
90
+ export function toM7Host(fx) {
91
+ const v1Rows = fx.input.v1Rows.map((r) => ({
92
+ sessionId: r.sessionId,
93
+ label: r.label,
94
+ effectiveSeq: r.effectiveSeq,
95
+ ts: r.ts,
96
+ }));
97
+ // For inject-after-copy, the injected row carries a non-canonical label.
98
+ // migratePressureV2 calls m7Copy first, which throws M7_PRESSURE_UNKNOWN for
99
+ // the bad label — the result is the same: ok:false, pointer on v1.
100
+ if (fx.input.injectedRow) {
101
+ v1Rows.push({
102
+ sessionId: fx.input.injectedRow.sessionId,
103
+ label: fx.input.injectedRow.label,
104
+ effectiveSeq: fx.input.injectedRow.effectiveSeq,
105
+ ts: fx.input.injectedRow.ts,
106
+ });
107
+ }
108
+ const v2Rows = [];
109
+ let activeVersion = fx.input.activeVersion ?? 1;
110
+ const host = {
111
+ v1Rows: () => v1Rows,
112
+ existingV2: () => v2Rows,
113
+ putV2: (rows) => v2Rows.push(...rows),
114
+ activeVersion: () => activeVersion,
115
+ switchToV2: () => {
116
+ activeVersion = 2;
117
+ },
118
+ get activeVersionAfter() {
119
+ return activeVersion;
120
+ },
121
+ get v2RowCount() {
122
+ return v2Rows.length;
123
+ },
124
+ };
125
+ return host;
126
+ }
@@ -48,15 +48,23 @@ function generationFromEconomics(economicsVersion) {
48
48
  }
49
49
  export function detectCollision(host) {
50
50
  const byHash = new Map();
51
- const seenIdentities = new Set();
51
+ // Track the v1 hash seen for each identity so a genuine collision (same
52
+ // identity, DIFFERENT v1 hash) is distinguished from a benign duplicate
53
+ // (same identity, SAME v1 hash — an idempotent resume re-reads the same row).
54
+ const identityHash = new Map();
52
55
  for (const v1 of host.v1Rows()) {
53
56
  const row = deriveRequestHashRow(host, v1);
54
57
  const id = identity(v1.profileId, v1.requestDigest);
55
- // Two distinct v1 rows (different v1 hash) with the same v2 hash collide,
56
- // even if they share an identity — the v2 table cannot distinguish them.
57
- if (seenIdentities.has(id))
58
- return true;
59
- seenIdentities.add(id);
58
+ const priorHash = identityHash.get(id);
59
+ if (priorHash !== undefined) {
60
+ // Same identity seen before. Only a collision if the v1 hashes differ —
61
+ // two genuinely distinct v1 rows collapsing to one v2 hash. Identical
62
+ // duplicates (same hash) are benign and skipped.
63
+ if (priorHash !== v1.hash)
64
+ return true;
65
+ continue;
66
+ }
67
+ identityHash.set(id, v1.hash);
60
68
  const prior = byHash.get(row.hash);
61
69
  if (prior !== undefined && prior !== id)
62
70
  return true;
@@ -73,17 +81,25 @@ export function isGenerationInvalidated(host, row) {
73
81
  }
74
82
  export function m5Copy(host) {
75
83
  const existing = new Map(host.existingV2().map((r) => [identity(r.profileId, r.requestDigest), r]));
84
+ // Track identities already in the `wanted` batch so a benign duplicate v1
85
+ // row (same identity, same hash) does not produce a duplicate v2 write that
86
+ // would later trip COUNT_MISMATCH in verify.
87
+ const batchSeen = new Set();
76
88
  const wanted = [];
77
89
  for (const v1 of host.v1Rows()) {
78
90
  const fresh = deriveRequestHashRow(host, v1);
79
91
  if (isGenerationInvalidated(host, fresh))
80
92
  continue;
81
- const stored = existing.get(identity(v1.profileId, v1.requestDigest));
93
+ const id = identity(v1.profileId, v1.requestDigest);
94
+ if (batchSeen.has(id))
95
+ continue;
96
+ const stored = existing.get(id);
82
97
  if (stored !== undefined &&
83
98
  stored.hash === fresh.hash &&
84
99
  stored.economicsVersion === fresh.economicsVersion) {
85
100
  continue;
86
101
  }
102
+ batchSeen.add(id);
87
103
  wanted.push(fresh);
88
104
  }
89
105
  if (wanted.length > 0)
@@ -0,0 +1,98 @@
1
+ /**
2
+ * platform/cross-read.ts — VC8C neutral stdin/stdout cross-conformance framing.
3
+ *
4
+ * Both the TS reference and an external Rust binary speak ONE neutral wire
5
+ * format: a sequence of length-prefixed JSON records. Each record is:
6
+ *
7
+ * [4-byte big-endian length][exactly that many bytes of canonical JSON]
8
+ *
9
+ * The TS side encodes and decodes this framing; the external runner (a local
10
+ * subprocess, PREVENT-PI-004: never a URL) reads the same frames on stdin and
11
+ * writes them back on stdout. Parity is then byte-for-byte: the canonical JSON
12
+ * bytes of each record and the failure code must match in both directions.
13
+ *
14
+ * `outputBytes` is the OCAML-free canonical representation of a fixture's
15
+ * produced bytes as a HEX STRING, so byte-equality is a plain string compare and
16
+ * the wire JSON never carries raw binary.
17
+ *
18
+ * Everything here is PURE: no clock, no storage, no network, no flag read. The
19
+ * flag gates only the reporter seam in emit.ts.
20
+ *
21
+ * PREVENT-011: no `any` type. PREVENT-PI-004: no network.
22
+ */
23
+ import { RUST_FRAME_TRUNCATED, RUST_PARITY_MISMATCH } from "./types.js";
24
+ /** Canonical JSON bytes for a neutral record (the wire form of a record body). */
25
+ export function encodeRecordJson(record) {
26
+ return new TextEncoder().encode(JSON.stringify(record));
27
+ }
28
+ /**
29
+ * Encode a list of neutral records into the length-framed neutral wire format.
30
+ * Each record is written as a 4-byte big-endian length followed by exactly that
31
+ * many canonical JSON bytes, concatenated in order.
32
+ */
33
+ export function encodeNeutralFrame(fixtures) {
34
+ const parts = [];
35
+ for (const fixture of fixtures) {
36
+ const body = encodeRecordJson(fixture);
37
+ const header = new Uint8Array(4);
38
+ new DataView(header.buffer).setUint32(0, body.length, false);
39
+ parts.push(header, body);
40
+ }
41
+ const total = parts.reduce((acc, p) => acc + p.length, 0);
42
+ const out = new Uint8Array(total);
43
+ let offset = 0;
44
+ for (const part of parts) {
45
+ out.set(part, offset);
46
+ offset += part.length;
47
+ }
48
+ return out;
49
+ }
50
+ /**
51
+ * Decode a neutral frame stream back into records. A partial trailing frame
52
+ * (a length prefix with fewer body bytes than declared) returns
53
+ * `{ ok: false, code: "RUST_FRAME_TRUNCATED" }` — the invariant the sprint's
54
+ * unique failure injection exercises.
55
+ */
56
+ export function decodeNeutralFrame(bytes) {
57
+ const records = [];
58
+ let offset = 0;
59
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
60
+ const decoder = new TextDecoder();
61
+ while (offset + 4 <= bytes.byteLength) {
62
+ const len = view.getUint32(offset, false);
63
+ offset += 4;
64
+ if (offset + len > bytes.byteLength) {
65
+ return { ok: false, code: RUST_FRAME_TRUNCATED };
66
+ }
67
+ const body = decoder.decode(bytes.subarray(offset, offset + len));
68
+ offset += len;
69
+ try {
70
+ records.push(JSON.parse(body));
71
+ }
72
+ catch {
73
+ return { ok: false, code: RUST_FRAME_TRUNCATED };
74
+ }
75
+ }
76
+ if (offset !== bytes.byteLength) {
77
+ // Trailing bytes fewer than one length prefix — the frame was cut mid-header.
78
+ return { ok: false, code: RUST_FRAME_TRUNCATED };
79
+ }
80
+ return { ok: true, records };
81
+ }
82
+ /**
83
+ * Compare one expected record against one actual record. Both the canonical
84
+ * output bytes (as hex strings) and the failure code must match. Any byte or
85
+ * code difference returns `{ ok: false, code: "RUST_PARITY_MISMATCH" }`.
86
+ */
87
+ export function compareFixtureOutput(expected, actual) {
88
+ if (expected.fixtureId !== actual.fixtureId) {
89
+ return { ok: false, code: RUST_PARITY_MISMATCH };
90
+ }
91
+ if (expected.outputBytes !== actual.outputBytes) {
92
+ return { ok: false, code: RUST_PARITY_MISMATCH };
93
+ }
94
+ if (expected.failureCode !== actual.failureCode) {
95
+ return { ok: false, code: RUST_PARITY_MISMATCH };
96
+ }
97
+ return { ok: true };
98
+ }