betterwithage commited on
Commit
5174357
·
verified ·
1 Parent(s): 2de9896

chore(sync): mirror backend .py + Dockerfile to Space (hf-sync mirror-app)

Browse files

Automated backend sync from szl-holdings/killinchu main via hf-sync.
Updated (differed from the Space): Dockerfile, a11oy_autoreview.py, szl_ecosystem_routes.py
Deleted (gone from the repo + Dockerfile COPY set): (none)

Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.

Files changed (3) hide show
  1. Dockerfile +3 -2
  2. a11oy_autoreview.py +197 -4
  3. szl_ecosystem_routes.py +9 -0
Dockerfile CHANGED
@@ -152,8 +152,9 @@ COPY static-vendor/a11oy-operator-widget.js ./static-vendor/a11oy-operator-widge
152
  COPY static-vendor/a11oy-operator-widget.css ./static-vendor/a11oy-operator-widget.css
153
  # RESTRAINT (DEV-WIRE-K R3): the SHARED governed code-frugality ladder, BYTE-IDENTICAL
154
  # to a11oy's szl_restraint.py (same bytes, both hf-sync lists — drift guard enforced),
155
- # its /elite tile, and the Chaski transport-level annotator (self-hosted, 0 CDN).
156
- COPY szl_restraint.py killinchu_restraint_tile.py ./
 
157
  COPY static-vendor/killinchu-restraint-chaski.js ./static-vendor/killinchu-restraint-chaski.js
158
  # Evidence & Research backend (curated + live arXiv/GitHub). serve.py imports this;
159
  # without this per-file COPY the import fails and /api/killinchu/v1/evidence/research 404s.
 
152
  COPY static-vendor/a11oy-operator-widget.css ./static-vendor/a11oy-operator-widget.css
153
  # RESTRAINT (DEV-WIRE-K R3): the SHARED governed code-frugality ladder, BYTE-IDENTICAL
154
  # to a11oy's szl_restraint.py (same bytes, both hf-sync lists — drift guard enforced),
155
+ # its /elite tile, the restraint nav wire-up (imported by serve.py for /elite/restraint
156
+ # navigation), and the Chaski transport-level annotator (self-hosted, 0 CDN).
157
+ COPY szl_restraint.py killinchu_restraint_tile.py killinchu_nav_wireup.py ./
158
  COPY static-vendor/killinchu-restraint-chaski.js ./static-vendor/killinchu-restraint-chaski.js
159
  # Evidence & Research backend (curated + live arXiv/GitHub). serve.py imports this;
160
  # without this per-file COPY the import fails and /api/killinchu/v1/evidence/research 404s.
a11oy_autoreview.py CHANGED
@@ -74,6 +74,17 @@ try:
74
  except Exception: # pragma: no cover
75
  _cal = None
76
 
 
 
 
 
 
 
 
 
 
 
 
77
  # ---------------------------------------------------------------------------
78
  # Doctrine constants (single source for this module).
79
  # ---------------------------------------------------------------------------
@@ -136,7 +147,7 @@ def _init_db() -> None:
136
  # OPA is CNCF-graduated; Rego is its policy language (github.com/open-policy-agent).
137
  # OSCAL = NIST machine-readable controls (github.com/usnistgov/OSCAL).
138
  # ===========================================================================
139
- POLICY_VERSION = "szl-autoreview-policy/1.0.0"
140
 
141
  # Each rule: id, human title, the boundary it guards, the default verdict it
142
  # proposes when it FIRES, the OSCAL control id(s), and the NIST AI RMF MANAGE
@@ -201,6 +212,31 @@ POLICY_RULES = [
201
  "decision is escalated to a human-on-loop with the signed "
202
  "classifier + CBF-QP + BFT decision-support bundle. No live effector.",
203
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  ]
205
 
206
  # Rego source text — the auditable, versioned policy. This is rendered verbatim
@@ -244,6 +280,16 @@ verdict := "escalate" {
244
  action_matches(input.action, "engage|weapon|intercept|kinetic|roe|fire control")
245
  } # OSCAL AC-3, AU-10 ; NIST AI RMF MANAGE 4.3
