pi-mega-compact 0.18.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/assets/vector-cortex/encoder-v1/manifest.json +1 -0
  2. package/assets/vector-cortex/encoder-v1/model-card.json +1 -0
  3. package/assets/vector-cortex/encoder-v1/model.onnx +0 -0
  4. package/assets/vector-cortex/encoder-v1/tokenizer.json +1 -0
  5. package/dist/config/vector-cortex.js +9 -0
  6. package/dist/config.js +1 -1
  7. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +1 -0
  8. package/dist/extensions/dashboard-server/routes-vector-cortex.js +89 -1
  9. package/dist/src/config/vector-cortex.js +9 -0
  10. package/dist/src/config.js +1 -1
  11. package/dist/src/vector-cortex/encoder/calibrate.js +174 -0
  12. package/dist/src/vector-cortex/encoder/emit-vc2c.js +59 -0
  13. package/dist/src/vector-cortex/encoder/fallback.js +62 -0
  14. package/dist/src/vector-cortex/encoder/select.js +172 -0
  15. package/dist/src/vector-cortex/encoder/types.js +31 -0
  16. package/dist/vector-cortex/encoder/calibrate.js +174 -0
  17. package/dist/vector-cortex/encoder/emit-vc2c.js +59 -0
  18. package/dist/vector-cortex/encoder/fallback.js +62 -0
  19. package/dist/vector-cortex/encoder/select.js +172 -0
  20. package/dist/vector-cortex/encoder/types.js +31 -0
  21. package/extensions/dashboard-client/dist/assets/{AreaChart-BQXFF7UI.js → AreaChart-DywU5SSt.js} +2 -2
  22. package/extensions/dashboard-client/dist/assets/{AreaChart-BQXFF7UI.js.map → AreaChart-DywU5SSt.js.map} +1 -1
  23. package/extensions/dashboard-client/dist/assets/{BarChart-BVtoz9Eg.js → BarChart-But7WN6K.js} +2 -2
  24. package/extensions/dashboard-client/dist/assets/{BarChart-BVtoz9Eg.js.map → BarChart-But7WN6K.js.map} +1 -1
  25. package/extensions/dashboard-client/dist/assets/{CacheTab-trI2TIGy.js → CacheTab-7aZWa1DR.js} +2 -2
  26. package/extensions/dashboard-client/dist/assets/{CacheTab-trI2TIGy.js.map → CacheTab-7aZWa1DR.js.map} +1 -1
  27. package/extensions/dashboard-client/dist/assets/{EventsTab-CNUbk9__.js → EventsTab-Bf_boNG2.js} +2 -2
  28. package/extensions/dashboard-client/dist/assets/{EventsTab-CNUbk9__.js.map → EventsTab-Bf_boNG2.js.map} +1 -1
  29. package/extensions/dashboard-client/dist/assets/{HealthTab-K-bd_CM-.js → HealthTab-AbGc1nXP.js} +2 -2
  30. package/extensions/dashboard-client/dist/assets/{HealthTab-K-bd_CM-.js.map → HealthTab-AbGc1nXP.js.map} +1 -1
  31. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CoEioKzx.js → MaintenanceTab-BfHHhn5F.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CoEioKzx.js.map → MaintenanceTab-BfHHhn5F.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CJo24ulv.js → MemoryMapTab-DrcWti0-.js} +2 -2
  34. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CJo24ulv.js.map → MemoryMapTab-DrcWti0-.js.map} +1 -1
  35. package/extensions/dashboard-client/dist/assets/{MetricsTab-DCNlQWWP.js → MetricsTab-ALiglXQq.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{MetricsTab-DCNlQWWP.js.map → MetricsTab-ALiglXQq.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{OverviewTab-CQQlyIQ6.js → OverviewTab-nnaJdNyR.js} +2 -2
  38. package/extensions/dashboard-client/dist/assets/{OverviewTab-CQQlyIQ6.js.map → OverviewTab-nnaJdNyR.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/{ReposTab-Cqm82_yn.js → ReposTab-BTK0nofM.js} +2 -2
  40. package/extensions/dashboard-client/dist/assets/{ReposTab-Cqm82_yn.js.map → ReposTab-BTK0nofM.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/{SessionsTab-A8xTAdET.js → SessionsTab-RXPNqO6x.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{SessionsTab-A8xTAdET.js.map → SessionsTab-RXPNqO6x.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{SetupTab-CqaSgaZl.js → SetupTab-Dab0AVfs.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{SetupTab-CqaSgaZl.js.map → SetupTab-Dab0AVfs.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-C_f3q8GR.js → TimeSavedCard-DIVG8hdt.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-C_f3q8GR.js.map → TimeSavedCard-DIVG8hdt.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{TurnsTab-BEsafACo.js → TurnsTab-9uz2fBwx.js} +2 -2
  48. package/extensions/dashboard-client/dist/assets/{TurnsTab-BEsafACo.js.map → TurnsTab-9uz2fBwx.js.map} +1 -1
  49. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-DD1CMK80.js → VectorCortexTab-DnKY61EJ.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DnKY61EJ.js.map +1 -0
  51. package/extensions/dashboard-client/dist/assets/{WikiTab-Bf01M2_Z.js → WikiTab-CpcgaggS.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{WikiTab-Bf01M2_Z.js.map → WikiTab-CpcgaggS.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{button-BEJPTk_E.js → button-B0Q2fuKY.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{button-BEJPTk_E.js.map → button-B0Q2fuKY.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/{card-DK1V382s.js → card-Cnik3TFr.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{card-DK1V382s.js.map → card-Cnik3TFr.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CRtaPphL.js → generateCategoricalChart-DrSwgn1V.js} +2 -2
  58. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CRtaPphL.js.map → generateCategoricalChart-DrSwgn1V.js.map} +1 -1
  59. package/extensions/dashboard-client/dist/assets/{index-CB6BslE4.js → index-DraytcOY.js} +3 -3
  60. package/extensions/dashboard-client/dist/assets/{index-CB6BslE4.js.map → index-DraytcOY.js.map} +1 -1
  61. package/extensions/dashboard-client/dist/assets/{switch-C4FV6B9k.js → switch-DVQ1aB9b.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{switch-C4FV6B9k.js.map → switch-DVQ1aB9b.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{toggle-GBmeENMd.js → toggle-Dkppx-49.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{toggle-GBmeENMd.js.map → toggle-Dkppx-49.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{useSSE-BOKZGSn1.js → useSSE-CPkjU4Gx.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{useSSE-BOKZGSn1.js.map → useSSE-CPkjU4Gx.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/index.html +1 -1
  68. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +2 -0
  69. package/extensions/dashboard-client/src/types/vector-cortex.ts +11 -0
  70. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +11 -0
  71. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +6 -0
  72. package/extensions/dashboard-server/routes-vector-cortex.ts +108 -1
  73. package/package.json +2 -1
  74. package/src/config/vector-cortex.ts +10 -0
  75. package/src/config.ts +1 -0
  76. package/src/vector-cortex/encoder/calibrate.ts +225 -0
  77. package/src/vector-cortex/encoder/emit-vc2c.ts +70 -0
  78. package/src/vector-cortex/encoder/fallback.ts +99 -0
  79. package/src/vector-cortex/encoder/select.ts +237 -0
  80. package/src/vector-cortex/encoder/types.ts +116 -0
  81. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD1CMK80.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"batch":1,"heads":{"cacheStability":64,"contradiction":128,"dependency":128,"payloadRouting":32,"semantic":384},"hiddenWidth":384,"maxTokens":512,"modelVersion":"encoder-v1-placeholder","onnx":{"bytes":42,"path":"model.onnx","sha256":"01cbed8b0b301609542ff8c392c3e7d927b0d848ac53a768dfffd33bfe6005ff"},"opset":17,"platform":"linux-x64","schema":"model-manifest-v1","semanticWidth":384,"tokenizer":{"bytes":8518,"path":"tokenizer.json","sha256":"ada18e5c4dfcb5c369c05f4ffc10bc40298ce707e78f16135c6d33019f6db8cd"},"totalBytes":8560,"trainingManifestDigest":"e9b2550dbb79141af84440dda65ca21d3509904b2c09a89316145137a5b5e3de"}
@@ -0,0 +1 @@
1
+ {"accepted":"local offline deterministic projection over verified asset (V1); trained weights + evaluation land in VC2B/VC2C","comparison":[{"candidate":"MiniLM-class transformer","latencyMs":8,"license":"permissive","offlineNode":"no (native addon)","quality":"high","sizeMB":120},{"candidate":"small BERT","latencyMs":10,"license":"permissive","offlineNode":"partial","quality":"high","sizeMB":110},{"candidate":"bag/subword baseline","latencyMs":1,"license":"permissive","offlineNode":"yes","quality":"medium","sizeMB":4}],"note":"placeholder asset — replaced by the VC2C export pipeline","reprintPermission":true,"schema":"model-card-v1"}
@@ -0,0 +1 @@
1
+ {"maxTokens":512,"schema":"wordpiece-tokenizer-v1","specialTokens":{"cls":2,"mask":4,"pad":0,"sep":3,"unk":1},"truncation":{"keepFirst":128,"keepLast":384},"vocab":[["[PAD]",0],["[UNK]",1],["[CLS]",2],["[SEP]",3],["[MASK]",4],["##tok0",5],["##tok1",6],["##tok2",7],["##tok3",8],["##tok4",9],["##tok5",10],["##tok6",11],["##tok7",12],["##tok8",13],["##tok9",14],["##tok10",15],["##tok11",16],["##tok12",17],["##tok13",18],["##tok14",19],["##tok15",20],["##tok16",21],["##tok17",22],["##tok18",23],["##tok19",24],["##tok20",25],["##tok21",26],["##tok22",27],["##tok23",28],["##tok24",29],["##tok25",30],["##tok26",31],["##tok27",32],["##tok28",33],["##tok29",34],["##tok30",35],["##tok31",36],["##tok32",37],["##tok33",38],["##tok34",39],["##tok35",40],["##tok36",41],["##tok37",42],["##tok38",43],["##tok39",44],["##tok40",45],["##tok41",46],["##tok42",47],["##tok43",48],["##tok44",49],["##tok45",50],["##tok46",51],["##tok47",52],["##tok48",53],["##tok49",54],["##tok50",55],["##tok51",56],["##tok52",57],["##tok53",58],["##tok54",59],["##tok55",60],["##tok56",61],["##tok57",62],["##tok58",63],["##tok59",64],["##tok60",65],["##tok61",66],["##tok62",67],["##tok63",68],["##tok64",69],["##tok65",70],["##tok66",71],["##tok67",72],["##tok68",73],["##tok69",74],["##tok70",75],["##tok71",76],["##tok72",77],["##tok73",78],["##tok74",79],["##tok75",80],["##tok76",81],["##tok77",82],["##tok78",83],["##tok79",84],["##tok80",85],["##tok81",86],["##tok82",87],["##tok83",88],["##tok84",89],["##tok85",90],["##tok86",91],["##tok87",92],["##tok88",93],["##tok89",94],["##tok90",95],["##tok91",96],["##tok92",97],["##tok93",98],["##tok94",99],["##tok95",100],["##tok96",101],["##tok97",102],["##tok98",103],["##tok99",104],["##tok100",105],["##tok101",106],["##tok102",107],["##tok103",108],["##tok104",109],["##tok105",110],["##tok106",111],["##tok107",112],["##tok108",113],["##tok109",114],["##tok110",115],["##tok111",116],["##tok112",117],["##tok113",118],["##tok114",119],["##tok115",120],["##tok116",121],["##tok117",122],["##tok118",123],["##tok119",124],["##tok120",125],["##tok121",126],["##tok122",127],["##tok123",128],["##tok124",129],["##tok125",130],["##tok126",131],["##tok127",132],["##tok128",133],["##tok129",134],["##tok130",135],["##tok131",136],["##tok132",137],["##tok133",138],["##tok134",139],["##tok135",140],["##tok136",141],["##tok137",142],["##tok138",143],["##tok139",144],["##tok140",145],["##tok141",146],["##tok142",147],["##tok143",148],["##tok144",149],["##tok145",150],["##tok146",151],["##tok147",152],["##tok148",153],["##tok149",154],["##tok150",155],["##tok151",156],["##tok152",157],["##tok153",158],["##tok154",159],["##tok155",160],["##tok156",161],["##tok157",162],["##tok158",163],["##tok159",164],["##tok160",165],["##tok161",166],["##tok162",167],["##tok163",168],["##tok164",169],["##tok165",170],["##tok166",171],["##tok167",172],["##tok168",173],["##tok169",174],["##tok170",175],["##tok171",176],["##tok172",177],["##tok173",178],["##tok174",179],["##tok175",180],["##tok176",181],["##tok177",182],["##tok178",183],["##tok179",184],["##tok180",185],["##tok181",186],["##tok182",187],["##tok183",188],["##tok184",189],["##tok185",190],["##tok186",191],["##tok187",192],["##tok188",193],["##tok189",194],["##tok190",195],["##tok191",196],["##tok192",197],["##tok193",198],["##tok194",199],["##tok195",200],["##tok196",201],["##tok197",202],["##tok198",203],["##tok199",204],["##tok200",205],["##tok201",206],["##tok202",207],["##tok203",208],["##tok204",209],["##tok205",210],["##tok206",211],["##tok207",212],["##tok208",213],["##tok209",214],["##tok210",215],["##tok211",216],["##tok212",217],["##tok213",218],["##tok214",219],["##tok215",220],["##tok216",221],["##tok217",222],["##tok218",223],["##tok219",224],["##tok220",225],["##tok221",226],["##tok222",227],["##tok223",228],["##tok224",229],["##tok225",230],["##tok226",231],["##tok227",232],["##tok228",233],["##tok229",234],["##tok230",235],["##tok231",236],["##tok232",237],["##tok233",238],["##tok234",239],["##tok235",240],["##tok236",241],["##tok237",242],["##tok238",243],["##tok239",244],["##tok240",245],["##tok241",246],["##tok242",247],["##tok243",248],["##tok244",249],["##tok245",250],["##tok246",251],["##tok247",252],["##tok248",253],["##tok249",254],["##tok250",255],["##tok251",256],["##tok252",257],["##tok253",258],["##tok254",259],["##tok255",260],["##tok256",261],["##tok257",262],["##tok258",263],["##tok259",264],["##tok260",265],["##tok261",266],["##tok262",267],["##tok263",268],["##tok264",269],["##tok265",270],["##tok266",271],["##tok267",272],["##tok268",273],["##tok269",274],["##tok270",275],["##tok271",276],["##tok272",277],["##tok273",278],["##tok274",279],["##tok275",280],["##tok276",281],["##tok277",282],["##tok278",283],["##tok279",284],["##tok280",285],["##tok281",286],["##tok282",287],["##tok283",288],["##tok284",289],["##tok285",290],["##tok286",291],["##tok287",292],["##tok288",293],["##tok289",294],["##tok290",295],["##tok291",296],["##tok292",297],["##tok293",298],["##tok294",299],["##tok295",300],["##tok296",301],["##tok297",302],["##tok298",303],["##tok299",304],["##tok300",305],["##tok301",306],["##tok302",307],["##tok303",308],["##tok304",309],["##tok305",310],["##tok306",311],["##tok307",312],["##tok308",313],["##tok309",314],["##tok310",315],["##tok311",316],["##tok312",317],["##tok313",318],["##tok314",319],["##tok315",320],["##tok316",321],["##tok317",322],["##tok318",323],["##tok319",324],["##tok320",325],["##tok321",326],["##tok322",327],["##tok323",328],["##tok324",329],["##tok325",330],["##tok326",331],["##tok327",332],["##tok328",333],["##tok329",334],["##tok330",335],["##tok331",336],["##tok332",337],["##tok333",338],["##tok334",339],["##tok335",340],["##tok336",341],["##tok337",342],["##tok338",343],["##tok339",344],["##tok340",345],["##tok341",346],["##tok342",347],["##tok343",348],["##tok344",349],["##tok345",350],["##tok346",351],["##tok347",352],["##tok348",353],["##tok349",354],["##tok350",355],["##tok351",356],["##tok352",357],["##tok353",358],["##tok354",359],["##tok355",360],["##tok356",361],["##tok357",362],["##tok358",363],["##tok359",364],["##tok360",365],["##tok361",366],["##tok362",367],["##tok363",368],["##tok364",369],["##tok365",370],["##tok366",371],["##tok367",372],["##tok368",373],["##tok369",374],["##tok370",375],["##tok371",376],["##tok372",377],["##tok373",378],["##tok374",379],["##tok375",380],["##tok376",381],["##tok377",382],["##tok378",383],["##tok379",384],["##tok380",385],["##tok381",386],["##tok382",387],["##tok383",388],["##tok384",389],["##tok385",390],["##tok386",391],["##tok387",392],["##tok388",393],["##tok389",394],["##tok390",395],["##tok391",396],["##tok392",397],["##tok393",398],["##tok394",399],["##tok395",400],["##tok396",401],["##tok397",402],["##tok398",403],["##tok399",404],["##tok400",405],["##tok401",406],["##tok402",407],["##tok403",408],["##tok404",409],["##tok405",410],["##tok406",411],["##tok407",412],["##tok408",413],["##tok409",414],["##tok410",415],["##tok411",416],["##tok412",417],["##tok413",418],["##tok414",419],["##tok415",420],["##tok416",421],["##tok417",422],["##tok418",423],["##tok419",424],["##tok420",425],["##tok421",426],["##tok422",427],["##tok423",428],["##tok424",429],["##tok425",430],["##tok426",431],["##tok427",432],["##tok428",433],["##tok429",434],["##tok430",435],["##tok431",436],["##tok432",437],["##tok433",438],["##tok434",439],["##tok435",440],["##tok436",441],["##tok437",442],["##tok438",443],["##tok439",444],["##tok440",445],["##tok441",446],["##tok442",447],["##tok443",448],["##tok444",449],["##tok445",450],["##tok446",451],["##tok447",452],["##tok448",453],["##tok449",454],["##tok450",455],["##tok451",456],["##tok452",457],["##tok453",458],["##tok454",459],["##tok455",460],["##tok456",461],["##tok457",462],["##tok458",463],["##tok459",464],["##tok460",465],["##tok461",466],["##tok462",467],["##tok463",468],["##tok464",469],["##tok465",470],["##tok466",471],["##tok467",472],["##tok468",473],["##tok469",474],["##tok470",475],["##tok471",476],["##tok472",477],["##tok473",478],["##tok474",479],["##tok475",480],["##tok476",481],["##tok477",482],["##tok478",483],["##tok479",484],["##tok480",485],["##tok481",486],["##tok482",487],["##tok483",488],["##tok484",489],["##tok485",490],["##tok486",491],["##tok487",492],["##tok488",493],["##tok489",494],["##tok490",495],["##tok491",496],["##tok492",497],["##tok493",498],["##tok494",499],["##tok495",500],["##tok496",501],["##tok497",502],["##tok498",503],["##tok499",504]]}
@@ -82,6 +82,15 @@ export const VC2A_ENABLED = () => sprintFlag("MEGACOMPACT_VC2A");
82
82
  * emit seam and the multi-head encoder producers (heads/trigram/lexical).
83
83
  */
84
84
  export const VC2B_ENABLED = () => sprintFlag("MEGACOMPACT_VC2B");
85
+ /**
86
+ * VC2C — encoder qualification + calibration (QualifiedEncoderV1 / CalibrationV1).
87
+ * Default ON. `MEGACOMPACT_VC2C=0` disables and is byte-identical to the
88
+ * predecessor (mode C: no qualification manifest is read or selected, the
89
+ * calibrate/select/fallback seams are idle, zero `vector_cortex_encoder_qualification_*`
90
+ * emissions; the trigram/lexical paths are unchanged). The real consumers are
91
+ * the encoder-qualification emit seam and the calibrate/select seams.
92
+ */
93
+ export const VC2C_ENABLED = () => sprintFlag("MEGACOMPACT_VC2C");
85
94
  // ---------------------------------------------------------------------------
86
95
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
87
96
  // 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, 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, 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";
@@ -142,6 +142,7 @@ export const SETTINGS = [
142
142
  boolDirect("MEGACOMPACT_VC1C", "VC1C Cross-Language Conformance v2", "FixtureManifestV2 canonical manifest validator + DowngradeReport deterministic downgrade export + MinHashV2 exact big-integer signatures and the M4 copy/validate/switch minhash-v2 migration (seed table frozen, cross-language byte-exact). OFF = mode C, v1 sync dedup scan unchanged, byte-identical.", true),
143
143
  boolDirect("MEGACOMPACT_VC2A", "VC2A Offline Model Runtime", "ModelManifestV1 digest-before-load ONNX runtime (opset17/batch1/max512) + asset-free trigram demotion. Asset path assets/vector-cortex/encoder-v1 is immutable/digest-pinned. OFF = mode C, byte-identical to predecessor.", true),
144
144
  boolDirect("MEGACOMPACT_VC2B", "VC2B Multi-Head Encoder", "VectorSetV1 five L2-normalized heads (384/128/128/64/32) with head-calibration draft + asset-free trigram B (512d) and lexical C fallbacks, plus the per-head emit seam. OFF = mode C, no per-head vectors emitted, byte-identical predecessor.", true),
145
+ boolDirect("MEGACOMPACT_VC2C", "VC2C Encoder Qualification + Calibration", "QualifiedEncoderV1/CalibrationV1: calibration fit on the calibration split only (held-out labels prohibited) + atomic selection across MODEL_ASSET and per-head EVALUATION thresholds (any field failure demotes all of A). OFF = mode C, no qualification/calibration selection, byte-identical predecessor.", true),
145
146
  ],
146
147
  },
147
148
  {
@@ -14,11 +14,16 @@
14
14
  * Guardrails: PREVENT-PI-004 (local filesystem read only), PREVENT-011 (no
15
15
  * `any`), reader-only aggregate (never payloads/prompts/ledger — EVAL-REDACT-002).
16
16
  */
17
- import { VC0A_ENABLED, VC0C_ENABLED, VC1B_ENABLED } from "../../src/config.js";
17
+ import { createHash } from "node:crypto";
18
+ import { readFileSync, statSync } from "node:fs";
19
+ import { join, dirname } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ import { VC0A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC2C_ENABLED } from "../../src/config.js";
18
22
  import { readEvalRows } from "../../src/vector-cortex/eval/persist.js";
19
23
  import { summarizeEvalRows } from "../../src/vector-cortex/eval/reader.js";
20
24
  import { createVectorCortexSafety } from "../mega-runtime/vector-cortex-safety.js";
21
25
  import { createLedgerStore } from "../../src/vector-cortex/ledger/store.js";
26
+ import { readEncoderManifest, verifyEncoderAsset, detectPlatform } from "../../src/vector-cortex/encoder/asset.js";
22
27
  /** Admin capability: an actor-supplied subsystem selector for breaker reset. */
23
28
  function readJsonBody(req, cb) {
24
29
  let body = "";
@@ -50,6 +55,84 @@ function sendJson(res, status, body) {
50
55
  res.writeHead(status, { "Content-Type": "application/json" });
51
56
  res.end(JSON.stringify(body));
52
57
  }
58
+ /** Resolve the committed encoder-v1 asset dir by walking up to the repo root. */
59
+ function encoderAssetDir() {
60
+ let dir = dirname(fileURLToPath(import.meta.url));
61
+ const rel = join("assets", "vector-cortex", "encoder-v1");
62
+ for (let i = 0; i < 8; i++) {
63
+ const candidate = join(dir, rel);
64
+ try {
65
+ // guardrails-allow PREVENT-PI-004: local asset filesystem read (loopback)
66
+ readFileSync(join(candidate, "manifest.json"));
67
+ return candidate;
68
+ }
69
+ catch {
70
+ /* keep walking */
71
+ }
72
+ const next = dirname(dir);
73
+ if (next === dir)
74
+ break;
75
+ dir = next;
76
+ }
77
+ return null;
78
+ }
79
+ let healthCache = null;
80
+ /** Stable cache key: manifest bytes + (size,mtime) of the asset files + platform. */
81
+ function healthFactCacheKey(dir, manifestPath) {
82
+ const platform = detectPlatform();
83
+ if (!platform)
84
+ return null;
85
+ const parts = [platform];
86
+ try {
87
+ for (const p of [manifestPath, join(dir, "model.onnx"), join(dir, "tokenizer.json")]) {
88
+ const s = statSync(p);
89
+ parts.push(`${s.size}:${s.mtimeMs}`);
90
+ }
91
+ // guardrails-allow PREVENT-PI-004: local manifest filesystem read (loopback)
92
+ parts.push(createHash("sha256").update(readFileSync(manifestPath)).digest("hex"));
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ return parts.join("|");
98
+ }
99
+ function computeEncoderHealthFacts(dir) {
100
+ const manifest = readEncoderManifest(dir);
101
+ if (manifest === null)
102
+ return { assetDigest: null, mode: "C" };
103
+ const manifestPath = join(dir, "manifest.json");
104
+ const key = healthFactCacheKey(dir, manifestPath);
105
+ if (key === null)
106
+ return null;
107
+ if (healthCache !== null && healthCache.key === key)
108
+ return healthCache.facts;
109
+ let digest;
110
+ try {
111
+ // guardrails-allow PREVENT-PI-004: local manifest filesystem read (loopback)
112
+ digest = createHash("sha256").update(readFileSync(manifestPath)).digest("hex");
113
+ }
114
+ catch {
115
+ return { assetDigest: null, mode: "B" };
116
+ }
117
+ const verify = verifyEncoderAsset(dir, manifest, detectPlatform());
118
+ const facts = { assetDigest: digest, mode: verify.ok ? "A" : "B" };
119
+ healthCache = { key, facts };
120
+ return facts;
121
+ }
122
+ function encoderHealthFacts() {
123
+ if (!VC2C_ENABLED()) {
124
+ // Rollback: MEGACOMPACT_VC2C=0 selects C — report the inactive (flag-gated)
125
+ // mode with no asset digest rather than an on-disk-computed A/B.
126
+ return { assetDigest: null, mode: "C" };
127
+ }
128
+ const dir = encoderAssetDir();
129
+ if (dir === null)
130
+ return { assetDigest: null, mode: "C" };
131
+ const facts = computeEncoderHealthFacts(dir);
132
+ if (facts === null)
133
+ return { assetDigest: null, mode: "C" };
134
+ return facts;
135
+ }
53
136
  /** Reader-only aggregate GET /api/vector-cortex/evaluation. */
54
137
  export function handleVectorCortexEvaluation(req, res, ctx) {
55
138
  const url = req.url ?? "";
@@ -107,6 +190,7 @@ export function handleVectorCortexHealth(req, res, ctx) {
107
190
  const enabled = VC0C_ENABLED();
108
191
  const safety = enabled ? createVectorCortexSafety({ stateDir: ctx.stateDir }) : null;
109
192
  const card = safety ? safety.health() : null;
193
+ const enc = encoderHealthFacts();
110
194
  const fallback = {
111
195
  enabled: false,
112
196
  mode: "C",
@@ -126,6 +210,8 @@ export function handleVectorCortexHealth(req, res, ctx) {
126
210
  updatedAt: new Date().toISOString(),
127
211
  aggregate: "CLOSED_A",
128
212
  stateSource: "ephemeral",
213
+ encoderAssetDigest: enc.assetDigest,
214
+ encoderMode: enc.mode,
129
215
  };
130
216
  if (!card) {
131
217
  sendJson(res, 200, fallback);
@@ -161,6 +247,8 @@ export function handleVectorCortexHealth(req, res, ctx) {
161
247
  updatedAt: new Date().toISOString(),
162
248
  aggregate,
163
249
  stateSource: card.stateSource,
250
+ encoderAssetDigest: enc.assetDigest,
251
+ encoderMode: enc.mode,
164
252
  };
165
253
  sendJson(res, 200, body);
166
254
  return true;
@@ -82,6 +82,15 @@ export const VC2A_ENABLED = () => sprintFlag("MEGACOMPACT_VC2A");
82
82
  * emit seam and the multi-head encoder producers (heads/trigram/lexical).
83
83
  */
84
84
  export const VC2B_ENABLED = () => sprintFlag("MEGACOMPACT_VC2B");
85
+ /**
86
+ * VC2C — encoder qualification + calibration (QualifiedEncoderV1 / CalibrationV1).
87
+ * Default ON. `MEGACOMPACT_VC2C=0` disables and is byte-identical to the
88
+ * predecessor (mode C: no qualification manifest is read or selected, the
89
+ * calibrate/select/fallback seams are idle, zero `vector_cortex_encoder_qualification_*`
90
+ * emissions; the trigram/lexical paths are unchanged). The real consumers are
91
+ * the encoder-qualification emit seam and the calibrate/select seams.
92
+ */
93
+ export const VC2C_ENABLED = () => sprintFlag("MEGACOMPACT_VC2C");
85
94
  // ---------------------------------------------------------------------------
86
95
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
87
96
  // 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, 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, 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,174 @@
1
+ /**
2
+ * vector-cortex/encoder/calibrate.ts — VC2C calibration fit (task 2).
3
+ *
4
+ * Fits a `CalibrationV1` using ONLY the calibration split. Held-out
5
+ * (test/eval) labels are STRICTLY PROHIBITED from the fit inputs: the fit
6
+ * function rejects (ENC_QUALIFICATION_HELD_OUT_IN_FIT) any example whose
7
+ * `itemId` appears in the caller's held-out set. Ties in example score are
8
+ * broken deterministically by item ID (stable score/id ties), never by arrival
9
+ * order, so the fit is invariant to row order.
10
+ *
11
+ * The calibration split assignment is grouped by repository+session
12
+ * (EVALUATION.md §corpus): every label-bearing item carries a `repository` +
13
+ * `session` group, and the split digest is the canonical SHA-256 over the
14
+ * sorted group list. A single repo/session group NEVER crosses split boundaries
15
+ * (the caller seeds a group wholly into the calibration split or not at all).
16
+ *
17
+ * The fit itself is a deterministic seeded per-head temperature + threshold
18
+ * calibration over the calibration-only examples (real isotonic/Platt weights
19
+ * land with trained weights; the contract, split isolation, held-out
20
+ * prohibition, stable ties, and frozen-temperature/threshold surface are all
21
+ * normative here). Pi-agnostic, zero network (PREVENT-PI-004), no `any`
22
+ * (PREVENT-011).
23
+ */
24
+ import { createHash } from "node:crypto";
25
+ import { ENCODER_HEAD_ORDER, ENCODER_SEED, ENC_QUALIFICATION_FAIL, } from "./types.js";
26
+ /** Canonical digests of a sorted stable representation (order-invariant). */
27
+ function digestStrings(values) {
28
+ const sorted = [...values].sort();
29
+ return createHash("sha256").update(sorted.join("\n")).digest("hex");
30
+ }
31
+ /**
32
+ * Compute the calibration split digest for a set of (repository, session) groups
33
+ * present in the fit. The group list is canonicalized (sorted, deduped) so the
34
+ * digest is invariant to row order within the input. A caller that seeds WHOLE
35
+ * groups into the calibration split guarantees no group crosses a boundary.
36
+ */
37
+ /**
38
+ * Render one (repository, session) group as an injective canonical string.
39
+ * Each field is length-prefixed (`<len>:<value>`), so two distinct pairs can
40
+ * never collide to the same rendering — e.g. `{r:"a", s:"b::c"}` renders as
41
+ * `1:a3:b::c` while `{r:"a::b", s:"c"}` renders as `4:a::b1:c`. A plain
42
+ * `repository::session` join would conflate those when identifiers happen to
43
+ * contain "::"; length-prefixing makes the split digest sound for arbitrary
44
+ * repository/session identifiers.
45
+ */
46
+ function renderGroup(g) {
47
+ return `${g.repository.length}:${g.repository}${g.session.length}:${g.session}`;
48
+ }
49
+ export function calibrationSplitDigest(groups) {
50
+ const rendered = new Set();
51
+ for (const g of groups)
52
+ rendered.add(renderGroup(g));
53
+ return digestStrings([...rendered]);
54
+ }
55
+ /** A deterministic 32-bit LCG step (matches the heads/runtime projectors). */
56
+ function nextState(state) {
57
+ return (state * 1664525 + 1013904223) >>> 0;
58
+ }
59
+ /**
60
+ * Deterministic per-head temperature in a stable, healthy range (e.g. 0.8..1.5).
61
+ *
62
+ * The caller-supplied `seed` is mixed into the LCG state through independent
63
+ * steps so it ALWAYS affects the temperature — even when it equals ENCODER_SEED.
64
+ * (A naive `ENCODER_SEED ^ head.length ^ seed` cancels the two seed terms when
65
+ * `seed === ENCODER_SEED`, leaving a pure function of the head — the default
66
+ * path would make the seed option inert. Here the seed seeds the PRNG first,
67
+ * then the head name is folded in, so both vary the fit independently.) The fit
68
+ * stays deterministic for a fixed (seed, head) across processes.
69
+ */
70
+ function fitTemperature(head, seed) {
71
+ let state = (seed >>> 0) ^ 0x9e3779b9;
72
+ state = nextState(state);
73
+ state = (state ^ (head.length >>> 0)) >>> 0;
74
+ state = nextState(state);
75
+ state = (state ^ 0x85ebca6b) >>> 0;
76
+ state = nextState(state);
77
+ const r = (state / 4294967296) % 1;
78
+ return 0.8 + r * 0.7; // 0.8 .. 1.5
79
+ }
80
+ /**
81
+ * Deterministic per-head decision threshold derived from the calibration
82
+ * distribution. For a head with BOTH classes present the threshold is the
83
+ * midpoint between the highest-scoring negative (label 0) and the lowest-scoring
84
+ * positive (label 1) — a true between-class balance point that NEVER lands on a
85
+ * negative example's own score (code-review Q03): a future inference at the
86
+ * highest calibration negative is still classified negative, and one at the
87
+ * lowest calibration positive is still classified positive. Degenerate heads with
88
+ * a single class fall back conservatively (no positives -> just above the top
89
+ * observed score; no negatives -> just below the lowest observed positive) and an
90
+ * empty head defaults to 0.5. Scoring is order-invariant: ties in score resolve by
91
+ * item ID bytewise (stable score/id ties), never by arrival order, so the fit is
92
+ * invariant to row order. This frozen threshold is a normative placeholder (real
93
+ * trained weights land later).
94
+ */
95
+ function fitThreshold(head, examples) {
96
+ const headEx = examples
97
+ .filter((e) => e.head === head)
98
+ .slice()
99
+ .sort((a, b) => (a.score - b.score) || (a.itemId < b.itemId ? -1 : a.itemId > b.itemId ? 1 : 0));
100
+ if (headEx.length === 0)
101
+ return 0.5;
102
+ let highestNeg = -Infinity;
103
+ let lowestPos = Infinity;
104
+ for (const e of headEx) {
105
+ if (e.label === 0)
106
+ highestNeg = Math.max(highestNeg, e.score);
107
+ else
108
+ lowestPos = Math.min(lowestPos, e.score);
109
+ }
110
+ if (lowestPos === Infinity) {
111
+ // Only negatives observed — no positive class to balance against. Set the
112
+ // threshold just above the top observed score so no observed negative is
113
+ // re-admitted (a genuine future positive must exceed all calibration negatives).
114
+ return Math.max(0.5, highestNeg + 0.05);
115
+ }
116
+ if (highestNeg === -Infinity) {
117
+ // Only positives observed — set the threshold just below the lowest observed
118
+ // positive so every observed positive is admitted.
119
+ return Math.max(0, lowestPos - 0.05);
120
+ }
121
+ // Both classes present: the midpoint strictly between the highest negative and
122
+ // the lowest positive is the between-class balance point (Q03).
123
+ return (highestNeg + lowestPos) / 2;
124
+ }
125
+ /**
126
+ * Fit `CalibrationV1` over the calibration split only (task 2).
127
+ *
128
+ * - rejects any item whose `itemId` is in `heldOutIds` (held-out labels are
129
+ * prohibited from fit inputs).
130
+ * - treats `groups` as the calibration split units; the emitted split digest
131
+ * covers only the groups ACTUALLY present in the fit examples (plus the
132
+ * declared `groups`, when supplied — see below).
133
+ * - stable score/id ties (never arrival order).
134
+ *
135
+ * Held-out labels are prohibited by construction: the caller passes the full set
136
+ * of held-out item IDs, and the fit fails loudly if any calibration input is
137
+ * actually a held-out item — the fit can never silently learn from review labels.
138
+ */
139
+ export function fitCalibration(examples, options = {}) {
140
+ const seed = options.seed ?? ENCODER_SEED;
141
+ const heldOut = new Set(options.heldOutIds ?? []);
142
+ for (const e of examples) {
143
+ if (heldOut.has(e.itemId)) {
144
+ return {
145
+ ok: false,
146
+ code: ENC_QUALIFICATION_FAIL.HELD_OUT_IN_FIT,
147
+ reason: `held-out item ${e.itemId} leaked into calibration fit`,
148
+ };
149
+ }
150
+ }
151
+ // Stable order by (score, itemId) — arrival order never affects the digest or
152
+ // the fit; ties resolve by item ID bytewise (stable score/id ties).
153
+ const stable = examples
154
+ .slice()
155
+ .sort((a, b) => (a.score - b.score) || (a.itemId < b.itemId ? -1 : a.itemId > b.itemId ? 1 : 0));
156
+ const usedGroups = options.groups ?? stable.map((e) => ({ repository: e.repository, session: e.session }));
157
+ const splitDigest = calibrationSplitDigest(usedGroups);
158
+ const temperatures = {};
159
+ const thresholds = {};
160
+ for (const head of ENCODER_HEAD_ORDER) {
161
+ temperatures[head] = fitTemperature(head, seed);
162
+ thresholds[head] = fitThreshold(head, stable);
163
+ }
164
+ const calibration = {
165
+ schema: "calibration-v1",
166
+ headOrder: [...ENCODER_HEAD_ORDER],
167
+ calibrationSplitDigest: splitDigest,
168
+ fittedOnCalibrationOnly: true,
169
+ temperatures,
170
+ thresholds,
171
+ seed,
172
+ };
173
+ return { ok: true, calibration };
174
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * vector-cortex/encoder/emit-vc2c.ts — VC2C observability seam.
3
+ *
4
+ * Owns the two VC2C events (task 5), gated on `MEGACOMPACT_VC2C` so the
5
+ * flag-OFF path emits zero events (mode C parity, byte-identical predecessor):
6
+ *
7
+ * vector_cortex_encoder_qualification_passed — a QualifiedEncoderV1 produced (A)
8
+ * vector_cortex_encoder_qualification_demoted — qualification failed; all of A demoted
9
+ *
10
+ * Every event is a JSON line with `ts` + `event` (ENGINEERING_PRACTICES §8); the
11
+ * `ts` is the numeric epoch-ms timestamp the `Logger` injects (LogEntry.ts is
12
+ * `number`), so these events are consistent with the rest of the log stream
13
+ * (code-review Q04 — no ISO-string ts override). The emitters are non-fatal
14
+ * (never break the agent loop). Pi-agnostic, zero network (PREVENT-PI-004), no
15
+ * `any` (PREVENT-011).
16
+ */
17
+ import { VC2C_ENABLED } from "../../config/vector-cortex.js";
18
+ import { Logger } from "../../log.js";
19
+ /**
20
+ * The default emitter: routes both VC2C events into the append-only structured
21
+ * logger (`src/log.ts`) as JSON lines with `event` (the logger injects the
22
+ * numeric `ts`). Supplying `emit:` to `createEncoderQualificationReporter`
23
+ * replaces this with a caller-provided sink (used by tests and downstream
24
+ * consumers). Making the default a REAL producer means a caller that just
25
+ * invokes the producer seam (`selectQualifiedEncoder`) without injecting an
26
+ * emitter still yields structured telemetry instead of silently dropping every
27
+ * event (task 5). Best-effort: the logger swallows all I/O errors.
28
+ */
29
+ function defaultEmitFor() {
30
+ const logger = new Logger();
31
+ return (event, fields) => {
32
+ logger.info(event, fields);
33
+ };
34
+ }
35
+ /**
36
+ * Flag-gated emit, defaulting to a real logger-backed sink. The returned
37
+ * reporter is itself flag-gated (`VC2C_ENABLED`), so wiring it into a producer
38
+ * seam yields zero emissions when `MEGACOMPACT_VC2C=0` (byte-identical to the
39
+ * predecessor). Pass an explicit `emit` to route elsewhere (tests, downstream
40
+ * consumers); omit it to emit real structured log lines. The `ts` is set by the
41
+ * underlying sink (numeric epoch-ms from `Logger`), not overridden here (Q04).
42
+ */
43
+ export function createEncoderQualificationReporter(emit) {
44
+ const sink = emit ?? defaultEmitFor();
45
+ const fire = (event, fields) => {
46
+ if (!VC2C_ENABLED())
47
+ return;
48
+ try {
49
+ sink(event, fields);
50
+ }
51
+ catch {
52
+ /* non-fatal observability */
53
+ }
54
+ };
55
+ return {
56
+ qualificationPassed: (fields) => fire("vector_cortex_encoder_qualification_passed", fields),
57
+ qualificationDemoted: (fields) => fire("vector_cortex_encoder_qualification_demoted", fields),
58
+ };
59
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * vector-cortex/encoder/fallback.ts — VC2C qualification fallback selection.
3
+ *
4
+ * After a qualification demotion (qualify/select fails to produce mode A), this
5
+ * seam selects the mode-B/C fallback and routes the encode request to the
6
+ * independently initialized VC2B producers (trigram B / lexical C). It is the
7
+ * breaker-recovery handoff for the encoder triad:
8
+ *
9
+ * A = fully qualified learned asset (QualifiedEncoderV1).
10
+ * B = asset-free trigram, forced by ANY one failed qualification threshold
11
+ * (or a qualification-manifest digest mismatch).
12
+ * C = token/phrase lexical, forced when A is absent AND B itself errors
13
+ * (injected B error), or when the caller forces C.
14
+ *
15
+ * Breaker recovery follows TRIAD_RESILIENCE: a demotion may probe/promote, never
16
+ * directly re-qualify A; the fallback only ever SELECTS B or C locally, never a
17
+ * remote fetch (PREVENT-PI-004). C states its loss of old semantic context.
18
+ *
19
+ * Pi-agnostic, zero network (PREVENT-PI-004), no `any` (PREVENT-011).
20
+ */
21
+ import { createEncoderHeadsReporter } from "./emit-vc2b.js";
22
+ import { embedTrigram512, selectTrigramBFallback, ENCODER_TRIGRAM_WIDTH } from "./trigram.js";
23
+ import { embedLexical, selectLexicalC, ENCODER_LEXICAL_WIDTH, ENCODER_LEXICAL_LIMITATION } from "./lexical.js";
24
+ /** Deterministic text derived from an int token sequence (mirrors router.ts). */
25
+ function textFromTokens(tokens) {
26
+ return tokens.join("-");
27
+ }
28
+ /**
29
+ * Select the B/C fallback for a failed qualification and produce the encode
30
+ * vector. When `injectBError` or `forceC` is set, selection lands on lexical C
31
+ * (absent A + injected B error -> C); otherwise a qualification THRESHOLD or
32
+ * DIGEST mismatch selects trigram B. Never returns without an explicit verdict.
33
+ */
34
+ export function selectQualificationFallback(qualificationCode, tokens, options = {}) {
35
+ const reporter = options.reporter ?? createEncoderHeadsReporter();
36
+ const tokensText = textFromTokens(tokens);
37
+ // A absent + injected/forced B error -> C (lexical) with the documented
38
+ // semantic-context limitation.
39
+ if (options.forceC || options.injectBError) {
40
+ selectLexicalC({ reporter });
41
+ const vector = embedLexical(tokensText);
42
+ return {
43
+ ok: true,
44
+ mode: "C",
45
+ vector,
46
+ width: ENCODER_LEXICAL_WIDTH,
47
+ code: options.injectBError ? "ENC_B_INJECTED_ERROR" : qualificationCode,
48
+ limitation: ENCODER_LEXICAL_LIMITATION,
49
+ };
50
+ }
51
+ // A qualification demotion selects the independently initialized trigram B.
52
+ selectTrigramBFallback({ reporter });
53
+ const vector = embedTrigram512(tokensText);
54
+ return {
55
+ ok: true,
56
+ mode: "B",
57
+ vector,
58
+ width: ENCODER_TRIGRAM_WIDTH,
59
+ code: qualificationCode,
60
+ limitation: null,
61
+ };
62
+ }