Spaces:
Running
Running
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- Dockerfile +12 -0
- NOTICE +53 -0
- serve.py +25 -0
- szl_budget_router.py +349 -0
- szl_consensus_clusters.py +306 -0
- szl_mission_ledger.py +217 -0
- 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 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 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 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>"""
|