246
 
 
 
 
 
 
 
 
 
 
 
247
  # Lambda gate: the verdict is advisory under Conjecture 1 — never asserted 100% safe.
248
  allow_auto { input.dial >= autonomy_threshold[verdict]; lambda_ok }
249
  lambda_ok { input.lambda_eff < 1.0 } # Conjecture 1 — trust < 100%
@@ -394,6 +440,107 @@ def _lambda_effective(verdict: str, fired_rules: list, intent_just: bool) -> flo
394
  return round(min(LAMBDA_CEIL, lam), 4)
395
 
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  class _AutoReviewEngine:
398
  def __init__(self, sign_fn=None, verify_fn=None, pub_pem_fn=None, ns="a11oy"):
399
  self.sign_fn = sign_fn
@@ -482,6 +629,8 @@ class _AutoReviewEngine:
482
  run_id=None, seq=None, persist=True, calibrate_only=False):
483
  action_text = ("%s %s" % (tool or "", tool_input or "")).strip()
484
  ws = _workspace_inspect(tool_input or "", workspace)
 
 
485
  fired = []
486
  # evaluate rules in priority order; the most severe fired verdict wins.
487
  severity = {"allow": 0, "narrow": 1, "block-with-explanation": 2, "escalate": 3}
@@ -495,6 +644,12 @@ class _AutoReviewEngine:
495
  m = None
496
  if not m:
497
  continue
 
 
 
 
 
 
498
  # INTENT-RELATIVE: if the user's intent justifies this boundary,
499
  # the rule does NOT fire (e.g. user explicitly asked to deploy).
500
  intent_just = _intent_justifies(intent, rule.get("intent_pattern", rule["pattern"]))
@@ -554,6 +709,10 @@ class _AutoReviewEngine:
554
  "conformal": cset,
555
  "policy_version": POLICY_VERSION,
556
  "rego_sha256": REGO_SHA256,
 
 
 
 
557
  "label": "HEURISTIC", # deterministic rule+feature scorer, not a learned model
558
  "_probs": probs,
559
  "_idx": idx,
@@ -582,6 +741,16 @@ class _AutoReviewEngine:
582
  "rego_sha256")}
583
  receipt_core["module"] = MODULE
584
  receipt_core["trust_status"] = "Conjecture 1 (advisory — NOT a proven oracle)"
 
 
 
 
 
 
 
 
 
 
585
  dec_hash = _sha(receipt_core)
586
  envelope = None
587
  if self.sign_fn is not None and not calibrate_only:
@@ -797,10 +966,25 @@ def register(app, ns="a11oy", sign_fn=None, verify_fn=None, pub_pem_fn=None,
797
  actions = d.get("actions") # optional explicit [{tool,tool_input}]
798
  workspace = d.get("workspace")
799
  if not actions:
800
- # default demo plan derived from the intent (one safe, one risky)
 
 
801
  actions = [
802
  {"tool": "read", "tool_input": "README.md"},
803
  {"tool": "shell", "tool_input": "cat .env api_key token"},
 
 
 
 
 
 
 
 
 
 
 
 
 
804
  ]
805
  trace = []
806
  for i, a in enumerate(actions):
@@ -819,7 +1003,10 @@ def register(app, ns="a11oy", sign_fn=None, verify_fn=None, pub_pem_fn=None,
819
  "signed": bool((verdict.get("receipt") or {}).get("envelope", {}) and
820
  (verdict["receipt"]["envelope"] or {}).get("signed")),
821
  "self_correct_hint": verdict.get("self_correct_hint"),
822
- "conformal_set": (verdict.get("conformal") or {}).get("set")}
 
 
 
823
  if verdict["verdict"] == "allow":
824
  step["action"] = "EXECUTED (simulated tool call)"
825
  elif verdict["verdict"] == "narrow":
@@ -902,7 +1089,13 @@ def register(app, ns="a11oy", sign_fn=None, verify_fn=None, pub_pem_fn=None,
902
  "Lambda-gate (Conjecture 1)", "DSSE-signed verdicts",
903
  "OPA/Rego + OSCAL + NIST AI RMF MANAGE",
904
  "conformal calibration (Dev B)", "ECE/Brier gate (Dev B)",
905
- "flapping detection"],
 
 
 
 
 
 
906
  "label": "EXPERIMENTAL"})
