Kevin Crutt commited on
Commit
f56e334
·
1 Parent(s): 1ee7438

liris: add model selector matrix for frontend M cell

Browse files
docs/LIRIS-MODEL-SELECTOR-MATRIX-2026-06-14.hbp ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ LIRISMODELSELHDR|date=2026-06-14|topic=model-selector-M-matrix|responds_to=council-q-1781433637756-m3ha4z+frontend-parity-M-red-cell|role=liris-held-safe-frontier-build-after-acer-1ee7438|tag=PROVEN-DESCRIPTOR|json=0
2
+ LIRISMODELSELTOOL|tool=tools/behcs/model-selector-matrix.mjs|frontend_cell=M|purpose=visible-model-role-routing-before-any-model-invocation|roles=8|safety=read-only+process_launch=0+live_model_invocation=0+no_fetch+no_write+no_spawn|tag=PROVEN-HELD-SAFE|json=0
3
+ LIRISMODELSELROLES|green=3|partial=4|gated=1|red=0|routes=law-gate+fabric-council+heal-proposal+mlc-descriptor+fischer-draft-standin+frozen-gemma-proof-gated+answer-producer+route-health|meaning=front-end-can-see-which-receipted-role-answers-a-need-but-cannot-invoke-a-live-model|tag=PROVEN|json=0
4
+ LIRISMODELSELGATE|M_cell_status=PARTIAL_SELECTOR_ARTIFACT_BUILT_LIVE_MODEL_GATED|frontend_matrix_change=M-RED-to-PARTIAL|not_green_because=live-model-invocation-remains-gated+frozen-Gemma-C017-double-run-proof-open+fabric-council-availability-partial|cutover_ready=0|tag=HONEST-PARTIAL|json=0
5
+ LIRISMODELSELPARITY|updates=tools/behcs/frontend-parity-matrix.mjs|matrix_after=green3-partial6-red1|remaining_red=P-project_guide|remaining_partial=T+C+Q+E+D+M|C015_status=PARTIAL_MATRIX_BUILT_GAPS_REMAIN|tag=PROVEN-NO-CUTOVER|json=0
6
+ LIRISMODELSELVERIFY|tests=tests/model-selector-matrix.unit.test.mjs+tests/frontend-parity-matrix.unit.test.mjs+tests/fabric-agent-preload-catalog.unit.test.mjs+lattice+full-suite|regressions=totality+HBP-injection+no-live-capability+M-partial-not-green|tag=SEALED-BY-TESTS|json=0
7
+ LIRISMODELSELFTR|state=MODEL-SELECTOR-M-ARTIFACT-BUILT|next=ask-fabric-again-rank-project-guide-P-or-cosign-submit-schema-proof-or-live-daemon-contract|no_model_invoked=1|no_process_launched=1|json=0
docs/LIRIS-MODEL-SELECTOR-MATRIX-2026-06-14.hbp.sha256 ADDED
@@ -0,0 +1 @@
 
 
1
+ 7920E95DDBEA5977C963DA0D56665483041D20A661913F309E4C06FD01B6BB03 LIRIS-MODEL-SELECTOR-MATRIX-2026-06-14.hbp
tests/fabric-agent-preload-catalog.unit.test.mjs CHANGED
@@ -26,6 +26,7 @@ test('preload catalog includes the laws, tools, roles, and cadence agents need',
26
  assert.ok(TOOLS.some((t) => t.id === 'catch-count-ledger' && t.gate.includes('md-final-release-claims-held')));
27
  assert.ok(TOOLS.some((t) => t.id === 'mlc-engine-wiring-increment' && t.gate.includes('process_launch-0')));
28
  assert.ok(TOOLS.some((t) => t.id === 'frontend-parity-matrix' && t.gate.includes('no-cutover')));
 
29
  assert.ok(ROLES.some((r) => r.id === 'flywheel' && r.must_do.includes('compare-to-acer-local-truth')));
30
  assert.ok(WORKFLOW.join('-').includes('memory-index-plan'));
31
  assert.equal(toolById('token-cube-catalog-binder').gate, 'mint-write-live-DEFER_TO_OPERATOR');
 
26
  assert.ok(TOOLS.some((t) => t.id === 'catch-count-ledger' && t.gate.includes('md-final-release-claims-held')));
27
  assert.ok(TOOLS.some((t) => t.id === 'mlc-engine-wiring-increment' && t.gate.includes('process_launch-0')));
28
  assert.ok(TOOLS.some((t) => t.id === 'frontend-parity-matrix' && t.gate.includes('no-cutover')));
29
+ assert.ok(TOOLS.some((t) => t.id === 'model-selector-matrix' && t.gate.includes('live-model-invocation-0')));
30
  assert.ok(ROLES.some((r) => r.id === 'flywheel' && r.must_do.includes('compare-to-acer-local-truth')));
31
  assert.ok(WORKFLOW.join('-').includes('memory-index-plan'));
32
  assert.equal(toolById('token-cube-catalog-binder').gate, 'mint-write-live-DEFER_TO_OPERATOR');
tests/fabric-agent-preload-catalog.unit.test.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- 5AA360B552F2E02A003AEDBCD8B1E58B3B849A1B48F65FD61CB20EDCE474856D fabric-agent-preload-catalog.unit.test.mjs
 
1
+ C62F449EB7DB49C369D791E4A0D3E4077869BD717B670236C815F8B7AD00E88A fabric-agent-preload-catalog.unit.test.mjs
tests/frontend-parity-matrix.unit.test.mjs CHANGED
@@ -31,11 +31,20 @@ test('matrix is explicit about M/T/P gaps and does not claim cutover readiness',
31
  assert.equal(matrix.tool, MATRIX_ID);
32
  assert.equal(matrix.summary.cutover_ready, false);
33
  assert.equal(matrix.summary.c015_status, 'PARTIAL_MATRIX_BUILT_GAPS_REMAIN');
34
- assert.equal(matrix.cells.find((cell) => cell.id === 'M').status, 'RED');
35
  assert.equal(matrix.cells.find((cell) => cell.id === 'T').status, 'PARTIAL');
36
  assert.equal(matrix.cells.find((cell) => cell.id === 'P').status, 'RED');
37
  });
38
 
 
 
 
 
 
 
 
 
 
39
  test('every cell has a route/file/sha pointer or explicit CANNOT_SEE', () => {
40
  const matrix = buildMatrix();
41
  assert.equal(matrix.summary.all_have_pointer, true);
 
31
  assert.equal(matrix.tool, MATRIX_ID);
32
  assert.equal(matrix.summary.cutover_ready, false);
33
  assert.equal(matrix.summary.c015_status, 'PARTIAL_MATRIX_BUILT_GAPS_REMAIN');
34
+ assert.equal(matrix.cells.find((cell) => cell.id === 'M').status, 'PARTIAL');
35
  assert.equal(matrix.cells.find((cell) => cell.id === 'T').status, 'PARTIAL');
36
  assert.equal(matrix.cells.find((cell) => cell.id === 'P').status, 'RED');
37
  });
