File size: 3,626 Bytes
518343a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Contradiction probe — cheap consistency check between the index branch's
// top-k predicted block-set and the sparse branch's actual attended block-set.
//
// Per the MiniMax M2 retrospective, a sparse plan that AGREES with its index
// at small scale but DIVERGES at deep multi-hop is the silent failure mode.
// The probe is therefore the gate between sparse and full attention: low
// agreement triggers `sparse.contradiction.v1` and a paired `sparse.escalated.v1`.

import type { SparseAttentionEnvelope } from "./envelope.js";
import type { SparseReceiptCommon } from "./receipts.js";

export interface SparseContradictionReceipt extends SparseReceiptCommon {
  readonly receiptClass: "sparse.contradiction.v1";
  readonly regimeRef: string; // SparseAttentionEnvelope.regimeId
  readonly agreement: number; // [0,1]
  readonly threshold: number; // envelope.minIndexAgreement
  readonly indexBlocks: ReadonlyArray<number>;
  readonly sparseBlocks: ReadonlyArray<number>;
}

export interface SparseEscalatedReceipt extends SparseReceiptCommon {
  readonly receiptClass: "sparse.escalated.v1";
  readonly regimeRef: string;
  readonly fromMode: "sparse";
  readonly toMode: "full";
  readonly reasonReceiptRef: string; // points at the contradiction receipt
}

/**
 * Jaccard agreement between two block-id sets. ∈ [0,1]. Pure; deterministic.
 */
export function jaccard(a: ReadonlyArray<number>, b: ReadonlyArray<number>): number {
  if (a.length === 0 && b.length === 0) return 1;
  const A = new Set(a);
  const B = new Set(b);
  let inter = 0;
  for (const x of A) if (B.has(x)) inter += 1;
  const union = A.size + B.size - inter;
  return union === 0 ? 1 : inter / union;
}

export interface ProbeInput {
  readonly envelope: SparseAttentionEnvelope;
  readonly indexBlocks: ReadonlyArray<number>;
  readonly sparseBlocks: ReadonlyArray<number>;
  readonly tenant: string;
  readonly nonce: string;
  readonly issuedAt?: string;
}

export type ProbeOutput =
  | { contradicted: false; agreement: number }
  | {
      contradicted: true;
      agreement: number;
      contradiction: SparseContradictionReceipt;
      escalation: SparseEscalatedReceipt;
    };

/**
 * Probe and (on contradiction) emit a paired contradiction + escalation
 * receipt. Caller is responsible for persisting the receipts and for
 * actually switching the orchestrator to full attention — this function
 * is pure and side-effect-free.
 */
export function probe(input: ProbeInput): ProbeOutput {
  const { envelope, indexBlocks, sparseBlocks, tenant, nonce } = input;
  const issuedAt = input.issuedAt ?? new Date().toISOString();
  const agreement = jaccard(indexBlocks, sparseBlocks);
  if (agreement >= envelope.minIndexAgreement) {
    return { contradicted: false, agreement };
  }
  const contradictionId = `${envelope.regimeId}:${nonce}:contradiction`;
  const contradiction: SparseContradictionReceipt = {
    receiptClass: "sparse.contradiction.v1",
    freshnessNonce: nonce,
    issuedAt,
    tenant,
    parentRef: envelope.regimeId,
    regimeRef: envelope.regimeId,
    agreement,
    threshold: envelope.minIndexAgreement,
    indexBlocks: [...indexBlocks].sort((a, b) => a - b),
    sparseBlocks: [...sparseBlocks].sort((a, b) => a - b),
  };
  const escalation: SparseEscalatedReceipt = {
    receiptClass: "sparse.escalated.v1",
    freshnessNonce: nonce,
    issuedAt,
    tenant,
    parentRef: envelope.regimeId,
    regimeRef: envelope.regimeId,
    fromMode: "sparse",
    toMode: "full",
    reasonReceiptRef: contradictionId,
  };
  return { contradicted: true, agreement, contradiction, escalation };
}