907
 
908
  # ---- serve the Auto-Review tab page (0 CDN; in-image, no shared bytes) ----
 
74
  except Exception: # pragma: no cover
75
  _cal = None
76
 
77
+ # a11oy Restraint (R1) — the governed frugality ladder (provenance + citations in
78
+ # szl_restraint / /api/a11oy/v1/restraint/info). Imported
79
+ # import-SAFE (never raises): when a code-shaped action is proposed, the Auto-Review
80
+ # classifier consults the restraint ladder as an ADDITIONAL governance signal, so a
81
+ # bloated diff that skipped the ladder is flagged/narrowed. We READ szl_restraint
82
+ # only (descend_ladder); we never mutate it (R1 owns that module).
83
+ try:
84
+ import szl_restraint as _restraint
85
+ except Exception: # pragma: no cover
86
+ _restraint = None
87
+
88
  # ---------------------------------------------------------------------------
89
  # Doctrine constants (single source for this module).
90
  # ---------------------------------------------------------------------------
 
147
  # OPA is CNCF-graduated; Rego is its policy language (github.com/open-policy-agent).
148
  # OSCAL = NIST machine-readable controls (github.com/usnistgov/OSCAL).
149
  # ===========================================================================
150
+ POLICY_VERSION = "szl-autoreview-policy/1.1.0" # 1.1.0: + AR-006 restraint prefer-minimal-diff (R5)
151
 
152
  # Each rule: id, human title, the boundary it guards, the default verdict it
153
  # proposes when it FIRES, the OSCAL control id(s), and the NIST AI RMF MANAGE
 
212
  "decision is escalated to a human-on-loop with the signed "
213
  "classifier + CBF-QP + BFT decision-support bundle. No live effector.",
214
  },
215
+ {
216
+ # R5 — RESTRAINT as a governance signal. Fires only on a CODE-shaped action
217
+ # (the agent proposing a diff) when the a11oy Restraint ladder (R1) reports
218
+ # the proposed code is BLOATED relative to where the frugality ladder would
219
+ # have stopped — i.e. the agent skipped YAGNI/stdlib/native/one-line and is
220
+ # about to emit far more code than the task needs. The verdict is NARROW:
221
+ # send the diff back through the restraint ladder (minimal viable diff)
222
+ # rather than landing the bloat. INTENT-RELATIVE: a task that genuinely
223
+ # asks for the larger construct does not trip it (see restraint_signal()).
224
+ "id": "AR-006-prefer-minimal-diff",
225
+ "title": "Bloated diff that skipped the restraint ladder — prefer minimal diff",
226
+ # matched against the action text only to confirm it is code-shaped; the
227
+ # real decision comes from the restraint ladder signal (computed below).
228
+ "pattern": r"(diff|patch|\+\+\+ |--- |def |class |function |import |require\(|<component|new file|write_file|apply_patch|edit_file)",
229
+ "intent_pattern": r"(framework|extensib|plugin|generic|scalbl|abstraction layer|for later|future-proof|just in case|enterprise)",
230
+ "verdict": "narrow",
231
+ "oscal": ["SA-8", "SA-15", "CM-7"], # security engineering principles, dev process/least functionality
232
+ "nist": "MANAGE 2.3", # manage residual risk; minimise attack/maintenance surface
233
+ "explain": "The proposed diff is larger than the a11oy Restraint ladder "
234
+ "(R1) would emit for this task — it appears to have skipped "
235
+ "YAGNI / stdlib / native / one-line rungs. Narrowed: re-run the "
236
+ "diff through the restraint ladder and land the minimal viable "
237
+ "diff (fewest files, smallest surface). Aligns with secure-by-"
238
+ "design least-functionality; not a certification.",
239
+ },
240
  ]
241
 
