quantum-cortex 1.0.0__py3-none-any.whl

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 (41) hide show
  1. cortex_c2/__init__.py +161 -0
  2. cortex_c2/consolidation.py +146 -0
  3. cortex_c2/router_ablation.py +254 -0
  4. cortex_c2/router_v2.py +109 -0
  5. cortex_c2b/__init__.py +724 -0
  6. cortex_c2b/companion_demo.py +203 -0
  7. cortex_c2b/crypto.py +102 -0
  8. cortex_c2b/external_arm.py +591 -0
  9. cortex_c2b/hm_lm.py +983 -0
  10. cortex_c2b/hm_protocol.py +308 -0
  11. cortex_c2b/lifecycle.py +454 -0
  12. cortex_c2b/lm_bridge.py +338 -0
  13. cortex_c2b/organ_use.py +254 -0
  14. cortex_c2b/read_path.py +218 -0
  15. cortex_c2b/reconstruction_probe.py +471 -0
  16. cortex_c2b/write_path.py +176 -0
  17. cortex_data/__init__.py +439 -0
  18. cortex_data/relay.py +174 -0
  19. cortex_eval/__init__.py +20 -0
  20. cortex_eval/ablation_mqar.py +187 -0
  21. cortex_eval/confirmation_run.py +133 -0
  22. cortex_eval/domain_map.py +127 -0
  23. cortex_eval/external_baseline.py +148 -0
  24. cortex_eval/ingest_confirmation.py +194 -0
  25. cortex_eval/mqar.py +122 -0
  26. cortex_eval/multiseed.py +148 -0
  27. cortex_eval/recurrent_base.py +197 -0
  28. cortex_eval/recurrent_ladder.py +159 -0
  29. cortex_eval/resumable_ladder.py +231 -0
  30. cortex_eval/resumable_rb.py +207 -0
  31. cortex_eval/run_mqar.py +127 -0
  32. cortex_eval/serial_position.py +116 -0
  33. quantum_cortex-1.0.0.dist-info/METADATA +209 -0
  34. quantum_cortex-1.0.0.dist-info/RECORD +41 -0
  35. quantum_cortex-1.0.0.dist-info/WHEEL +5 -0
  36. quantum_cortex-1.0.0.dist-info/licenses/AUTHORS.md +30 -0
  37. quantum_cortex-1.0.0.dist-info/licenses/LICENSE +73 -0
  38. quantum_cortex-1.0.0.dist-info/licenses/LICENSE-DOCS.md +34 -0
  39. quantum_cortex-1.0.0.dist-info/licenses/NOTICE +50 -0
  40. quantum_cortex-1.0.0.dist-info/top_level.txt +5 -0
  41. train.py +885 -0