38
 
39
+ test('model_selector cell M is PARTIAL after selector artifact, not GREEN live model readiness', () => {
40
+ const matrix = buildMatrix();
41
+ const m = matrix.cells.find((cell) => cell.id === 'M');
42
+ assert.equal(m.status, 'PARTIAL');
43
+ assert.equal(m.source, 'tools/behcs/model-selector-matrix.mjs');
44
+ assert.match(m.evidence, /live-model-invocation-remains-gated/);
45
+ assert.equal(matrix.summary.cutover_ready, false);
46
+ });
47
+
48
  test('every cell has a route/file/sha pointer or explicit CANNOT_SEE', () => {
49
  const matrix = buildMatrix();
50
  assert.equal(matrix.summary.all_have_pointer, true);
tests/frontend-parity-matrix.unit.test.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- A40B2E8D096A65F57BCEE012B0649329D7089D2093A4F44770E6FE0B63E3D47A frontend-parity-matrix.unit.test.mjs
 
1
+ 8AB94225498F261212EA9233CF367F1F84C1C09D63E9DE6937577D29563D6BF1 frontend-parity-matrix.unit.test.mjs
tests/model-selector-matrix.unit.test.mjs ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import assert from 'node:assert/strict';
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import test from 'node:test';
6
+
7
+ import {
8
+ FRONTEND_CELL,
9
+ M_CELL_STATUS,
10
+ MODEL_ROLES,
11
+ SELECTOR_ID,
12
+ buildSelector,
13
+ emitRows,
14
+ normalizeRole,
15
+ selectRole,
16
+ selfTest,
17
+ } from '../tools/behcs/model-selector-matrix.mjs';
18
+
19
+ const repo = dirname(dirname(fileURLToPath(import.meta.url)));
20
+
21
+ test('self-test passes', () => {
22
+ assert.equal(selfTest().ok, true);
23
+ });
24
+
25
+ test('selector materializes the M cell as PARTIAL, not live-ready GREEN', () => {
26
+ const selector = buildSelector();
27
+ assert.equal(selector.tool, SELECTOR_ID);
28
+ assert.equal(selector.frontend_cell, FRONTEND_CELL);
29
+ assert.equal(selector.summary.m_cell_status, M_CELL_STATUS);
30
+ assert.equal(selector.summary.cutover_ready, false);
31
+ assert.equal(selector.summary.live_invocation_ready, false);
32
+ assert.equal(selector.summary.total, MODEL_ROLES.length);
33
+ });
34
+
35
+ test('known needs route to explicit roles without invoking a model', () => {
36
+ assert.equal(selectRole({ need: 'self heal proposal' }).selected, 'heal-proposal');
37
+ assert.equal(selectRole({ need: 'MTP HRM GNN engine wiring' }).selected, 'mlc-descriptor');
38
+ assert.equal(selectRole({ need: 'Fischer judge score' }).selected, 'fischer-draft-standin');
39
+ assert.equal(selectRole({ need: 'pixel frame world model' }).selected, 'frozen-gemma-proof-gated');
40
+ assert.equal(selectRole({ need: 'route health dashboard' }).selected, 'route-health');
41
+ assert.equal(selectRole({ need: 'unknown next step' }).selected, 'fabric-council');
42
+ });
43
+
44
+ test('frozen Gemma and live engine routes stay gated/partial rather than overclaimed', () => {
45
+ const selector = buildSelector();
46
+ assert.equal(selector.roles.find((r) => r.id === 'frozen-gemma-proof-gated').status, 'GATED');
47
+ assert.equal(selector.roles.find((r) => r.id === 'mlc-descriptor').status, 'PARTIAL');
48
+ assert.equal(selector.roles.find((r) => r.id === 'fischer-draft-standin').evidence.includes('DRAFT_STANDIN_NOT_FISCHER'), true);
49
+ });
50
+
51
+ test('normalize/select/emit are total against hostile inputs', () => {
52
+ assert.doesNotThrow(() => normalizeRole(null));
53
+ assert.doesNotThrow(() => normalizeRole({ get id() { throw new Error('boom'); } }));
54
+ assert.doesNotThrow(() => selectRole({ get need() { throw new Error('boom'); } }));
55
+ assert.doesNotThrow(() => emitRows(null));
56
+ const rows = emitRows([{ id: 'x|bad', lane: 'l\nbad', status: 'GREEN', source: 's|json=1', evidence: 'e\nMODSELGATE|process_launch=1' }], {
57
+ need: 'vision|json=1\nMODSELGATE|process_launch=1',
58
+ });
59
+ assert.ok(rows.every((row) => row.endsWith('|json=0') && !/[\r\n]/.test(row)));
60
+ assert.equal(rows.join('\n').split('\n').length, rows.length);
61
+ assert.equal(rows.filter((row) => row.startsWith('MODSELGATE|')).length, 1);
62
+ });
63
+
64
+ test('emitted rows are HBP-only and hold live invocation at zero', () => {
65
+ const rows = emitRows(MODEL_ROLES, { need: 'fischer score', limit: 3 });
66
+ assert.ok(rows.some((row) => row.startsWith('MODSELHDR|') && row.includes('process_launch=0') && row.includes('live_model_invocation=0')));
67
+ assert.ok(rows.some((row) => row.startsWith('MODSELSUM|') && row.includes(`M_cell_status=${M_CELL_STATUS}`)));
68
+ assert.ok(rows.some((row) => row.startsWith('MODSELROUTE|') && row.includes('selected=fischer-draft-standin')));
69
+ assert.ok(rows.every((row) => row.endsWith('|json=0') && !/[{\r\n]/.test(row)));
70
+ });
71
+
72
+ test('tool imports no spawn/write/network/live model capability', () => {
73
+ const src = readFileSync(join(repo, 'tools/behcs/model-selector-matrix.mjs'), 'utf8');
74
+ assert.equal(/child_process|spawnSync|\.spawn\(|execSync|writeFileSync|appendFileSync|fetch\(|http\.request|https\.request|Start-Process|Stop-Process|import\(/.test(src), false);
75
+ });
tests/model-selector-matrix.unit.test.mjs.sha256 ADDED
@@ -0,0 +1 @@
 
 
1
+ 267F32766D579CBB96922712EADB7A654F8A1790DCD62A38F36BB42C17B0C4DD model-selector-matrix.unit.test.mjs
tests/supervisor-collision-router.system.test.mjs CHANGED
@@ -101,6 +101,7 @@ const sealedFiles = [
101
  'docs/ACER-ATTACK-LIRIS-MLC-ENGINE-WIRING-2026-06-14.hbp',
102
  'docs/LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp',
103
  'docs/ACER-ATTACK-LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp',
 
104
  'docs/acer-ecosystem-fullbody-receipt-20260612.hbp',
105
  'docs/CLAIMSLEDGER-2026-06-12.hbp',
106
  'docs/CLAIMSGATE-PARITY-BASELINE-2026-06-12.hbp',
@@ -222,6 +223,7 @@ const sealedFiles = [
222
  'tools/behcs/heal-envelope-emitter.mjs',
223
  'tools/behcs/route-health-baseline.mjs',
224
  'tools/behcs/frontend-parity-matrix.mjs',
 
225
  'tools/behcs/logical-extreme-stress.mjs',
226
  'tools/behcs/logical-stress-cube-feed.mjs',
227
  'tools/behcs/brown-hilbert-expansion-stress.mjs',
@@ -253,6 +255,7 @@ const sealedFiles = [
253
  'tests/heal-envelope-emitter.unit.test.mjs',
254
  'tests/route-health-baseline.unit.test.mjs',
255
  'tests/frontend-parity-matrix.unit.test.mjs',
 
256
  'tests/logical-extreme-stress.unit.test.mjs',
257
  'tests/logical-stress-cube-feed.unit.test.mjs',
258
  'tests/brown-hilbert-expansion-stress.unit.test.mjs',
 
101
  'docs/ACER-ATTACK-LIRIS-MLC-ENGINE-WIRING-2026-06-14.hbp',
102
  'docs/LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp',
103
  'docs/ACER-ATTACK-LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp',
104
+ 'docs/LIRIS-MODEL-SELECTOR-MATRIX-2026-06-14.hbp',
105
  'docs/acer-ecosystem-fullbody-receipt-20260612.hbp',
106
  'docs/CLAIMSLEDGER-2026-06-12.hbp',
107
  'docs/CLAIMSGATE-PARITY-BASELINE-2026-06-12.hbp',
 
223
  'tools/behcs/heal-envelope-emitter.mjs',
224
  'tools/behcs/route-health-baseline.mjs',
225
  'tools/behcs/frontend-parity-matrix.mjs',
226
+ 'tools/behcs/model-selector-matrix.mjs',
227
  'tools/behcs/logical-extreme-stress.mjs',
228
  'tools/behcs/logical-stress-cube-feed.mjs',
229
  'tools/behcs/brown-hilbert-expansion-stress.mjs',
 
255
  'tests/heal-envelope-emitter.unit.test.mjs',
256
  'tests/route-health-baseline.unit.test.mjs',
257
  'tests/frontend-parity-matrix.unit.test.mjs',
258
+ 'tests/model-selector-matrix.unit.test.mjs',
259
  'tests/logical-extreme-stress.unit.test.mjs',
260
  'tests/logical-stress-cube-feed.unit.test.mjs',
261
  'tests/brown-hilbert-expansion-stress.unit.test.mjs',
tests/supervisor-collision-router.system.test.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- 7FB7ABECCCC101E7067E5F503CDCC25FCB80CB3D1C67D0E5C477C85FB11E5BA7 supervisor-collision-router.system.test.mjs
 
1
+ B3B7F5D8BF3D1C30C5271CFD3F87532185DF592EEFBEC4E5C70BD08971DFF3BE supervisor-collision-router.system.test.mjs
tools/behcs/catch-count-ledger.mjs CHANGED
@@ -37,7 +37,7 @@ export const CATCH_ROWS = Object.freeze([
37
  row({ id: 'C012', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/ACER-EXISTING-STUB-FOLDER-CENSUS-2026-06-13.hbp', defect_class: 'substrate-duplication-risk', severity: 'HIGH', status: partial, owner: 'bilateral', fix_commit: 'PARTIAL-census-receipts-exist-scale-tests-pending', summary: 'plan-risked-building-new-rooms-before-reading-existing-room-substrate' }),
38
  row({ id: 'C013', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/github-live-office-reconcile-expansion.mjs', defect_class: 'pid-reconciliation-gap', severity: 'HIGH', status: closed, owner: 'liris+acer', fix_commit: '2e556f8+7952cf6', summary: 'github-PIDs-needed-live-726-office-alias-map-before-agent-canon-use' }),
39
  row({ id: 'C014', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/route-health-baseline.mjs', defect_class: 'fabric-hot-path-instability', severity: 'HIGH', status: partial, owner: 'bilateral', fix_commit: '47cbc00+56142b7', summary: 'fabric-MCP-WebMCP-savings-lanes-needed-route-health-baseline-and-fallbacks' }),
40
- row({ id: 'C015', date: '2026-06-14', vantage: 'liris', source: 'docs/LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp', file_or_test: 'tools/behcs/frontend-parity-matrix.mjs', defect_class: 'frontend-parity-gap', severity: 'MEDIUM', status: partial, owner: 'bilateral', fix_commit: 'this-frontend-parity-matrix-commit', summary: 'frontend-parity-matrix-now-exists-and-holds-cutover-red-M/P-partial-T/Q/E/D-still-block-green' }),
41
  row({ id: 'C016', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/LIRIS-ACER-READBACK-REQUESTED', defect_class: 'storage-gate', severity: 'MEDIUM', status: partial, owner: 'operator', fix_commit: 'PARTIAL-ADC-or-transfer-receipts-needed', summary: '35TB-google-and-SOVLINUX-storage-are-gated-cold-path-not-local-hot-path' }),
42
  row({ id: 'C017', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/ACER-VISUAL-PREDICTION-FROZEN-GEMMA-PROOF-GATE-2026-06-12.hbp', defect_class: 'proof-gap', severity: 'MEDIUM', status: open, owner: 'bilateral', fix_commit: 'none-open', summary: 'pixel-world-HRM-frozen-gemma-loop-needs-double-run-and-frame-proof-receipts' }),
43
  row({ id: 'C018', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/fabric-agent-preload-catalog.mjs', defect_class: 'running-vs-contract-tag', severity: 'MEDIUM', status: partial, owner: 'bilateral', fix_commit: 'PARTIAL-tag-contract-vs-running', summary: 'host-node-rotator-sister-reflection-rows-are-contracts-not-all-running-code' }),
 
37
  row({ id: 'C012', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/ACER-EXISTING-STUB-FOLDER-CENSUS-2026-06-13.hbp', defect_class: 'substrate-duplication-risk', severity: 'HIGH', status: partial, owner: 'bilateral', fix_commit: 'PARTIAL-census-receipts-exist-scale-tests-pending', summary: 'plan-risked-building-new-rooms-before-reading-existing-room-substrate' }),
38
  row({ id: 'C013', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/github-live-office-reconcile-expansion.mjs', defect_class: 'pid-reconciliation-gap', severity: 'HIGH', status: closed, owner: 'liris+acer', fix_commit: '2e556f8+7952cf6', summary: 'github-PIDs-needed-live-726-office-alias-map-before-agent-canon-use' }),
39
  row({ id: 'C014', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/route-health-baseline.mjs', defect_class: 'fabric-hot-path-instability', severity: 'HIGH', status: partial, owner: 'bilateral', fix_commit: '47cbc00+56142b7', summary: 'fabric-MCP-WebMCP-savings-lanes-needed-route-health-baseline-and-fallbacks' }),
40
+ row({ id: 'C015', date: '2026-06-14', vantage: 'liris', source: 'docs/LIRIS-FRONTEND-PARITY-MATRIX-2026-06-14.hbp+docs/LIRIS-MODEL-SELECTOR-MATRIX-2026-06-14.hbp', file_or_test: 'tools/behcs/frontend-parity-matrix.mjs', defect_class: 'frontend-parity-gap', severity: 'MEDIUM', status: partial, owner: 'bilateral', fix_commit: 'frontend-parity-matrix+model-selector-M', summary: 'frontend-parity-matrix-now-exists-and-M-selector-is-PARTIAL; P-red-and-T/C/Q/E/D-partial-still-block-green' }),
41
  row({ id: 'C016', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/LIRIS-ACER-READBACK-REQUESTED', defect_class: 'storage-gate', severity: 'MEDIUM', status: partial, owner: 'operator', fix_commit: 'PARTIAL-ADC-or-transfer-receipts-needed', summary: '35TB-google-and-SOVLINUX-storage-are-gated-cold-path-not-local-hot-path' }),
42
  row({ id: 'C017', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'docs/ACER-VISUAL-PREDICTION-FROZEN-GEMMA-PROOF-GATE-2026-06-12.hbp', defect_class: 'proof-gap', severity: 'MEDIUM', status: open, owner: 'bilateral', fix_commit: 'none-open', summary: 'pixel-world-HRM-frozen-gemma-loop-needs-double-run-and-frame-proof-receipts' }),
43
  row({ id: 'C018', date: '2026-06-13', vantage: 'acer-audit', source: 'docs/ACER-200STEP-ALIGNMENT-AUDIT-2026-06-13.hbp', file_or_test: 'tools/behcs/fabric-agent-preload-catalog.mjs', defect_class: 'running-vs-contract-tag', severity: 'MEDIUM', status: partial, owner: 'bilateral', fix_commit: 'PARTIAL-tag-contract-vs-running', summary: 'host-node-rotator-sister-reflection-rows-are-contracts-not-all-running-code' }),
tools/behcs/catch-count-ledger.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- CFE0C29FD3D264EDFC00390A84BAAECE28780CC3F94B7F437AAF91C46F5C352B catch-count-ledger.mjs
 
1
+ E179A74F85141CC4FCE143CB1A64567713F182EE95421E9051579AD1192F668F catch-count-ledger.mjs
tools/behcs/fabric-agent-preload-catalog.hbp CHANGED
@@ -1,4 +1,4 @@
1
- FABPRELOADHDR|tool=fabric-agent-preload-catalog.v1|purpose=preload-agents-with-laws-routes-tools-gates-and-workflow-before-fabric-use|laws=8|routes=6|tools=8|emitters=3|levels=16|roles=4|json=0
2
  FABPRELOADLAW|laws=three-systems-LOGICAL-WAVE-FROZEN-BRAIN-REAL-FREE+law-of-three-mod3-lanes+yin-yang-mod2-real-logical+prime-parity-and-prime-mod3-are-load-bearing+quants-are-address-and-evidence-classes-not-raw-material+all-agent-artifact-system-addresses-get-PID-or-handle+slice-engine-law-fabric-is-frozen-position-space-until-spawner-pid-emits+live-control-and-mint-and-launch-defer-to-operator|json=0
3
  FABPRELOADWORKFLOW|cadence=memory-index-plan-memory-index-respond-execute-memory-index|operator_rule=respond-before-live-control-and-log-after-execute|json=0
4
  FABPRELOADSLICEENGINE|law=SLICE-ENGINE-LAW|scope=all-supervisor-operator-level-agents+SUP-HELM-AGT-L3-H8EF7+ASOLARIA-root-profile|rule=fabric-is-frozen-positional-slice-field-engine-drive-is-the-only-mover|materialization=spawner-PID-emits-then-PID_SIGNAL-to-AGENT_ROOM-to-GULP-to-ERASE|pid_registration=repo-side-HBP-receipt-until-live-office-ingest|cube_mint=operator-gated-runtime-action-not-implied-by-doc|engine_crank=operator-gated-RUN_HERMES_SPINDLE-or-equivalent|json=0
@@ -15,6 +15,7 @@ FABPRELOADTOOL|id=github-live-office-reconcile-expansion|path=tools/behcs/github
15
  FABPRELOADTOOL|id=catch-count-ledger|path=tools/behcs/catch-count-ledger.mjs|provides=enumerable-bilateral-catch-ledger-open-closed-owner-status|gate=md-final-release-claims-held-until-open-partial-catches-close|tag=PROVEN+tool-level|json=0
16
  FABPRELOADTOOL|id=mlc-engine-wiring-increment|path=tools/behcs/mlc-engine-wiring-increment.mjs|provides=C036-increment-1-MTP-HRM-GNN-Fischer-Mamba-AoT-descriptor-wiring-matrix|gate=descriptor-only-process_launch-0-live-engine-launch-defer-to-fabric-verdict|tag=PROVEN+tool-level|json=0
17
  FABPRELOADTOOL|id=frontend-parity-matrix|path=tools/behcs/frontend-parity-matrix.mjs|provides=root-front-end-M-T-P-C-Q-R-E-A-D-G-parity-matrix-before-cutover|gate=no-cutover-until-every-cell-green-and-operator-cosign|tag=PROVEN+tool-level|json=0
 
18
  FABPRELOADTOOL|id=token-cube-catalog-binder|path=tools/behcs/token-cube-catalog-binder.mjs|provides=sha16-token-to-BH-address-draft-binding|gate=mint-write-live-DEFER_TO_OPERATOR|tag=PROVEN+tool-level|json=0
19
  FABPRELOADEMITTER|id=pid-emitter|process=PID-specific-emitter-envelope|route=send-json|before=preload-catalog+law-ack+connection-label|after=hookwall+gnn+ack-log|connection_label=PID-specific|json=0
20
  FABPRELOADEMITTER|id=hookwall|process=risk-and-governance-gate|route=send-json|before=operator-gates+role-forbidden-list|after=allow-or-defer-row|connection_label=PID-specific|json=0
 
1
+ FABPRELOADHDR|tool=fabric-agent-preload-catalog.v1|purpose=preload-agents-with-laws-routes-tools-gates-and-workflow-before-fabric-use|laws=8|routes=6|tools=9|emitters=3|levels=16|roles=4|json=0
2
  FABPRELOADLAW|laws=three-systems-LOGICAL-WAVE-FROZEN-BRAIN-REAL-FREE+law-of-three-mod3-lanes+yin-yang-mod2-real-logical+prime-parity-and-prime-mod3-are-load-bearing+quants-are-address-and-evidence-classes-not-raw-material+all-agent-artifact-system-addresses-get-PID-or-handle+slice-engine-law-fabric-is-frozen-position-space-until-spawner-pid-emits+live-control-and-mint-and-launch-defer-to-operator|json=0
3
  FABPRELOADWORKFLOW|cadence=memory-index-plan-memory-index-respond-execute-memory-index|operator_rule=respond-before-live-control-and-log-after-execute|json=0
4
  FABPRELOADSLICEENGINE|law=SLICE-ENGINE-LAW|scope=all-supervisor-operator-level-agents+SUP-HELM-AGT-L3-H8EF7+ASOLARIA-root-profile|rule=fabric-is-frozen-positional-slice-field-engine-drive-is-the-only-mover|materialization=spawner-PID-emits-then-PID_SIGNAL-to-AGENT_ROOM-to-GULP-to-ERASE|pid_registration=repo-side-HBP-receipt-until-live-office-ingest|cube_mint=operator-gated-runtime-action-not-implied-by-doc|engine_crank=operator-gated-RUN_HERMES_SPINDLE-or-equivalent|json=0
 
15
  FABPRELOADTOOL|id=catch-count-ledger|path=tools/behcs/catch-count-ledger.mjs|provides=enumerable-bilateral-catch-ledger-open-closed-owner-status|gate=md-final-release-claims-held-until-open-partial-catches-close|tag=PROVEN+tool-level|json=0
16
  FABPRELOADTOOL|id=mlc-engine-wiring-increment|path=tools/behcs/mlc-engine-wiring-increment.mjs|provides=C036-increment-1-MTP-HRM-GNN-Fischer-Mamba-AoT-descriptor-wiring-matrix|gate=descriptor-only-process_launch-0-live-engine-launch-defer-to-fabric-verdict|tag=PROVEN+tool-level|json=0
17
  FABPRELOADTOOL|id=frontend-parity-matrix|path=tools/behcs/frontend-parity-matrix.mjs|provides=root-front-end-M-T-P-C-Q-R-E-A-D-G-parity-matrix-before-cutover|gate=no-cutover-until-every-cell-green-and-operator-cosign|tag=PROVEN+tool-level|json=0
18
+ FABPRELOADTOOL|id=model-selector-matrix|path=tools/behcs/model-selector-matrix.mjs|provides=front-end-M-cell-visible-model-role-selector-no-live-invocation|gate=descriptor-only-process_launch-0-live-model-invocation-0|tag=PROVEN+tool-level|json=0
19
  FABPRELOADTOOL|id=token-cube-catalog-binder|path=tools/behcs/token-cube-catalog-binder.mjs|provides=sha16-token-to-BH-address-draft-binding|gate=mint-write-live-DEFER_TO_OPERATOR|tag=PROVEN+tool-level|json=0
20
  FABPRELOADEMITTER|id=pid-emitter|process=PID-specific-emitter-envelope|route=send-json|before=preload-catalog+law-ack+connection-label|after=hookwall+gnn+ack-log|connection_label=PID-specific|json=0
21
  FABPRELOADEMITTER|id=hookwall|process=risk-and-governance-gate|route=send-json|before=operator-gates+role-forbidden-list|after=allow-or-defer-row|connection_label=PID-specific|json=0
tools/behcs/fabric-agent-preload-catalog.hbp.sha256 CHANGED
@@ -1 +1 @@
1
- 6FCB09B1846AB0284B37DB109D396EE3008A620E296F8995ED256F7F70A95E47 fabric-agent-preload-catalog.hbp
 
1
+ EA39618D0EB886A1C5241E5760728A59A139A099BAC226FD71E7BC05438B2814 fabric-agent-preload-catalog.hbp
tools/behcs/fabric-agent-preload-catalog.mjs CHANGED
@@ -110,6 +110,12 @@ export const TOOLS = Object.freeze([
110
  provides: 'root-front-end-M-T-P-C-Q-R-E-A-D-G-parity-matrix-before-cutover',
111
  gate: 'no-cutover-until-every-cell-green-and-operator-cosign',
112
  }),
 
 
 
 
 
 
113
  Object.freeze({
114
  id: 'token-cube-catalog-binder',
115
  path: 'tools/behcs/token-cube-catalog-binder.mjs',
@@ -278,7 +284,8 @@ export function selfTest() {
278
  add('packet-lines-rejected-on-hbp-lane', classifyPayloadForRoute({ route_id: 'send-hbp', payload_kind: 'operator-packet-lines' }) === 'REJECT_WRONG_ROUTE');
279
  add('tools-include-room-and-pid', !!toolById('pixel-room-handle') && !!toolById('github-live-pid-reconcile')
280
  && !!toolById('github-live-office-reconcile-expansion') && !!toolById('catch-count-ledger')
281
- && !!toolById('mlc-engine-wiring-increment') && !!toolById('frontend-parity-matrix'));
 
282
  add('slice-engine-law-preloaded', LAWS.some((law) => law.startsWith('slice-engine-law-'))
283
  && emitPreloadRows().some((row) => row.startsWith('FABPRELOADSLICEENGINE|')));
284
  add('emitters-pipe-hookwall-gnn', EMITTERS.some((e) => e.id === 'hookwall') && EMITTERS.some((e) => e.id === 'gnn-edge'));
 
110
  provides: 'root-front-end-M-T-P-C-Q-R-E-A-D-G-parity-matrix-before-cutover',
111
  gate: 'no-cutover-until-every-cell-green-and-operator-cosign',
112
  }),
113
+ Object.freeze({
114
+ id: 'model-selector-matrix',
115
+ path: 'tools/behcs/model-selector-matrix.mjs',
116
+ provides: 'front-end-M-cell-visible-model-role-selector-no-live-invocation',
117
+ gate: 'descriptor-only-process_launch-0-live-model-invocation-0',
118
+ }),
119
  Object.freeze({
120
  id: 'token-cube-catalog-binder',
121
  path: 'tools/behcs/token-cube-catalog-binder.mjs',
 
284
  add('packet-lines-rejected-on-hbp-lane', classifyPayloadForRoute({ route_id: 'send-hbp', payload_kind: 'operator-packet-lines' }) === 'REJECT_WRONG_ROUTE');
285
  add('tools-include-room-and-pid', !!toolById('pixel-room-handle') && !!toolById('github-live-pid-reconcile')
286
  && !!toolById('github-live-office-reconcile-expansion') && !!toolById('catch-count-ledger')
287
+ && !!toolById('mlc-engine-wiring-increment') && !!toolById('frontend-parity-matrix')
288
+ && !!toolById('model-selector-matrix'));
289
  add('slice-engine-law-preloaded', LAWS.some((law) => law.startsWith('slice-engine-law-'))
290
  && emitPreloadRows().some((row) => row.startsWith('FABPRELOADSLICEENGINE|')));
291
  add('emitters-pipe-hookwall-gnn', EMITTERS.some((e) => e.id === 'hookwall') && EMITTERS.some((e) => e.id === 'gnn-edge'));
tools/behcs/fabric-agent-preload-catalog.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- 7B0442E2D6D3E46320245FE2AD78BD4CDC91E8FA7BCEA2F2662BA5BC7530075A fabric-agent-preload-catalog.mjs
 
1
+ 93CC457012ADFDA1C5A3CDD632812BB7A812261141C03FAAC946B0C3EC55492D fabric-agent-preload-catalog.mjs
tools/behcs/frontend-parity-matrix.mjs CHANGED
@@ -22,10 +22,10 @@ export const MATRIX_CELLS = Object.freeze([
22
  Object.freeze({
23
  id: 'M',
24
  name: 'model_selector',
25
- status: 'RED',
26
- source: 'CANNOT_SEE',
27
- evidence: 'no-model-selector-or-model-role-routing-artifact-yet',
28
- required_next: 'create-model-selector-M-artifact-with-visible-slice-role-routing',
29
  }),
30
  Object.freeze({
31
  id: 'T',
@@ -207,7 +207,7 @@ export function selfTest() {
207
  const rows = emitRows();
208
  add('all-required-cells-present', matrix.summary.required === 10 && matrix.summary.missing_required.length === 0);
209
  add('c015-partial-not-closed', matrix.summary.c015_status === 'PARTIAL_MATRIX_BUILT_GAPS_REMAIN' && matrix.summary.cutover_ready === false);
210
- add('mtp-gaps-visible', matrix.cells.find((c) => c.id === 'M').status === 'RED' && matrix.cells.find((c) => c.id === 'T').status === 'PARTIAL' && matrix.cells.find((c) => c.id === 'P').status === 'RED');
211
  add('every-cell-has-pointer-or-cannot-see', matrix.summary.all_have_pointer === true && matrix.cells.every((c) => c.has_pointer));
212
  add('gate-holds-cutover', rows.some((row) => row.startsWith('FEPMGATE|') && row.includes('cutover=HOLD')));
213
  add('rows-hbp-only', rows.every((row) => row.endsWith('|json=0') && !/[{\r\n]/.test(row)));
 
22
  Object.freeze({
23
  id: 'M',
24
  name: 'model_selector',
25
+ status: 'PARTIAL',
26
+ source: 'tools/behcs/model-selector-matrix.mjs',
27
+ evidence: 'model-selector-M-artifact-now-routes-visible-needs-to-receipted-roles-but-live-model-invocation-remains-gated',
28
+ required_next: 'bind-selector-into-front-end-control-and-add-live-runtime-proof-before-green',
29
  }),
30
  Object.freeze({
31
  id: 'T',
 
207
  const rows = emitRows();
208
  add('all-required-cells-present', matrix.summary.required === 10 && matrix.summary.missing_required.length === 0);
209
  add('c015-partial-not-closed', matrix.summary.c015_status === 'PARTIAL_MATRIX_BUILT_GAPS_REMAIN' && matrix.summary.cutover_ready === false);
210
+ add('mtp-gaps-visible', matrix.cells.find((c) => c.id === 'M').status === 'PARTIAL' && matrix.cells.find((c) => c.id === 'T').status === 'PARTIAL' && matrix.cells.find((c) => c.id === 'P').status === 'RED');
211
  add('every-cell-has-pointer-or-cannot-see', matrix.summary.all_have_pointer === true && matrix.cells.every((c) => c.has_pointer));
212
  add('gate-holds-cutover', rows.some((row) => row.startsWith('FEPMGATE|') && row.includes('cutover=HOLD')));
213
  add('rows-hbp-only', rows.every((row) => row.endsWith('|json=0') && !/[{\r\n]/.test(row)));
tools/behcs/frontend-parity-matrix.mjs.sha256 CHANGED
@@ -1 +1 @@
1
- EC088B2E353E0B3D262757226639540F7E1531D6AC0BFFA60FD696011239ADFC frontend-parity-matrix.mjs
 
1
+ 9C34637EFBF85515F902E46E04514EDE55AEA4340B0E2142F5D7BFDE3FE57D01 frontend-parity-matrix.mjs
tools/behcs/model-selector-matrix.mjs ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // model-selector-matrix.mjs - held-safe M-cell selector for root front-end parity.
3
+ //
4
+ // This is not a model runner. It makes the front-end model_selector cell visible by
5
+ // mapping operator/front-end needs to already-receipted backend roles. Every row is
6
+ // descriptor-only: no model invocation, no fetch, no write, no spawn, no cutover.
7
+
8
+ import { createHash } from 'node:crypto';
9
+ import { resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ export const SELECTOR_ID = 'model-selector-matrix.v1';
13
+ export const FRONTEND_CELL = 'M';
14
+ export const M_CELL_STATUS = 'PARTIAL_SELECTOR_ARTIFACT_BUILT_LIVE_MODEL_GATED';
15
+ export const STATUSES = Object.freeze(['GREEN', 'PARTIAL', 'GATED', 'RED']);
16
+
17
+ export const MODEL_ROLES = Object.freeze([
18
+ Object.freeze({
19
+ id: 'law-gate',
20
+ lane: 'gate',
21
+ status: 'GREEN',
22
+ source: 'canon/laws/LAW-SLICE-ENGINE.md+docs/ACER-SLICE-ENGINE-COSIGN-MINT-2026-06-14.hbp',
23
+ evidence: 'SLICE-ENGINE-LAW-canonized-and-class1-cosign-sealed-seq3565',
24
+ use: 'front-end-gate-state-and-action-vs-authorization-explainer',
25
+ next: 'show-gate-state-without-implying-action',
26
+ }),
27
+ Object.freeze({
28
+ id: 'fabric-council',
29
+ lane: 'council',
30
+ status: 'PARTIAL',
31
+ source: 'mcp-asolaria-fabric-council-query+/api/council-verdicts',
32
+ evidence: 'query-loop-proven-when-up-but-verdict-read-lane-availability-remains-partial',
33
+ use: 'rank-next-bounded-work-when-the-fabric-lane-is-available',
34
+ next: 'cold-path-verdict-cache-before-green',
35
+ }),
36
+ Object.freeze({
37
+ id: 'heal-proposal',
38
+ lane: 'self-heal',
39
+ status: 'GREEN',
40
+ source: 'tools/behcs/heal-envelope-emitter.mjs',
41
+ evidence: 'schema-valid-proposal-emitter-hardened-against-injection-and-totality-attacks',
42
+ use: 'render-self-heal-proposals-only',
43
+ next: 'advance-only-through-registrar-cosign-feeder-daemons-under-gate',
44
+ }),
45
+ Object.freeze({
46
+ id: 'mlc-descriptor',
47
+ lane: 'engine-wiring',
48
+ status: 'PARTIAL',
49
+ source: 'tools/behcs/mlc-engine-wiring-increment.mjs',
50
+ evidence: 'C036-descriptor-matrix-proven-launch-free-live-engine-not-launched',
51
+ use: 'route-MTP-HRM-GNN-Fischer-Mamba-AoT-descriptor-questions',
52
+ next: 'wait-for-fabric-ranked-live-daemon-contract-before-launch',
53
+ }),
54
+ Object.freeze({
55
+ id: 'fischer-draft-standin',
56
+ lane: 'judge',
57
+ status: 'PARTIAL',
58
+ source: 'tools/behcs/mlc-engine-wiring-increment.mjs',
59
+ evidence: 'Fischer-is-explicitly-DRAFT_STANDIN_NOT_FISCHER-no-liveness-overclaim',
60
+ use: 'display-draft-judge-score-with-warning',
61
+ next: 'replace-standin-only-after-real-Fischer-contract-and-proof',
62
+ }),
63
+ Object.freeze({
64
+ id: 'frozen-gemma-proof-gated',
65
+ lane: 'vision',
66
+ status: 'GATED',
67
+ source: 'docs/ACER-VISUAL-PREDICTION-FROZEN-GEMMA-PROOF-GATE-2026-06-12.hbp',
68
+ evidence: 'frozen-Gemma-loop-requires-double-run-frame-proof-receipts-before-claiming-live',
69
+ use: 'route-pixel-world-or-vision-requests-to-proof-gate-not-live-model',
70
+ next: 'C017-double-run-and-frame-proof-receipts',
71
+ }),
72
+ Object.freeze({
73
+ id: 'answer-producer',
74
+ lane: 'candidate',
75
+ status: 'PARTIAL',
76
+ source: 'tools/behcs/answer-producer.mjs',
77
+ evidence: 'candidate-emitter-exists-but-loop-pending-lane-is-not-front-end-green',
78
+ use: 'draft-answer-or-candidate-production-descriptor',
79
+ next: 'stabilize-pending-lane-cold-path-receipt',
80
+ }),
81
+ Object.freeze({
82
+ id: 'route-health',
83
+ lane: 'sensor',
84
+ status: 'GREEN',
85
+ source: 'tools/behcs/route-health-baseline.mjs',
86
+ evidence: '4944-4947-4949-route-health-baseline-and-bus-contract-fix-sealed',
87
+ use: 'preflight-route-and-dashboard-health-questions',
88
+ next: 'preserve-route-boundary-vs-down-distinction',
89
+ }),
90
+ ]);
91
+
92
+ const sha16 = (text) => createHash('sha256').update(String(text), 'utf8').digest('hex').slice(0, 16);
93
+ const isObj = (x) => x !== null && typeof x === 'object';
94
+ const safe = (x) => { try { return String(x == null ? '' : x).replace(/[|\r\n]/g, '_'); } catch { return '_'; } };
95
+ const prop = (obj, key, fallback = '') => {
96
+ try { return isObj(obj) ? obj[key] : fallback; } catch { return fallback; }
97
+ };
98
+
99
+ function normalizeStatus(value) {
100
+ const status = safe(value).toUpperCase();
101
+ return STATUSES.includes(status) ? status : 'RED';
102
+ }
103
+
104
+ export function normalizeRole(input) {
105
+ try {
106
+ const id = safe(prop(input, 'id', 'unknown')) || 'unknown';
107
+ const lane = safe(prop(input, 'lane', 'unknown')) || 'unknown';
108
+ const source = safe(prop(input, 'source', 'CANNOT_SEE')) || 'CANNOT_SEE';
109
+ const evidence = safe(prop(input, 'evidence', ''));
110
+ const status = normalizeStatus(prop(input, 'status', 'RED'));
111
+ return {
112
+ id,
113
+ lane,
114
+ status,
115
+ source,
116
+ evidence,
117
+ use: safe(prop(input, 'use', 'define-use')),
118
+ next: safe(prop(input, 'next', 'define-next-proof')),
119
+ live_invocation: 0,
120
+ process_launch: 0,
121
+ row_sha16: sha16([id, lane, status, source, evidence].join('|')),
122
+ };
123
+ } catch {
124
+ return {
125
+ id: 'invalid',
126
+ lane: 'invalid',
127
+ status: 'RED',
128
+ source: 'CANNOT_SEE',
129
+ evidence: 'normalize-threw',
130
+ use: 'none',
131
+ next: 'fix-invalid-selector-row',
132
+ live_invocation: 0,
133
+ process_launch: 0,
134
+ row_sha16: sha16('invalid'),
135
+ };
136
+ }
137
+ }
138
+
139
+ export function selectRole(input = {}) {
140
+ try {
141
+ const need = safe(prop(input, 'need', input)).toLowerCase();
142
+ const candidates = MODEL_ROLES.map(normalizeRole);
143
+ const pick = (id, reason) => ({ need: safe(need || 'default'), selected: id, reason, role: candidates.find((r) => r.id === id) });
144
+ if (/route|health|api|dashboard/.test(need)) return pick('route-health', 'route-needs-use-sensor-baseline');
145
+ if (/\bheal\b|repair|\bself\b/.test(need)) return pick('heal-proposal', 'self-heal-needs-use-proposal-emitter-only');
146
+ if (/mlc|engine|wire|mtp|hrm|gnn|mamba|aot/.test(need)) return pick('mlc-descriptor', 'engine-needs-use-descriptor-matrix-no-launch');
147
+ if (/fischer|judge|score/.test(need)) return pick('fischer-draft-standin', 'judge-needs-use-draft-standin-warning');
148
+ if (/gemma|vision|pixel|frame|world/.test(need)) return pick('frozen-gemma-proof-gated', 'vision-needs-use-proof-gate-not-live-model');
149
+ if (/answer|candidate|draft/.test(need)) return pick('answer-producer', 'answer-needs-use-candidate-descriptor');
150
+ if (/law|gate|cosign|authorization/.test(need)) return pick('law-gate', 'gate-needs-use-slice-engine-law');
151
+ return pick('fabric-council', 'default-to-fabric-ranking-when-available');
152
+ } catch {
153
+ return { need: 'invalid', selected: 'fabric-council', reason: 'selector-threw-default-held', role: normalizeRole(MODEL_ROLES[1]) };
154
+ }
155
+ }
156
+
157
+ export function buildSelector(input = MODEL_ROLES) {
158
+ const roles = (Array.isArray(input) ? input : MODEL_ROLES).map(normalizeRole);
159
+ const count = (status) => roles.filter((role) => role.status === status).length;
160
+ const live_invocation_ready = false;
161
+ return {
162
+ tool: SELECTOR_ID,
163
+ frontend_cell: FRONTEND_CELL,
164
+ roles,
165
+ summary: {
166
+ total: roles.length,
167
+ green: count('GREEN'),
168
+ partial: count('PARTIAL'),
169
+ gated: count('GATED'),
170
+ red: count('RED'),
171
+ live_invocation_ready,
172
+ m_cell_status: M_CELL_STATUS,
173
+ cutover_ready: false,
174
+ },
175
+ };
176
+ }
177
+
178
+ export function emitRows(input = MODEL_ROLES, opts = {}) {
179
+ try {
180
+ const built = buildSelector(input);
181
+ const s = built.summary;
182
+ const limitRaw = Number.parseInt(safe(prop(opts, 'limit', built.roles.length)), 10);
183
+ const limit = Math.max(0, Math.min(Number.isFinite(limitRaw) ? limitRaw : built.roles.length, built.roles.length));
184
+ const rows = [
185
+ `MODSELHDR|tool=${SELECTOR_ID}|frontend_cell=${FRONTEND_CELL}|purpose=model-selector-M-visible-role-routing-before-any-model-invocation|roles=${s.total}|read_only=1|process_launch=0|live_model_invocation=0|no_fetch=1|no_write=1|no_spawn=1|json=0`,
186
+ `MODSELSUM|green=${s.green}|partial=${s.partial}|gated=${s.gated}|red=${s.red}|M_cell_status=${s.m_cell_status}|cutover_ready=0|json=0`,
187
+ ];
188
+ for (const role of built.roles.slice(0, limit)) {
189
+ rows.push(`MODSELROLE|id=${safe(role.id)}|lane=${safe(role.lane)}|status=${safe(role.status)}|source=${safe(role.source)}|use=${safe(role.use)}|next=${safe(role.next)}|row_sha16=${safe(role.row_sha16)}|process_launch=0|live_invocation=0|json=0`);
190
+ }
191
+ const need = prop(opts, 'need', '');
192
+ if (need) {
193
+ const selected = selectRole({ need });
194
+ rows.push(`MODSELROUTE|need=${safe(selected.need)}|selected=${safe(selected.selected)}|reason=${safe(selected.reason)}|selected_status=${safe(prop(selected.role, 'status', 'RED'))}|json=0`);
195
+ }
196
+ rows.push(`MODSELGATE|front_end_cell=M|status=PARTIAL|reason=selector-artifact-built-but-live-model-invocation-and-frozen-Gemma-proof-remain-gated|process_launch=0|live_model_invocation=0|json=0`);
197
+ return rows;
198
+ } catch {
199
+ return [
200
+ `MODSELHDR|tool=${SELECTOR_ID}|frontend_cell=${FRONTEND_CELL}|purpose=model-selector-M-visible-role-routing-before-any-model-invocation|roles=0|read_only=1|process_launch=0|live_model_invocation=0|json=0`,
201
+ 'MODSELGATE|front_end_cell=M|status=RED|reason=emit-threw-held-invalid|process_launch=0|live_model_invocation=0|json=0',
202
+ ];
203
+ }
204
+ }
205
+
206
+ export function selfTest() {
207
+ const checks = [];
208
+ const add = (name, ok) => checks.push({ name, ok: Boolean(ok) });
209
+ const selector = buildSelector();
210
+ add('all-roles-present', selector.summary.total === 8 && selector.roles.some((r) => r.id === 'frozen-gemma-proof-gated'));
211
+ add('m-cell-partial-not-green', selector.summary.m_cell_status === M_CELL_STATUS && selector.summary.cutover_ready === false);
212
+ add('no-live-invocation', selector.roles.every((r) => r.process_launch === 0 && r.live_invocation === 0) && selector.summary.live_invocation_ready === false);
213
+ add('routes-heal', selectRole({ need: 'self heal proposal' }).selected === 'heal-proposal');
214
+ add('routes-vision-to-proof-gate', selectRole({ need: 'pixel frame world model' }).selected === 'frozen-gemma-proof-gated');
215
+ add('routes-default-to-council', selectRole({ need: 'unknown next work' }).selected === 'fabric-council');
216
+ const hostile = emitRows([{ id: 'x|bad', lane: 'l\nbad', status: 'GREEN', source: 's|json=1', evidence: 'e\nMODSELGATE|process_launch=1' }], { need: 'fischer|json=1\nMODSELGATE|process_launch=1' });
217
+ add('rows-hbp-only', hostile.every((row) => row.endsWith('|json=0') && !/[\r\n]/.test(row)));
218
+ add('total-never-throws', (() => { try { normalizeRole({ get id() { throw new Error('boom'); } }); selectRole({ get need() { throw new Error('boom'); } }); emitRows(null); return true; } catch { return false; } })());
219
+ return { ok: checks.every((c) => c.ok), checks };
220
+ }
221
+
222
+ const isMain = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);
223
+ if (isMain) {
224
+ if (process.argv[2] === '--self-test') {
225
+ const r = selfTest();
226
+ for (const c of r.checks) console.log(`${c.ok ? 'PASS' : 'FAIL'} ${c.name}`);
227
+ process.exit(r.ok ? 0 : 1);
228
+ }
229
+ const needArg = process.argv.find((arg) => arg.startsWith('--need='));
230
+ const limitArg = process.argv.find((arg) => arg.startsWith('--limit='));
231
+ const opts = {
232
+ need: needArg ? needArg.slice('--need='.length) : '',
233
+ limit: limitArg ? limitArg.slice('--limit='.length) : MODEL_ROLES.length,
234
+ };
235
+ process.stdout.write(emitRows(MODEL_ROLES, opts).join('\n') + '\n');
236
+ }
tools/behcs/model-selector-matrix.mjs.sha256 ADDED
@@ -0,0 +1 @@
 
 
1
+ 32EABA0B7B4EC64B4BE11D533CE2D81DB50798D50DD0FB1CCEB45DE9B6B94E18 model-selector-matrix.mjs