betterwithage commited on
Commit
528f7c0
·
verified ·
1 Parent(s): 929fc8b

re-sweep wave2: ship 4 REAL 0-CDN operator surfaces — /sovereign-search (tantivy-wasm MIT), /consensus-clusters (ngraph.leiden MIT), /mission-ledger (research-arcade MIT), /budget-router (BudgetMem+MemSkill Apache, GraphPlanner MIT)

Browse files
Files changed (7) hide show
  1. Dockerfile +12 -0
  2. NOTICE +53 -0
  3. serve.py +25 -0
  4. szl_budget_router.py +349 -0
  5. szl_consensus_clusters.py +306 -0
  6. szl_mission_ledger.py +217 -0
  7. szl_sovereign_search.py +346 -0
Dockerfile CHANGED
@@ -392,6 +392,18 @@ COPY szl_abacus_verify.py ./szl_abacus_verify.py
392
  COPY szl_decision_uncertainty.py ./szl_decision_uncertainty.py
393
  COPY szl_gor_audit.py ./szl_gor_audit.py
394
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  CMD ["python", "serve.py"]
396
 
397
 
 
392
  COPY szl_decision_uncertainty.py ./szl_decision_uncertainty.py
393
  COPY szl_gor_audit.py ./szl_gor_audit.py
394
 
395
+ # ADDITIVE (RE-SWEEP WAVE 2, 2026-06, Yachay): four MORE operator surfaces from the
396
+ # P0 re-sweep backlog, each adopting a PERMISSIVE pattern (MIT/Apache; NOTICE updated)
397
+ # and evolving it into an a11oy-native mechanism. Stdlib-only (no torch/numpy/CDN);
398
+ # graph tabs render with the already-vendored cytoscape. Per-file COPY (this Dockerfile
399
+ # never uses `COPY . .`) -- without these the imports fail and /sovereign-search,
400
+ # /consensus-clusters, /mission-ledger, /budget-router fall through to the SPA shell.
401
+ # serve.py imports them try/except-guarded.
402
+ COPY szl_sovereign_search.py ./szl_sovereign_search.py
403
+ COPY szl_consensus_clusters.py ./szl_consensus_clusters.py
404
+ COPY szl_mission_ledger.py ./szl_mission_ledger.py
405
+ COPY szl_budget_router.py ./szl_budget_router.py
406
+
407
  CMD ["python", "serve.py"]
408
 
409
 
NOTICE CHANGED
@@ -116,3 +116,56 @@ repositories, then evolve each into an original a11oy mechanism:
116
 
117
  Copyright (c) of each library belongs to its respective authors. Full license
118
  texts ship with each minified bundle's upstream distribution.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  Copyright (c) of each library belongs to its respective authors. Full license
118
  texts ship with each minified bundle's upstream distribution.