cortex_c2b/hm_lm.py ADDED
@@ -0,0 +1,983 @@
1
+ """cortex_c2b.hm_lm -- the H.M. protocol on the MODEL (ADR-008; spec amendment 2026-09-12).
2
+
3
+ The organ-level protocol (`hm_protocol.py`) reads payloads with a hash-seeded
4
+ cue encoder and a reference skill probe. This module runs the same two arms on
5
+ the language model itself, under the cite-or-abstain contract:
6
+
7
+ Arm E, journal ON : for each planted fact, the query's cue comes from the
8
+ model, the organ returns a labelled read window, the model generates, and
9
+ the answer is parsed against the contract. STRICT recall = a valid citation
10
+ (a label that exists, whose episode holds the attribute) AND the exact
11
+ attribute. Reported next to it: abstention rate on the negative control,
12
+ false abstention on planted facts (the cost of prudence), invalid citation
13
+ rate (a claim with a fake provenance, the worst case), attribute exactness
14
+ given a valid citation, retrieval hit rate (the encoder), attention mass on
15
+ the retrieved bytes (the reader). A failure is attributable (invariant 8).
16
+ Arm E, journal OFF: the reader is absent (no window, gate 0). A citation is
17
+ impossible, so the strict recall is zero by construction -- the parametric
18
+ floor in its cleanest form; the guess rate and the by-chance attribute hits
19
+ are reported so the floor keeps its meaning.
20
+ Arm S: ordinary language modelling with the reader ACTIVE on whatever the
21
+ journal returns for each span (journal ON) against the reader absent (OFF):
22
+ `hm_skill_delta` = relative perplexity degradation. This is the real e_S:
23
+ retrieval noise must not hurt the model.
24
+
25
+ Verdict (amended spec): pass = skill delta <= e_S AND strict recall ON - strict
26
+ recall OFF >= d AND strict recall OFF <= floor AND invalid citation <= 1% AND
27
+ claims on the negative control < 10%. `persistent` / `claimable` as in the organ
28
+ protocol: a claim needs the journal on disk, durable, and session B reopened
29
+ from the disk alone -- the strict version runs session B in another process
30
+ (`tests/test_c2b_lm_bridge.py`). Thresholds d, e_S, floor untouched.
31
+ """
32
+ from __future__ import annotations
33
+
34
+ import math
35
+
36
+ import numpy as np
37
+ import torch
38
+
39
+ from cortex_c2b import Journal, MODE_DURABLE, POLICY_STOP, lifecycle_declaration
40
+ from cortex_c2b.hm_protocol import (Fact, generate_facts, SCHEMAS, _ATTRS, DELTA, EPSILON_S,
41
+ FLOOR_MARGIN, NEGCTRL_INVALID, N_FACTS, N_NEGCTRL)
42
+ from cortex_c2b.lm_bridge import (JournalBridge, encode_query, parse_contract, build_read_window, NEWLINE, TOK_EPI,
43
+ TOK_CITE, TOK_ANS, TOK_UNKNOWN, LABELS)
44
+ from cortex_c2b.write_path import WritePath
45
+ from cortex_c2b.read_path import JournalPath, STATE_EVICTED
46
+
47
+ INVALID_CITATION_MAX = 0.01 # declared 2026-09-12, before any run on the model
48
+
49
+
50
+ POLICIES = ("plain", "head", "head+pointer", "mark-veto", "mark-veto+value")
51
+
52
+
53
+ def policy_outcome(kind: str, label, labels: dict, marks_by_pointer: dict, payloads, own_pointer, attr: str | None,
54
+ expected_attr: str, policy: str, threshold: float | None):
55
+ """ADR-008 amendment 2026-09-18, the two organ side decode policies, one function used
56
+ live (in the probe) and in replay (on an answers file), so the two cannot disagree.
57
+ `mark-veto`: the model's citation stands unless the cited line's mark is below the
58
+ oracle's threshold (or the label is not in the window); then it is an abstention.
59
+ `mark-veto+value`: the veto, and the attribute answered is the cited line's own
60
+ value, read by the organ from the line's statement by the generator's templates,
61
+ never generated; a vetoed or missing line is an abstention.
62
+ Returns (kind, attr, outcome) with outcome in strict | valid | invalid | abstain."""
63
+ from cortex_c2b.hm_protocol import value_of_statement
64
+ if kind == "unknown":
65
+ return kind, None, "abstain"
66
+ if policy in ("mark-veto", "mark-veto+value"):
67
+ pointer = labels.get(label) if kind == "cite" else None
68
+ mark = marks_by_pointer.get(pointer) if pointer is not None else None
69
+ if pointer is None or mark is None or (threshold is not None and mark < threshold):
70
+ return "unknown", None, "abstain" # the organ vetoes: no line, or a low mark
71
+ if policy == "mark-veto+value":
72
+ payload = payloads.get(pointer) if pointer in payloads else b""
73
+ v = value_of_statement(payload.decode("utf-8", errors="replace")) if payload else None
74
+ attr = v[2] if v else None
75
+ if attr is None:
76
+ return "unknown", None, "abstain"
77
+ if kind == "cite" and label in labels:
78
+ payload = payloads.get(labels[label]) if labels[label] in payloads else b""
79
+ cited_ok = (attr or "").encode("utf-8") in payload and len(attr or "") > 0
80
+ if not cited_ok:
81
+ return kind, attr, "invalid"
82
+ return kind, attr, ("strict" if attr == expected_attr else "valid")
83
+ return kind, attr, "invalid"
84
+
85
+
86
+ def pointer_label(model, window_bytes: bytes) -> int | None:
87
+ """The pointer policy's citation: the label of the window line that received the
88
+ most of the reader's attention from the decision position (heads averaged, the
89
+ null slot excluded), read from the reader's last attention map. None when the
90
+ window has no labelled line."""
91
+ attn = None
92
+ for blk in model.blocks:
93
+ r = getattr(blk, "reader", None)
94
+ if r is not None and getattr(r, "last_attn", None) is not None:
95
+ attn = r.last_attn[0, :, -1, 1:].mean(dim=0) # (r,): the decision position, real bytes only
96
+ if attn is None:
97
+ return None
98
+ best, best_mass = None, -1.0
99
+ start = 0
100
+ n = len(window_bytes)
101
+ while start < n:
102
+ end = window_bytes.find(bytes([NEWLINE]), start)
103
+ end = n if end < 0 else end
104
+ label = window_bytes[start] if end > start else None
105
+ if label in LABELS:
106
+ stop = min(end + 1, attn.numel())
107
+ m = float(attn[start:stop].sum()) if stop > start else -1.0
108
+ if m > best_mass:
109
+ best, best_mass = label, m
110
+ start = end + 1
111
+ return best
112
+
113
+
114
+ @torch.no_grad()
115
+ def generate(model, prefix: list[int], window_tokens=None, window_mask=None, gate: float = 1.0,
116
+ max_new: int = 48, policy: str = "plain", window_bytes: bytes | None = None) -> tuple[list[int], float | None]:
117
+ """Greedy byte-level decoding until a newline. Returns (new tokens, the
118
+ reader's attention mass at the first decoding step).
119
+
120
+ `policy` (ADR-008 amendment of 2026-09-14, declared before any measurement):
121
+ plain -- every token from the model's argmax, the head at most a bias (v7 as trained);
122
+ head -- the matching head decides: m <= 0 emits the abstention, m > 0 forces <CITE>
123
+ and the model generates the label and the attribute;
124
+ head+pointer -- the head decides, and the label is read off the reader's attention
125
+ (the line with the most attention from the decision position); the
126
+ model generates only the attribute.
127
+ Without a window, or without a head, every policy is the plain one."""
128
+ if policy not in POLICIES:
129
+ raise ValueError(f"unknown policy {policy!r}; one of {POLICIES}")
130
+ device = next(model.parameters()).device
131
+ idx = torch.tensor([prefix], dtype=torch.long, device=device)
132
+ out, mass, match = [], None, None
133
+ has_head = getattr(model, "match_head", None) is not None
134
+ forced: list[int] = []
135
+ for step in range(max_new):
136
+ # RES-21: the matching head fires at the decision position only, i.e. on the first step
137
+ dpos = (torch.tensor([idx.size(1) - 1], device=device)
138
+ if (step == 0 and has_head and window_tokens is not None) else None)
139
+ logits, _ = model(idx[:, -model.cfg.block_size:], window_tokens=window_tokens,
140
+ window_mask=window_mask, gate=gate, decision_pos=dpos)
141
+ if step == 0:
142
+ mass = model.read_mass()
143
+ match = getattr(model, "last_match_logit", None) # the head's judgment at the decision step
144
+ if policy != "plain" and has_head and window_tokens is not None and match is not None:
145
+ if float(match[0]) <= 0.0:
146
+ out = [TOK_UNKNOWN, NEWLINE]
147
+ break
148
+ forced = [TOK_CITE]
149
+ if policy == "head+pointer":
150
+ label = pointer_label(model, window_bytes or b"")
151
+ if label is not None:
152
+ forced += [label, TOK_ANS]
153
+ if forced:
154
+ nxt = forced.pop(0)
155
+ else:
156
+ nxt = int(logits[0, -1].argmax())
157
+ out.append(nxt)
158
+ if nxt == NEWLINE:
159
+ break
160
+ idx = torch.cat([idx, torch.tensor([[nxt]], device=device)], dim=1)
161
+ if has_head:
162
+ model.last_match_logit = match # later steps reset it; the probe reads the decision step's
163
+ return out, mass
164
+
165
+
166
+ def _probe(model, bridge: JournalBridge, facts: list[Fact], journal_on: bool, window_len: int,
167
+ pointer_of: dict[str, str], policy: str = "plain", veto_threshold: float | None = None) -> dict:
168
+ """One arm over a fact list. `pointer_of`: entity -> pointer for planted facts
169
+ (empty for the negative control)."""
170
+ n = max(1, len(facts))
171
+ strict = abstain = invalid = valid = attr_ok = guess = attr_by_chance = hit = 0
172
+ masses = []
173
+ match_right = match_n = 0 # RES-21: the head's own judgment, against the read
174
+ mark_own, mark_other_max, mark_max = [], [], [] # RES-23: the organ's similarities per read
175
+ answers = [] # Rev66: every answer kept per line, a recording, not a variable
176
+ for f in facts:
177
+ query = encode_query(f.query)
178
+ read = None
179
+ if journal_on:
180
+ cue = bridge.cues([query])[0]
181
+ read = bridge.read(cue)
182
+ if read.scores:
183
+ own_p = pointer_of.get(f.entity) if pointer_of else None
184
+ others = [sc for pt, sc in zip(read.pointers, read.scores) if pt != own_p]
185
+ if own_p in read.pointers:
186
+ mark_own.append(float(read.scores[read.pointers.index(own_p)]))
187
+ if others:
188
+ mark_other_max.append(float(max(others)))
189
+ mark_max.append(float(max(read.scores)))
190
+ wt, wm = bridge.window_tensors([read.window], window_len)
191
+ toks, mass = generate(model, query, wt, wm, gate=1.0, policy=policy, window_bytes=read.window)
192
+ if mass is not None:
193
+ masses.append(mass)
194
+ present = (pointer_of.get(f.entity) in read.pointers) if pointer_of else False
195
+ hit += int(present) if pointer_of else 0
196
+ m = getattr(model, "last_match_logit", None)
197
+ if m is not None:
198
+ match_right += int((float(m[0]) > 0) == present); match_n += 1
199
+ labels = read.labels
200
+ else:
201
+ toks, _ = generate(model, query, None, None, gate=0.0)
202
+ labels = {}
203
+ kind, label, attr = parse_contract(toks)
204
+ own_p = pointer_of.get(f.entity) if pointer_of else None
205
+ mbp = dict(zip(read.pointers, [float(x) for x in read.scores])) if (read is not None and read.scores) else {}
206
+ if policy in ("mark-veto", "mark-veto+value"):
207
+ kind, attr, _ = policy_outcome(kind, label, labels, mbp, bridge.j.payloads, own_p, attr, f.attr, policy, veto_threshold)
208
+ rec = {"entity": f.entity, "schema": f.schema, "attr": f.attr, "kind": kind,
209
+ "label": (chr(label) if isinstance(label, int) else label) if label is not None else None, "answered": attr,
210
+ "outcome": None, "own_in_window": (own_p in read.pointers) if (read is not None and own_p) else None}
211
+ if read is not None and read.scores:
212
+ marks_by_pointer = dict(zip(read.pointers, [float(x) for x in read.scores]))
213
+ rec["mark_own"] = marks_by_pointer.get(own_p) if own_p else None
214
+ rec["mark_max"] = max(marks_by_pointer.values())
215
+ rec["cited_pointer_is_own"] = (labels.get(label) == own_p) if (kind == "cite" and label in labels and own_p) else None
216
+ rec["mark_cited"] = marks_by_pointer.get(labels[label]) if (kind == "cite" and label in labels) else None
217
+ rec["cited_is_highest_mark"] = (labels.get(label) is not None and marks_by_pointer.get(labels[label]) == rec["mark_max"]) if (kind == "cite" and label in labels) else None
218
+ answers.append(rec)
219
+ if kind == "unknown":
220
+ abstain += 1; rec["outcome"] = "abstain"
221
+ continue
222
+ guess += 1
223
+ if kind == "cite" and label in labels:
224
+ payload = bridge.j.payloads.get(labels[label]) if labels[label] in bridge.j.payloads else b""
225
+ cited_ok = (attr or "").encode("utf-8") in payload and len(attr or "") > 0
226
+ if cited_ok:
227
+ valid += 1; rec["outcome"] = "valid"
228
+ if attr == f.attr:
229
+ strict += 1; attr_ok += 1; rec["outcome"] = "strict"
230
+ else:
231
+ invalid += 1; rec["outcome"] = "invalid" # a label that exists but does not hold the claim
232
+ else:
233
+ invalid += 1; rec["outcome"] = "invalid" # a citation of nothing, or a malformed claim
234
+ if attr == f.attr:
235
+ attr_by_chance += 1
236
+ return {"recall_strict": strict / n, "abstain_rate": abstain / n, "guess_rate": guess / n,
237
+ "invalid_citation_rate": invalid / n, "valid_citation_rate": valid / n,
238
+ "attr_exact_given_valid": (attr_ok / valid) if valid else None,
239
+ "attr_hit_any": attr_by_chance / n,
240
+ "retrieval_hit": (hit / n) if (journal_on and pointer_of) else None,
241
+ "attention_mass": (float(np.mean(masses)) if masses else None),
242
+ "match_acc": (match_right / match_n) if match_n else None,
243
+ "marks": {"own": _stats(mark_own), "other_max": _stats(mark_other_max), "max": _stats(mark_max)},
244
+ "answers": answers}
245
+
246
+
247
+ @torch.no_grad()
248
+ def retrieval_probe(model, journal, facts: list[Fact], neg: list[Fact], pointer_of: dict[str, str],
249
+ k: int = 4, seed: int = 0) -> dict:
250
+ """ADR-008 amendment 2026-09-19, the retrieval policy probe: the first factor of the
251
+ decomposition (retrieval) read on its own, with no generation and no training, on a
252
+ checkpoint and a sealed journal that already exist. For each protocol question the
253
+ query cue is the model's own; the same entries are indexed twice, ranked by the dense
254
+ cosine of every run to date and by the expand and sparsify tag of ADR-003 line 6
255
+ (the dentate gyrus in the map, the mushroom body in Dasgupta, Stevens and Navlakha
256
+ 2017). Reports, per ranking: whether the own episode is among the candidates the
257
+ buckets return at all, and whether it is in the top k. A miss in the first is the
258
+ bucketing's, a miss in the second only is the ranking's; the two are never mixed."""
259
+ from cortex_c2b.read_path import CueIndex, JournalPath
260
+ device = next(model.parameters()).device
261
+ out = {"probe": "retrieval policy (ADR-003 line 6): dense cosine against expand and sparsify",
262
+ "k": k, "n_facts": len(facts), "n_negctrl": len(neg), "rankings": {}}
263
+ for rank in ("cosine", "fly"):
264
+ jp = JournalPath(journal, index=CueIndex(seed=seed, rank=rank), seed=seed)
265
+ for eid, e in journal._entries.items():
266
+ if e.state != STATE_EVICTED:
267
+ jp.index.add(eid, np.asarray(e.cue, dtype=np.float32))
268
+ bridge = JournalBridge(model, journal, journal_path=jp, k=k, seed=seed, device=device)
269
+ in_cand = in_topk = 0; ranks = []; margins = []; neg_best = []
270
+ for f in facts:
271
+ cue = bridge.cues([encode_query(f.query)])[0]
272
+ own = pointer_of.get(f.entity)
273
+ scored = jp.index.query(np.asarray(cue, dtype=np.float32), k=len(jp.index))
274
+ by_pointer = []
275
+ for eid, sc in scored:
276
+ e, pointer = jp._resolve(eid)
277
+ by_pointer.append((pointer, sc))
278
+ pos = next((i for i, (pt, _) in enumerate(by_pointer) if pt == own), None)
279
+ if pos is None:
280
+ continue
281
+ in_cand += 1; ranks.append(pos + 1)
282
+ if pos < k:
283
+ in_topk += 1
284
+ others = [sc for i, (pt, sc) in enumerate(by_pointer) if pt != own]
285
+ margins.append(float(by_pointer[pos][1] - max(others))) if others else None
286
+ for f in neg:
287
+ cue = bridge.cues([encode_query(f.query)])[0]
288
+ sc = jp.index.query(np.asarray(cue, dtype=np.float32), k=1)
289
+ neg_best.append(float(sc[0][1]) if sc else float("nan"))
290
+ n = len(facts)
291
+ out["rankings"][rank] = {
292
+ "own_in_candidates": in_cand / n, "own_in_top_k": in_topk / n,
293
+ "own_rank": _stats([float(r) for r in ranks]), "own_minus_best_other": _stats(margins),
294
+ "negctrl_best_score": _stats([x for x in neg_best if x == x])}
295
+ c, f_ = out["rankings"]["cosine"], out["rankings"]["fly"]
296
+ out["reading"] = {"top_k_cosine": c["own_in_top_k"], "top_k_fly": f_["own_in_top_k"],
297
+ "candidate_misses_cosine": round(1 - c["own_in_candidates"], 4),
298
+ "candidate_misses_fly": round(1 - f_["own_in_candidates"], 4)}
299
+ return out
300
+
301
+
302
+ def replay_policy(answers_file: dict, policy: str, threshold: float | None = None) -> dict:
303
+ """Post hoc: apply an organ side policy to the answers a session B kept (Rev66 files), with
304
+ the oracle's threshold of that file. The rates it returns are what the live policy would
305
+ have produced on the same checkpoint and reads, because both use `policy_outcome`;
306
+ a replay on the seed the policy was conceived on is a post hoc reading, never a result."""
307
+ thr = answers_file["hm_mark_oracle"]["threshold"] if threshold is None else float(threshold) # a sweep reads the sensitivity
308
+ def outcome(a):
309
+ kind, label = a["kind"], a.get("label")
310
+ labels = {label: "cited"} if (kind == "cite" and a.get("mark_cited") is not None) else {}
311
+ mbp = {"cited": a["mark_cited"]} if a.get("mark_cited") is not None else {}
312
+ own = "cited" if a.get("cited_pointer_is_own") else "own"
313
+ # the value read from the cited line is the fact's attribute iff the cited line is the own one
314
+ payloads = {"cited": (f"x took place in {a['attr']}" if a.get("cited_pointer_is_own") else "x took place in other").encode()}
315
+ if policy == "mark-veto+value":
316
+ k, attr, o = policy_outcome(kind, label, labels, mbp, payloads, own, a.get("answered"), a["attr"], "mark-veto", thr)
317
+ if o == "abstain":
318
+ return "abstain"
319
+ return "strict" if a.get("cited_pointer_is_own") else "invalid"
320
+ k, attr, o = policy_outcome(kind, label, labels, mbp, {"cited": ((a.get("answered") or "") if a["outcome"] in ("strict", "valid") else "").encode()}, own, a.get("answered"), a["attr"], policy, thr)
321
+ if policy == "mark-veto" and o != "abstain":
322
+ return a["outcome"] # the model's own outcome stands when not vetoed
323
+ return o
324
+ on = [outcome(a) for a in answers_file["answers"]["on"]]; n = len(on)
325
+ neg = [outcome(a) for a in answers_file["answers"]["negctrl"]]
326
+ strict, valid, invalid, abst = on.count("strict"), on.count("strict") + on.count("valid"), on.count("invalid"), on.count("abstain")
327
+ negc = sum(1 for o in neg if o != "abstain")
328
+ return {"policy": policy, "post_hoc": True, "threshold": thr, "hm_recall_on": strict / n, "hm_valid_citation_on": valid / n,
329
+ "hm_invalid_citation_on": invalid / n, "hm_false_abstention_on": abst / n, "hm_negctrl_rate": negc / len(neg),
330
+ "gate_conditions_without_skill_arm": int(strict / n >= DELTA and negc / len(neg) < NEGCTRL_INVALID and invalid / n <= INVALID_CITATION_MAX)}
331
+
332
+
333
+ def _stats(xs: list[float]) -> dict | None:
334
+ if not xs:
335
+ return None
336
+ a = np.asarray(xs, dtype=np.float64)
337
+ return {"n": int(a.size), "mean": float(a.mean()), "min": float(a.min()), "max": float(a.max()),
338
+ "q10": float(np.quantile(a, 0.10)), "q50": float(np.quantile(a, 0.50)), "q90": float(np.quantile(a, 0.90))}
339
+
340
+
341
+ @torch.no_grad()
342
+ def mark_oracle(model, bridge: JournalBridge, facts: list[Fact], neg: list[Fact], pointer_of: dict[str, str],
343
+ train_seed: int = 100_000, train_n: int = 200, k: int | None = None) -> dict:
344
+ """RES-23, declared before v9 ran: the ceiling the organ's marks allow, with no
345
+ model output. A rule: cite the line with the highest mark when that mark clears
346
+ a threshold, else abstain. The threshold is read from the training facts family
347
+ only (a scratch journal of `train_n` training facts planted with this model's
348
+ cues, seed `train_seed`, and their never planted negatives), never from the
349
+ protocol's facts; then the rule is applied to the protocol's own reads. Strict
350
+ recall counts the own episode cited; a claim on a never planted entity counts
351
+ against; the skill arm does not apply (no model). Deterministic from the
352
+ checkpoint and the sealed journal."""
353
+ from cortex_c2b.organ_use import training_facts, plant_pool
354
+ k = k or bridge.k
355
+ rng_state = bridge.rng.bit_generator.state # the oracle reads through the bridge and must leave no trace:
356
+ tf, tneg, _ = training_facts(train_n, train_seed) # its reads would otherwise advance the window shuffle and make
357
+ # two policies see different windows (found and fixed 2026-09-20)
358
+ scratch = JournalBridge(model, Journal(), k=k, budget_bytes=bridge.budget, seed=0, shuffle_seed=1,
359
+ device=bridge.device, mark=bridge.mark)
360
+ plant_pool(scratch, tf)
361
+ pos, negs = [], []
362
+ for f in tf:
363
+ r = scratch.read(scratch.cues([encode_query(f.query)])[0])
364
+ own = scratch.pointer_of.get(f.entity)
365
+ if r.scores and own in r.pointers:
366
+ pos.append((max(r.scores), r.pointers[int(np.argmax(r.scores))] == own))
367
+ elif r.scores:
368
+ pos.append((max(r.scores), False))
369
+ for f in tneg:
370
+ r = scratch.read(scratch.cues([encode_query(f.query)])[0])
371
+ negs.append(max(r.scores) if r.scores else 0.0)
372
+ cands = sorted({m for m, _ in pos} | set(negs))
373
+ best_thr, best_acc = 0.0, -1.0
374
+ for thr in cands: # claim iff max mark > thr; the best on the training family
375
+ acc = (sum(1 for m, right in pos if m > thr and right) + sum(1 for m in negs if m <= thr)) / max(1, len(pos) + len(negs))
376
+ if acc > best_acc:
377
+ best_thr, best_acc = float(thr), float(acc)
378
+ def apply(fs, planted: bool):
379
+ claims = strict = 0
380
+ for f in fs:
381
+ r = bridge.read(bridge.cues([encode_query(f.query)])[0])
382
+ if not r.scores:
383
+ continue
384
+ top = int(np.argmax(r.scores))
385
+ if r.scores[top] > best_thr:
386
+ claims += 1
387
+ if planted and r.pointers[top] == pointer_of.get(f.entity):
388
+ strict += 1
389
+ return claims / max(1, len(fs)), strict / max(1, len(fs))
390
+ on_claims, on_strict = apply(facts, True)
391
+ neg_claims, _ = apply(neg, False)
392
+ bridge.rng.bit_generator.state = rng_state # restored: the probes that follow see the same windows as a
393
+ # run without the oracle, whatever the policy
394
+ gap = on_strict - 0.0 # off is zero by construction: no window, no citation
395
+ passes = (gap >= DELTA) and (neg_claims < NEGCTRL_INVALID)
396
+ return {"oracle": "mark oracle (RES-23): cite the highest marked line above a threshold read from the training family",
397
+ "threshold": best_thr, "training_accuracy": best_acc, "training_pairs": len(pos) + len(negs),
398
+ "recall_on_strict": on_strict, "claims_on": on_claims, "negctrl_claims": neg_claims,
399
+ "gap": gap, "passes_without_skill_arm": bool(passes), "encoder": "this checkpoint's own cue encoder"}
400
+
401
+
402
+ @torch.no_grad()
403
+ def skill_delta(model, bridge: JournalBridge, val_batches: list[tuple[torch.Tensor, torch.Tensor]],
404
+ window_len: int) -> tuple[float, float, float]:
405
+ """Arm S: val loss with the reader active on whatever the journal returns for
406
+ each span (ON) against the reader absent (OFF). Returns (delta, ppl_on, ppl_off)."""
407
+ on, off = [], []
408
+ for x, y in val_batches:
409
+ _, l_off = model(x, y, gate=0.0)
410
+ cues = bridge.cues([row.tolist() for row in x])
411
+ windows = [bridge.read(c).window for c in cues]
412
+ wt, wm = bridge.window_tensors(windows, window_len)
413
+ _, l_on = model(x, y, window_tokens=wt, window_mask=wm, gate=1.0)
414
+ on.append(float(l_on)); off.append(float(l_off))
415
+ ppl_on, ppl_off = math.exp(float(np.mean(on))), math.exp(float(np.mean(off)))
416
+ return max(0.0, (ppl_on - ppl_off) / ppl_off), ppl_on, ppl_off
417
+
418
+
419
+ def run_hm_lm(model, journal_factory, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL, seed: int = 0,
420
+ k: int = 4, budget: int = 256, window_len: int = 256, val_batches=None,
421
+ on_disk: bool = False, reopen: bool = True, mark: bool = False) -> dict:
422
+ """`journal_factory()` returns the run's journal (a fresh one on the first
423
+ call; the same on-disk scope reopened on the second). Session A plants the
424
+ frozen protocol's facts through the model's cues; session B reopens the
425
+ journal (a new object, the index recomputed) and probes."""
426
+ facts, gen_hash = generate_facts(n_facts, seed)
427
+ neg, _ = generate_facts(n_negctrl, seed + 10_000)
428
+ chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS]))
429
+ floor = chance + FLOOR_MARGIN
430
+ device = next(model.parameters()).device
431
+ model.eval()
432
+
433
+ # --- session A: plant through the NORMAL gated path, cues from the model ---
434
+ journal = journal_factory()
435
+ bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, device=device, mark=mark)
436
+ admitted = 0
437
+ for i, f in enumerate(facts):
438
+ admitted += int(bridge.write(f.statement, f.schema, now=float(i), entity=f.entity).admitted)
439
+ pointer_of = dict(bridge.pointer_of)
440
+ durable_a = journal.mode == MODE_DURABLE
441
+
442
+ # --- session B: nothing of A but the journal (reopened when on disk) ---
443
+ if on_disk and reopen:
444
+ del bridge
445
+ journal = journal_factory()
446
+ bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device,
447
+ mark=mark)
448
+ storage = lifecycle_declaration(journal)["storage"]
449
+ persistent = bool(on_disk and reopen and durable_a and journal.mode == MODE_DURABLE)
450
+
451
+ on = _probe(model, bridge, facts, True, window_len, pointer_of)
452
+ off = _probe(model, bridge, facts, False, window_len, pointer_of)
453
+ neg_on = _probe(model, bridge, neg, True, window_len, {})
454
+ neg_off = _probe(model, bridge, neg, False, window_len, {})
455
+ delta, ppl_on, ppl_off = skill_delta(model, bridge, val_batches, window_len) if val_batches else (0.0, None, None)
456
+ oracle = mark_oracle(model, bridge, facts, neg, pointer_of)
457
+
458
+ negctrl_claims = neg_on["guess_rate"] # a claim on a never-planted entity
459
+ valid = negctrl_claims < NEGCTRL_INVALID
460
+ dissociation = (delta <= EPSILON_S and (on["recall_strict"] - off["recall_strict"]) >= DELTA
461
+ and off["recall_strict"] <= floor and on["invalid_citation_rate"] <= INVALID_CITATION_MAX
462
+ and valid)
463
+ return {
464
+ "benchmark": "hm_protocol_lm",
465
+ "spec": "docs/benchmarks/hm-protocol.md (frozen thresholds; LM arm amendment 2026-09-12)",
466
+ "thresholds": {"delta": DELTA, "epsilon_s": EPSILON_S, "floor": floor, "chance": chance,
467
+ "negctrl_invalid": NEGCTRL_INVALID, "invalid_citation_max": INVALID_CITATION_MAX},
468
+ "generator": {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed,
469
+ "admitted": admitted},
470
+ "hm_recall_on": on["recall_strict"], "hm_recall_off": off["recall_strict"],
471
+ "hm_gap": on["recall_strict"] - off["recall_strict"],
472
+ "hm_skill_delta": delta, "ppl_on": ppl_on, "ppl_off": ppl_off,
473
+ "hm_negctrl_rate": negctrl_claims, "hm_negctrl_on": neg_on["attr_hit_any"],
474
+ "hm_negctrl_abstain_on": neg_on["abstain_rate"], "hm_negctrl_abstain_off": neg_off["abstain_rate"],
475
+ "hm_false_abstention_on": on["abstain_rate"], "hm_invalid_citation_on": on["invalid_citation_rate"],
476
+ "hm_valid_citation_on": on["valid_citation_rate"], "hm_attr_exact_given_valid": on["attr_exact_given_valid"],
477
+ "hm_retrieval_hit": on["retrieval_hit"], "hm_attention_mass": on["attention_mass"],
478
+ "hm_marks": {"on": on["marks"], "negctrl": neg_on["marks"], "written_in_window": bool(mark)},
479
+ "hm_mark_oracle": oracle,
480
+ "hm_guess_rate_off": off["guess_rate"], "hm_attr_hit_off_by_chance": off["attr_hit_any"],
481
+ "hm_match_acc_on": on["match_acc"], "hm_match_acc_negctrl": neg_on["match_acc"], # RES-21 diagnostics (None without the head)
482
+ "run_valid": valid, "hm_dissociation_pass": int(dissociation),
483
+ "persistent": persistent, "claimable": int(dissociation and persistent),
484
+ "storage": {"on_disk": on_disk, "mode": storage["mode"], "policy": storage["policy"],
485
+ "plaintext_scope": storage["plaintext_scope"]},
486
+ "attribution": {"encoder": on["retrieval_hit"], "reader": on["attention_mass"],
487
+ "answer": on["attr_exact_given_valid"]},
488
+ "verdict": (("PASS" if dissociation else ("INVALID" if not valid else "FAIL"))
489
+ + " -- LM arm, cite-or-abstain contract"
490
+ + ("" if persistent else " -- persistent: false, not claimable")),
491
+ }
492
+
493
+
494
+ # ---------------------------------------------------------------------------- #
495
+ # Session B in a NEW process: the model from its checkpoint, the journal from #
496
+ # the sealed disk, nothing else (ADR-007 D8, the named boundary on the model) #
497
+ # ---------------------------------------------------------------------------- #
498
+ @torch.no_grad()
499
+ def shape_probe(model, bridge: JournalBridge, facts: list[Fact], neg: list[Fact], window_len: int,
500
+ pointer_of: dict[str, str], seed: int = 0) -> dict:
501
+ """A diagnostic outside the frozen protocol (like the memorisation probe): does the
502
+ cite or abstain decision follow the NUMBER OF LINES in the window? Run v5
503
+ (`db028e6a4262`) trained on paired negatives whose withheld window had k - 1 lines,
504
+ while every protocol window has k. Four claim rates, greedy decoding, gate on:
505
+ planted_withheld_short : the planted fact, its own episode withheld, k - 1 lines (the v5 negative as trained)
506
+ planted_withheld_kept : the same, k lines (the k + 1 th match fills the slot: the v6 negative)
507
+ absent_k_lines : a never planted entity, k lines (the protocol's negative control)
508
+ absent_short : the same, the best match dropped, k - 1 lines
509
+ A decision that reads the window claims alike at k - 1 and k lines; a decision that
510
+ counts lines claims at k and abstains at k - 1."""
511
+ rng = np.random.default_rng(seed + 3)
512
+ model.eval()
513
+
514
+ def claims(items, query):
515
+ window, labels = build_read_window(items, bridge.budget, rng)
516
+ wt, wm = bridge.window_tensors([window], window_len)
517
+ toks, _ = generate(model, query, wt, wm, gate=1.0)
518
+ kind, _, _ = parse_contract(toks)
519
+ return int(kind != "unknown"), len(labels)
520
+
521
+ from cortex_c2b.organ_use import withheld_items
522
+ out = {"planted_withheld_short": [0, 0], "planted_withheld_kept": [0, 0],
523
+ "absent_k_lines": [0, 0], "absent_short": [0, 0]}
524
+ lines = {k: [] for k in out}
525
+ own_retrieved = 0
526
+ bridge.pointer_of.update(pointer_of) # the fill of the kept shape draws among the planted
527
+ for f in facts:
528
+ own = pointer_of.get(f.entity)
529
+ if own is None:
530
+ continue
531
+ query = encode_query(f.query)
532
+ cue = bridge.cues([query])[0]
533
+ if own not in bridge.read(cue).pointers:
534
+ continue # only the pairs whose halves differ in shape
535
+ own_retrieved += 1
536
+ short = withheld_items(bridge, cue, own, rng, keep_shape=False) # as v5 built it
537
+ kept = withheld_items(bridge, cue, own, rng, keep_shape=True) # as v6 builds it
538
+ for name, items in (("planted_withheld_short", short), ("planted_withheld_kept", kept)):
539
+ c, n_lines = claims(items, query)
540
+ out[name][0] += c; out[name][1] += 1; lines[name].append(n_lines)
541
+ for f in neg:
542
+ query = encode_query(f.query)
543
+ cue = bridge.cues([query])[0]
544
+ hits = bridge.jp.retrieve(cue, k=bridge.k)
545
+ top = [(e.pointer, payload) for e, payload, _ in hits]
546
+ for name, items in (("absent_k_lines", top), ("absent_short", top[1:])):
547
+ c, n_lines = claims(items, query)
548
+ out[name][0] += c; out[name][1] += 1; lines[name].append(n_lines)
549
+ return {"probe": "shape (lines in the window) -- a diagnostic, not a protocol measure",
550
+ "k": bridge.k, "pairs_with_own_retrieved": own_retrieved,
551
+ **{name: {"claim_rate": (c / n if n else None), "n": n,
552
+ "lines_mean": (float(np.mean(lines[name])) if lines[name] else None)}
553
+ for name, (c, n) in out.items()}}
554
+
555
+
556
+ @torch.no_grad()
557
+ def _head_inputs(model, bridge: JournalBridge, window, window_len: int, query: list[int]):
558
+ """The two vectors the matching head receives at the decision position, for one query
559
+ and one window: the residual stream before the reader and the reader's attended values."""
560
+ wt, wm = bridge.window_tensors([window], window_len)
561
+ idx = torch.tensor([query], dtype=torch.long, device=next(model.parameters()).device)
562
+ model(idx[:, -model.cfg.block_size:], window_tokens=wt, window_mask=wm, gate=1.0)
563
+ blk = model.blocks[model.cfg.journal_block]
564
+ pre, att = blk.last_pre[0, -1], blk.last_attended[0, -1]
565
+ return torch.cat([pre, att]).float().cpu().numpy()
566
+
567
+
568
+ def representation_probe(model, bridge: JournalBridge, facts: list[Fact], window_len: int,
569
+ pointer_of: dict[str, str], seed: int = 0, steps: int = 400, lr: float = 0.5) -> dict:
570
+ """A post hoc linear probe (logistic regression, numpy) on the two vectors the matching
571
+ head receives, over minimal pairs of the given facts: the plain read (present, when the
572
+ own episode was retrieved) against the same read with the own episode withheld and the
573
+ shape kept (absent). Half the pairs train the probe, the other half test it. Reads: a
574
+ test accuracy well above 0.5 means the comparison is linearly extractable from those
575
+ vectors and the trained head's constant answer is a fault of the coupled training; an
576
+ accuracy near 0.5 means the reader's representation does not carry it."""
577
+ from cortex_c2b.organ_use import withheld_items
578
+ rng = np.random.default_rng(seed + 5)
579
+ model.eval()
580
+ bridge.pointer_of.update(pointer_of)
581
+ X, y = [], []
582
+ for f in facts:
583
+ own = pointer_of.get(f.entity)
584
+ if own is None:
585
+ continue
586
+ query = encode_query(f.query)
587
+ cue = bridge.cues([query])[0]
588
+ read = bridge.read(cue)
589
+ if own not in read.pointers:
590
+ continue
591
+ X.append(_head_inputs(model, bridge, read.window, window_len, query)); y.append(1.0)
592
+ items = withheld_items(bridge, cue, own, rng, keep_shape=True)
593
+ window, _ = build_read_window(items, bridge.budget, rng)
594
+ X.append(_head_inputs(model, bridge, window, window_len, query)); y.append(0.0)
595
+ X = np.asarray(X, dtype=np.float64); y = np.asarray(y, dtype=np.float64)
596
+ n = len(y)
597
+ if n < 8:
598
+ return {"probe": "representation (linear, post hoc)", "n_pairs": n // 2, "note": "too few pairs"}
599
+ mu, sd = X.mean(0), X.std(0) + 1e-6
600
+ Xn = (X - mu) / sd
601
+ order = rng.permutation(n // 2) # split by pair, never by example
602
+ train_pairs, test_pairs = order[: len(order) // 2], order[len(order) // 2:]
603
+ tr = np.concatenate([[2 * i, 2 * i + 1] for i in train_pairs]); te = np.concatenate([[2 * i, 2 * i + 1] for i in test_pairs])
604
+ w = np.zeros(Xn.shape[1]); b = 0.0
605
+ for _ in range(steps): # plain gradient descent with a small ridge
606
+ z = Xn[tr] @ w + b; p = 1.0 / (1.0 + np.exp(-z))
607
+ g = p - y[tr]
608
+ w -= lr * (Xn[tr].T @ g / len(tr) + 1e-3 * w); b -= lr * g.mean()
609
+ def acc(ix):
610
+ return float((((Xn[ix] @ w + b) > 0).astype(float) == y[ix]).mean())
611
+ return {"probe": "representation (linear, post hoc): logistic regression on the head's two vectors over minimal pairs",
612
+ "n_pairs": n // 2, "train_pairs": len(train_pairs), "test_pairs": len(test_pairs),
613
+ "train_accuracy": acc(tr), "test_accuracy": acc(te), "chance": 0.5,
614
+ "reading": ("the comparison is linearly extractable from the head's vectors: the trained head's constant is a fault of the coupled training"
615
+ if acc(te) >= 0.8 else
616
+ "the comparison is not linearly extractable from the head's vectors at this size: the reader's representation does not carry it"
617
+ if acc(te) <= 0.6 else "partially extractable; neither reading is clean")}
618
+
619
+
620
+ def representation_probe_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, seed: int = 0,
621
+ k: int = 4, budget: int = 256, window_len: int = 256) -> dict:
622
+ import train
623
+ from cortex_c2b import content_hash
624
+ from cortex_c2b.crypto import key_from_env
625
+ device = torch.device("cpu")
626
+ model = train.VanillaGPT(cfg).to(device)
627
+ ck = torch.load(ckpt_path, map_location=device, weights_only=False)
628
+ model.load_state_dict(ck["model"]); model.eval()
629
+ journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
630
+ bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device)
631
+ facts, gen_hash = generate_facts(n_facts, seed)
632
+ pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
633
+ if content_hash(f.statement.encode("utf-8")) in journal.payloads}
634
+ r = representation_probe(model, bridge, facts, window_len, pointer_of, seed=seed)
635
+ r["checkpoint"] = {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"), "step": ck.get("step")}
636
+ r["generator"] = {"config_hash": gen_hash, "n_facts": n_facts, "seed": seed}
637
+ return r
638
+
639
+
640
+ def shape_probe_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL,
641
+ seed: int = 0, k: int = 4, budget: int = 256, window_len: int = 256) -> dict:
642
+ """The shape probe from the disk alone (checkpoint, sealed journal, key in the environment)."""
643
+ import train
644
+ from cortex_c2b import content_hash
645
+ from cortex_c2b.crypto import key_from_env
646
+ device = torch.device("cpu")
647
+ model = train.VanillaGPT(cfg).to(device)
648
+ ck = torch.load(ckpt_path, map_location=device, weights_only=False)
649
+ model.load_state_dict(ck["model"]); model.eval()
650
+ journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
651
+ bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device)
652
+ facts, gen_hash = generate_facts(n_facts, seed)
653
+ neg, _ = generate_facts(n_negctrl, seed + 10_000)
654
+ pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
655
+ if content_hash(f.statement.encode("utf-8")) in journal.payloads}
656
+ r = shape_probe(model, bridge, facts, neg, window_len, pointer_of, seed=seed)
657
+ r["checkpoint"] = {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
658
+ "step": ck.get("step")}
659
+ r["generator"] = {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed}
660
+ r["journal"] = {"path": str(journal_path), "entries": len(journal), "planted_found": len(pointer_of)}
661
+ return r
662
+
663
+
664
+ def _verdict(on: dict, off: dict, neg_on: dict, floor: float) -> dict:
665
+ """The four gate conditions a new process can evaluate without the skill arm (the skill
666
+ delta is a property of the model on ordinary text and does not depend on the decode
667
+ policy; the in process value stands): gap >= delta, off <= floor, invalid citations
668
+ <= 0.01, claims on never planted entities < 0.10."""
669
+ gap = on["recall_strict"] - off["recall_strict"]
670
+ valid = neg_on["guess_rate"] < NEGCTRL_INVALID
671
+ four = (gap >= DELTA and off["recall_strict"] <= floor and on["invalid_citation_rate"] <= INVALID_CITATION_MAX and valid)
672
+ return {"hm_gap": gap, "run_valid": valid, "gate_conditions_without_skill_arm": int(four),
673
+ "verdict_without_skill_arm": ("PASS" if four else ("INVALID" if not valid else "FAIL")) + " -- the skill delta is the in process arm's"}
674
+
675
+
676
+ def session_b_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL,
677
+ seed: int = 0, k: int = 4, budget: int = 256, window_len: int = 256,
678
+ train_pool_seed: int | None = None, train_pool_n: int = 200, policy: str = "plain") -> dict:
679
+ """Reopen everything from the disk alone and probe. The planted facts are found
680
+ by content addressing (a statement's pointer is the hash of its bytes), so no
681
+ state of session A is needed beyond the journal file and the checkpoint."""
682
+ import train
683
+ from cortex_c2b import content_hash
684
+ from cortex_c2b.crypto import key_from_env
685
+ device = torch.device("cpu")
686
+ model = train.VanillaGPT(cfg).to(device)
687
+ ck = torch.load(ckpt_path, map_location=device, weights_only=False)
688
+ model.load_state_dict(ck["model"]); model.eval()
689
+ journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
690
+ mark = bool(getattr(cfg, "journal_familiarity_mark", False))
691
+ bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device,
692
+ mark=mark)
693
+ facts, gen_hash = generate_facts(n_facts, seed)
694
+ neg, _ = generate_facts(n_negctrl, seed + 10_000)
695
+ pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
696
+ if content_hash(f.statement.encode("utf-8")) in journal.payloads}
697
+ thr = mark_oracle(model, bridge, facts, neg, pointer_of)["threshold"] if policy.startswith("mark-veto") else None
698
+ on = _probe(model, bridge, facts, True, window_len, pointer_of, policy=policy, veto_threshold=thr)
699
+ off = _probe(model, bridge, facts, False, window_len, pointer_of, policy=policy, veto_threshold=thr)
700
+ neg_on = _probe(model, bridge, neg, True, window_len, {}, policy=policy, veto_threshold=thr)
701
+ storage = lifecycle_declaration(journal)["storage"]
702
+ # the memorisation probe (run dc34fcf000aa): the same contract on entities the model SAW during training
703
+ # (the first training pool), planted into a memory scope; a model that reads its window scores alike on
704
+ # seen and unseen entities, a model that memorised entity -> attribute scores far higher on the seen ones
705
+ train_probe = None
706
+ if train_pool_seed is not None:
707
+ from cortex_c2b.organ_use import training_facts, plant_pool
708
+ tf, _, _ = training_facts(train_pool_n, seed=train_pool_seed)
709
+ jb2 = JournalBridge(model, Journal(), k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 2, device=device)
710
+ plant_pool(jb2, tf)
711
+ tp = _probe(model, jb2, tf, True, window_len, dict(jb2.pointer_of), policy=policy)
712
+ train_probe = {"seed": train_pool_seed, "n": len(tf), "recall_strict": tp["recall_strict"],
713
+ "valid_citation": tp["valid_citation_rate"], "invalid_citation": tp["invalid_citation_rate"],
714
+ "abstain_rate": tp["abstain_rate"], "retrieval_hit": tp["retrieval_hit"]}
715
+ chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS])); floor = chance + FLOOR_MARGIN
716
+ verdict_fields = _verdict(on, off, neg_on, floor)
717
+ oracle = mark_oracle(model, bridge, facts, neg, pointer_of)
718
+ return {"benchmark": "hm_protocol_lm_session_b_new_process", "policy": policy, **verdict_fields,
719
+ "hm_marks": {"on": on["marks"], "negctrl": neg_on["marks"], "written_in_window": mark},
720
+ "hm_mark_oracle": oracle,
721
+ "answers": {"on": on["answers"], "off": off["answers"], "negctrl": neg_on["answers"]},
722
+ "training_pool_probe": train_probe,
723
+ "checkpoint": {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
724
+ "step": ck.get("step")},
725
+ "journal": {"path": str(journal_path), "entries": len(journal), "mode": storage["mode"],
726
+ "policy": storage["policy"], "planted_found": len(pointer_of)},
727
+ "generator": {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed},
728
+ "hm_recall_on": on["recall_strict"], "hm_recall_off": off["recall_strict"],
729
+ "hm_false_abstention_on": on["abstain_rate"], "hm_invalid_citation_on": on["invalid_citation_rate"],
730
+ "hm_valid_citation_on": on["valid_citation_rate"], "hm_attr_exact_given_valid": on["attr_exact_given_valid"],
731
+ "hm_retrieval_hit": on["retrieval_hit"], "hm_attention_mass": on["attention_mass"],
732
+ "hm_negctrl_rate": neg_on["guess_rate"], "hm_negctrl_abstain_on": neg_on["abstain_rate"],
733
+ "hm_match_acc_on": on["match_acc"], "hm_match_acc_negctrl": neg_on["match_acc"],
734
+ "persistent": storage["mode"] == MODE_DURABLE, "process": "new (nothing of session A but the disk)"}
735
+
736
+
737
+
738
+ # ---------------------------------------------------------------------------- #
739
+ # The extraction probe (ADR-008 amendment 2026-09-30, declared before any #
740
+ # measurement): one domain's questions against another domain's sealed journal, #
741
+ # read at the organ level and at the system level, never mixed #
742
+ # ---------------------------------------------------------------------------- #
743
+ EXTRACTION_COMMIT_PREFIX = "quantum-cortex extraction probe, domain B seed: "
744
+ EXTRACTION_SEED_COMMITMENTS = ("6942036613cde5fa",) # ADR-008, 2026-09-30: the hash written before the run; the seed after
745
+
746
+
747
+ def seed_commitment(seed: int) -> str:
748
+ """The commitment of a domain B seed: sha256 of the declared prefix and the seed, sixteen hex characters."""
749
+ import hashlib
750
+ return hashlib.sha256(f"{EXTRACTION_COMMIT_PREFIX}{int(seed)}".encode("utf-8")).hexdigest()[:16]
751
+
752
+
753
+ def _arm_scalars(arm: dict) -> dict:
754
+ """An arm without its per line answers (kept separately)."""
755
+ return {k: v for k, v in arm.items() if k != "answers"}
756
+
757
+
758
+ @torch.no_grad()
759
+ def extraction_probe_from_disk(ckpt_path, journal_path, cfg, domain_b_seed: int, n_facts: int = N_FACTS,
760
+ n_negctrl: int = N_NEGCTRL, seed: int = 0, k: int = 4, budget: int = 256,
761
+ window_len: int = 256, policies: tuple = ("plain", "mark-veto+value"),
762
+ commitments: tuple = EXTRACTION_SEED_COMMITMENTS) -> dict:
763
+ """ADR-008 amendment 2026-09-30. On a checkpoint and its sealed journal (domain A, the
764
+ protocol's 200 facts), plant a second domain B (the same generator, a committed seed,
765
+ entities disjoint from A and from the never planted control) into a fresh journal in a
766
+ temporary directory under a throwaway key, then run two arms over the questions of A:
767
+ P, with the journal of A open (session B's own arm through this code path, which must
768
+ reproduce the record to the thousandth), and X, with the journal of B open and the
769
+ journal of A never touched by the arm. X is read at two levels, never mixed: the organ
770
+ (no pointer of A in B's store nor in any read of A's questions on B: a construction
771
+ assertion, exactly zero or the probe aborts) and the system (the claim rate against
772
+ the frozen negative control line, the strict recall of A against the frozen floor of
773
+ the OFF arm, the highest mark of a line of B for a question of A against the oracle's
774
+ threshold). No new threshold. The seed of B must match a commitment written in
775
+ ADR-008 before the run; the journal of B and its key are discarded after."""
776
+ import shutil
777
+ import tempfile
778
+ from pathlib import Path
779
+ import train
780
+ from cortex_c2b import content_hash
781
+ from cortex_c2b.crypto import key_from_env, generate_key
782
+ if seed_commitment(domain_b_seed) not in commitments:
783
+ raise SystemExit("extraction probe: the domain B seed does not match a commitment written in ADR-008 "
784
+ "before the run; nothing was loaded and nothing runs")
785
+ if int(domain_b_seed) in (seed, seed + 10_000, 100_000):
786
+ raise SystemExit("extraction probe: the domain B seed must differ from domain A's, from the never planted "
787
+ "control's and from the oracle's training family's")
788
+ device = torch.device("cpu")
789
+ model = train.VanillaGPT(cfg).to(device)
790
+ ck = torch.load(ckpt_path, map_location=device, weights_only=False)
791
+ model.load_state_dict(ck["model"]); model.eval()
792
+ mark = bool(getattr(cfg, "journal_familiarity_mark", False))
793
+ journal_a = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
794
+ facts_a, gen_hash_a = generate_facts(n_facts, seed)
795
+ neg, _ = generate_facts(n_negctrl, seed + 10_000)
796
+ pointers_a = {content_hash(f.statement.encode("utf-8")) for f in facts_a}
797
+ pointer_of_a = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts_a
798
+ if content_hash(f.statement.encode("utf-8")) in journal_a.payloads}
799
+ # ---- domain B: the same generator, the committed seed, disjoint by entity from A and from the control
800
+ facts_b, gen_hash_b = generate_facts(n_facts, int(domain_b_seed))
801
+ known = {f.entity for f in facts_a} | {f.entity for f in neg}
802
+ collisions = sorted({f.entity for f in facts_b if f.entity in known})
803
+ if collisions:
804
+ raise SystemExit(f"extraction probe: domain B collides with domain A or the never planted control on "
805
+ f"{len(collisions)} entities (first: {collisions[:3]}); aborted, the next committed seed is used")
806
+ tmp = Path(tempfile.mkdtemp(prefix="quantum-cortex-extraction-domain-b-"))
807
+ key_b = generate_key() # a throwaway key: generated here, never written, discarded with the directory
808
+ try:
809
+ journal_b = Journal(tmp / "journal.jsonl", key=key_b, policy=POLICY_STOP)
810
+ planter = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, device=device, mark=mark)
811
+ admitted_b = 0
812
+ for i, f in enumerate(facts_b): # the normal gated write path, this checkpoint's cues, as session A wrote A
813
+ admitted_b += int(planter.write(f.statement, f.schema, now=float(i), entity=f.entity).admitted)
814
+ del planter, journal_b
815
+ journal_b = Journal(tmp / "journal.jsonl", key=key_b, policy=POLICY_STOP) # reopened from the disk, as A is
816
+ # ---- the organ level: none of A's pointers in B's store, none in any read of A's questions on B
817
+ in_store = sorted(p for p in pointers_a if p in journal_b.payloads)
818
+ reader_b = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
819
+ device=device, mark=mark)
820
+ in_reads = 0
821
+ b_marks_for_a: list[float] = []
822
+ for f in facts_a:
823
+ r = reader_b.read(reader_b.cues([encode_query(f.query)])[0])
824
+ in_reads += len(set(r.pointers) & pointers_a)
825
+ if r.scores:
826
+ b_marks_for_a.append(float(max(r.scores)))
827
+ organ = {"a_pointers_in_b_store": len(in_store), "a_pointers_in_reads_of_b": in_reads,
828
+ "queries": len(facts_a), "b_entries": len(journal_b), "b_admitted": admitted_b,
829
+ "max_mark_of_b_line_for_a_query": _stats(b_marks_for_a), "reads_with_lines": len(b_marks_for_a)}
830
+ if in_store or in_reads:
831
+ raise SystemExit("extraction probe: the organ returned a pointer of domain A from the journal of domain B; "
832
+ "a construction failure, nothing is read")
833
+ # ---- the system level, per policy: P on A and X on B, the same shuffle seed, the same threshold
834
+ system = {}
835
+ for policy in policies:
836
+ bridge_a = JournalBridge(model, journal_a, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
837
+ device=device, mark=mark)
838
+ thr = (mark_oracle(model, bridge_a, facts_a, neg, pointer_of_a)["threshold"]
839
+ if policy.startswith("mark-veto") else None)
840
+ P = _probe(model, bridge_a, facts_a, True, window_len, pointer_of_a, policy=policy, veto_threshold=thr)
841
+ bridge_b = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
842
+ device=device, mark=mark)
843
+ X = _probe(model, bridge_b, facts_a, True, window_len, pointer_of_a, policy=policy, veto_threshold=thr)
844
+ above = (sum(1 for a in X["answers"] if a.get("mark_max") is not None and a["mark_max"] > thr)
845
+ if thr is not None else None)
846
+ n = max(1, len(facts_a))
847
+ system[policy] = {
848
+ "veto_threshold": thr,
849
+ "P_same_domain": _arm_scalars(P), "X_cross_domain": _arm_scalars(X),
850
+ "X_claims": X["guess_rate"], "X_claims_under_negctrl_line": bool(X["guess_rate"] < NEGCTRL_INVALID),
851
+ "X_strict_recall_of_a": X["recall_strict"],
852
+ "X_coincident_strict_hits": int(round(X["recall_strict"] * n)),
853
+ "X_b_lines_above_threshold": above,
854
+ "answers": {"P": P["answers"], "X": X["answers"]}}
855
+ finally:
856
+ shutil.rmtree(tmp, ignore_errors=True) # the journal of B and its key leave no trace
857
+ chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS])); floor = chance + FLOOR_MARGIN
858
+ for policy in system:
859
+ system[policy]["X_strict_recall_under_floor"] = bool(system[policy]["X_strict_recall_of_a"] <= floor)
860
+ storage = lifecycle_declaration(journal_a)["storage"]
861
+ return {"benchmark": "extraction_probe", "spec": "ADR-008 amendment 2026-09-30 (declared before any measurement)",
862
+ "thresholds": {"negctrl_invalid": NEGCTRL_INVALID, "floor": floor, "chance": chance,
863
+ "invalid_citation_max": INVALID_CITATION_MAX, "organ_level": 0,
864
+ "note": "none new: the four numbers frozen on 2026-09-12"},
865
+ "domain_a": {"generator_config_hash": gen_hash_a, "seed": seed, "n_facts": n_facts,
866
+ "planted_found": len(pointer_of_a), "n_negctrl_for_oracle": n_negctrl},
867
+ "domain_b": {"seed": int(domain_b_seed), "commitment": seed_commitment(domain_b_seed),
868
+ "generator_config_hash": gen_hash_b, "n_facts": n_facts, "admitted": admitted_b,
869
+ "entities_disjoint_from_a_and_control": True, "sealed": True,
870
+ "key": "throwaway, generated in the process, never written, discarded with the directory"},
871
+ "organ_level": organ, "system_level": system, "policies": list(policies),
872
+ "checkpoint": {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
873
+ "step": ck.get("step")},
874
+ "journal": {"path": str(journal_path), "entries": len(journal_a), "mode": storage["mode"],
875
+ "policy": storage["policy"]},
876
+ "persistent": storage["mode"] == MODE_DURABLE,
877
+ "process": "new (nothing of session A but the disk); domain B planted, reopened from its disk and deleted in this process"}
878
+
879
+ def _cli():
880
+ import argparse, json
881
+ from pathlib import Path
882
+ import train
883
+ ap = argparse.ArgumentParser(description="the H.M. protocol's LM arm, session B in a new process")
884
+ ap.add_argument("--session-b", action="store_true")
885
+ ap.add_argument("--retrieval-probe", action="store_true", help="ADR-008 amendment 2026-09-19: the retrieval policy probe on a checkpoint and its sealed journal (no generation, no training)")
886
+ ap.add_argument("--extraction-probe", action="store_true", help="ADR-008 amendment 2026-09-30: the extraction probe, the questions of domain A against the sealed journal of a second domain B, on a checkpoint and its sealed journal (no training)")
887
+ ap.add_argument("--domain-b-seed", type=int, default=None, help="with --extraction-probe: the seed of domain B, committed by its hash in ADR-008 before the run and revealed with the run")
888
+ ap.add_argument("--replay", default=None, help="post hoc: apply --policy to a session B answers file (Rev66) and print the rates; never a result on the seed the policy was conceived on")
889
+ ap.add_argument("--threshold", type=float, default=None, help="with --replay: override the oracle's veto threshold (a sensitivity sweep)")
890
+ ap.add_argument("--shape-probe", action="store_true",
891
+ help="the shape probe alone (claims at k - 1 against k lines), a diagnostic: metrics/mqar/shape-probe-<run_id>.json")
892
+ ap.add_argument("--representation-probe", action="store_true",
893
+ help="a post hoc linear probe on the matching head's two vectors over minimal pairs: metrics/mqar/representation-probe-<run_id>.json")
894
+ ap.add_argument("--ckpt", required=True); ap.add_argument("--journal", required=True)
895
+ ap.add_argument("--config", required=True, help="the run's config json (the model's shape)")
896
+ ap.add_argument("--out", default=None); ap.add_argument("--seed", type=int, default=0)
897
+ ap.add_argument("--n-facts", type=int, default=N_FACTS); ap.add_argument("--n-negctrl", type=int, default=N_NEGCTRL)
898
+ ap.add_argument("--probe-training-pool", action="store_true",
899
+ help="also probe the contract on the run's FIRST training pool (entities seen in training): memorisation evidence")
900
+ ap.add_argument("--policy", default="plain", choices=list(POLICIES),
901
+ help="decode policy: plain | head | head+pointer (ADR-008 amendment 2026-09-14) | mark-veto | mark-veto+value (amendment 2026-09-18)")
902
+ args = ap.parse_args()
903
+ if not (args.session_b or args.shape_probe or args.representation_probe or args.retrieval_probe or args.extraction_probe):
904
+ if args.replay:
905
+ r = replay_policy(json.loads(Path(args.replay).read_text(encoding="utf-8")), args.policy, threshold=args.threshold)
906
+ print(json.dumps(r, indent=2)); return
907
+ ap.error("one of --session-b / --shape-probe / --representation-probe / --retrieval-probe / --extraction-probe / --replay is required")
908
+ cfg = train.load_config(args.config)
909
+ if args.extraction_probe: # ADR-008 amendment 2026-09-30
910
+ if args.domain_b_seed is None:
911
+ ap.error("--extraction-probe needs --domain-b-seed (the committed seed, revealed with the run)")
912
+ r = extraction_probe_from_disk(args.ckpt, args.journal, cfg, args.domain_b_seed, n_facts=args.n_facts,
913
+ n_negctrl=args.n_negctrl, seed=args.seed, k=cfg.journal_k,
914
+ budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
915
+ out = Path(args.out) if args.out else Path("metrics/mqar") / f"extraction-probe-{r['checkpoint']['run_id']}.json"
916
+ out.parent.mkdir(parents=True, exist_ok=True)
917
+ out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
918
+ print(f"retained: {out}")
919
+ short = {"checkpoint": r["checkpoint"],
920
+ "domain_b": {k2: r["domain_b"][k2] for k2 in ("seed", "commitment", "admitted")},
921
+ "organ_level": {k2: r["organ_level"][k2] for k2 in ("a_pointers_in_b_store", "a_pointers_in_reads_of_b", "queries")},
922
+ "system_level": {}}
923
+ for pol, d in r["system_level"].items():
924
+ short["system_level"][pol] = {k2: v for k2, v in d.items() if k2 in (
925
+ "veto_threshold", "X_claims", "X_claims_under_negctrl_line", "X_strict_recall_of_a",
926
+ "X_strict_recall_under_floor", "X_coincident_strict_hits", "X_b_lines_above_threshold")}
927
+ short["system_level"][pol]["P"] = {k3: d["P_same_domain"][k3] for k3 in (
928
+ "recall_strict", "invalid_citation_rate", "abstain_rate", "valid_citation_rate")}
929
+ print(json.dumps(short, indent=2))
930
+ return
931
+ if args.retrieval_probe: # ADR-008 amendment 2026-09-19 (RES-24)
932
+ from cortex_c2b.crypto import key_from_env
933
+ from cortex_c2b import content_hash
934
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
935
+ model = train.VanillaGPT(cfg).to(device)
936
+ ck = torch.load(args.ckpt, map_location=device, weights_only=False)
937
+ model.load_state_dict(ck["model"]); model.eval()
938
+ journal = Journal(args.journal, key=key_from_env(), policy=POLICY_STOP)
939
+ facts, _ = generate_facts(args.n_facts, args.seed)
940
+ neg, _ = generate_facts(args.n_negctrl, args.seed + 10_000)
941
+ pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
942
+ if content_hash(f.statement.encode("utf-8")) in journal.payloads}
943
+ r = retrieval_probe(model, journal, facts, neg, pointer_of, k=cfg.journal_k, seed=args.seed)
944
+ r["checkpoint"] = {"path": str(args.ckpt), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash")}
945
+ r["journal"] = {"path": str(args.journal), "entries": len(journal._entries)}
946
+ out = Path(args.out) if args.out else Path("metrics/mqar") / f"retrieval-probe-{ck.get('run_id')}.json"
947
+ out.parent.mkdir(parents=True, exist_ok=True)
948
+ out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
949
+ print(f"retained: {out}")
950
+ print(json.dumps({"reading": r["reading"], "rankings": {k2: {kk: v2[kk] for kk in ("own_in_candidates", "own_in_top_k")} for k2, v2 in r["rankings"].items()}}, indent=2))
951
+ return
952
+ if args.representation_probe:
953
+ r = representation_probe_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, seed=args.seed,
954
+ k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
955
+ out = Path(args.out) if args.out else Path("metrics/mqar") / f"representation-probe-{r['checkpoint']['run_id']}.json"
956
+ out.parent.mkdir(parents=True, exist_ok=True)
957
+ out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
958
+ print(f"retained: {out}")
959
+ print(json.dumps(r, indent=2))
960
+ return
961
+ if args.shape_probe:
962
+ r = shape_probe_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, n_negctrl=args.n_negctrl, seed=args.seed,
963
+ k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
964
+ out = Path(args.out) if args.out else Path("metrics/mqar") / f"shape-probe-{r['checkpoint']['run_id']}.json"
965
+ out.parent.mkdir(parents=True, exist_ok=True)
966
+ out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
967
+ print(f"retained: {out}")
968
+ print(json.dumps(r, indent=2))
969
+ return
970
+ r = session_b_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, n_negctrl=args.n_negctrl,
971
+ seed=args.seed, k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes,
972
+ train_pool_seed=(cfg.journal_train_seed if args.probe_training_pool else None),
973
+ train_pool_n=cfg.journal_pool_facts, policy=args.policy)
974
+ suffix = "" if args.policy == "plain" else "-" + args.policy.replace("+", "-")
975
+ out = Path(args.out) if args.out else Path("metrics/mqar") / f"hm-lm-{r['checkpoint']['run_id']}-session-b{suffix}.json"
976
+ out.parent.mkdir(parents=True, exist_ok=True)
977
+ out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
978
+ print(f"retained: {out}")
979
+ print(json.dumps(r, indent=2))
980
+
981
+
982
+ if __name__ == "__main__":
983
+ _cli()