242
  # Rego source text — the auditable, versioned policy. This is rendered verbatim
 
280
  action_matches(input.action, "engage|weapon|intercept|kinetic|roe|fire control")
281
  } # OSCAL AC-3, AU-10 ; NIST AI RMF MANAGE 4.3
282
 
283
+ # AR-006 RESTRAINT governance signal: a code diff that skipped the a11oy
284
+ # Restraint ladder (R1) and is bloated relative to its minimal rung -> narrow
285
+ # (re-run through the ladder; land the minimal viable diff). The restraint rung
286
+ # + lines-saved come from szl_restraint.descend_ladder (input.restraint.*).
287
+ verdict := "narrow" {
288
+ input.restraint.code_shaped == true
289
+ input.restraint.bloated == true
290
+ not intent_justifies(input.intent, "framework|extensib|plugin|generic|abstraction|for later|future-proof")
291
+ } # OSCAL SA-8, SA-15, CM-7 ; NIST AI RMF MANAGE 2.3
292
+
293
  # Lambda gate: the verdict is advisory under Conjecture 1 — never asserted 100% safe.
294
  allow_auto { input.dial >= autonomy_threshold[verdict]; lambda_ok }
295
  lambda_ok { input.lambda_eff < 1.0 } # Conjecture 1 — trust < 100%
 
440
  return round(min(LAMBDA_CEIL, lam), 4)
441
 
442
 