119
+
120
+ ================================================================================
121
+ RE-SWEEP WAVE 2 PATTERN ADOPTION (fashion-thinking — permissive only, NOTICE
122
+ attribution, EVOLVED into a11oy-native mechanisms; NO upstream source copied;
123
+ stdlib-only clean-room re-implementations, 0 CDN; graph tabs use the already-
124
+ vendored cytoscape)
125
+ ================================================================================
126
+ Four additional additive operator surfaces adopt the PATTERN of the following
127
+ founder-followed repositories, then evolve each into an original a11oy mechanism:
128
+
129
+ tantivy-wasm — (c) phiresky — MIT License
130
+ https://github.com/phiresky/tantivy-wasm
131
+ Adopted: sovereign, in-deployment full-text search (no external search service,
132
+ no egress — the index lives with the data). Evolved -> Sovereign Corpus Search
133
+ (/sovereign-search): a dependency-free, stdlib-only Okapi BM25 engine indexing
134
+ the live governance corpus (model catalog, decision receipts, doctrine) so an
135
+ operator runs sub-second relevance search with ZERO backend service and ZERO CDN.
136
+ No Tantivy/Rust/WASM code; clean-room.
137
+
138
+ ngraph.leiden — (c) Andrei Kashcha (anvaka) — MIT License
139
+ https://github.com/anvaka/ngraph.leiden
140
+ Adopted: fast, deterministic community detection (Leiden/Louvain modularity).
141
+ Evolved -> Consensus Clusters (/consensus-clusters): a clean-room Louvain
142
+ modularity optimizer over a similarity graph of governed decision nodes (edges
143
+ weighted by shared sensitivity / mission tier / model family) that surfaces
144
+ consensus clusters and isolated outliers. No ngraph/JS source; rendered with the
145
+ already-vendored cytoscape (0 CDN).
146
+
147
+ research-arcade — (c) ulab-uiuc — MIT License
148
+ https://github.com/ulab-uiuc/research-arcade
149
+ Adopted: heterogeneous graph interface unifying disparate structured sources with
150
+ typed nodes/edges. Evolved -> Mission Ledger (/mission-ledger): a live
151
+ heterogeneous knowledge graph over documents -> objectives -> decisions (decisions
152
+ drawn from the real Khipu chain) with typed edges (cites/supports/resulted_in) so
153
+ provenance from source material to governed action is explicit. No research-arcade
154
+ source, no SQL backend; rendered with the already-vendored cytoscape (0 CDN).
155
+
156
+ BudgetMem — (c) ViktorAxelsen — Apache-2.0 [ICML'26]
157
+ https://github.com/ViktorAxelsen/BudgetMem
158
+ MemSkill — (c) ViktorAxelsen — Apache-2.0
159
+ https://github.com/ViktorAxelsen/MemSkill
160
+ GraphPlanner — (c) ulab-uiuc — MIT License [ICLR'26]
161
+ https://github.com/ulab-uiuc/GraphPlanner
162
+ Adopted: BudgetMem module-level budget tiers (Low/Mid/High) + query-aware tier
163
+ router; MemSkill meta-memory ("learn WHAT to preserve"); GraphPlanner's
164
+ route-by-graph-signal idea. Evolved -> Budget-Tier Router + Decision Skeletons
165
+ (/budget-router): routes each governed decision by RISK (sensitivity x
166
+ reversibility) to the cheapest mission budget tier — Tactical (<=1s) / Operational
167
+ (<=10s) / Strategic (<=60s) — then to the smallest sufficient air-gap-safe model;
168
+ distils reusable "Decision Skeletons" as bounded meta-memory. Stdlib-only; no
169
+ BudgetMem/MemSkill/GraphPlanner source; clean-room.
170
+
171
+ Copyright (c) of each library belongs to its respective authors.
serve.py CHANGED
@@ -437,6 +437,31 @@ for _mined_mod in (
437
  except Exception as _mined_e: # additive: never break the Space
438
  print(f"[a11oy] MINED-UPGRADE {_mined_mod} NOT registered: {_mined_e!r}; SPA + API unaffected", file=sys.stderr)
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
 
441
  # ---------------------------------------------------------------------------
442
  # Load gates manifest at startup
 
437
  except Exception as _mined_e: # additive: never break the Space
438
  print(f"[a11oy] MINED-UPGRADE {_mined_mod} NOT registered: {_mined_e!r}; SPA + API unaffected", file=sys.stderr)
439
 
440
+ # ---------------------------------------------------------------------------
441
+ # ADDITIVE (RE-SWEEP WAVE 2, 2026-06, Yachay): four MORE self-contained,
442
+ # dependency-free operator surfaces from the P0 re-sweep backlog. Same doctrine:
443
+ # adopt a PERMISSIVE pattern (MIT/Apache), attribute in NOTICE, EVOLVE to an
444
+ # a11oy-native mechanism. All stdlib-only (no torch/numpy/CDN); graph tabs render
445
+ # with the ALREADY-VENDORED cytoscape (/vendor/*, 0 CDN). Registered BEFORE the SPA
446
+ # catch-all; try/except-guarded so a missing dep can NEVER take the Space down.
447
+ # 5) szl_sovereign_search (tantivy-wasm MIT, phiresky) -> /sovereign-search
448
+ # 6) szl_consensus_clusters (ngraph.leiden MIT, anvaka) -> /consensus-clusters
449
+ # 7) szl_mission_ledger (research-arcade MIT, ulab-uiuc) -> /mission-ledger
450
+ # 8) szl_budget_router (BudgetMem+MemSkill Apache; GraphPlanner MIT) -> /budget-router
451
+ # ---------------------------------------------------------------------------
452
+ for _resweep_mod in (
453
+ "szl_sovereign_search",
454
+ "szl_consensus_clusters",
455
+ "szl_mission_ledger",
456
+ "szl_budget_router",
457
+ ):
458
+ try:
459
+ _m = __import__(_resweep_mod)
460
+ _status = _m.register(app, ns="a11oy")
461
+ print(f"[a11oy] RESWEEP-W2 {_resweep_mod} registered: {_status}", file=sys.stderr)
462
+ except Exception as _rs_e: # additive: never break the Space
463
+ print(f"[a11oy] RESWEEP-W2 {_resweep_mod} NOT registered: {_rs_e!r}; SPA + API unaffected", file=sys.stderr)
464
+
465
 
466
  # ---------------------------------------------------------------------------
467
  # Load gates manifest at startup
szl_budget_router.py ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
3
+ # Authored by A11oy Full-Stack Team. Co-Authored-By: Perplexity Computer Agent.
4
+ #
5
+ # THIRD-PARTY PATTERN ADOPTION (fashion-thinking, NOTICE attribution):
6
+ # BudgetMem — ViktorAxelsen/BudgetMem — Apache-2.0 —
7
+ # https://github.com/ViktorAxelsen/BudgetMem
8
+ # MemSkill — ViktorAxelsen/MemSkill — Apache-2.0 —
9
+ # https://github.com/ViktorAxelsen/MemSkill
10
+ # GraphPlanner — ulab-uiuc/GraphPlanner — MIT —
11
+ # https://github.com/ulab-uiuc/GraphPlanner
12
+ #
13
+ # We adopt THREE patterns and EVOLVE them into one a11oy-native mechanism:
14
+ # • BudgetMem's module-level BUDGET TIERS (Low/Mid/High) + a lightweight query-aware
15
+ # budget-tier ROUTER. We map the tiers onto Warhacker MISSION CONSTRAINTS —
16
+ # Tactical (≤1s), Operational (≤10s), Strategic (≤1min) — so sovereign hardware is
17
+ # never choked by unnecessary chain-of-thought on a time-critical decision.
18
+ # • GraphPlanner's idea of routing by graph-derived signal: we route by DECISION RISK
19
+ # (sensitivity × time-budget × reversibility) rather than by learned RL value.
20
+ # • MemSkill's meta-memory ("learn WHAT to preserve"): we evolve it into "DECISION
21
+ # SKELETONS" — reusable, distilled patterns of how the substrate successfully
22
+ # navigated a class of governed trade-offs, kept across runs.
23
+ # Original, dependency-free implementation — no BudgetMem/MemSkill/GraphPlanner
24
+ # source, no torch/numpy. Reuses the live governance catalog from
25
+ # szl_governance_gateway when present. Λ = Conjecture 1. Doctrine v11 LOCKED 749/14/163.
26
+ """szl_budget_router — ADDITIVE cost-aware budget-tier router + Decision Skeletons.
27
+
28
+ Endpoints (mounted before the SPA catch-all):
29
+ GET /budget-router — operator tab (HTML, 0 CDN)
30
+ GET /api/a11oy/v1/budget/tiers — the 3 mission budget tiers
31
+ POST /api/a11oy/v1/budget/route — route a decision by risk → tier → model
32
+ GET /api/a11oy/v1/budget/skeletons — learned Decision Skeletons (meta-memory)
33
+ """
34
+ from __future__ import annotations
35
+
36
+ import re
37
+ import time
38
+ from typing import Any
39
+
40
+ from fastapi import FastAPI, Request
41
+ from fastapi.responses import HTMLResponse, JSONResponse
42
+
43
+ DOCTRINE = {"version": "v11", "counts": "749/14/163", "lambda": "Conjecture 1"}
44
+
45
+ # --- Mission budget tiers (BudgetMem Low/Mid/High → Warhacker mission constraints) ---
46
+ TIERS: list[dict[str, Any]] = [
47
+ {"tier": "TACTICAL", "budget": "LOW", "deadline_s": 1, "max_model_tier": "T1",
48
+ "cot": "none", "note": "time-critical: cheapest sufficient model, no chain-of-thought"},
49
+ {"tier": "OPERATIONAL", "budget": "MID", "deadline_s": 10, "max_model_tier": "T3",
50
+ "cot": "short", "note": "balanced: mid-tier reasoning with a short rationale"},
51
+ {"tier": "STRATEGIC", "budget": "HIGH", "deadline_s": 60, "max_model_tier": "T6",
52
+ "cot": "full", "note": "deliberate: high-tier model, full chain-of-thought permitted"},
53
+ ]
54
+ _TIER_BY_NAME = {t["tier"]: t for t in TIERS}
55
+
56
+ # Reversibility cues raise risk: an irreversible action deserves a bigger budget.
57
+ _IRREVERSIBLE_RX = re.compile(
58
+ r"\b(launch|fire|strike|delete|destroy|deploy|commit|authoriz|release|publish|"
59
+ r"terminate|engage|weapon|kill)\b", re.I)
60
+ _REVERSIBLE_RX = re.compile(r"\b(draft|preview|simulate|estimate|summari|triage|sort|list|query)\b", re.I)
61
+
62
+
63
+ def _classify_sensitivity(text: str, declared: str | None) -> dict[str, Any]:
64
+ """Reuse the live governance classifier when present; else a safe fallback."""
65
+ try:
66
+ import szl_governance_gateway as _gg # type: ignore
67
+ return _gg.classify(text, declared)
68
+ except Exception:
69
+ return {"class": (declared or "PUBLIC").upper(), "rank": 0, "signals": ["fallback"]}
70
+
71
+
72
+ def _reversibility(text: str) -> tuple[float, str]:
73
+ if _IRREVERSIBLE_RX.search(text or ""):
74
+ return 1.0, "irreversible"
75
+ if _REVERSIBLE_RX.search(text or ""):
76
+ return 0.0, "reversible"
77
+ return 0.5, "uncertain"
78
+
79
+
80
+ def assess_risk(query: str, *, declared: str | None = None,
81
+ deadline_s: float | None = None) -> dict[str, Any]:
82
+ """Decision risk = f(sensitivity, reversibility). Higher risk → higher tier.
83
+ A tighter deadline CAPS the tier (you cannot afford full CoT under 1s)."""
84
+ sens = _classify_sensitivity(query, declared)
85
+ sens_norm = sens["rank"] / 3.0 # 0..1 over PUBLIC..SECRET
86
+ rev_score, rev_label = _reversibility(query)
87
+ # Weighted risk: sensitivity dominates, reversibility modulates.
88
+ risk = round(0.65 * sens_norm + 0.35 * rev_score, 4)
89
+ if risk >= 0.6:
90
+ want = "STRATEGIC"
91
+ elif risk >= 0.3:
92
+ want = "OPERATIONAL"
93
+ else:
94
+ want = "TACTICAL"
95
+ # Deadline cap: if the operator imposes a tighter deadline, downshift the tier.
96
+ chosen = want
97
+ capped_by_deadline = False
98
+ if deadline_s is not None:
99
+ affordable = [t["tier"] for t in TIERS if t["deadline_s"] <= deadline_s]
100
+ order = ["TACTICAL", "OPERATIONAL", "STRATEGIC"]
101
+ if affordable:
102
+ best_affordable = max(affordable, key=lambda n: order.index(n))
103
+ if order.index(best_affordable) < order.index(want):
104
+ chosen = best_affordable
105
+ capped_by_deadline = True
106
+ else:
107
+ chosen = "TACTICAL"; capped_by_deadline = True
108
+ return {
109
+ "sensitivity": sens, "reversibility": {"score": rev_score, "label": rev_label},
110
+ "risk": risk, "risk_band": ("HIGH" if risk >= 0.6 else "MODERATE" if risk >= 0.3 else "LOW"),
111
+ "risk_implied_tier": want, "chosen_tier": chosen,
112
+ "capped_by_deadline": capped_by_deadline,
113
+ }
114
+
115
+
116
+ def _pick_model(max_model_tier: str, sensitivity_rank: int) -> dict[str, Any] | None:
117
+ """Smallest sufficient model within the tier ceiling, respecting the air-gap floor."""
118
+ try:
119
+ import szl_governance_gateway as _gg # type: ignore
120
+ cat = getattr(_gg, "CATALOG", [])
121
+ order = getattr(_gg, "_TIER_ORDER", {})
122
+ floor = getattr(_gg, "_AIRGAP_FLOOR", 2)
123
+ except Exception:
124
+ return None
125
+ ceil = order.get(max_model_tier, 9)
126
+ airgap_required = sensitivity_rank >= floor
127
+ elig = [m for m in cat
128
+ if order.get(m["tier"], 9) <= ceil
129
+ and (not airgap_required or m.get("zone") == "AIRGAP")]
130
+ if not elig:
131
+ return None
132
+ elig.sort(key=lambda m: (order.get(m["tier"], 9), -m.get("ctx", 0)))
133
+ return elig[0]
134
+
135
+
136
+ def route(query: str, *, declared: str | None = None,
137
+ deadline_s: float | None = None) -> dict[str, Any]:
138
+ risk = assess_risk(query, declared=declared, deadline_s=deadline_s)
139
+ tier = _TIER_BY_NAME[risk["chosen_tier"]]
140
+ model = _pick_model(tier["max_model_tier"], risk["sensitivity"]["rank"])
141
+ decision = {
142
+ "query": query, "tier": tier, "risk": risk, "chosen_model": model,
143
+ "airgap_required": risk["sensitivity"]["rank"] >= 2,
144
+ "policy": "risk(sensitivity×reversibility) → mission tier (deadline-capped) → smallest sufficient air-gap-safe model",
145
+ "honest": None if model else "no model within tier ceiling + air-gap floor — raise the tier or add an AIRGAP model",
146
+ }
147
+ _learn_skeleton(decision)
148
+ return decision
149
+
150
+
151
+ # ---------------------------------------------------------------------------
152
+ # Decision Skeletons (MemSkill meta-memory, evolved). A skeleton is a DISTILLED,
153
+ # reusable pattern for a class of decisions: (risk_band, tier, airgap) → how it was
154
+ # handled, plus how many times that pattern recurred. We keep WHAT generalizes
155
+ # (the band+tier+model family) and forget the per-query noise — the "skill of
156
+ # remembering" applied to governance trade-offs. In-memory ring (bounded), no PII.
157
+ # ---------------------------------------------------------------------------
158
+ _SKELETONS: dict[str, dict[str, Any]] = {}
159
+
160
+
161
+ def _learn_skeleton(decision: dict[str, Any]) -> None:
162
+ band = decision["risk"]["risk_band"]
163
+ tier = decision["tier"]["tier"]
164
+ airgap = decision["airgap_required"]
165
+ model_fam = ((decision.get("chosen_model") or {}).get("id") or "—").split("-")[0]
166
+ key = f"{band}|{tier}|{'airgap' if airgap else 'cloud'}|{model_fam}"
167
+ sk = _SKELETONS.get(key)
168
+ if sk is None:
169
+ sk = {
170
+ "key": key, "risk_band": band, "tier": tier,
171
+ "airgap": airgap, "model_family": model_fam,
172
+ "uses": 0, "first_seen": time.time(), "last_seen": time.time(),
173
+ "lesson": _lesson(band, tier, airgap),
174
+ }
175
+ _SKELETONS[key] = sk
176
+ sk["uses"] += 1
177
+ sk["last_seen"] = time.time()
178
+ # Bound the meta-memory: keep the 64 most-used skeletons (forget the rest).
179
+ if len(_SKELETONS) > 64:
180
+ worst = min(_SKELETONS.values(), key=lambda s: (s["uses"], s["last_seen"]))
181
+ _SKELETONS.pop(worst["key"], None)
182
+
183
+
184
+ def _lesson(band: str, tier: str, airgap: bool) -> str:
185
+ z = "air-gapped (classified)" if airgap else "cloud-eligible"
186
+ return (f"{band}-risk decisions routed to the {tier} budget tier on {z} compute "
187
+ f"resolved within budget; reuse this skeleton for the same risk class.")
188
+
189
+
190
+ def skeletons() -> dict[str, Any]:
191
+ sks = sorted(_SKELETONS.values(), key=lambda s: s["uses"], reverse=True)
192
+ return {"skeleton_count": len(sks), "skeletons": sks,
193
+ "hint": None if sks else "no skeletons yet — route decisions via POST /api/a11oy/v1/budget/route to learn them"}
194
+
195
+
196
+ def register(app: FastAPI, ns: str = "a11oy") -> str:
197
+ @app.get(f"/api/{ns}/v1/budget/tiers", include_in_schema=False)
198
+ async def _tiers() -> JSONResponse:
199
+ return JSONResponse({"doctrine": DOCTRINE, "tiers": TIERS,
200
+ "pattern_source": "ViktorAxelsen/BudgetMem (Apache-2.0) — module budget tiers, evolved to mission constraints"})
201
+
202
+ @app.post(f"/api/{ns}/v1/budget/route", include_in_schema=False)
203
+ async def _route(request: Request) -> JSONResponse:
204
+ try:
205
+ body = await request.json()
206
+ except Exception:
207
+ body = {}
208
+ q = (body or {}).get("query") or (body or {}).get("q") or ""
209
+ declared = (body or {}).get("classification")
210
+ dl = (body or {}).get("deadline_s")
211
+ try:
212
+ dl = float(dl) if dl is not None else None
213
+ except Exception:
214
+ dl = None
215
+ if not q:
216
+ return JSONResponse({"error": "provide {query: ...}"}, status_code=400)
217
+ return JSONResponse({"doctrine": DOCTRINE, **route(q, declared=declared, deadline_s=dl)})
218
+
219
+ @app.get(f"/api/{ns}/v1/budget/skeletons", include_in_schema=False)
220
+ async def _sk() -> JSONResponse:
221
+ return JSONResponse({"doctrine": DOCTRINE, **skeletons(),
222
+ "pattern_source": "ViktorAxelsen/MemSkill (Apache-2.0) — meta-memory, evolved to Decision Skeletons"})
223
+
224
+ @app.get("/budget-router", include_in_schema=False)
225
+ async def _page() -> HTMLResponse:
226
+ return HTMLResponse(_PAGE_HTML)
227
+
228
+ return f"budget-router mounted: GET /budget-router + /api/{ns}/v1/budget/(tiers|route|skeletons)"
229
+
230
+
231
+ _PAGE_HTML = """<!DOCTYPE html>
232
+ <html lang="en"><head><meta charset="utf-8">
233
+ <meta name="viewport" content="width=device-width,initial-scale=1">
234
+ <title>a11oy · Budget-Tier Router + Decision Skeletons</title>
235
+ <style>
236
+ :root{--bg:#0b0f14;--panel:#121922;--ink:#e8eef5;--muted:#8aa0b4;--gold:#d9b46a;
237
+ --green:#3fb950;--blue:#1f6feb;--red:#f85149;--amber:#d29922;--line:#1e2a36;}
238
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);
239
+ font:15px/1.5 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
240
+ .wrap{max-width:980px;margin:0 auto;padding:24px 18px 64px}
241
+ h1{font-size:24px;margin:.2em 0}.sub{color:var(--muted);margin:0 0 18px}
242
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;margin:14px 0}
243
+ .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
244
+ input,select{background:#0d141c;border:1px solid var(--line);border-radius:8px;color:var(--ink);padding:9px 11px;font-size:14px}
245
+ input{flex:1;min-width:200px}
246
+ button{background:var(--gold);color:#1a1205;border:0;border-radius:8px;padding:9px 16px;font-weight:700;cursor:pointer}
247
+ button:hover{filter:brightness(1.08)}
248
+ .pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600}
249
+ .green{background:rgba(63,185,80,.15);color:var(--green)}
250
+ .blue{background:rgba(31,111,235,.18);color:#79b8ff}
251
+ .red{background:rgba(248,81,73,.15);color:var(--red)}
252
+ .amber{background:rgba(210,153,34,.15);color:var(--amber)}
253
+ .tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:6px}
254
+ .tcard{background:#0d141c;border:1px solid var(--line);border-radius:10px;padding:12px}
255
+ .tcard h3{margin:.1em 0;font-size:16px}
256
+ pre{background:#0d141c;border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;font-size:12.5px;white-space:pre-wrap}
257
+ .foot{color:var(--muted);font-size:12px;margin-top:24px;border-top:1px solid var(--line);padding-top:12px}
258
+ code{color:var(--gold)}
259
+ .sk{border-top:1px solid var(--line);padding:10px 0}.sk:first-child{border-top:0}
260
+ .sk .k{color:var(--gold);font-weight:700}.sk .l{color:var(--muted);font-size:13px}
261
+ .uses{float:right;color:var(--green);font-variant-numeric:tabular-nums}
262
+ .chips{margin-top:10px}.chip{display:inline-block;margin:2px 4px 2px 0;padding:4px 10px;border:1px solid var(--line);
263
+ border-radius:999px;font-size:12px;color:var(--muted);cursor:pointer;background:#0d141c}.chip:hover{border-color:var(--gold);color:var(--gold)}
264
+ </style></head>
265
+ <body><div class="wrap">
266
+ <h1>Budget-Tier Router <span class="pill green">cost-aware</span> <span class="pill blue">Decision Skeletons</span></h1>
267
+ <p class="sub">Routes each governed decision by <b>risk</b> (sensitivity × reversibility) to the
268
+ cheapest mission budget tier that still satisfies it — <b>Tactical (≤1s)</b>, <b>Operational (≤10s)</b>,
269
+ <b>Strategic (≤1min)</b> — then to the smallest sufficient air-gap-safe model. Patterns from
270
+ <code>BudgetMem</code> + <code>MemSkill</code> (Apache-2.0) and <code>GraphPlanner</code> (MIT),
271
+ evolved into mission-bound budget routing with reusable Decision Skeletons. 0 CDN.</p>
272
+
273
+ <div class="card">
274
+ <h3 style="margin-top:0">Mission budget tiers</h3>
275
+ <div class="tiers" id="tiers"></div>
276
+ </div>
277
+
278
+ <div class="card">
279
+ <h3 style="margin-top:0">Route a decision</h3>
280
+ <div class="row">
281
+ <input id="q" placeholder="describe the decision (e.g. authorize drone strike on classified grid ref)…"/>
282
+ <select id="cls"><option value="">auto-classify</option><option>PUBLIC</option><option>INTERNAL</option><option>RESTRICTED</option><option>SECRET</option></select>
283
+ <select id="dl"><option value="">no deadline</option><option value="1">≤1s</option><option value="10">≤10s</option><option value="60">≤60s</option></select>
284
+ <button id="go">Route</button>
285
+ </div>
286
+ <div class="chips" id="chips">
287
+ <span class="chip">summarize today's maritime intel feed</span>
288
+ <span class="chip">authorize a drone strike on classified grid ref</span>
289
+ <span class="chip">draft an internal roadmap memo</span>
290
+ </div>
291
+ <pre id="out" style="margin-top:12px">Route a decision to see the tier + model + risk verdict.</pre>
292
+ </div>
293
+
294
+ <div class="card">
295
+ <div class="row" style="justify-content:space-between"><h3 style="margin:0">Decision Skeletons (learned)</h3>
296
+ <button id="ref" style="background:#1f6feb;color:#fff">Refresh</button></div>
297
+ <div id="sks" style="margin-top:8px">No skeletons yet.</div>
298
+ </div>
299
+
300
+ <p class="foot">a11oy · Doctrine v11 LOCKED 749/14/163 · Λ = Conjecture 1 ·
301
+ patterns: BudgetMem + MemSkill (Apache-2.0), GraphPlanner (MIT), evolved · sovereign 0-CDN.</p>
302
+ </div>
303
+ <script>
304
+ const $=s=>document.querySelector(s);
305
+ function band(b){return b==='HIGH'?'red':b==='MODERATE'?'amber':'green'}
306
+ async function tiers(){
307
+ const d=await(await fetch('/api/a11oy/v1/budget/tiers')).json();
308
+ const box=$('#tiers');box.innerHTML='';
309
+ for(const t of d.tiers){
310
+ const el=document.createElement('div');el.className='tcard';
311
+ el.innerHTML='<h3>'+t.tier+' <span class="pill blue">'+t.budget+'</span></h3>'
312
+ +'<div class="l" style="color:var(--muted);font-size:13px">deadline ≤'+t.deadline_s+'s · max model '+t.max_model_tier+' · CoT '+t.cot+'</div>'
313
+ +'<div style="margin-top:6px;font-size:13px">'+t.note+'</div>';
314
+ box.appendChild(el);
315
+ }
316
+ }
317
+ async function route(){
318
+ const q=$('#q').value;if(!q)return;
319
+ const body={query:q};const c=$('#cls').value;const dl=$('#dl').value;
320
+ if(c)body.classification=c;if(dl)body.deadline_s=Number(dl);
321
+ const d=await(await fetch('/api/a11oy/v1/budget/route',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(body)})).json();
322
+ const r=d.risk||{};const m=d.chosen_model;
323
+ $('#out').textContent=
324
+ 'TIER: '+(d.tier&&d.tier.tier)+' (budget '+(d.tier&&d.tier.budget)+', deadline ≤'+(d.tier&&d.tier.deadline_s)+'s)\\n'
325
+ +'RISK: '+r.risk+' ['+r.risk_band+'] sensitivity='+(r.sensitivity&&r.sensitivity.class)
326
+ +' reversibility='+(r.reversibility&&r.reversibility.label)+(r.capped_by_deadline?' (tier capped by deadline)':'')+'\\n'
327
+ +'AIR-GAP REQUIRED: '+d.airgap_required+'\\n'
328
+ +'MODEL: '+(m?(m.id+' ['+m.tier+'/'+m.zone+', '+m.license+']'):('— '+(d.honest||'')))+'\\n\\n'
329
+ +'policy: '+d.policy;
330
+ loadSk();
331
+ }
332
+ async function loadSk(){
333
+ const d=await(await fetch('/api/a11oy/v1/budget/skeletons')).json();
334
+ const box=$('#sks');box.innerHTML='';
335
+ if(!d.skeletons.length){box.innerHTML='<div class="l">'+(d.hint||'No skeletons yet.')+'</div>';return}
336
+ for(const s of d.skeletons){
337
+ const el=document.createElement('div');el.className='sk';
338
+ el.innerHTML='<span class="uses">×'+s.uses+'</span>'
339
+ +'<div class="k">'+s.key+'</div><div class="l">'+s.lesson+'</div>';
340
+ box.appendChild(el);
341
+ }
342
+ }
343
+ $('#go').addEventListener('click',route);
344
+ $('#q').addEventListener('keydown',e=>{if(e.key==='Enter')route()});
345
+ $('#ref').addEventListener('click',loadSk);
346
+ document.querySelectorAll('.chip').forEach(c=>c.addEventListener('click',()=>{$('#q').value=c.textContent;route();}));
347
+ tiers();loadSk();
348
+ </script>
349
+ </body></html>"""
szl_consensus_clusters.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
3
+ # Authored by A11oy Full-Stack Team. Co-Authored-By: Perplexity Computer Agent.
4
+ #
5
+ # THIRD-PARTY PATTERN ADOPTION (fashion-thinking, NOTICE attribution):
6
+ # ngraph.leiden — anvaka/ngraph.leiden — MIT License —
7
+ # https://github.com/anvaka/ngraph.leiden
8
+ # We adopt the PATTERN of fast, DETERMINISTIC community detection (Leiden/Louvain
9
+ # modularity optimization) on a graph. We EVOLVE it from a generic graph utility
10
+ # into "Consensus Clusters": we build a similarity graph over the substrate's
11
+ # governed DECISION NODES (edges weighted by shared sensitivity class, mission
12
+ # tier and chosen-model family) and run a clean-room Louvain modularity optimizer
13
+ # to surface clusters of mutually-reinforcing decisions — and the isolated
14
+ # OUTLIERS that warrant operator attention. Original, dependency-free
15
+ # implementation (no ngraph/JS source, no torch/numpy). Rendered with the
16
+ # ALREADY-VENDORED cytoscape.min.js (/vendor/*, 0 CDN). Λ = Conjecture 1.
17
+ """szl_consensus_clusters — ADDITIVE decision-node clustering tab + API.
18
+
19
+ Endpoints (mounted before the SPA catch-all):
20
+ GET /consensus-clusters — operator tab (HTML, cytoscape 0 CDN)
21
+ GET /api/a11oy/v1/clusters/graph — clustered decision graph (cytoscape elements)
22
+ GET /api/a11oy/v1/clusters/summary — modularity, cluster sizes, outliers
23
+
24
+ Decision nodes come from the REAL governance Khipu chain (szl_khipu). If no
25
+ decisions exist yet, a small in-substrate seed graph (the doctrine relationships)
26
+ is clustered so the algorithm is demonstrably real. Doctrine v11 LOCKED 749/14/163.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ from typing import Any
31
+
32
+ from fastapi import FastAPI
33
+ from fastapi.responses import HTMLResponse, JSONResponse
34
+
35
+ DOCTRINE = {"version": "v11", "counts": "749/14/163", "lambda": "Conjecture 1"}
36
+
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Build a weighted similarity graph over governed decisions.
40
+ # ---------------------------------------------------------------------------
41
+ def _decision_nodes() -> list[dict[str, Any]]:
42
+ nodes: list[dict[str, Any]] = []
43
+ try:
44
+ import szl_khipu # type: ignore
45
+ dag = szl_khipu.get_dag("governance-gateway", ns="a11oy")
46
+ for r in dag.tail(200):
47
+ nodes.append({
48
+ "id": (r.get("digest") or "")[:16] or f"seq{r.get('seq')}",
49
+ "label": f"#{r.get('seq','?')}",
50
+ "sens": str(r.get("sensitivity") or r.get("class") or "PUBLIC"),
51
+ "tier": str(r.get("min_tier") or r.get("zone") or "T?"),
52
+ "model": str(r.get("chosen") or "—"),
53
+ })
54
+ except Exception:
55
+ pass
56
+ if nodes:
57
+ return nodes
58
+ # Honest seed: the substrate's own doctrine relationships (so clustering is real
59
+ # even before decisions are driven). Three natural families.
60
+ return [
61
+ {"id": "d-airgap", "label": "air-gap", "sens": "SECRET", "tier": "T2", "model": "olmo"},
62
+ {"id": "d-classified", "label": "classified", "sens": "SECRET", "tier": "T2", "model": "deepseek"},
63
+ {"id": "d-restricted", "label": "restricted", "sens": "RESTRICTED", "tier": "T1", "model": "qwen3"},
64
+ {"id": "d-internal", "label": "internal", "sens": "INTERNAL", "tier": "T2", "model": "claude"},
65
+ {"id": "d-roadmap", "label": "roadmap", "sens": "INTERNAL", "tier": "T2", "model": "claude"},
66
+ {"id": "d-public", "label": "public", "sens": "PUBLIC", "tier": "T5", "model": "gemini"},
67
+ {"id": "d-triage", "label": "triage", "sens": "PUBLIC", "tier": "T1", "model": "qwen3"},
68
+ {"id": "d-research", "label": "research", "sens": "PUBLIC", "tier": "T5", "model": "gemini"},
69
+ ]
70
+
71
+
72
+ def _build_weighted_graph() -> tuple[list[dict[str, Any]], dict[tuple[str, str], float]]:
73
+ nodes = _decision_nodes()
74
+ weights: dict[tuple[str, str], float] = {}
75
+ for i in range(len(nodes)):
76
+ for j in range(i + 1, len(nodes)):
77
+ a, b = nodes[i], nodes[j]
78
+ w = 0.0
79
+ if a["sens"] == b["sens"]:
80
+ w += 1.5
81
+ if a["tier"] == b["tier"]:
82
+ w += 1.0
83
+ if a["model"] == b["model"] and a["model"] != "—":
84
+ w += 1.0
85
+ if w > 0:
86
+ weights[(a["id"], b["id"])] = w
87
+ return nodes, weights
88
+
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Clean-room Louvain modularity optimization (deterministic single pass + refine).
92
+ # ---------------------------------------------------------------------------
93
+ def _louvain(nodes: list[dict[str, Any]],
94
+ weights: dict[tuple[str, str], float]) -> dict[str, int]:
95
+ ids = [n["id"] for n in nodes]
96
+ adj: dict[str, dict[str, float]] = {i: {} for i in ids}
97
+ m2 = 0.0 # 2m (sum of all edge weights, counted once per pair → 2x in degree sums)
98
+ for (u, v), w in weights.items():
99
+ adj[u][v] = adj[u].get(v, 0.0) + w
100
+ adj[v][u] = adj[v].get(u, 0.0) + w
101
+ m2 += 2 * w
102
+ if m2 == 0:
103
+ return {i: idx for idx, i in enumerate(ids)}
104
+ k = {i: sum(adj[i].values()) for i in ids}
105
+ comm = {i: idx for idx, i in enumerate(ids)} # start: singleton communities
106
+ sigma_tot: dict[int, float] = {idx: k[i] for idx, i in enumerate(ids)}
107
+
108
+ improved = True
109
+ passes = 0
110
+ while improved and passes < 20:
111
+ improved = False
112
+ passes += 1
113
+ for i in ids:
114
+ ci = comm[i]
115
+ # remove i from its community
116
+ sigma_tot[ci] -= k[i]
117
+ # weight from i to each neighboring community
118
+ wj: dict[int, float] = {}
119
+ for j, w in adj[i].items():
120
+ wj[comm[j]] = wj.get(comm[j], 0.0) + w
121
+ best_c, best_gain = ci, 0.0
122
+ for c, wic in wj.items():
123
+ gain = wic - sigma_tot.get(c, 0.0) * k[i] / m2
124
+ if gain > best_gain:
125
+ best_gain, best_c = gain, c
126
+ # also consider staying (gain of own community already removed)
127
+ comm[i] = best_c
128
+ sigma_tot[best_c] = sigma_tot.get(best_c, 0.0) + k[i]
129
+ if best_c != ci:
130
+ improved = True
131
+ # relabel communities 0..N-1 deterministically
132
+ relabel: dict[int, int] = {}
133
+ for i in ids:
134
+ c = comm[i]
135
+ if c not in relabel:
136
+ relabel[c] = len(relabel)
137
+ comm[i] = relabel[c]
138
+ return comm
139
+
140
+
141
+ def _modularity(nodes, weights, comm) -> float:
142
+ ids = [n["id"] for n in nodes]
143
+ m2 = 2 * sum(weights.values())
144
+ if m2 == 0:
145
+ return 0.0
146
+ k: dict[str, float] = {i: 0.0 for i in ids}
147
+ for (u, v), w in weights.items():
148
+ k[u] += w
149
+ k[v] += w
150
+ q = 0.0
151
+ for (u, v), w in weights.items():
152
+ if comm[u] == comm[v]:
153
+ q += 2 * (w - k[u] * k[v] / m2)
154
+ # diagonal (self) terms are zero here (no self-loops)
155
+ return round(q / m2, 4)
156
+
157
+
158
+ _PALETTE = ["#3fb950", "#1f6feb", "#d9b46a", "#f85149", "#a371f7", "#db61a2", "#56d4dd", "#e3b341"]
159
+
160
+
161
+ def build_graph() -> dict[str, Any]:
162
+ nodes, weights = _build_weighted_graph()
163
+ comm = _louvain(nodes, weights)
164
+ cy_nodes = []
165
+ for n in nodes:
166
+ c = comm[n["id"]]
167
+ cy_nodes.append({"data": {
168
+ "id": n["id"], "label": n["label"], "cluster": c,
169
+ "color": _PALETTE[c % len(_PALETTE)],
170
+ "sens": n["sens"], "tier": n["tier"], "model": n["model"]}})
171
+ cy_edges = []
172
+ for (u, v), w in weights.items():
173
+ intra = comm[u] == comm[v]
174
+ cy_edges.append({"data": {
175
+ "id": f"e-{u}-{v}", "source": u, "target": v,
176
+ "weight": w, "intra": intra}})
177
+ return {"nodes": cy_nodes, "edges": cy_edges, "communities": comm,
178
+ "node_count": len(cy_nodes), "edge_count": len(cy_edges)}
179
+
180
+
181
+ def summarize() -> dict[str, Any]:
182
+ nodes, weights = _build_weighted_graph()
183
+ comm = _louvain(nodes, weights)
184
+ sizes: dict[int, int] = {}
185
+ for c in comm.values():
186
+ sizes[c] = sizes.get(c, 0) + 1
187
+ outliers = [nid for nid, c in comm.items() if sizes[c] == 1]
188
+ q = _modularity(nodes, weights, comm)
189
+ seeded = not _has_live_decisions()
190
+ return {
191
+ "node_count": len(nodes), "edge_count": len(weights),
192
+ "cluster_count": len(sizes), "modularity": q,
193
+ "cluster_sizes": [{"cluster": c, "size": s} for c, s in sorted(sizes.items())],
194
+ "consensus_clusters": sum(1 for s in sizes.values() if s >= 2),
195
+ "outliers": outliers,
196
+ "algorithm": "Louvain modularity optimization (clean-room, deterministic), evolved from anvaka/ngraph.leiden (MIT)",
197
+ "seeded": seeded,
198
+ "hint": ("clustering the in-substrate doctrine seed graph — drive /governance-gateway to cluster real decisions"
199
+ if seeded else None),
200
+ }
201
+
202
+
203
+ def _has_live_decisions() -> bool:
204
+ try:
205
+ import szl_khipu # type: ignore
206
+ dag = szl_khipu.get_dag("governance-gateway", ns="a11oy")
207
+ return len(dag.tail(1)) > 0
208
+ except Exception:
209
+ return False
210
+
211
+
212
+ def register(app: FastAPI, ns: str = "a11oy") -> str:
213
+ @app.get(f"/api/{ns}/v1/clusters/graph", include_in_schema=False)
214
+ async def _graph() -> JSONResponse:
215
+ g = build_graph()
216
+ return JSONResponse({"doctrine": DOCTRINE, "nodes": g["nodes"], "edges": g["edges"],
217
+ "node_count": g["node_count"], "edge_count": g["edge_count"],
218
+ "pattern_source": "anvaka/ngraph.leiden (MIT) — community detection, evolved to consensus clusters"})
219
+
220
+ @app.get(f"/api/{ns}/v1/clusters/summary", include_in_schema=False)
221
+ async def _summary() -> JSONResponse:
222
+ return JSONResponse({"doctrine": DOCTRINE, **summarize()})
223
+
224
+ @app.get("/consensus-clusters", include_in_schema=False)
225
+ async def _page() -> HTMLResponse:
226
+ return HTMLResponse(_PAGE_HTML)
227
+
228
+ return f"consensus-clusters mounted: GET /consensus-clusters + /api/{ns}/v1/clusters/(graph|summary)"
229
+
230
+
231
+ _PAGE_HTML = """<!DOCTYPE html>
232
+ <html lang="en"><head><meta charset="utf-8">
233
+ <meta name="viewport" content="width=device-width,initial-scale=1">
234
+ <title>a11oy · Consensus Clusters</title>
235
+ <script src="/vendor/cytoscape.min.js"></script>
236
+ <style>
237
+ :root{--bg:#0b0f14;--panel:#121922;--ink:#e8eef5;--muted:#8aa0b4;--gold:#d9b46a;
238
+ --green:#3fb950;--blue:#1f6feb;--line:#1e2a36;}
239
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);
240
+ font:15px/1.5 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
241
+ .wrap{max-width:1080px;margin:0 auto;padding:24px 18px 64px}
242
+ h1{font-size:24px;margin:.2em 0}.sub{color:var(--muted);margin:0 0 18px}
243
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;margin:14px 0}
244
+ .row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
245
+ button{background:var(--gold);color:#1a1205;border:0;border-radius:8px;padding:9px 16px;font-weight:700;cursor:pointer}
246
+ button:hover{filter:brightness(1.08)}
247
+ #cy{width:100%;height:460px;background:#0d141c;border:1px solid var(--line);border-radius:10px}
248
+ .pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600}
249
+ .green{background:rgba(63,185,80,.15);color:var(--green)}
250
+ .blue{background:rgba(31,111,235,.18);color:#79b8ff}
251
+ pre{background:#0d141c;border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;font-size:12.5px;white-space:pre-wrap}
252
+ .foot{color:var(--muted);font-size:12px;margin-top:24px;border-top:1px solid var(--line);padding-top:12px}
253
+ code{color:var(--gold)}
254
+ </style></head>
255
+ <body><div class="wrap">
256
+ <h1>Consensus Clusters <span class="pill blue">Louvain</span></h1>
257
+ <p class="sub">Builds a similarity graph over governed <b>decision nodes</b> (edges weighted by shared
258
+ sensitivity, mission tier and model family) and runs modularity optimization to surface
259
+ <b>consensus clusters</b> of mutually-reinforcing decisions — and the isolated <b>outliers</b>
260
+ worth an operator's attention. Pattern from <code>anvaka/ngraph.leiden</code> (MIT), evolved.
261
+ Rendered with vendored cytoscape (0&nbsp;CDN).</p>
262
+
263
+ <div class="card">
264
+ <div class="row" style="justify-content:space-between">
265
+ <div id="legend" class="row"></div>
266
+ <button id="ref">Recompute clusters</button>
267
+ </div>
268
+ <div id="cy" style="margin-top:12px"></div>
269
+ <pre id="sum" style="margin-top:12px">Computing communities…</pre>
270
+ </div>
271
+
272
+ <p class="foot">a11oy · Doctrine v11 LOCKED 749/14/163 · Λ = Conjecture 1 ·
273
+ pattern: anvaka/ngraph.leiden (MIT), evolved · sovereign 0-CDN (vendored cytoscape).</p>
274
+ </div>
275
+ <script>
276
+ const $=s=>document.querySelector(s);
277
+ let cy=null;
278
+ async function load(){
279
+ const g=await(await fetch('/api/a11oy/v1/clusters/graph')).json();
280
+ const s=await(await fetch('/api/a11oy/v1/clusters/summary')).json();
281
+ if(cy)cy.destroy();
282
+ cy=cytoscape({container:$('#cy'),elements:[].concat(g.nodes,g.edges),
283
+ style:[
284
+ {selector:'node',style:{'background-color':'data(color)','label':'data(label)',
285
+ 'color':'#e8eef5','font-size':'10px','text-valign':'top','width':24,'height':24,
286
+ 'border-width':2,'border-color':'#0b0f14'}},
287
+ {selector:'edge[?intra]',style:{'line-color':'#3fb950','width':'mapData(weight,0,3,1,5)','opacity':0.7,'curve-style':'haystack'}},
288
+ {selector:'edge[!intra]',style:{'line-color':'#33425a','width':1,'opacity':0.35,'line-style':'dotted','curve-style':'haystack'}}
289
+ ],
290
+ layout:{name:'cose',animate:false,padding:24,nodeRepulsion:9000,idealEdgeLength:70}});
291
+ const leg=$('#legend');leg.innerHTML='';
292
+ const colors=[...new Set(g.nodes.map(n=>n.data.color))];
293
+ colors.forEach((c,i)=>{const sp=document.createElement('span');sp.className='pill';
294
+ sp.style.background=c+'33';sp.style.color=c;sp.textContent='cluster '+i;leg.appendChild(sp);
295
+ leg.appendChild(document.createTextNode(' '));});
296
+ $('#sum').textContent='nodes='+s.node_count+' edges='+s.edge_count
297
+ +' clusters='+s.cluster_count+' modularity Q='+s.modularity
298
+ +' consensus_clusters='+s.consensus_clusters+' outliers='+(s.outliers.length)
299
+ +'\\n'+s.algorithm+(s.seeded?('\\n\\nNOTE: '+s.hint):'')
300
+ +'\\n\\ncluster sizes:\\n'+JSON.stringify(s.cluster_sizes,null,2)
301
+ +(s.outliers.length?('\\noutliers: '+s.outliers.join(', ')):'');
302
+ }
303
+ $('#ref').addEventListener('click',load);
304
+ load();
305
+ </script>
306
+ </body></html>"""
szl_mission_ledger.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
3
+ # Authored by A11oy Full-Stack Team. Co-Authored-By: Perplexity Computer Agent.
4
+ #
5
+ # THIRD-PARTY PATTERN ADOPTION (fashion-thinking, NOTICE attribution):
6
+ # research-arcade — ulab-uiuc/research-arcade — MIT License —
7
+ # https://github.com/ulab-uiuc/research-arcade
8
+ # We adopt the PATTERN of a HETEROGENEOUS GRAPH interface that unifies disparate
9
+ # structured sources (papers, reviews, authors) into one graph with typed nodes
10
+ # and graph-like CRUD. We EVOLVE it into a live "Mission Ledger": a heterogeneous
11
+ # knowledge graph over THREE a11oy node types — DOCUMENTS (governed source
12
+ # material), OBJECTIVES (mission goals) and DECISIONS (governed AI outputs, drawn
13
+ # from the real Khipu chain) — with typed edges (cites, supports, resulted_in) so
14
+ # the provenance from source material → mission objective → governed action is
15
+ # explicit and auditable. Original, dependency-free implementation (no
16
+ # research-arcade source, no SQL backend, no torch/numpy). Rendered with the
17
+ # ALREADY-VENDORED cytoscape.min.js (/vendor/*, 0 CDN). Λ = Conjecture 1.
18
+ """szl_mission_ledger — ADDITIVE heterogeneous knowledge-graph tab + API.
19
+
20
+ Endpoints (mounted before the SPA catch-all):
21
+ GET /mission-ledger — operator tab (HTML, cytoscape 0 CDN)
22
+ GET /api/a11oy/v1/ledger/graph — heterogeneous KG (cytoscape elements)
23
+ GET /api/a11oy/v1/ledger/summary — node/edge type counts, coverage
24
+
25
+ DECISION nodes are pulled from the REAL governance Khipu chain when present and
26
+ linked into the document/objective scaffold. Doctrine v11 LOCKED 749/14/163.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ from typing import Any
31
+
32
+ from fastapi import FastAPI
33
+ from fastapi.responses import HTMLResponse, JSONResponse
34
+
35
+ DOCTRINE = {"version": "v11", "counts": "749/14/163", "lambda": "Conjecture 1"}
36
+
37
+ # In-substrate scaffold: documents + objectives (the governed mission context).
38
+ # These are real a11oy doctrine artifacts; decisions are appended from the live chain.
39
+ _DOCUMENTS = [
40
+ {"id": "doc-roe", "label": "Rules of Engagement", "type": "document", "tag": "RESTRICTED"},
41
+ {"id": "doc-airgap", "label": "Air-Gap Sovereignty Spec", "type": "document", "tag": "doctrine"},
42
+ {"id": "doc-intel", "label": "Maritime Intel Brief", "type": "document", "tag": "RESTRICTED"},
43
+ {"id": "doc-policy", "label": "Governance Policy Manifest", "type": "document", "tag": "doctrine"},
44
+ ]
45
+ _OBJECTIVES = [
46
+ {"id": "obj-sovereign", "label": "Run classified decisions air-gapped", "type": "objective"},
47
+ {"id": "obj-audit", "label": "Keep every decision tamper-evident", "type": "objective"},
48
+ {"id": "obj-cost", "label": "Minimize compute on time-critical calls", "type": "objective"},
49
+ ]
50
+ # Static provenance edges in the scaffold (documents → objectives).
51
+ _SCAFFOLD_EDGES = [
52
+ ("doc-roe", "obj-sovereign", "supports"),
53
+ ("doc-airgap", "obj-sovereign", "cites"),
54
+ ("doc-intel", "obj-sovereign", "supports"),
55
+ ("doc-policy", "obj-audit", "cites"),
56
+ ("doc-policy", "obj-cost", "supports"),
57
+ ]
58
+ # Which objective a decision serves, inferred from its governance fields.
59
+ def _objective_for(rec: dict[str, Any]) -> str:
60
+ sens = str(rec.get("sensitivity") or rec.get("class") or "").upper()
61
+ if sens in ("RESTRICTED", "SECRET") or rec.get("airgap_required"):
62
+ return "obj-sovereign"
63
+ if str(rec.get("min_tier") or "").upper() in ("T0", "T1"):
64
+ return "obj-cost"
65
+ return "obj-audit"
66
+
67
+
68
+ def _decisions() -> list[dict[str, Any]]:
69
+ out: list[dict[str, Any]] = []
70
+ try:
71
+ import szl_khipu # type: ignore
72
+ dag = szl_khipu.get_dag("governance-gateway", ns="a11oy")
73
+ for r in dag.tail(60):
74
+ out.append(r)
75
+ except Exception:
76
+ pass
77
+ return out
78
+
79
+
80
+ def build_graph() -> dict[str, Any]:
81
+ nodes: list[dict[str, Any]] = []
82
+ edges: list[dict[str, Any]] = []
83
+ for d in _DOCUMENTS:
84
+ nodes.append({"data": {**d}})
85
+ for o in _OBJECTIVES:
86
+ nodes.append({"data": {**o}})
87
+ for (s, t, rel) in _SCAFFOLD_EDGES:
88
+ edges.append({"data": {"id": f"e-{s}-{t}", "source": s, "target": t, "rel": rel}})
89
+
90
+ decs = _decisions()
91
+ for r in decs:
92
+ did = "dec-" + ((r.get("digest") or "")[:12] or f"seq{r.get('seq')}")
93
+ nodes.append({"data": {
94
+ "id": did, "label": f"decision #{r.get('seq','?')}", "type": "decision",
95
+ "model": str(r.get("chosen") or "—"), "sens": str(r.get("sensitivity") or "?")}})
96
+ obj = _objective_for(r)
97
+ edges.append({"data": {"id": f"e-{did}-{obj}", "source": obj, "target": did, "rel": "resulted_in"}})
98
+
99
+ return {"nodes": nodes, "edges": edges,
100
+ "node_count": len(nodes), "edge_count": len(edges),
101
+ "decision_count": len(decs)}
102
+
103
+
104
+ def summarize() -> dict[str, Any]:
105
+ g = build_graph()
106
+ by_type: dict[str, int] = {}
107
+ for n in g["nodes"]:
108
+ t = n["data"].get("type", "?")
109
+ by_type[t] = by_type.get(t, 0) + 1
110
+ by_rel: dict[str, int] = {}
111
+ for e in g["edges"]:
112
+ rl = e["data"].get("rel", "?")
113
+ by_rel[rl] = by_rel.get(rl, 0) + 1
114
+ return {
115
+ "node_count": g["node_count"], "edge_count": g["edge_count"],
116
+ "node_types": by_type, "edge_types": by_rel,
117
+ "decision_count": g["decision_count"],
118
+ "structure": "heterogeneous KG — documents → objectives → decisions",
119
+ "hint": (None if g["decision_count"]
120
+ else "scaffold only — drive /governance-gateway to attach real decisions to the ledger"),
121
+ }
122
+
123
+
124
+ def register(app: FastAPI, ns: str = "a11oy") -> str:
125
+ @app.get(f"/api/{ns}/v1/ledger/graph", include_in_schema=False)
126
+ async def _graph() -> JSONResponse:
127
+ g = build_graph()
128
+ return JSONResponse({"doctrine": DOCTRINE, **g,
129
+ "pattern_source": "ulab-uiuc/research-arcade (MIT) — heterogeneous graph interface, evolved to Mission Ledger"})
130
+
131
+ @app.get(f"/api/{ns}/v1/ledger/summary", include_in_schema=False)
132
+ async def _summary() -> JSONResponse:
133
+ return JSONResponse({"doctrine": DOCTRINE, **summarize()})
134
+
135
+ @app.get("/mission-ledger", include_in_schema=False)
136
+ async def _page() -> HTMLResponse:
137
+ return HTMLResponse(_PAGE_HTML)
138
+
139
+ return f"mission-ledger mounted: GET /mission-ledger + /api/{ns}/v1/ledger/(graph|summary)"
140
+
141
+
142
+ _PAGE_HTML = """<!DOCTYPE html>
143
+ <html lang="en"><head><meta charset="utf-8">
144
+ <meta name="viewport" content="width=device-width,initial-scale=1">
145
+ <title>a11oy · Mission Ledger</title>
146
+ <script src="/vendor/cytoscape.min.js"></script>
147
+ <style>
148
+ :root{--bg:#0b0f14;--panel:#121922;--ink:#e8eef5;--muted:#8aa0b4;--gold:#d9b46a;
149
+ --green:#3fb950;--blue:#1f6feb;--purple:#a371f7;--line:#1e2a36;}
150
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);
151
+ font:15px/1.5 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
152
+ .wrap{max-width:1080px;margin:0 auto;padding:24px 18px 64px}
153
+ h1{font-size:24px;margin:.2em 0}.sub{color:var(--muted);margin:0 0 18px}
154
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;margin:14px 0}
155
+ .row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
156
+ button{background:var(--gold);color:#1a1205;border:0;border-radius:8px;padding:9px 16px;font-weight:700;cursor:pointer}
157
+ button:hover{filter:brightness(1.08)}
158
+ #cy{width:100%;height:460px;background:#0d141c;border:1px solid var(--line);border-radius:10px}
159
+ .pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600}
160
+ .doc{background:rgba(31,111,235,.18);color:#79b8ff}
161
+ .obj{background:rgba(217,180,106,.18);color:var(--gold)}
162
+ .dec{background:rgba(63,185,80,.15);color:var(--green)}
163
+ pre{background:#0d141c;border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;font-size:12.5px;white-space:pre-wrap}
164
+ .foot{color:var(--muted);font-size:12px;margin-top:24px;border-top:1px solid var(--line);padding-top:12px}
165
+ code{color:var(--gold)}
166
+ .legend span{margin-right:6px}
167
+ </style></head>
168
+ <body><div class="wrap">
169
+ <h1>Mission Ledger <span class="pill dec">knowledge graph</span></h1>
170
+ <p class="sub">A heterogeneous knowledge graph linking <span class="pill doc">documents</span>
171
+ (governed source material) → <span class="pill obj">objectives</span> (mission goals) →
172
+ <span class="pill dec">decisions</span> (governed AI outputs from the real Khipu chain), with typed
173
+ edges (<code>cites</code>, <code>supports</code>, <code>resulted_in</code>) so provenance from source
174
+ to action is explicit. Pattern from <code>ulab-uiuc/research-arcade</code> (MIT), evolved.
175
+ Rendered with vendored cytoscape (0&nbsp;CDN).</p>
176
+
177
+ <div class="card">
178
+ <div class="row" style="justify-content:space-between">
179
+ <div class="legend"><span class="pill doc">document</span><span class="pill obj">objective</span><span class="pill dec">decision</span></div>
180
+ <button id="ref">Refresh ledger</button>
181
+ </div>
182
+ <div id="cy" style="margin-top:12px"></div>
183
+ <pre id="sum" style="margin-top:12px">Loading mission ledger…</pre>
184
+ </div>
185
+
186
+ <p class="foot">a11oy · Doctrine v11 LOCKED 749/14/163 · Λ = Conjecture 1 ·
187
+ pattern: ulab-uiuc/research-arcade (MIT), evolved · sovereign 0-CDN (vendored cytoscape).</p>
188
+ </div>
189
+ <script>
190
+ const $=s=>document.querySelector(s);
191
+ let cy=null;
192
+ const COLOR={document:'#1f6feb',objective:'#d9b46a',decision:'#3fb950'};
193
+ async function load(){
194
+ const g=await(await fetch('/api/a11oy/v1/ledger/graph')).json();
195
+ const s=await(await fetch('/api/a11oy/v1/ledger/summary')).json();
196
+ if(cy)cy.destroy();
197
+ cy=cytoscape({container:$('#cy'),elements:[].concat(g.nodes,g.edges),
198
+ style:[
199
+ {selector:'node',style:{'background-color':ele=>COLOR[ele.data('type')]||'#888',
200
+ 'label':'data(label)','color':'#e8eef5','font-size':'9px','text-valign':'bottom',
201
+ 'text-margin-y':3,'width':22,'height':22}},
202
+ {selector:'node[type="objective"]',style:{'shape':'round-rectangle','width':30,'height':22}},
203
+ {selector:'node[type="decision"]',style:{'shape':'diamond'}},
204
+ {selector:'edge',style:{'line-color':'#33425a','width':1.6,'curve-style':'bezier',
205
+ 'target-arrow-shape':'triangle','target-arrow-color':'#33425a','label':'data(rel)',
206
+ 'font-size':'7px','color':'#8aa0b4','text-rotation':'autorotate'}}
207
+ ],
208
+ layout:{name:'breadthfirst',directed:true,padding:24,spacingFactor:1.25}});
209
+ $('#sum').textContent='nodes='+s.node_count+' edges='+s.edge_count
210
+ +' decisions='+s.decision_count+'\\nnode types: '+JSON.stringify(s.node_types)
211
+ +'\\nedge types: '+JSON.stringify(s.edge_types)
212
+ +(s.hint?('\\n\\nHINT: '+s.hint):'');
213
+ }
214
+ $('#ref').addEventListener('click',load);
215
+ load();
216
+ </script>
217
+ </body></html>"""
szl_sovereign_search.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
3
+ # Authored by A11oy Full-Stack Team. Co-Authored-By: Perplexity Computer Agent.
4
+ #
5
+ # THIRD-PARTY PATTERN ADOPTION (fashion-thinking, NOTICE attribution):
6
+ # tantivy-wasm — phiresky/tantivy-wasm — MIT License —
7
+ # https://github.com/phiresky/tantivy-wasm
8
+ # We adopt the PATTERN of a SOVEREIGN, in-deployment full-text search engine that
9
+ # needs NO external Elasticsearch/Meilisearch service and NO network egress — the
10
+ # index lives with the data and queries are answered locally. tantivy-wasm proves
11
+ # this by running the Rust Tantivy engine in WASM and fetching index segments via
12
+ # HTTP range requests. We EVOLVE the IDEA into a dependency-free, stdlib-only
13
+ # server-side BM25 engine that indexes the a11oy GOVERNANCE CORPUS (decision
14
+ # receipts, model catalog, doctrine docs, gates manifest) so an operator can run
15
+ # sub-second relevance search over the entire governed-AI substrate with ZERO
16
+ # backend service and ZERO CDN — the literal air-gap requirement. Original,
17
+ # clean-room implementation; no Tantivy/Rust/WASM code; no torch/numpy.
18
+ """szl_sovereign_search — ADDITIVE in-substrate full-text search tab + API.
19
+
20
+ Endpoints (mounted before the SPA catch-all):
21
+ GET /sovereign-search — operator tab (HTML, 0 CDN)
22
+ GET /api/a11oy/v1/search/corpus — corpus stats (docs, terms)
23
+ GET /api/a11oy/v1/search/query?q=...&k=... — BM25 ranked results
24
+ GET /api/a11oy/v1/search/reindex — rebuild the index from live sources
25
+
26
+ The corpus is assembled from REAL a11oy substrate:
27
+ • the live governance model catalog (szl_governance_gateway, if present)
28
+ • governance decision receipts (szl_khipu DAG, if present)
29
+ • doctrine / disclosure snippets shipped in-image
30
+ The BM25 ranker is a textbook Okapi BM25 (k1=1.5, b=0.75) computed over the live
31
+ corpus — no external service. Λ = Conjecture 1. Doctrine v11 LOCKED 749/14/163.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ import math
36
+ import re
37
+ import time
38
+ from typing import Any
39
+
40
+ from fastapi import FastAPI
41
+ from fastapi.responses import HTMLResponse, JSONResponse
42
+
43
+ DOCTRINE = {"version": "v11", "counts": "749/14/163", "lambda": "Conjecture 1"}
44
+
45
+ _TOKEN_RE = re.compile(r"[a-z0-9][a-z0-9\-_.]*")
46
+ # Minimal English stop list — keeps the index lean without external NLTK data.
47
+ _STOP = {
48
+ "the", "a", "an", "and", "or", "of", "to", "in", "is", "are", "for", "on",
49
+ "with", "as", "by", "at", "be", "this", "that", "it", "from", "we", "our",
50
+ }
51
+
52
+
53
+ def _tokenize(text: str) -> list[str]:
54
+ return [t for t in _TOKEN_RE.findall((text or "").lower()) if t not in _STOP and len(t) > 1]
55
+
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Corpus assembly — every document is sourced from a REAL a11oy substrate.
59
+ # ---------------------------------------------------------------------------
60
+ def _corpus() -> list[dict[str, Any]]:
61
+ docs: list[dict[str, Any]] = []
62
+
63
+ # 1) Governance model catalog (the routing decision substrate).
64
+ try:
65
+ import szl_governance_gateway as _gg # type: ignore
66
+ for m in getattr(_gg, "CATALOG", []) or []:
67
+ docs.append({
68
+ "id": f"model:{m.get('id')}",
69
+ "kind": "model",
70
+ "title": f"{m.get('id')} · {m.get('tier','?')}/{m.get('zone','?')}",
71
+ "body": " ".join(str(v) for v in m.values()),
72
+ "ref": "/governance-gateway",
73
+ })
74
+ except Exception:
75
+ pass
76
+
77
+ # 2) Governance decision receipts (real Khipu hash-chain).
78
+ try:
79
+ import szl_khipu # type: ignore
80
+ dag = szl_khipu.get_dag("governance-gateway", ns="a11oy")
81
+ for r in dag.tail(200):
82
+ docs.append({
83
+ "id": f"receipt:{(r.get('digest') or '')[:16]}",
84
+ "kind": "receipt",
85
+ "title": f"decision #{r.get('seq','?')} · {r.get('action','?')}",
86
+ "body": " ".join(str(v) for v in r.values()),
87
+ "ref": "/gor-audit",
88
+ })
89
+ except Exception:
90
+ pass
91
+
92
+ # 3) Doctrine / disclosure corpus shipped in-image (always available — gives the
93
+ # search engine a non-empty corpus even before any decision is driven).
94
+ docs.extend(_DOCTRINE_DOCS)
95
+ return docs
96
+
97
+
98
+ # Curated, in-image doctrine snippets (the substrate's own governed knowledge).
99
+ _DOCTRINE_DOCS: list[dict[str, Any]] = [
100
+ {"id": "doc:airgap", "kind": "doctrine", "title": "Air-gap sovereignty floor", "ref": "/governance-gateway",
101
+ "body": "RESTRICTED and SECRET classified data force air-gap routing and may never leave for cloud "
102
+ "endpoints. The sovereign floor guarantees governed decisions on classified material run on "
103
+ "self-hosted, clean-lineage weights with zero network egress."},
104
+ {"id": "doc:khipu", "kind": "doctrine", "title": "Khipu hash-chained receipts", "ref": "/gor-audit",
105
+ "body": "Every governed decision is signed as a hash-chained Khipu receipt with sequence, digest and "
106
+ "prev-link so the entire decision history is tamper-evident and replayable for audit."},
107
+ {"id": "doc:lambda", "kind": "doctrine", "title": "Lambda is Conjecture 1", "ref": "/",
108
+ "body": "The Lambda quantity is labelled Conjecture 1 — an honest, unproven conjecture. The doctrine "
109
+ "locks exactly the proven formulas and never overstates the proof status of any claim."},
110
+ {"id": "doc:budget", "kind": "doctrine", "title": "Budget-tier decision routing", "ref": "/budget-router",
111
+ "body": "Mission constraints map to budget tiers: Tactical demands sub-second low-tier inference, "
112
+ "Operational allows mid-tier reasoning, Strategic permits high-tier chain-of-thought. The router "
113
+ "selects the cheapest tier that satisfies the decision risk so sovereign hardware is never choked."},
114
+ {"id": "doc:consensus", "kind": "doctrine", "title": "Consensus clusters of decisions", "ref": "/consensus-clusters",
115
+ "body": "Related governed-AI decision nodes are grouped into consensus clusters by community detection so "
116
+ "operators can see which decisions reinforce one another and which are isolated outliers."},
117
+ {"id": "doc:mission-ledger", "kind": "doctrine", "title": "Mission Ledger knowledge graph", "ref": "/mission-ledger",
118
+ "body": "The Mission Ledger links documents, mission objectives and the resulting AI decisions into one "
119
+ "heterogeneous knowledge graph so the provenance from source material to governed action is explicit."},
120
+ ]
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # BM25 index (Okapi BM25, stdlib-only).
125
+ # ---------------------------------------------------------------------------
126
+ class _Bm25Index:
127
+ K1 = 1.5
128
+ B = 0.75
129
+
130
+ def __init__(self) -> None:
131
+ self.docs: list[dict[str, Any]] = []
132
+ self.tokens: list[list[str]] = []
133
+ self.df: dict[str, int] = {}
134
+ self.avgdl: float = 0.0
135
+ self.built_at: float = 0.0
136
+
137
+ def build(self) -> None:
138
+ self.docs = _corpus()
139
+ self.tokens = [_tokenize(d["title"] + " " + d["body"]) for d in self.docs]
140
+ self.df = {}
141
+ for toks in self.tokens:
142
+ for t in set(toks):
143
+ self.df[t] = self.df.get(t, 0) + 1
144
+ n = len(self.tokens)
145
+ self.avgdl = (sum(len(t) for t in self.tokens) / n) if n else 0.0
146
+ self.built_at = time.time()
147
+
148
+ def _idf(self, term: str) -> float:
149
+ n = len(self.docs)
150
+ df = self.df.get(term, 0)
151
+ # BM25 idf with +1 to keep it non-negative.
152
+ return math.log(1 + (n - df + 0.5) / (df + 0.5))
153
+
154
+ def query(self, q: str, k: int = 8) -> list[dict[str, Any]]:
155
+ qterms = _tokenize(q)
156
+ if not qterms or not self.docs:
157
+ return []
158
+ scored: list[tuple[float, int, list[str]]] = []
159
+ for i, toks in enumerate(self.tokens):
160
+ if not toks:
161
+ continue
162
+ tf: dict[str, int] = {}
163
+ for t in toks:
164
+ tf[t] = tf.get(t, 0) + 1
165
+ dl = len(toks)
166
+ score = 0.0
167
+ hits: list[str] = []
168
+ for term in qterms:
169
+ f = tf.get(term, 0)
170
+ if f == 0:
171
+ continue
172
+ hits.append(term)
173
+ idf = self._idf(term)
174
+ denom = f + self.K1 * (1 - self.B + self.B * dl / (self.avgdl or 1))
175
+ score += idf * (f * (self.K1 + 1)) / (denom or 1)
176
+ if score > 0:
177
+ scored.append((score, i, hits))
178
+ scored.sort(key=lambda x: x[0], reverse=True)
179
+ out: list[dict[str, Any]] = []
180
+ for score, i, hits in scored[:k]:
181
+ d = self.docs[i]
182
+ out.append({
183
+ "id": d["id"], "kind": d["kind"], "title": d["title"], "ref": d.get("ref"),
184
+ "score": round(score, 4), "matched_terms": hits,
185
+ "snippet": _snippet(d["body"], hits),
186
+ })
187
+ return out
188
+
189
+ def stats(self) -> dict[str, Any]:
190
+ kinds: dict[str, int] = {}
191
+ for d in self.docs:
192
+ kinds[d["kind"]] = kinds.get(d["kind"], 0) + 1
193
+ return {
194
+ "doc_count": len(self.docs),
195
+ "unique_terms": len(self.df),
196
+ "avg_doc_len": round(self.avgdl, 2),
197
+ "by_kind": kinds,
198
+ "built_at": self.built_at,
199
+ "ranker": "Okapi BM25 (k1=1.5, b=0.75), stdlib-only, 0 service",
200
+ }
201
+
202
+
203
+ def _snippet(body: str, hits: list[str]) -> str:
204
+ low = body.lower()
205
+ pos = -1
206
+ for h in hits:
207
+ p = low.find(h)
208
+ if p >= 0:
209
+ pos = p
210
+ break
211
+ if pos < 0:
212
+ return body[:160]
213
+ start = max(0, pos - 60)
214
+ return ("…" if start else "") + body[start:start + 200] + ("…" if start + 200 < len(body) else "")
215
+
216
+
217
+ _INDEX = _Bm25Index()
218
+
219
+
220
+ def register(app: FastAPI, ns: str = "a11oy") -> str:
221
+ _INDEX.build()
222
+
223
+ @app.get(f"/api/{ns}/v1/search/corpus", include_in_schema=False)
224
+ async def _corpus_stats() -> JSONResponse:
225
+ if not _INDEX.docs:
226
+ _INDEX.build()
227
+ return JSONResponse({"doctrine": DOCTRINE, **_INDEX.stats(),
228
+ "pattern_source": "phiresky/tantivy-wasm (MIT) — sovereign in-deployment search, evolved to stdlib BM25"})
229
+
230
+ @app.get(f"/api/{ns}/v1/search/query", include_in_schema=False)
231
+ async def _query(q: str = "", k: int = 8) -> JSONResponse:
232
+ if not _INDEX.docs:
233
+ _INDEX.build()
234
+ k = max(1, min(int(k or 8), 25))
235
+ results = _INDEX.query(q, k)
236
+ return JSONResponse({"doctrine": DOCTRINE, "query": q, "k": k,
237
+ "result_count": len(results), "results": results})
238
+
239
+ @app.get(f"/api/{ns}/v1/search/reindex", include_in_schema=False)
240
+ async def _reindex() -> JSONResponse:
241
+ _INDEX.build()
242
+ return JSONResponse({"doctrine": DOCTRINE, "reindexed": True, **_INDEX.stats()})
243
+
244
+ @app.get("/sovereign-search", include_in_schema=False)
245
+ async def _page() -> HTMLResponse:
246
+ return HTMLResponse(_PAGE_HTML)
247
+
248
+ return f"sovereign-search mounted: GET /sovereign-search + /api/{ns}/v1/search/(corpus|query|reindex)"
249
+
250
+
251
+ _PAGE_HTML = """<!DOCTYPE html>
252
+ <html lang="en"><head><meta charset="utf-8">
253
+ <meta name="viewport" content="width=device-width,initial-scale=1">
254
+ <title>a11oy · Sovereign Corpus Search</title>
255
+ <style>
256
+ :root{--bg:#0b0f14;--panel:#121922;--ink:#e8eef5;--muted:#8aa0b4;--gold:#d9b46a;
257
+ --green:#3fb950;--blue:#1f6feb;--line:#1e2a36;}
258
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);
259
+ font:15px/1.5 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
260
+ .wrap{max-width:920px;margin:0 auto;padding:24px 18px 64px}
261
+ h1{font-size:24px;margin:.2em 0}.sub{color:var(--muted);margin:0 0 18px}
262
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;margin:14px 0}
263
+ .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
264
+ input{flex:1;min-width:200px;background:#0d141c;border:1px solid var(--line);border-radius:8px;
265
+ color:var(--ink);padding:10px 12px;font-size:15px}
266
+ button{background:var(--gold);color:#1a1205;border:0;border-radius:8px;padding:10px 16px;font-weight:700;cursor:pointer}
267
+ button:hover{filter:brightness(1.08)}
268
+ .pill{display:inline-block;padding:2px 9px;border-radius:999px;font-size:12px;font-weight:600}
269
+ .green{background:rgba(63,185,80,.15);color:var(--green)}
270
+ .blue{background:rgba(31,111,235,.18);color:#79b8ff}
271
+ .res{border-top:1px solid var(--line);padding:12px 0}
272
+ .res:first-child{border-top:0}
273
+ .res .t{font-weight:700}.res .m{color:var(--muted);font-size:13px}
274
+ .kind{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--gold)}
275
+ .sc{float:right;color:var(--green);font-variant-numeric:tabular-nums}
276
+ a{color:#79b8ff}
277
+ .foot{color:var(--muted);font-size:12px;margin-top:24px;border-top:1px solid var(--line);padding-top:12px}
278
+ code{color:var(--gold)}
279
+ .stat{color:var(--muted);font-size:13px;margin-top:8px}
280
+ .chips{margin-top:10px}.chip{display:inline-block;margin:2px 4px 2px 0;padding:4px 10px;border:1px solid var(--line);
281
+ border-radius:999px;font-size:12px;color:var(--muted);cursor:pointer;background:#0d141c}
282
+ .chip:hover{border-color:var(--gold);color:var(--gold)}
283
+ </style></head>
284
+ <body><div class="wrap">
285
+ <h1>Sovereign Corpus Search <span class="pill green">0 CDN · 0 service</span></h1>
286
+ <p class="sub">Sub-second BM25 relevance search over the entire a11oy governed-AI substrate —
287
+ model catalog, decision receipts and doctrine — answered <b>locally</b>, with no Elasticsearch,
288
+ no Meilisearch and no network egress. Pattern from <code>phiresky/tantivy-wasm</code> (MIT),
289
+ evolved into a dependency-free server-side Okapi&nbsp;BM25 engine (the air-gap search story).</p>
290
+
291
+ <div class="card">
292
+ <div class="row">
293
+ <input id="q" placeholder="search the governance corpus (e.g. air-gap classified, budget tier, khipu receipt)…" />
294
+ <button id="go">Search</button>
295
+ <button id="rx" style="background:#1f6feb;color:#fff">Reindex</button>
296
+ </div>
297
+ <div class="chips" id="chips">
298
+ <span class="chip">air-gap classified</span>
299
+ <span class="chip">budget tier mission</span>
300
+ <span class="chip">khipu hash-chain receipt</span>
301
+ <span class="chip">consensus cluster</span>
302
+ <span class="chip">conjecture lambda</span>
303
+ </div>
304
+ <div class="stat" id="stat">Building index…</div>
305
+ </div>
306
+
307
+ <div class="card" id="resCard" style="display:none">
308
+ <div id="res"></div>
309
+ </div>
310
+
311
+ <p class="foot">a11oy · Doctrine v11 LOCKED 749/14/163 · Λ = Conjecture 1 ·
312
+ pattern: phiresky/tantivy-wasm (MIT), evolved · sovereign 0-CDN (no JS deps at all).</p>
313
+ </div>
314
+ <script>
315
+ const $=s=>document.querySelector(s);
316
+ async function stats(){
317
+ const s=await(await fetch('/api/a11oy/v1/search/corpus')).json();
318
+ const bk=Object.entries(s.by_kind||{}).map(([k,v])=>k+'='+v).join(', ');
319
+ $('#stat').textContent='index ready · '+s.doc_count+' docs ('+bk+') · '
320
+ +s.unique_terms+' unique terms · '+s.ranker;
321
+ }
322
+ async function run(q){
323
+ if(!q){return}
324
+ const d=await(await fetch('/api/a11oy/v1/search/query?q='+encodeURIComponent(q)+'&k=8')).json();
325
+ const box=$('#res'); box.innerHTML='';
326
+ $('#resCard').style.display='block';
327
+ if(!d.results.length){box.innerHTML='<div class="res m">No matches in the governed corpus for “'+q+'”.</div>';return}
328
+ for(const r of d.results){
329
+ const el=document.createElement('div');el.className='res';
330
+ el.innerHTML='<span class="sc">'+r.score.toFixed(3)+'</span>'
331
+ +'<div class="kind">'+r.kind+'</div>'
332
+ +'<div class="t">'+r.title+'</div>'
333
+ +'<div class="m">'+r.snippet+'</div>'
334
+ +'<div class="m">matched: '+r.matched_terms.join(', ')
335
+ +(r.ref?(' · <a href="'+r.ref+'">'+r.ref+'</a>'):'')+'</div>';
336
+ box.appendChild(el);
337
+ }
338
+ }
339
+ $('#go').addEventListener('click',()=>run($('#q').value));
340
+ $('#q').addEventListener('keydown',e=>{if(e.key==='Enter')run($('#q').value)});
341
+ $('#rx').addEventListener('click',async()=>{await fetch('/api/a11oy/v1/search/reindex');stats();});
342
+ document.querySelectorAll('.chip').forEach(c=>c.addEventListener('click',()=>{
343
+ $('#q').value=c.textContent;run(c.textContent);}));
344
+ stats();
345
+ </script>
346
+ </body></html>"""