capsule-engine 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. capsule_engine/__init__.py +17 -0
  2. capsule_engine/bundle_viewer/__init__.py +21 -0
  3. capsule_engine/bundle_viewer/static/offline_shell.html +1509 -0
  4. capsule_engine/bundle_viewer/viewer.py +57 -0
  5. capsule_engine/cli/__init__.py +5 -0
  6. capsule_engine/cli/bundle_cmd.py +269 -0
  7. capsule_engine/cli/console_cmd.py +70 -0
  8. capsule_engine/cli/constraints_cmd.py +74 -0
  9. capsule_engine/cli/format.py +173 -0
  10. capsule_engine/cli/guard_cmds.py +361 -0
  11. capsule_engine/cli/init_cmds.py +169 -0
  12. capsule_engine/cli/main.py +120 -0
  13. capsule_engine/cli/packs_cmds.py +96 -0
  14. capsule_engine/cli/telemetry_cmd.py +80 -0
  15. capsule_engine/cli/tenant_cmds.py +199 -0
  16. capsule_engine/cli/verify_cmd.py +110 -0
  17. capsule_engine/compiler/__init__.py +13 -0
  18. capsule_engine/compiler/effect_model.py +79 -0
  19. capsule_engine/compiler/vocabulary.py +253 -0
  20. capsule_engine/console/__init__.py +10 -0
  21. capsule_engine/console/api.py +251 -0
  22. capsule_engine/console/components.css +406 -0
  23. capsule_engine/console/console.css +130 -0
  24. capsule_engine/console/console.html +51 -0
  25. capsule_engine/console/console.js +330 -0
  26. capsule_engine/console/gallery.html +274 -0
  27. capsule_engine/console/server.py +137 -0
  28. capsule_engine/console/tokens.css +68 -0
  29. capsule_engine/conversation/__init__.py +88 -0
  30. capsule_engine/conversation/capsules.py +268 -0
  31. capsule_engine/conversation/exchange.py +279 -0
  32. capsule_engine/conversation/merkle.py +80 -0
  33. capsule_engine/conversation/session.py +101 -0
  34. capsule_engine/envcompat.py +15 -0
  35. capsule_engine/events/__init__.py +14 -0
  36. capsule_engine/events/capsule.py +133 -0
  37. capsule_engine/examples/__init__.py +9 -0
  38. capsule_engine/examples/data/tau2_airline/LICENSE-tau2-bench +21 -0
  39. capsule_engine/examples/data/tau2_airline/PROVENANCE.json +70 -0
  40. capsule_engine/examples/data/tau2_airline/hand_labels.json +893 -0
  41. capsule_engine/examples/data/tau2_airline/pilot1_live_gemini-2-5-flash_airline_shift1.jsonl +190 -0
  42. capsule_engine/examples/data/tau2_airline/tau2_committed_claude-3-7-sonnet_airline_trial0.jsonl +556 -0
  43. capsule_engine/examples/data/tau2_airline/tau2_committed_gpt-4-1-mini_airline_trial0.jsonl +480 -0
  44. capsule_engine/examples/data/tau2_airline/tau2_committed_gpt-4-1_airline_trial0.jsonl +496 -0
  45. capsule_engine/examples/data/tau2_airline/tau2_committed_o4-mini_airline_trial0.jsonl +392 -0
  46. capsule_engine/examples/data/tau2_airline/tau2_conversations_claude-3-7-sonnet_airline_4trials.jsonl +200 -0
  47. capsule_engine/examples/tau2_airline_reference.py +460 -0
  48. capsule_engine/examples/tau2_conversation_exchange.py +92 -0
  49. capsule_engine/folds/__init__.py +104 -0
  50. capsule_engine/folds/account_core.py +89 -0
  51. capsule_engine/folds/approval_latency.py +86 -0
  52. capsule_engine/folds/catalog.py +79 -0
  53. capsule_engine/folds/catalog_defs/actions.count_by_developer.yaml +10 -0
  54. capsule_engine/folds/catalog_defs/actions.executed_count.yaml +16 -0
  55. capsule_engine/folds/catalog_defs/actions.last_decision.yaml +12 -0
  56. capsule_engine/folds/catalog_defs/counterparty.seen_before.v2.yaml +34 -0
  57. capsule_engine/folds/catalog_defs/counterparty.seen_before.v3.yaml +38 -0
  58. capsule_engine/folds/catalog_defs/counterparty.seen_before.yaml +28 -0
  59. capsule_engine/folds/catalog_defs/disclosure.class_seen.yaml +29 -0
  60. capsule_engine/folds/catalog_defs/disclosure.statement_seen.yaml +32 -0
  61. capsule_engine/folds/catalog_defs/hold.active_exposure.yaml +44 -0
  62. capsule_engine/folds/catalog_defs/spend.weekly.v2.yaml +38 -0
  63. capsule_engine/folds/catalog_defs/spend.weekly.v3.1.yaml +71 -0
  64. capsule_engine/folds/catalog_defs/spend.weekly.v3.yaml +76 -0
  65. capsule_engine/folds/catalog_defs/spend.weekly.yaml +33 -0
  66. capsule_engine/folds/counterparty_signals.py +179 -0
  67. capsule_engine/folds/definition.py +421 -0
  68. capsule_engine/folds/duration.py +16 -0
  69. capsule_engine/folds/engine.py +436 -0
  70. capsule_engine/folds/errors.py +44 -0
  71. capsule_engine/folds/loader.py +24 -0
  72. capsule_engine/folds/ordering.py +143 -0
  73. capsule_engine/folds/paths.py +25 -0
  74. capsule_engine/folds/record_type_coverage.py +58 -0
  75. capsule_engine/folds/reducers.py +75 -0
  76. capsule_engine/folds/retention_continuity.py +117 -0
  77. capsule_engine/folds/set_membership.py +59 -0
  78. capsule_engine/folds/taxonomy.py +231 -0
  79. capsule_engine/guards/__init__.py +59 -0
  80. capsule_engine/guards/action.py +446 -0
  81. capsule_engine/guards/action_taxonomy.json +27 -0
  82. capsule_engine/guards/action_taxonomy_v2.json +27 -0
  83. capsule_engine/guards/action_taxonomy_v3.json +27 -0
  84. capsule_engine/guards/action_taxonomy_v4.json +27 -0
  85. capsule_engine/guards/action_taxonomy_v5.json +27 -0
  86. capsule_engine/guards/capsule.py +377 -0
  87. capsule_engine/guards/checks/__init__.py +268 -0
  88. capsule_engine/guards/checks/action_class_gate.py +199 -0
  89. capsule_engine/guards/checks/base.py +33 -0
  90. capsule_engine/guards/checks/caps.py +517 -0
  91. capsule_engine/guards/checks/channel_change.py +64 -0
  92. capsule_engine/guards/checks/commitment.py +41 -0
  93. capsule_engine/guards/checks/counterparty_identity_change.py +95 -0
  94. capsule_engine/guards/checks/counterparty_list.py +176 -0
  95. capsule_engine/guards/checks/counterparty_seen_before.py +160 -0
  96. capsule_engine/guards/checks/credential_pattern.py +73 -0
  97. capsule_engine/guards/checks/dedupe.py +259 -0
  98. capsule_engine/guards/checks/destination_rail.py +120 -0
  99. capsule_engine/guards/checks/field_change_count.py +100 -0
  100. capsule_engine/guards/checks/offer_expiry.py +134 -0
  101. capsule_engine/guards/checks/plan_containment.py +180 -0
  102. capsule_engine/guards/checks/price_floor.py +211 -0
  103. capsule_engine/guards/checks/promise_never.py +236 -0
  104. capsule_engine/guards/checks/promise_requires_approval.py +214 -0
  105. capsule_engine/guards/checks/recipient_role.py +117 -0
  106. capsule_engine/guards/checks/recurring_charge.py +49 -0
  107. capsule_engine/guards/checks/refundability.py +46 -0
  108. capsule_engine/guards/checks/release_on_acceptance.py +286 -0
  109. capsule_engine/guards/checks/required_disclosure.py +233 -0
  110. capsule_engine/guards/checks/single_commitment.py +221 -0
  111. capsule_engine/guards/checks/task_authority.py +182 -0
  112. capsule_engine/guards/checks/upfront_amount.py +71 -0
  113. capsule_engine/guards/checks/verify_before_dispatch.py +75 -0
  114. capsule_engine/guards/classes.py +393 -0
  115. capsule_engine/guards/engine.py +926 -0
  116. capsule_engine/guards/history_state.py +127 -0
  117. capsule_engine/guards/plan.py +237 -0
  118. capsule_engine/guards/revocation.py +120 -0
  119. capsule_engine/guards/signing.py +60 -0
  120. capsule_engine/guards/statements.py +62 -0
  121. capsule_engine/guards/tool_call.py +83 -0
  122. capsule_engine/guards/wickets/__init__.py +23 -0
  123. capsule_engine/guards/wickets/catalog.py +75 -0
  124. capsule_engine/guards/wickets/catalog_defs/action_class_gate.v1.1.yaml +38 -0
  125. capsule_engine/guards/wickets/catalog_defs/action_class_gate.v1.2.yaml +52 -0
  126. capsule_engine/guards/wickets/catalog_defs/action_class_gate.yaml +34 -0
  127. capsule_engine/guards/wickets/catalog_defs/caps.v2.yaml +30 -0
  128. capsule_engine/guards/wickets/catalog_defs/caps.v3.yaml +49 -0
  129. capsule_engine/guards/wickets/catalog_defs/caps.v4.yaml +45 -0
  130. capsule_engine/guards/wickets/catalog_defs/caps.v5.1.yaml +53 -0
  131. capsule_engine/guards/wickets/catalog_defs/caps.v5.yaml +43 -0
  132. capsule_engine/guards/wickets/catalog_defs/caps.yaml +17 -0
  133. capsule_engine/guards/wickets/catalog_defs/caps_holds.yaml +18 -0
  134. capsule_engine/guards/wickets/catalog_defs/channel_change.v1.1.yaml +18 -0
  135. capsule_engine/guards/wickets/catalog_defs/channel_change.yaml +10 -0
  136. capsule_engine/guards/wickets/catalog_defs/counterparty_identity_change.yaml +9 -0
  137. capsule_engine/guards/wickets/catalog_defs/counterparty_list.yaml +20 -0
  138. capsule_engine/guards/wickets/catalog_defs/counterparty_seen_before.v2.yaml +15 -0
  139. capsule_engine/guards/wickets/catalog_defs/counterparty_seen_before.v3.yaml +23 -0
  140. capsule_engine/guards/wickets/catalog_defs/counterparty_seen_before.v4.yaml +26 -0
  141. capsule_engine/guards/wickets/catalog_defs/counterparty_seen_before.yaml +14 -0
  142. capsule_engine/guards/wickets/catalog_defs/credential_pattern.yaml +14 -0
  143. capsule_engine/guards/wickets/catalog_defs/dedupe.yaml +19 -0
  144. capsule_engine/guards/wickets/catalog_defs/destination_rail.seller.v1.1.yaml +32 -0
  145. capsule_engine/guards/wickets/catalog_defs/destination_rail.seller.yaml +10 -0
  146. capsule_engine/guards/wickets/catalog_defs/destination_rail.yaml +9 -0
  147. capsule_engine/guards/wickets/catalog_defs/hold_reconcile.yaml +14 -0
  148. capsule_engine/guards/wickets/catalog_defs/material_fields_changed.v1.1.yaml +22 -0
  149. capsule_engine/guards/wickets/catalog_defs/material_fields_changed.yaml +14 -0
  150. capsule_engine/guards/wickets/catalog_defs/offer_expiry.v1.1.yaml +31 -0
  151. capsule_engine/guards/wickets/catalog_defs/offer_expiry.yaml +24 -0
  152. capsule_engine/guards/wickets/catalog_defs/offer_fields_changed.v1.1.yaml +22 -0
  153. capsule_engine/guards/wickets/catalog_defs/offer_fields_changed.yaml +10 -0
  154. capsule_engine/guards/wickets/catalog_defs/price_floor.yaml +32 -0
  155. capsule_engine/guards/wickets/catalog_defs/promise_never.v1.1.yaml +64 -0
  156. capsule_engine/guards/wickets/catalog_defs/promise_never.yaml +16 -0
  157. capsule_engine/guards/wickets/catalog_defs/promise_requires_approval.v1.1.yaml +26 -0
  158. capsule_engine/guards/wickets/catalog_defs/promise_requires_approval.yaml +21 -0
  159. capsule_engine/guards/wickets/catalog_defs/recipient_role.seller.v1.1.yaml +23 -0
  160. capsule_engine/guards/wickets/catalog_defs/recipient_role.seller.v1.2.yaml +24 -0
  161. capsule_engine/guards/wickets/catalog_defs/recipient_role.seller.yaml +10 -0
  162. capsule_engine/guards/wickets/catalog_defs/recipient_role.v1.1.yaml +26 -0
  163. capsule_engine/guards/wickets/catalog_defs/recipient_role.yaml +11 -0
  164. capsule_engine/guards/wickets/catalog_defs/recipient_seen_before.yaml +16 -0
  165. capsule_engine/guards/wickets/catalog_defs/recurring_charge.yaml +9 -0
  166. capsule_engine/guards/wickets/catalog_defs/refundability.v1.1.yaml +15 -0
  167. capsule_engine/guards/wickets/catalog_defs/refundability.yaml +7 -0
  168. capsule_engine/guards/wickets/catalog_defs/release_on_acceptance.seller.v1.1.yaml +44 -0
  169. capsule_engine/guards/wickets/catalog_defs/release_on_acceptance.seller.v1.2.yaml +63 -0
  170. capsule_engine/guards/wickets/catalog_defs/release_on_acceptance.seller.yaml +36 -0
  171. capsule_engine/guards/wickets/catalog_defs/required_disclosure.v1.1.yaml +36 -0
  172. capsule_engine/guards/wickets/catalog_defs/required_disclosure.v1.2.yaml +42 -0
  173. capsule_engine/guards/wickets/catalog_defs/required_disclosure.yaml +25 -0
  174. capsule_engine/guards/wickets/catalog_defs/single_commitment.seller.yaml +14 -0
  175. capsule_engine/guards/wickets/catalog_defs/task_authority.v1.1.yaml +20 -0
  176. capsule_engine/guards/wickets/catalog_defs/task_authority.yaml +8 -0
  177. capsule_engine/guards/wickets/catalog_defs/upfront_amount.v1.1.yaml +19 -0
  178. capsule_engine/guards/wickets/catalog_defs/upfront_amount.yaml +11 -0
  179. capsule_engine/guards/wickets/catalog_defs/verify_before_dispatch.yaml +9 -0
  180. capsule_engine/guards/wickets/definition.py +164 -0
  181. capsule_engine/guards/wickets/errors.py +18 -0
  182. capsule_engine/guards/wickets/loader.py +26 -0
  183. capsule_engine/guards/wickets/retired.py +62 -0
  184. capsule_engine/holds/__init__.py +35 -0
  185. capsule_engine/holds/capsules.py +266 -0
  186. capsule_engine/holds/engine.py +476 -0
  187. capsule_engine/holds/errors.py +29 -0
  188. capsule_engine/holds/policy.py +62 -0
  189. capsule_engine/holds/scope.py +47 -0
  190. capsule_engine/mcp/__init__.py +16 -0
  191. capsule_engine/mcp/config.py +117 -0
  192. capsule_engine/mcp/server.py +338 -0
  193. capsule_engine/mcp/tools.py +424 -0
  194. capsule_engine/packaging.py +60 -0
  195. capsule_engine/packs/__init__.py +45 -0
  196. capsule_engine/packs/backfill_coverage.py +238 -0
  197. capsule_engine/packs/catalog/airline-engagement/AI-BOOTSTRAP.md +53 -0
  198. capsule_engine/packs/catalog/airline-engagement/folds/session_close.yaml +8 -0
  199. capsule_engine/packs/catalog/airline-engagement/pack.yaml +146 -0
  200. capsule_engine/packs/catalog/eu-ai-act/fixtures/trace/good_execution.json +36 -0
  201. capsule_engine/packs/catalog/eu-ai-act/fixtures/trace/oversight_declared.json +36 -0
  202. capsule_engine/packs/catalog/eu-ai-act/fixtures/trace/tampered_execution.json +36 -0
  203. capsule_engine/packs/catalog/eu-ai-act/fixtures/trace/trusted_issuer.jwk.json +5 -0
  204. capsule_engine/packs/catalog/eu-ai-act/obligations-pack-note.md +68 -0
  205. capsule_engine/packs/catalog/eu-ai-act/obligations-register.yaml +761 -0
  206. capsule_engine/packs/catalog/eu-ai-act/pack.yaml +88 -0
  207. capsule_engine/packs/catalog/eu-ai-act/register.yaml +96 -0
  208. capsule_engine/packs/catalog/eu-ai-act-deterministic/fixtures/synthetic_internal_assist_sample.jsonl +6 -0
  209. capsule_engine/packs/catalog/eu-ai-act-deterministic/pack.yaml +580 -0
  210. capsule_engine/packs/catalog/everyday/AI-BOOTSTRAP.md +194 -0
  211. capsule_engine/packs/catalog/everyday/fixtures/mini_ledger.jsonl +45 -0
  212. capsule_engine/packs/catalog/everyday/pack.yaml +480 -0
  213. capsule_engine/packs/catalog/payments-safety/AI-BOOTSTRAP.md +83 -0
  214. capsule_engine/packs/catalog/payments-safety/fixtures/mini_ledger.jsonl +8 -0
  215. capsule_engine/packs/catalog/payments-safety/folds/spend_weekly.yaml +29 -0
  216. capsule_engine/packs/catalog/payments-safety/pack.yaml +112 -0
  217. capsule_engine/packs/catalog/seller/fixtures/mini_ledger.jsonl +83 -0
  218. capsule_engine/packs/catalog/seller/pack.yaml +503 -0
  219. capsule_engine/packs/catalog/standard-vendor/AI-BOOTSTRAP.md +70 -0
  220. capsule_engine/packs/catalog/standard-vendor/folds/session_close.yaml +8 -0
  221. capsule_engine/packs/catalog/standard-vendor/pack.yaml +466 -0
  222. capsule_engine/packs/contract_diff.py +809 -0
  223. capsule_engine/packs/contract_validate.py +185 -0
  224. capsule_engine/packs/corpus_verify.py +126 -0
  225. capsule_engine/packs/enforce.py +81 -0
  226. capsule_engine/packs/errors.py +159 -0
  227. capsule_engine/packs/eu_ai_act_trace_field_map.py +46 -0
  228. capsule_engine/packs/install.py +266 -0
  229. capsule_engine/packs/loader.py +1462 -0
  230. capsule_engine/packs/measurability_report.py +280 -0
  231. capsule_engine/packs/obligation_results.py +72 -0
  232. capsule_engine/packs/pins.py +92 -0
  233. capsule_engine/packs/schema.py +1053 -0
  234. capsule_engine/packs/thresholds.py +146 -0
  235. capsule_engine/packs/trace_attainment.py +188 -0
  236. capsule_engine/packs/trace_reader.py +104 -0
  237. capsule_engine/policy/__init__.py +37 -0
  238. capsule_engine/policy/activation.py +87 -0
  239. capsule_engine/policy/catalog_defs/default.yaml +23 -0
  240. capsule_engine/policy/catalog_defs/holds.yaml +27 -0
  241. capsule_engine/policy/errors.py +46 -0
  242. capsule_engine/policy/limits.py +287 -0
  243. capsule_engine/policy/loader.py +26 -0
  244. capsule_engine/policy/manifest.py +289 -0
  245. capsule_engine/policy/profile.py +281 -0
  246. capsule_engine/policy/resolve.py +224 -0
  247. capsule_engine/register/__init__.py +39 -0
  248. capsule_engine/register/compiler.py +219 -0
  249. capsule_engine/register/errors.py +40 -0
  250. capsule_engine/register/loader.py +247 -0
  251. capsule_engine/register/schema.py +129 -0
  252. capsule_engine/registry/__init__.py +19 -0
  253. capsule_engine/registry/conventions.json +162 -0
  254. capsule_engine/registry/conventions.py +188 -0
  255. capsule_engine/registry/cpb_registry.json +78 -0
  256. capsule_engine/report/__init__.py +62 -0
  257. capsule_engine/report/build.py +267 -0
  258. capsule_engine/report/coverage.py +777 -0
  259. capsule_engine/report/errors.py +35 -0
  260. capsule_engine/report/live_check.py +126 -0
  261. capsule_engine/report/live_history.py +420 -0
  262. capsule_engine/report/model.py +95 -0
  263. capsule_engine/report/render.py +467 -0
  264. capsule_engine/report/replay.py +1677 -0
  265. capsule_engine/report/result.py +504 -0
  266. capsule_engine/report/result_from_folds.py +222 -0
  267. capsule_engine/report/result_from_layers.py +670 -0
  268. capsule_engine/report/sale_bundle.py +864 -0
  269. capsule_engine/report/static/verify.js +451 -0
  270. capsule_engine/schemas/evidence-contract-v0.json +383 -0
  271. capsule_engine/schemas/vendor/epistemic-types.json +21 -0
  272. capsule_engine/schemas/vendor/evidence-result-v0.json +463 -0
  273. capsule_engine/telemetry/__init__.py +9 -0
  274. capsule_engine/telemetry/consent.py +39 -0
  275. capsule_engine/telemetry/events.py +93 -0
  276. capsule_engine/telemetry/fixtures/synthetic_events.jsonl +45 -0
  277. capsule_engine/telemetry/funnel.py +147 -0
  278. capsule_engine/telemetry/record.py +106 -0
  279. capsule_engine/telemetry/sink.py +61 -0
  280. capsule_engine/telemetry/state.py +60 -0
  281. capsule_engine/tenants.py +327 -0
  282. capsule_engine-0.1.0.dist-info/METADATA +47 -0
  283. capsule_engine-0.1.0.dist-info/RECORD +288 -0
  284. capsule_engine-0.1.0.dist-info/WHEEL +5 -0
  285. capsule_engine-0.1.0.dist-info/entry_points.txt +2 -0
  286. capsule_engine-0.1.0.dist-info/licenses/LICENSE +202 -0
  287. capsule_engine-0.1.0.dist-info/licenses/capsule_engine/examples/data/tau2_airline/LICENSE-tau2-bench +21 -0
  288. capsule_engine-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,281 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """A policy profile: one user's own values for the parameters a pack exposes,
3
+ kept as its own artifact so the pack and its wickets stay byte-identical for
4
+ every user.
5
+
6
+ Before this existed, accepting a limit (``packs/enforce.py``'s
7
+ ``accept_thresholds``) merged the value into the ``caps`` wicket's config,
8
+ so 25.00 -> 40.00 moved the wicket's digest, then the pack's, then the
9
+ manifest's. That made "what was in force" provable, but it also meant two
10
+ households with different limits could not run the same wicket. The checks
11
+ already take their limits as arguments (``guards/checks/caps.py``), so the
12
+ fix is where the value is stored, not how it is evaluated: the wicket carries
13
+ the pack's default, a profile carries the user's value, and the manifest pins
14
+ the profile by digest (``Manifest.profile_digest``) the same way it pins
15
+ every fold and wicket. "What was in force" stays provable because the
16
+ manifest digest moves when the profile does, and the activation record
17
+ carries the profile's values (``policy/activation.py``).
18
+
19
+ Shape (``policy-profile/v0``)::
20
+
21
+ format: policy-profile/v0
22
+ packs:
23
+ - pack: asg/everyday # publisher/name -- no version
24
+ parameters:
25
+ caps: # the check the values configure
26
+ per_action_minor: {money.purchase: 4000}
27
+ caps_minor: {money.purchase: 20000}
28
+ counterparty_list: # a list, not class values
29
+ mode: deny # required: deny | allow
30
+ entries: # each names its kind and form
31
+ - {kind: payee, fp_alg: hmac-sha256-deal-key, value: <hex>}
32
+ - {kind: target, value: shop/example}
33
+ disposition: ask # optional: deny | ask
34
+
35
+ ``pack`` names a pack without its version so a profile can outlive a pack
36
+ upgrade; carrying values across an upgrade is a later step, and today a
37
+ value the installed pack no longer defines fails closed at resolve time.
38
+ The profile names no owner: two households that chose the same values have
39
+ the same profile digest.
40
+
41
+ Which parameters a profile may set is a closed table (``CONFIGURABLE``), and
42
+ a value may only replace a default the pack's wicket already declares: a
43
+ profile never adds a class the pack does not limit. Anything else is refused
44
+ (``resolve.py``), never ignored.
45
+
46
+ ``counterparty_list`` is the one check whose values are not per-class
47
+ amounts: its ``entries`` replace the wicket's (empty) list whole. A profile
48
+ that sets it must name ``mode``, so a list is never read as deny when the
49
+ user meant allow. Each entry names its ``kind`` (``target``, or a
50
+ fingerprint kind with its ``fp_alg``; see ``guards/checks/
51
+ counterparty_list.py``). Entries are stored in a canonical order, so the
52
+ profile digest does not depend on the order the user typed them.
53
+ """
54
+ from __future__ import annotations
55
+
56
+ import json
57
+ import re
58
+ from collections.abc import Iterable
59
+ from dataclasses import dataclass, field
60
+ from pathlib import Path
61
+ from typing import Any, TypedDict
62
+
63
+ from agent_action_capsule.canonical import json_digest
64
+
65
+ from ..guards.checks.counterparty_list import (
66
+ CLEAR_KINDS,
67
+ DISPOSITIONS,
68
+ FINGERPRINT_KINDS,
69
+ MODES,
70
+ ListEntry,
71
+ sorted_entries,
72
+ )
73
+ from .errors import MALFORMED_PROFILE, PolicyManifestError
74
+
75
+ __all__ = [
76
+ "CONFIGURABLE",
77
+ "LIST_CHECK",
78
+ "LIST_KEYS",
79
+ "PROFILE_FORMAT",
80
+ "PackParameters",
81
+ "PolicyProfile",
82
+ "load_profile_file",
83
+ "pack_name",
84
+ "parse_profile",
85
+ ]
86
+
87
+ PROFILE_FORMAT = "policy-profile/v0"
88
+
89
+ # check -> the config keys a profile may set on a wicket running that check.
90
+ # Every value is a mapping of action class -> non-negative integer minor units.
91
+ CONFIGURABLE: dict[str, frozenset[str]] = {
92
+ "caps": frozenset({"caps_minor", "per_action_minor"}),
93
+ }
94
+
95
+ # The one check whose profile values are not per-class amounts, and the keys
96
+ # a profile may set on it. Kept out of ``CONFIGURABLE``, whose every key is a
97
+ # per-class limit to its readers.
98
+ LIST_CHECK = "counterparty_list"
99
+ LIST_KEYS = frozenset({"mode", "entries", "disposition"})
100
+
101
+ # publisher/name: a pack_id (``publisher/name/semver``) without its version.
102
+ PACK_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9_.-]*/[a-z0-9][a-z0-9-]*$")
103
+
104
+ # Largest integer every JSON implementation reads exactly.
105
+ _MAX_SAFE_INT = 2**53 - 1
106
+
107
+ ClassValues = dict[str, int]
108
+ # A ``caps`` key carries ClassValues; a ``counterparty_list`` key carries its
109
+ # mode or disposition (str) or its entries (list[ListEntry]).
110
+ ParameterValue = ClassValues | str | list[ListEntry]
111
+
112
+
113
+ class PackParametersDict(TypedDict):
114
+ pack: str
115
+ parameters: dict[str, dict[str, ParameterValue]]
116
+
117
+
118
+ class PolicyProfileDict(TypedDict):
119
+ format: str
120
+ packs: list[PackParametersDict]
121
+
122
+
123
+ @dataclass(frozen=True)
124
+ class PackParameters:
125
+ """The values one profile sets for one pack: check -> key -> class -> value."""
126
+
127
+ pack: str
128
+ parameters: dict[str, dict[str, ParameterValue]] = field(default_factory=dict)
129
+
130
+ def canonical_dict(self) -> PackParametersDict:
131
+ return {"pack": self.pack, "parameters": self.parameters}
132
+
133
+
134
+ @dataclass(frozen=True)
135
+ class PolicyProfile:
136
+ packs: tuple[PackParameters, ...] = ()
137
+
138
+ def canonical_dict(self) -> PolicyProfileDict:
139
+ """The JCS-canonicalizable form. Mapping order carries no meaning
140
+ (JCS sorts keys); ``packs`` order does, like every list a manifest
141
+ digests."""
142
+ return {"format": PROFILE_FORMAT, "packs": [p.canonical_dict() for p in self.packs]}
143
+
144
+ def profile_digest(self) -> str:
145
+ """SHA-256 over the JCS bytes of ``canonical_dict()`` -- the digest
146
+ ``Manifest.profile_digest`` pins."""
147
+ return json_digest(self.canonical_dict())
148
+
149
+ def for_pack(self, pack_id: str) -> PackParameters | None:
150
+ name = pack_name(pack_id)
151
+ return next((p for p in self.packs if p.pack == name), None)
152
+
153
+
154
+ def pack_name(pack_id: str) -> str:
155
+ """``asg/everyday/0.1.0`` -> ``asg/everyday``."""
156
+ return pack_id.rsplit("/", 1)[0]
157
+
158
+
159
+ def _refuse(message: str) -> PolicyManifestError:
160
+ return PolicyManifestError(MALFORMED_PROFILE, message)
161
+
162
+
163
+ def _keys_exactly(keys: Iterable[object], expected: set[str], context: str) -> None:
164
+ actual = set(keys)
165
+ if actual != expected:
166
+ raise _refuse(f"{context} must have exactly the keys {sorted(expected)}, got {sorted(actual, key=str)}")
167
+
168
+
169
+ def _parse_class_values(raw: Any, context: str) -> ClassValues:
170
+ if not isinstance(raw, dict):
171
+ raise _refuse(f"{context} must be a mapping of action class to an integer")
172
+ out: ClassValues = {}
173
+ for action_class, value in raw.items():
174
+ if not isinstance(action_class, str) or not action_class:
175
+ raise _refuse(f"{context} has a key that is not a non-empty string: {action_class!r}")
176
+ # bool is an int subclass; True is not a limit.
177
+ if type(value) is not int or not 0 <= value <= _MAX_SAFE_INT:
178
+ raise _refuse(f"{context}[{action_class!r}] must be an integer from 0 to {_MAX_SAFE_INT}, got {value!r}")
179
+ out[action_class] = value
180
+ return out
181
+
182
+
183
+ def _nonempty_str(value: object) -> bool:
184
+ return isinstance(value, str) and bool(value)
185
+
186
+
187
+ def _parse_list_entry(raw: Any, context: str) -> ListEntry:
188
+ """``{kind: target, value}`` or ``{kind: <fingerprint kind>, fp_alg,
189
+ value}``. A bare string names no kind, and a name is never a kind."""
190
+ kind = raw.get("kind") if isinstance(raw, dict) else None
191
+ if kind in CLEAR_KINDS:
192
+ _keys_exactly(raw.keys(), {"kind", "value"}, f"{context} entry of kind {kind!r}")
193
+ elif kind in FINGERPRINT_KINDS:
194
+ _keys_exactly(raw.keys(), {"kind", "fp_alg", "value"}, f"{context} entry of kind {kind!r}")
195
+ if not _nonempty_str(raw["fp_alg"]):
196
+ raise _refuse(f"{context} entry has an fp_alg that is not a non-empty string: {raw['fp_alg']!r}")
197
+ else:
198
+ kinds = sorted(CLEAR_KINDS | FINGERPRINT_KINDS)
199
+ raise _refuse(f"{context} entries must be mappings whose kind is one of {kinds}, got {raw!r}")
200
+ if not _nonempty_str(raw["value"]):
201
+ raise _refuse(f"{context} entry has a value that is not a non-empty string: {raw['value']!r}")
202
+ entry: ListEntry = {"kind": kind, "value": raw["value"]}
203
+ if kind in FINGERPRINT_KINDS:
204
+ entry["fp_alg"] = raw["fp_alg"]
205
+ return entry
206
+
207
+
208
+ def _parse_list_parameters(raw: Any, context: str) -> dict[str, ParameterValue]:
209
+ keys = set(raw)
210
+ if not {"mode", "entries"} <= keys <= LIST_KEYS:
211
+ raise _refuse(
212
+ f"{context} must set 'mode' and 'entries' and may set 'disposition', got {sorted(keys, key=str)}"
213
+ )
214
+ if raw["mode"] not in MODES:
215
+ raise _refuse(f"{context}['mode'] must be one of {sorted(MODES)}, got {raw['mode']!r}")
216
+ if not isinstance(raw["entries"], list):
217
+ raise _refuse(f"{context}['entries'] must be a list, got {raw['entries']!r}")
218
+ entries = [_parse_list_entry(e, f"{context}['entries']") for e in raw["entries"]]
219
+ if len({json_digest(e) for e in entries}) != len(entries):
220
+ raise _refuse(f"{context}['entries'] lists a counterparty more than once")
221
+ out: dict[str, ParameterValue] = {"mode": raw["mode"], "entries": sorted_entries(entries)}
222
+ if "disposition" in raw:
223
+ if raw["disposition"] not in DISPOSITIONS:
224
+ raise _refuse(
225
+ f"{context}['disposition'] must be one of {sorted(DISPOSITIONS)}, got {raw['disposition']!r}"
226
+ )
227
+ out["disposition"] = raw["disposition"]
228
+ return out
229
+
230
+
231
+ def _parse_pack_entry(raw: Any) -> PackParameters:
232
+ if not isinstance(raw, dict):
233
+ raise _refuse(f"each packs entry must be a mapping: {raw!r}")
234
+ _keys_exactly(raw.keys(), {"pack", "parameters"}, "packs entry")
235
+ name = raw["pack"]
236
+ if not isinstance(name, str) or not PACK_NAME_RE.match(name):
237
+ raise _refuse(f"packs[].pack must be '<publisher>/<name>' with no version, got {name!r}")
238
+ raw_params = raw["parameters"]
239
+ if not isinstance(raw_params, dict):
240
+ raise _refuse(f"packs[{name!r}].parameters must be a mapping")
241
+ parameters: dict[str, dict[str, ParameterValue]] = {}
242
+ for check, keys in raw_params.items():
243
+ if not isinstance(check, str) or not isinstance(keys, dict):
244
+ raise _refuse(f"packs[{name!r}].parameters[{check!r}] must be a mapping of config key to values")
245
+ if check == LIST_CHECK:
246
+ parameters[check] = _parse_list_parameters(keys, f"packs[{name!r}].parameters[{check!r}]")
247
+ continue
248
+ parameters[check] = {
249
+ key: _parse_class_values(values, f"packs[{name!r}].parameters[{check!r}][{key!r}]")
250
+ for key, values in keys.items()
251
+ }
252
+ return PackParameters(pack=name, parameters=parameters)
253
+
254
+
255
+ def parse_profile(data: Any) -> PolicyProfile:
256
+ """Validate a plain dict (as loaded from JSON or YAML) into a ``PolicyProfile``.
257
+
258
+ Checks shape only. Whether each value names a parameter the installed
259
+ pack defines is checked against the resolved wickets (``resolve.py``)."""
260
+ if not isinstance(data, dict):
261
+ raise _refuse("profile must be a mapping")
262
+ _keys_exactly(data.keys(), {"format", "packs"}, "profile")
263
+ if data["format"] != PROFILE_FORMAT:
264
+ raise _refuse(f"format must be {PROFILE_FORMAT!r}, got {data['format']!r}")
265
+ if not isinstance(data["packs"], list):
266
+ raise _refuse("packs must be a list")
267
+ packs = tuple(_parse_pack_entry(entry) for entry in data["packs"])
268
+ names = [p.pack for p in packs]
269
+ duplicated = sorted({n for n in names if names.count(n) > 1})
270
+ if duplicated:
271
+ raise _refuse(f"pack {duplicated[0]!r} appears more than once")
272
+ return PolicyProfile(packs=packs)
273
+
274
+
275
+ def load_profile_file(path: str | Path) -> PolicyProfile:
276
+ path = Path(path)
277
+ try:
278
+ data = json.loads(path.read_text())
279
+ except json.JSONDecodeError as exc:
280
+ raise _refuse(f"{path} is not valid JSON: {exc}") from exc
281
+ return parse_profile(data)
@@ -0,0 +1,224 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Resolve a parsed ``Manifest`` against the real fold/wicket catalogs it
3
+ cites, cross-checking every pinned digest.
4
+
5
+ A manifest that fails to resolve (a cited fold_id/wicket_id no longer
6
+ exists, its current catalog digest no longer matches what the manifest
7
+ pinned, it names an evaluation ``engine`` this build doesn't recognize, or
8
+ the policy profile supplied is not the one it pins)
9
+ is not "real, loadable" -- every caller in this task (``manifest show``,
10
+ ``manifest activate``, ``guard dry-run``) treats a resolve failure as
11
+ fail-closed, never a best-effort partial load.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass, replace
16
+ from pathlib import Path
17
+
18
+ from ..folds.catalog import Catalog as FoldCatalog
19
+ from ..folds.definition import FoldDefinition
20
+ from ..guards.plan import PlanDefinition, parse_plan_definition
21
+ from ..guards.wickets.catalog import Catalog as WicketCatalog
22
+ from ..guards.wickets.definition import WicketDefinition
23
+ from .errors import (
24
+ FOLD_DIGEST_DRIFT,
25
+ PROFILE_DIGEST_DRIFT,
26
+ PROFILE_MISSING,
27
+ PROFILE_UNKNOWN_PACK,
28
+ PROFILE_UNKNOWN_PARAMETER,
29
+ PROFILE_UNPINNED,
30
+ UNKNOWN_ENGINE,
31
+ UNKNOWN_FOLD_ID,
32
+ UNKNOWN_WICKET_ID,
33
+ WICKET_DIGEST_DRIFT,
34
+ PolicyManifestError,
35
+ )
36
+ from .manifest import Manifest
37
+ from .profile import CONFIGURABLE, LIST_CHECK, LIST_KEYS, PolicyProfile, pack_name
38
+
39
+ __all__ = ["SUPPORTED_FOLD_ENGINES", "SUPPORTED_WICKET_ENGINES", "ResolvedManifest", "resolve_manifest"]
40
+
41
+ # The only evaluation engines this build knows how to run a fold/wicket
42
+ # entry through -- this repo's own built-in evaluator, one engine per entry
43
+ # kind. A manifest naming anything else is rejected fail-closed rather than
44
+ # silently ignored (see the ``engine`` design note in ``manifest.py``).
45
+ SUPPORTED_FOLD_ENGINES = frozenset({"fold/1"})
46
+ SUPPORTED_WICKET_ENGINES = frozenset({"wicket/1"})
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class ResolvedManifest:
51
+ """A manifest plus the real, digest-verified definitions it cites."""
52
+
53
+ manifest: Manifest
54
+ manifest_digest: str
55
+ folds: dict[str, FoldDefinition]
56
+ wickets: dict[str, WicketDefinition]
57
+ # The profile the manifest pins, verified against its digest and against
58
+ # the wickets above. ``None`` when the manifest pins none.
59
+ profile: PolicyProfile | None = None
60
+
61
+ def wicket_config(self, check: str) -> dict:
62
+ """The declarative ``config`` of the (first) resolved wicket
63
+ configuring the given check, or ``{}`` if none is active. These are
64
+ the pack's defaults; ``effective_values`` overlays the profile."""
65
+ for wicket in self.wickets.values():
66
+ if wicket.check == check:
67
+ return wicket.config
68
+ return {}
69
+
70
+ def effective_values(self, check: str, key: str) -> dict[str, int]:
71
+ """The wicket's default ``config[key]`` with every value the profile
72
+ sets for that check laid over it -- what the check actually enforces."""
73
+ out = dict(self.wicket_config(check).get(key) or {})
74
+ if self.profile is not None:
75
+ for entry in self.profile.packs:
76
+ out.update(entry.parameters.get(check, {}).get(key, {}))
77
+ return out
78
+
79
+ def caps_minor(self) -> dict[str, int]:
80
+ return self.effective_values("caps", "caps_minor")
81
+
82
+ def per_action_minor(self) -> dict[str, int]:
83
+ return self.effective_values("caps", "per_action_minor")
84
+
85
+ def per_action_reads(self) -> str | None:
86
+ return self.wicket_config("caps").get("per_action_reads")
87
+
88
+ def dedupe_window_days(self) -> int | None:
89
+ return self.wicket_config("dedupe").get("window_days")
90
+
91
+ def caps_fold(self) -> FoldDefinition | None:
92
+ fold_id = self.wicket_config("caps").get("fold_id")
93
+ return self.folds.get(fold_id) if fold_id else None
94
+
95
+ def configured_wickets(self, checks: frozenset[str]) -> tuple[WicketDefinition, ...]:
96
+ """Every resolved wicket configuring one of ``checks``, in manifest
97
+ order. A ``counterparty_list`` wicket comes back with the profile's
98
+ list values laid over its config: that is what the check runs with,
99
+ while the manifest still pins the wicket's own digest."""
100
+ return tuple(self._with_profile_list(w) for w in self.wickets.values() if w.check in checks)
101
+
102
+ def _with_profile_list(self, wicket: WicketDefinition) -> WicketDefinition:
103
+ if wicket.check != LIST_CHECK or self.profile is None:
104
+ return wicket
105
+ config = dict(wicket.config)
106
+ for entry in self.profile.packs:
107
+ config.update(entry.parameters.get(LIST_CHECK, {}))
108
+ return replace(wicket, config=config)
109
+
110
+ def plan(self) -> PlanDefinition | None:
111
+ """The compiled plan quoted directly in the resolved
112
+ ``plan_containment`` wicket's own ``config`` (``guards/plan.py``'s
113
+ module docstring: "a compiled plan IS a wicket config") -- ``None``
114
+ when no ``plan_containment`` wicket is active in this manifest."""
115
+ config = self.wicket_config("plan_containment")
116
+ return parse_plan_definition(config) if config else None
117
+
118
+
119
+ def _check_profile(manifest: Manifest, profile: PolicyProfile | None, wickets: dict[str, WicketDefinition]) -> None:
120
+ """Fail closed unless ``profile`` is exactly the one the manifest pins and
121
+ every value in it replaces a default an installed pack's wicket declares.
122
+ A ``counterparty_list`` list replaces the wicket's list whole."""
123
+ if manifest.profile_digest is None:
124
+ if profile is not None:
125
+ raise PolicyManifestError(
126
+ PROFILE_UNPINNED, "a profile was supplied but the manifest pins none, so nothing records it"
127
+ )
128
+ return
129
+ if profile is None:
130
+ raise PolicyManifestError(
131
+ PROFILE_MISSING, f"manifest pins profile {manifest.profile_digest}, but no profile was supplied"
132
+ )
133
+ if profile.profile_digest() != manifest.profile_digest:
134
+ raise PolicyManifestError(
135
+ PROFILE_DIGEST_DRIFT,
136
+ f"manifest pins profile {manifest.profile_digest}, but the supplied profile digests to "
137
+ f"{profile.profile_digest()}",
138
+ )
139
+ installed = {pack_name(p.pack_id) for p in manifest.packs}
140
+ for entry in profile.packs:
141
+ if entry.pack not in installed:
142
+ raise PolicyManifestError(
143
+ PROFILE_UNKNOWN_PACK, f"profile sets values for pack {entry.pack!r}, which the manifest does not install"
144
+ )
145
+ for check, keys in entry.parameters.items():
146
+ wicket = next((w for w in wickets.values() if w.check == check), None)
147
+ settable = LIST_KEYS if check == LIST_CHECK else CONFIGURABLE.get(check, frozenset())
148
+ for key, values in keys.items():
149
+ if wicket is None or key not in settable:
150
+ raise PolicyManifestError(
151
+ PROFILE_UNKNOWN_PARAMETER, f"profile sets {check}.{key}, which no installed wicket lets a user set"
152
+ )
153
+ if check == LIST_CHECK:
154
+ continue
155
+ defaults = wicket.config.get(key) or {}
156
+ undeclared = sorted(set(values) - set(defaults))
157
+ if undeclared:
158
+ raise PolicyManifestError(
159
+ PROFILE_UNKNOWN_PARAMETER,
160
+ f"profile sets {check}.{key} for {undeclared}, which wicket {wicket.wicket_id!r} declares "
161
+ "no default for; a profile replaces a default, it never adds a limit",
162
+ )
163
+
164
+
165
+ def resolve_manifest(
166
+ manifest: Manifest,
167
+ *,
168
+ fold_catalog_dir: str | Path,
169
+ wicket_catalog_dir: str | Path,
170
+ profile: PolicyProfile | None = None,
171
+ ) -> ResolvedManifest:
172
+ fold_catalog = FoldCatalog(fold_catalog_dir)
173
+ wicket_catalog = WicketCatalog(wicket_catalog_dir)
174
+
175
+ folds: dict[str, FoldDefinition] = {}
176
+ for ref in manifest.folds:
177
+ if ref.engine not in SUPPORTED_FOLD_ENGINES:
178
+ raise PolicyManifestError(
179
+ UNKNOWN_ENGINE,
180
+ f"manifest cites fold {ref.fold_id!r} with engine {ref.engine!r}, this build only "
181
+ f"evaluates fold engines {sorted(SUPPORTED_FOLD_ENGINES)}",
182
+ )
183
+ entry = fold_catalog.get(ref.fold_id)
184
+ if entry is None:
185
+ raise PolicyManifestError(
186
+ UNKNOWN_FOLD_ID, f"manifest cites fold_id {ref.fold_id!r}, not found in catalog {fold_catalog_dir}"
187
+ )
188
+ if entry.digest != ref.digest:
189
+ raise PolicyManifestError(
190
+ FOLD_DIGEST_DRIFT,
191
+ f"manifest pins fold {ref.fold_id!r} at digest {ref.digest}, but the catalog's current "
192
+ f"definition digests to {entry.digest} -- the fold definition has changed since the "
193
+ "manifest was written",
194
+ )
195
+ folds[ref.fold_id] = entry.definition
196
+
197
+ wickets: dict[str, WicketDefinition] = {}
198
+ for ref in manifest.wickets:
199
+ if ref.engine not in SUPPORTED_WICKET_ENGINES:
200
+ raise PolicyManifestError(
201
+ UNKNOWN_ENGINE,
202
+ f"manifest cites wicket {ref.wicket_id!r} with engine {ref.engine!r}, this build only "
203
+ f"evaluates wicket engines {sorted(SUPPORTED_WICKET_ENGINES)}",
204
+ )
205
+ entry = wicket_catalog.get(ref.wicket_id)
206
+ if entry is None:
207
+ raise PolicyManifestError(
208
+ UNKNOWN_WICKET_ID,
209
+ f"manifest cites wicket_id {ref.wicket_id!r}, not found in catalog {wicket_catalog_dir}",
210
+ )
211
+ if entry.digest != ref.digest:
212
+ raise PolicyManifestError(
213
+ WICKET_DIGEST_DRIFT,
214
+ f"manifest pins wicket {ref.wicket_id!r} at digest {ref.digest}, but the catalog's current "
215
+ f"definition digests to {entry.digest} -- the wicket definition has changed since the "
216
+ "manifest was written",
217
+ )
218
+ wickets[ref.wicket_id] = entry.definition
219
+
220
+ _check_profile(manifest, profile, wickets)
221
+
222
+ return ResolvedManifest(
223
+ manifest=manifest, manifest_digest=manifest.manifest_digest(), folds=folds, wickets=wickets, profile=profile
224
+ )
@@ -0,0 +1,39 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Obligation register format v0:
3
+ a loadable, GRC-shaped list of register rows, plus the OSS sample compiler that
4
+ turns one into an obligation-profile ``packs.schema.EvidenceContract``.
5
+
6
+ See the module docstrings in ``schema.py`` (``RegisterRow``/``ObligationRegister``,
7
+ the parsed register shape), ``loader.py`` (a register file/dict -> a validated
8
+ ``ObligationRegister``), and ``compiler.py`` (``EvidenceCompiler``, the row ->
9
+ requirement path) for the pieces.
10
+ """
11
+ from .compiler import (
12
+ EVIDENCE_CLASS_DEFAULTS,
13
+ EXCLUDED_CONTESTED_CLAUSE,
14
+ EXCLUDED_JUDGMENT_REQUIRED,
15
+ EXCLUDED_SYSTEM_OF_RECORD_READ_REQUIRED,
16
+ EvidenceCompiler,
17
+ ExcludedRow,
18
+ ObligationsPack,
19
+ )
20
+ from .errors import RegisterCompilerError, RegisterDefinitionError
21
+ from .loader import load_register_dict, load_register_file
22
+ from .schema import EVIDENCE_CLASS_VALUES, ObligationRegister, RegisterRow
23
+
24
+ __all__ = [
25
+ "RegisterRow",
26
+ "ObligationRegister",
27
+ "EVIDENCE_CLASS_VALUES",
28
+ "load_register_dict",
29
+ "load_register_file",
30
+ "RegisterDefinitionError",
31
+ "EvidenceCompiler",
32
+ "EVIDENCE_CLASS_DEFAULTS",
33
+ "RegisterCompilerError",
34
+ "ObligationsPack",
35
+ "ExcludedRow",
36
+ "EXCLUDED_JUDGMENT_REQUIRED",
37
+ "EXCLUDED_SYSTEM_OF_RECORD_READ_REQUIRED",
38
+ "EXCLUDED_CONTESTED_CLAUSE",
39
+ ]