443
+ # ---------------------------------------------------------------------------
444
+ # R5 — RESTRAINT GOVERNANCE SIGNAL.
445
+ # When the agent proposes CODE (a diff), the Auto-Review classifier consults the
446
+ # a11oy Restraint ladder (R1, szl_restraint.descend_ladder) as an ADDITIONAL
447
+ # governance signal. We compare the size of the PROPOSED diff against where the
448
+ # frugality ladder would have stopped. If the proposed diff is materially larger
449
+ # than the ladder's minimal-viable diff (i.e. it skipped YAGNI/stdlib/native/
450
+ # one-line rungs), we mark it `bloated` so AR-006 narrows it. Deterministic +
451
+ # HEURISTIC; READ-ONLY against szl_restraint (R1 owns that module). Never raises.
452
+ # ---------------------------------------------------------------------------
453
+ _CODE_SHAPE_RE = re.compile(
454
+ r"(?:^|\n)\s*(?:\+\+\+ |--- |@@ |def |class |function |const |let |var |"
455
+ r"import |from .+ import|require\(|<[A-Za-z][\w-]*[ />]|=>|public |private )"
456
+ r"|\b(?:diff|patch|new file|write_file|apply_patch|edit_file|create file)\b",
457
+ re.IGNORECASE,
458
+ )
459
+
460
+
461
+ def _looks_like_code(action_text: str) -> bool:
462
+ """True when the action text looks like a code diff / file write the agent is
463
+ about to emit (not a read/grep/shell side-effect). Deterministic HEURISTIC."""
464
+ if not action_text:
465
+ return False
466
+ if _CODE_SHAPE_RE.search(action_text):
467
+ return True
468
+ # multi-line block that is mostly indented (diff/source body)
469
+ lines = action_text.splitlines()
470
+ if len(lines) >= 4 and sum(1 for ln in lines if ln[:1] in (" ", "\t", "+", "-")) >= 3:
471
+ return True
472
+ return False
473
+
474
+
475
+ def restraint_signal(intent: str, tool: str, tool_input: str) -> dict:
476
+ """Compute the Restraint governance signal for a proposed action. Returns a
477
+ small, JSON-safe dict that is surfaced in the verdict trace + sealed into the
478
+ signed receipt, and whose `bloated` flag gates AR-006. ADDITIVE + READ-ONLY:
479
+ consults szl_restraint.descend_ladder (R1); never mutates it; never raises."""
480
+ action_text = ("%s %s" % (tool or "", tool_input or "")).strip()
481
+ code_shaped = _looks_like_code(action_text) or _looks_like_code(intent or "")
482
+ sig = {
483
+ "code_shaped": bool(code_shaped),
484
+ "available": _restraint is not None,
485
+ "bloated": False,
486
+ "label": "HEURISTIC",
487
+ "note": "Restraint signal is advisory (HEURISTIC); the a11oy Restraint "
488
+ "ladder (R1) is governed + measured on our stack. See "
489
+ "/api/a11oy/v1/restraint/info for full provenance + citations.",
490
+ }
491
+ if not code_shaped or _restraint is None:
492
+ if not code_shaped:
493
+ sig["note"] = "Action is not code-shaped; restraint ladder not consulted."
494
+ elif _restraint is None:
495
+ sig["note"] = "a11oy Restraint (R1) not importable here; signal unavailable."
496
+ return sig
497
+ try:
498
+ task = (intent or tool_input or "").strip() or action_text
499
+ dec = _restraint.descend_ladder(task, intensity="full")
500
+ rung = int(dec.get("stopped_at_rung", 6))
501
+ saved = ((dec.get("lines_saved_estimate") or {}).get("lines_saved_modeled") or 0)
502
+ baseline = ((dec.get("lines_saved_estimate") or {}).get("baseline_loc_modeled") or 0)
503
+ # Proposed-diff size proxy: count substantive (non-blank) lines in the
504
+ # action text the agent is about to emit.
505
+ proposed_loc = sum(1 for ln in (tool_input or "").splitlines() if ln.strip())
506
+ # BLOATED when the ladder would stop EARLY (rung <= 4: YAGNI/stdlib/native/
507
+ # installed/one-line all avoid bespoke code) AND the agent is proposing a
508
+ # diff materially larger than the ladder's minimal-viable kept LOC. If we
509
+ # cannot measure the proposed diff (proposed_loc==0, e.g. a one-line tool
510
+ # input), fall back to the ladder verdict alone for low rungs.
511
+ kept = ((dec.get("lines_saved_estimate") or {}).get("restraint_loc_modeled") or 0)
512
+ if proposed_loc > 0:
513
+ # The ladder stops EARLY (rungs 1-5: YAGNI / stdlib / native / installed
514
+ # dep / one-line all avoid bespoke multi-line code). If the agent is
515
+ # proposing materially more than the ladder's minimal kept LOC, it is
516
+ # bloated. rungs 1/2/5 (skip-it / stdlib / one-line) have the tightest
517
+ # ceiling; rungs 3/4 (native / installed dep) allow a little glue code.
518
+ if rung in (1, 2, 5):
519
+ bloated = proposed_loc > max(6, kept + 4)
520
+ elif rung in (3, 4):
521
+ bloated = proposed_loc > max(10, kept * 2)
522
+ else: # rung 6 minimal-viable: only bloated if far above the model
523
+ bloated = proposed_loc > max(18, kept * 2)
524
+ else:
525
+ bloated = rung <= 3 # ladder says a low rung would have sufficed
526
+ sig.update({
527
+ "bloated": bool(bloated),
528
+ "rung": rung,
529
+ "rung_key": dec.get("rung_key"),
530
+ "rung_name": dec.get("rung_name"),
531
+ "restraint_comment": dec.get("restraint_comment"),
532
+ "lines_saved_modeled": saved,
533
+ "baseline_loc_modeled": baseline,
534
+ "restraint_loc_modeled": kept,
535
+ "proposed_loc_observed": proposed_loc,
536
+ "ceiling": dec.get("ceiling"),
537
+ "endpoint": "/api/a11oy/v1/restraint/evaluate",
538
+ })
539
+ except Exception as e: # never let the restraint signal break a verdict
540
+ sig["note"] = "restraint ladder raised %s; signal advisory-unavailable" % type(e).__name__
541
+ return sig
542
+
543
+
544
  class _AutoReviewEngine:
545
  def __init__(self, sign_fn=None, verify_fn=None, pub_pem_fn=None, ns="a11oy"):
546
  self.sign_fn = sign_fn
 
629
  run_id=None, seq=None, persist=True, calibrate_only=False):
630
  action_text = ("%s %s" % (tool or "", tool_input or "")).strip()
