round-12: migrate P8 registries + P12 audit to AppStateStore
- app_state.py: host_state_db_path() (host-level DB, distinct from per-VM state_db_path); kv_get/kv_put/kv_delete on meta (namespaced keys; kv_put skips the no-secrets guard by design — registry docs hold user prompts, documented); audit_trail table (P12 record shape + hash chain, seq explicit); counts()/backup() gain audit_trail. - swarm.py / presentation.py / browser_tasks.py: JSON-file backends replaced by KV rows (p8-swarm / p8-ledger / p8-browser-tasks); legacy JSON imported once on first load (renamed .migrated); corrupt row / unreadable store returns empty (fail-safe, same semantics as before; next successful save self-heals). - audit_trail.py: StoreAuditTrail (same record shape, hash chain, filters, export as JSONL version, backed by audit_trail table); migrate_jsonl_to_store() moves a chain verbatim (broken chain refused, divergent seq refused, re-run idempotent). JSONL class kept for reading old files. - P12 dict contracts now run against real P3/P5 code (ApprovalQueue.record -> build_approval_card -> card_matches_channel; CapabilityIssuer.issue -> describe -> PermissionRegistry.record_grant). - P6->P3 local protocol test: real PrivsepClient + real worker server + real git status; worker git argv carries the real P3 helper + useHttpPath; no token in argv/env/audit. runuser + HTTP pull stay real-machine-only (see gap docs). - Gap docs updated: P8 migration done, guest-side browser follow-up explicitly left for the real-machine track (no task_id in the browser protocol yet; lifecycle needs a real VM to verify); P6 wiring + P12 migration marked done; production-readiness.md gains §20 (round-12 integration). - Tests: new cloud/tests/test_chv_muse_round12.py, 24/24 OK; test_chv_muse_p8.py ledger corrupt test rewritten for the KV backend (+ legacy-import test). P7+P8: 95+1 tests OK.
This commit is contained in:
1 parent
466cf7eae7
commit
d4cef29cdb
11 files changed
+1147
-63
No files matched your search
@@ -547,15 +547,37 @@ class PresentationTestCase(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
self.ledger.should_notify("t1", "", now=T0)
|
||||
|
||||
def test_ledger_corrupt_file_recovers(self):
|
||||
def test_ledger_corrupt_row_recovers(self):
|
||||
# round-12 迁移后:损坏的 KV 行回空账本(旧版是损坏的
|
||||
# JSON 文件 quarantine 后回空;语义同款,fail-safe)。
|
||||
self.assertTrue(self.ledger.should_notify("t1", "failed",
|
||||
now=T0))
|
||||
from cloud.chv.app_state import AppStateStore, \
|
||||
host_state_db_path
|
||||
with AppStateStore(host_state_db_path(self.state_dir)) as store:
|
||||
store.kv_put("p8-ledger", "registry", "{not json")
|
||||
self.assertTrue(self.ledger.should_notify("t2", "failed",
|
||||
now=T0))
|
||||
|
||||
def test_ledger_legacy_json_imported_once(self):
|
||||
# 旧版 JSON 文件首次加载时导入 KV 并改名 .migrated;
|
||||
# 之后再写坏 JSON 也不影响 KV 里的账本。
|
||||
with open(os.path.join(self.state_dir,
|
||||
"notification-ledger.json"),
|
||||
"w") as fh:
|
||||
fh.write('{"notified": {"t0:failed": 1000}}')
|
||||
self.assertFalse(self.ledger.should_notify(
|
||||
"t0", "failed", now=1000 + 10))
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.state_dir, "notification-ledger.json.migrated")))
|
||||
self.assertFalse(os.path.exists(os.path.join(
|
||||
self.state_dir, "notification-ledger.json")))
|
||||
with open(os.path.join(self.state_dir,
|
||||
"notification-ledger.json"),
|
||||
"w") as fh:
|
||||
fh.write("{not json")
|
||||
self.assertTrue(self.ledger.should_notify("t2", "failed",
|
||||
now=T0))
|
||||
self.assertFalse(self.ledger.should_notify(
|
||||
"t0", "failed", now=1000 + 20))
|
||||
|
||||
def test_present_or_log_combined(self):
|
||||
out = present_or_log({"status": "failed"}, self.ledger,
|
||||
|
||||
Reference in new issue
Block a user