Spaces:
Running
Running
feat: chain Council proposals into Khipu
Browse filesAdds a best-effort in-process Khipu receipt so freshly issued Council evidence can pass signature, payload-digest, and hash-chain verification. Persistence remains explicitly in-memory and resets on restart.
- a11oy_ayllu.py +37 -0
- test/test_council_contract.py +2 -0
a11oy_ayllu.py
CHANGED
|
@@ -174,6 +174,7 @@ def council_manifest(ns: str = "a11oy") -> Dict[str, Any]:
|
|
| 174 |
"persona/model/round/output digest per turn",
|
| 175 |
"Nemo governed-route decision and DSSE receipt",
|
| 176 |
"deterministic replay key over participants, mode, and output digests",
|
|
|
|
| 177 |
"outer Council DSSE receipt",
|
| 178 |
],
|
| 179 |
"limits": {
|
|
@@ -184,6 +185,7 @@ def council_manifest(ns: str = "a11oy") -> Dict[str, Any]:
|
|
| 184 |
"effectors": "none",
|
| 185 |
"decision_state": "PROPOSAL_ONLY",
|
| 186 |
"semantic_consensus": "NOT_MEASURED",
|
|
|
|
| 187 |
},
|
| 188 |
"reproduce": {
|
| 189 |
"manifest": base + "/council/manifest",
|
|
@@ -318,6 +320,38 @@ def _build_council_contract(prompt: str, result: Dict[str, Any],
|
|
| 318 |
}
|
| 319 |
|
| 320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
# --- The /ayllu page: a working chat + council UI. 0-CDN (pure inline markup, no
|
| 322 |
# external assets). `__NS__`/`__VERSION__` are substituted at render time so the
|
| 323 |
# JS+CSS braces stay literal (no f-string brace-doubling). --------------------
|
|
@@ -800,6 +834,7 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 800 |
signer = _runtime_signer(request)
|
| 801 |
nemo_route = _nemo_council_route(prompt, sign_fn=signer)
|
| 802 |
contract = _build_council_contract(prompt, result, nemo_route)
|
|
|
|
| 803 |
receipt_body = {
|
| 804 |
"council_id": council_id,
|
| 805 |
"prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
|
|
@@ -813,11 +848,13 @@ def register(app, ns: str = "a11oy") -> str:
|
|
| 813 |
"nemo_route_receipt_sha256": _receipt_sha(
|
| 814 |
(contract.get("routing") or {}).get("receipt")),
|
| 815 |
"human_checkpoint": contract["human_checkpoint"],
|
|
|
|
| 816 |
}
|
| 817 |
receipt = _make_receipt({
|
| 818 |
"schema": "szl.ayllu.council-receipt/v2",
|
| 819 |
"body": receipt_body,
|
| 820 |
"payload_digest": _sha256_json(receipt_body),
|
|
|
|
| 821 |
}, sign_fn=signer)
|
| 822 |
return JSONResponse({"council_id": council_id, "result": result,
|
| 823 |
"contract": contract, "receipt": receipt})
|
|
|
|
| 174 |
"persona/model/round/output digest per turn",
|
| 175 |
"Nemo governed-route decision and DSSE receipt",
|
| 176 |
"deterministic replay key over participants, mode, and output digests",
|
| 177 |
+
"in-process Khipu chain receipt (resets on process restart)",
|
| 178 |
"outer Council DSSE receipt",
|
| 179 |
],
|
| 180 |
"limits": {
|
|
|
|
| 185 |
"effectors": "none",
|
| 186 |
"decision_state": "PROPOSAL_ONLY",
|
| 187 |
"semantic_consensus": "NOT_MEASURED",
|
| 188 |
+
"chain_persistence": "IN_MEMORY_RESETS_ON_RESTART",
|
| 189 |
},
|
| 190 |
"reproduce": {
|
| 191 |
"manifest": base + "/council/manifest",
|
|
|
|
| 320 |
}
|
| 321 |
|
| 322 |
|
| 323 |
+
def _mint_council_chain(contract: Dict[str, Any], ns: str = "a11oy") -> Dict[str, Any]:
|
| 324 |
+
"""Append the proposal receipt to the shared in-process Khipu chain."""
|
| 325 |
+
try:
|
| 326 |
+
import szl_khipu
|
| 327 |
+
dag = szl_khipu.get_dag("ayllu_council", ns=ns)
|
| 328 |
+
receipt = dag.emit("ayllu.council.proposal", {
|
| 329 |
+
"contract_version": contract.get("contract_version"),
|
| 330 |
+
"decision_state": contract.get("decision_state"),
|
| 331 |
+
"evidence_state": contract.get("evidence_state"),
|
| 332 |
+
"prompt_sha256": contract.get("prompt_sha256"),
|
| 333 |
+
"replay_key": (contract.get("replay") or {}).get("key"),
|
| 334 |
+
})
|
| 335 |
+
chain = dag.verify_chain()
|
| 336 |
+
return {
|
| 337 |
+
"state": "LIVE",
|
| 338 |
+
"organ": "ayllu_council",
|
| 339 |
+
"receipt_id": receipt.get("digest"),
|
| 340 |
+
"seq": receipt.get("seq"),
|
| 341 |
+
"chain_verified": bool(chain.get("ok")),
|
| 342 |
+
"depth": dag.depth(),
|
| 343 |
+
"persistence": "IN_MEMORY_RESETS_ON_RESTART",
|
| 344 |
+
}
|
| 345 |
+
except Exception as exc:
|
| 346 |
+
return {
|
| 347 |
+
"state": "UNAVAILABLE",
|
| 348 |
+
"organ": "ayllu_council",
|
| 349 |
+
"receipt_id": None,
|
| 350 |
+
"error": type(exc).__name__,
|
| 351 |
+
"honesty": "Khipu append unavailable; no chain receipt fabricated.",
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
|
| 355 |
# --- The /ayllu page: a working chat + council UI. 0-CDN (pure inline markup, no
|
| 356 |
# external assets). `__NS__`/`__VERSION__` are substituted at render time so the
|
| 357 |
# JS+CSS braces stay literal (no f-string brace-doubling). --------------------
|
|
|
|
| 834 |
signer = _runtime_signer(request)
|
| 835 |
nemo_route = _nemo_council_route(prompt, sign_fn=signer)
|
| 836 |
contract = _build_council_contract(prompt, result, nemo_route)
|
| 837 |
+
contract["chain"] = _mint_council_chain(contract, ns=ns)
|
| 838 |
receipt_body = {
|
| 839 |
"council_id": council_id,
|
| 840 |
"prompt_sha256": hashlib.sha256(prompt.encode()).hexdigest(),
|
|
|
|
| 848 |
"nemo_route_receipt_sha256": _receipt_sha(
|
| 849 |
(contract.get("routing") or {}).get("receipt")),
|
| 850 |
"human_checkpoint": contract["human_checkpoint"],
|
| 851 |
+
"chain": contract["chain"],
|
| 852 |
}
|
| 853 |
receipt = _make_receipt({
|
| 854 |
"schema": "szl.ayllu.council-receipt/v2",
|
| 855 |
"body": receipt_body,
|
| 856 |
"payload_digest": _sha256_json(receipt_body),
|
| 857 |
+
"receipt_id": contract["chain"].get("receipt_id"),
|
| 858 |
}, sign_fn=signer)
|
| 859 |
return JSONResponse({"council_id": council_id, "result": result,
|
| 860 |
"contract": contract, "receipt": receipt})
|
test/test_council_contract.py
CHANGED
|
@@ -74,6 +74,8 @@ class CouncilContractTests(unittest.TestCase):
|
|
| 74 |
self.assertEqual(body["contract"]["decision_state"], "PROPOSAL_ONLY")
|
| 75 |
self.assertTrue(body["receipt"]["signed"])
|
| 76 |
self.assertEqual(body["contract"]["routing"]["state"], "HEURISTIC")
|
|
|
|
|
|
|
| 77 |
|
| 78 |
|
| 79 |
if __name__ == "__main__":
|
|
|
|
| 74 |
self.assertEqual(body["contract"]["decision_state"], "PROPOSAL_ONLY")
|
| 75 |
self.assertTrue(body["receipt"]["signed"])
|
| 76 |
self.assertEqual(body["contract"]["routing"]["state"], "HEURISTIC")
|
| 77 |
+
self.assertEqual(body["contract"]["chain"]["state"], "LIVE")
|
| 78 |
+
self.assertTrue(body["contract"]["chain"]["chain_verified"])
|
| 79 |
|
| 80 |
|
| 81 |
if __name__ == "__main__":
|