631
  ws = _workspace_inspect(tool_input or "", workspace)
632
+ # R5 — RESTRAINT governance signal (advisory; gates AR-006). Read-only.
633
+ rsig = restraint_signal(intent, tool, tool_input)
634
  fired = []
635
  # evaluate rules in priority order; the most severe fired verdict wins.
636
  severity = {"allow": 0, "narrow": 1, "block-with-explanation": 2, "escalate": 3}
 
644
  m = None
645
  if not m:
646
  continue
647
+ # R5 — AR-006 (prefer-minimal-diff) is RESTRAINT-driven: a code-shaped
648
+ # match alone is NOT enough; it fires only when the restraint ladder
649
+ # (R1) reports the proposed diff is BLOATED relative to its minimal rung.
650
+ if rule["id"] == "AR-006-prefer-minimal-diff" and not (
651
+ rsig.get("code_shaped") and rsig.get("bloated")):
652
+ continue
653
  # INTENT-RELATIVE: if the user's intent justifies this boundary,
654
  # the rule does NOT fire (e.g. user explicitly asked to deploy).
655
  intent_just = _intent_justifies(intent, rule.get("intent_pattern", rule["pattern"]))
 
709
  "conformal": cset,
710
  "policy_version": POLICY_VERSION,
711
  "rego_sha256": REGO_SHA256,
712
+ # R5 — RESTRAINT as a governance signal in the verdict trace. The rung
713
+ # is the a11oy Restraint ladder's stopping point for this action (R1).
714
+ "restraint": rsig,
715
+ "restraint_rung": rsig.get("rung"),
716
  "label": "HEURISTIC", # deterministic rule+feature scorer, not a learned model
717
  "_probs": probs,
718
  "_idx": idx,
 
741
  "rego_sha256")}
742
  receipt_core["module"] = MODULE
743
  receipt_core["trust_status"] = "Conjecture 1 (advisory — NOT a proven oracle)"
744
+ # R5 — seal the RESTRAINT signal (rung + bloated flag) INTO the signed
745
+ # verdict so the restraint rung is tamper-evident + replayable.
746
+ receipt_core["restraint"] = {
747
+ "code_shaped": rsig.get("code_shaped"),
748
+ "bloated": rsig.get("bloated"),
749
+ "rung": rsig.get("rung"),
750
+ "rung_key": rsig.get("rung_key"),
751
+ "lines_saved_modeled": rsig.get("lines_saved_modeled"),
752
+ "label": rsig.get("label"),
753
+ }
754
  dec_hash = _sha(receipt_core)
755
  envelope = None
756
  if self.sign_fn is not None and not calibrate_only:
 
966
  actions = d.get("actions") # optional explicit [{tool,tool_input}]
967
  workspace = d.get("workspace")
968
  if not actions:
969
+ # default demo plan derived from the intent (one safe, one risky, and
970
+ # one R5 RESTRAINT case: a bloated diff that skipped the ladder for a
971
+ # task the stdlib already solves -> AR-006 narrows it to a minimal diff)
972
  actions = [
973
  {"tool": "read", "tool_input": "README.md"},
974
  {"tool": "shell", "tool_input": "cat .env api_key token"},
975
+ {"tool": "write_file",
976
+ "tool_input": ("# format the current date as ISO 8601\n"
977
+ "class DateFormatter:\n"
978
+ " def __init__(self, sep='-', upper=False):\n"
979
+ " self.sep = sep\n"
980
+ " self.upper = upper\n"
981
+ " def _pad(self, n):\n"
982
+ " s = str(n)\n"
983
+ " return s if len(s) >= 2 else '0' + s\n"
984
+ " def format(self, y, m, d):\n"
985
+ " parts = [self._pad(y), self._pad(m), self._pad(d)]\n"
986
+ " out = self.sep.join(parts)\n"
987
+ " return out.upper() if self.upper else out\n")},
988
  ]
989
  trace = []
990
  for i, a in enumerate(actions):
 
1003
  "signed": bool((verdict.get("receipt") or {}).get("envelope", {}) and
1004
  (verdict["receipt"]["envelope"] or {}).get("signed")),
1005
  "self_correct_hint": verdict.get("self_correct_hint"),
1006
+ "conformal_set": (verdict.get("conformal") or {}).get("set"),
1007
+ # R5 — show the restraint rung in the verdict trace
1008
+ "restraint": verdict.get("restraint"),
1009
+ "restraint_rung": verdict.get("restraint_rung")}
1010
  if verdict["verdict"] == "allow":
1011
  step["action"] = "EXECUTED (simulated tool call)"
1012
  elif verdict["verdict"] == "narrow":
 
1089
  "Lambda-gate (Conjecture 1)", "DSSE-signed verdicts",
1090
  "OPA/Rego + OSCAL + NIST AI RMF MANAGE",
1091
  "conformal calibration (Dev B)", "ECE/Brier gate (Dev B)",
1092
+ "flapping detection",
1093
+ "restraint governance signal (AR-006 prefer-minimal-diff)"],
1094
+ "restraint_signal": {"available": _restraint is not None,
1095
+ "rule": "AR-006-prefer-minimal-diff",
1096
+ "reads": "szl_restraint.descend_ladder (R1)",
1097
+ "oscal": ["SA-8", "SA-15", "CM-7"],
1098
+ "nist_ai_rmf": "MANAGE 2.3"},
1099
  "label": "EXPERIMENTAL"})
1100
 
1101
  # ---- serve the Auto-Review tab page (0 CDN; in-image, no shared bytes) ----
szl_ecosystem_routes.py CHANGED
@@ -106,6 +106,8 @@ def build_kpi_board(ns: str) -> Dict[str, Any]:
106
  a_honest = _get_json(A11OY_BASE + "/api/a11oy/v1/honest")
107
  a_lambda = _get_json(A11OY_BASE + "/api/a11oy/v1/lambda")
108
  chapaq = _get_json(KILLINCHU_BASE + "/api/killinchu/v1/gov/chapaq-verdict")
 
 
109
 
110
  # locked-8 (G1) - read live, flag any source reporting != 8
111
  lock = (a_honest or {}).get("doctrine_lock", {}) if a_honest else {}
@@ -164,6 +166,13 @@ def build_kpi_board(ns: str) -> Dict[str, Any]:
164
  "killinchu": {"reachable": chapaq is not None, "role": "C-UAS / maritime sensing"},
165
  },
166
  "chapaq_verdict": (chapaq or {}).get("data") if chapaq else None,
 
 
 
 
 
 
 
167
  "doctrine": "v11",
168
  }
169
 
 
106
  a_honest = _get_json(A11OY_BASE + "/api/a11oy/v1/honest")
107
  a_lambda = _get_json(A11OY_BASE + "/api/a11oy/v1/lambda")
108
  chapaq = _get_json(KILLINCHU_BASE + "/api/killinchu/v1/gov/chapaq-verdict")
109
+ # R4 (2026-06-14): a11oy Restraint frugality->energy tile (live, honest).
110
+ restraint_kpi = _get_json(A11OY_BASE + "/api/a11oy/v1/restraint/kpi")
111
 
112
  # locked-8 (G1) - read live, flag any source reporting != 8
113
  lock = (a_honest or {}).get("doctrine_lock", {}) if a_honest else {}
 
166
  "killinchu": {"reachable": chapaq is not None, "role": "C-UAS / maritime sensing"},
167
  },
168
  "chapaq_verdict": (chapaq or {}).get("data") if chapaq else None,
169
+ "restraint": (restraint_kpi if isinstance(restraint_kpi, dict) else {
170
+ "tile": "restraint", "label": "SAMPLE",
171
+ "note": ("a11oy Restraint KPI endpoint unreachable — honest empty tile. "
172
+ "Frugality rate + cumulative lines/tokens/joules saved appear "
173
+ "once /api/a11oy/v1/restraint/kpi responds. Joules MEASURED only "
174
+ "on a live GPU probe, else SAMPLE. Adopts Ponytail (MIT)."),
175
+ }),
176
  "doctrine": "v11",
177
  }
178