tgi-foundation 0.2.0.dev0__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 (252) hide show
  1. tgi/__init__.py +7 -0
  2. tgi/__main__.py +194 -0
  3. tgi/acquisition_history.py +45 -0
  4. tgi/acquisition_history_check.py +51 -0
  5. tgi/acquisition_monitor.py +66 -0
  6. tgi/acquisition_monitor_check.py +78 -0
  7. tgi/acquisition_projection.py +70 -0
  8. tgi/acquisition_projection_check.py +93 -0
  9. tgi/acquisition_role_cells.py +53 -0
  10. tgi/acquisition_role_constraints.py +13 -0
  11. tgi/acquisition_role_constraints_check.py +66 -0
  12. tgi/acquisition_snapshot.py +35 -0
  13. tgi/acquisition_stream.py +21 -0
  14. tgi/acquisition_witness.py +117 -0
  15. tgi/actuation_goal_frontier.py +117 -0
  16. tgi/actuation_goal_frontier_check.py +147 -0
  17. tgi/actuation_goal_workflow.py +159 -0
  18. tgi/actuation_sequence_feedback.py +41 -0
  19. tgi/actuation_sequence_feedback_check.py +43 -0
  20. tgi/ask_certificate.py +74 -0
  21. tgi/bands.py +59 -0
  22. tgi/capacity_graph.py +70 -0
  23. tgi/closed_actuation_goal_frontier.py +13 -0
  24. tgi/closed_actuation_goal_frontier_check.py +19 -0
  25. tgi/closed_actuation_goal_workflow.py +159 -0
  26. tgi/closure.py +202 -0
  27. tgi/compact_acquisition_map.py +68 -0
  28. tgi/compact_acquisition_map_check.py +91 -0
  29. tgi/compact_complete_role_core.py +36 -0
  30. tgi/compact_complete_role_core_check.py +62 -0
  31. tgi/compact_measured_path.py +84 -0
  32. tgi/compact_role_observation.py +13 -0
  33. tgi/compact_role_observation_check.py +66 -0
  34. tgi/compatible_joins.py +33 -0
  35. tgi/complete_role_core.py +53 -0
  36. tgi/complete_role_core_check.py +54 -0
  37. tgi/context_family_selection.py +45 -0
  38. tgi/context_family_selection_check.py +32 -0
  39. tgi/context_family_separation.py +43 -0
  40. tgi/context_family_separation_check.py +47 -0
  41. tgi/contextual_isometry.py +62 -0
  42. tgi/contextual_isometry_check.py +54 -0
  43. tgi/continuation_checkpoint.py +75 -0
  44. tgi/continuation_cycle.py +102 -0
  45. tgi/continuation_goal_frontier.py +13 -0
  46. tgi/continuation_goal_frontier_check.py +19 -0
  47. tgi/continuation_goal_workflow.py +159 -0
  48. tgi/continuation_learning.py +36 -0
  49. tgi/continuation_learning_check.py +53 -0
  50. tgi/correspondence.py +76 -0
  51. tgi/cross_action_correspondence.py +182 -0
  52. tgi/cross_action_correspondence_check.py +92 -0
  53. tgi/cross_view_identity_map.py +41 -0
  54. tgi/cross_view_identity_map_check.py +48 -0
  55. tgi/derivation.py +74 -0
  56. tgi/empirical_transport.py +74 -0
  57. tgi/empirical_transport_check.py +78 -0
  58. tgi/events.py +36 -0
  59. tgi/evidence_response.py +24 -0
  60. tgi/evidence_response_check.py +33 -0
  61. tgi/exact_field.py +101 -0
  62. tgi/experience.py +68 -0
  63. tgi/experience_cli.py +44 -0
  64. tgi/feedback_update.py +32 -0
  65. tgi/feedback_update_check.py +38 -0
  66. tgi/formation.py +74 -0
  67. tgi/frame_cli.py +26 -0
  68. tgi/frame_engine.py +214 -0
  69. tgi/functional_capacity.py +74 -0
  70. tgi/functional_capacity_check.py +110 -0
  71. tgi/functional_transition.py +40 -0
  72. tgi/functional_transition_check.py +58 -0
  73. tgi/geometry.py +143 -0
  74. tgi/grounding.py +285 -0
  75. tgi/guarded_word_closure.py +148 -0
  76. tgi/guarded_word_closure_check.py +171 -0
  77. tgi/guarded_word_goal_frontier.py +13 -0
  78. tgi/guarded_word_goal_frontier_check.py +19 -0
  79. tgi/guarded_word_goal_workflow.py +160 -0
  80. tgi/identity.py +32 -0
  81. tgi/incidence.py +155 -0
  82. tgi/incremental.py +134 -0
  83. tgi/intervention_composition.py +63 -0
  84. tgi/intervention_composition_check.py +84 -0
  85. tgi/intervention_continuity.py +43 -0
  86. tgi/intervention_continuity_check.py +56 -0
  87. tgi/intervention_correspondence.py +156 -0
  88. tgi/intervention_correspondence_check.py +80 -0
  89. tgi/intervention_edit_contrast.py +27 -0
  90. tgi/intervention_edit_contrast_check.py +41 -0
  91. tgi/intervention_experiment.py +62 -0
  92. tgi/intervention_experiment_check.py +78 -0
  93. tgi/intervention_resolution.py +45 -0
  94. tgi/intervention_resolution_check.py +70 -0
  95. tgi/intervention_roles.py +83 -0
  96. tgi/intervention_roles_check.py +91 -0
  97. tgi/intervention_scope.py +34 -0
  98. tgi/intervention_sequence_experiment.py +71 -0
  99. tgi/intervention_sequence_experiment_check.py +97 -0
  100. tgi/interventions.py +240 -0
  101. tgi/joint_correspondence.py +73 -0
  102. tgi/joint_frontier_acquisition.py +114 -0
  103. tgi/joint_frontier_acquisition_check.py +116 -0
  104. tgi/joint_goal_observation.py +49 -0
  105. tgi/joint_goal_observation_check.py +55 -0
  106. tgi/joint_goal_traversal.py +101 -0
  107. tgi/joint_goal_traversal_check.py +114 -0
  108. tgi/joint_observed_laws.py +40 -0
  109. tgi/joint_observed_laws_check.py +63 -0
  110. tgi/joint_temporal_candidates.py +40 -0
  111. tgi/joint_temporal_candidates_check.py +68 -0
  112. tgi/lineage_knowledge.py +29 -0
  113. tgi/lineage_knowledge_check.py +41 -0
  114. tgi/lineage_state.py +39 -0
  115. tgi/lineage_state_check.py +47 -0
  116. tgi/local_acquisition_ownership.py +65 -0
  117. tgi/local_acquisition_runtime.py +119 -0
  118. tgi/local_acquisition_selection.py +41 -0
  119. tgi/local_acquisition_transition.py +50 -0
  120. tgi/local_acquisition_transition_check.py +80 -0
  121. tgi/local_edit_transport_classes.py +220 -0
  122. tgi/local_edit_transport_classes_check.py +128 -0
  123. tgi/local_evidence_persistence.py +67 -0
  124. tgi/measured_actuation_transition.py +56 -0
  125. tgi/measured_actuation_transition_check.py +81 -0
  126. tgi/measured_capacity.py +61 -0
  127. tgi/measured_context.py +52 -0
  128. tgi/measured_context_check.py +65 -0
  129. tgi/measured_context_laws.py +75 -0
  130. tgi/measured_context_laws_check.py +91 -0
  131. tgi/measured_context_layout.py +49 -0
  132. tgi/measured_occurrence_path.py +70 -0
  133. tgi/momentum.py +43 -0
  134. tgi/observation_context_laws.py +32 -0
  135. tgi/observation_context_laws_check.py +40 -0
  136. tgi/observation_epochs.py +35 -0
  137. tgi/observation_epochs_check.py +51 -0
  138. tgi/observation_point.py +92 -0
  139. tgi/observation_projection.py +45 -0
  140. tgi/observation_projection_check.py +48 -0
  141. tgi/observed_actuation_cycle.py +114 -0
  142. tgi/observed_actuation_discovery.py +41 -0
  143. tgi/observed_actuation_discovery_check.py +54 -0
  144. tgi/observed_actuation_learning.py +71 -0
  145. tgi/observed_actuation_learning_check.py +73 -0
  146. tgi/observed_actuation_runtime.py +114 -0
  147. tgi/observed_context_actions.py +66 -0
  148. tgi/observed_context_actions_check.py +81 -0
  149. tgi/observed_joint_state.py +52 -0
  150. tgi/observed_joint_state_check.py +55 -0
  151. tgi/observed_refinement.py +43 -0
  152. tgi/observed_response.py +20 -0
  153. tgi/observed_response_check.py +27 -0
  154. tgi/observed_role_core.py +41 -0
  155. tgi/observed_role_core_check.py +54 -0
  156. tgi/occurrence_route.py +56 -0
  157. tgi/operator_windows.py +84 -0
  158. tgi/operator_windows_check.py +111 -0
  159. tgi/organization.py +484 -0
  160. tgi/organization_check.py +143 -0
  161. tgi/organization_cli.py +77 -0
  162. tgi/organization_inventory_check.py +82 -0
  163. tgi/organization_snapshot.py +50 -0
  164. tgi/path_isometry.py +82 -0
  165. tgi/path_temporal_feasibility.py +69 -0
  166. tgi/phase_evidence.py +100 -0
  167. tgi/port_distinction_exploration.py +37 -0
  168. tgi/port_distinction_exploration_check.py +40 -0
  169. tgi/proofcheck.py +117 -0
  170. tgi/provenance.py +19 -0
  171. tgi/recent_observation.py +145 -0
  172. tgi/relational.py +222 -0
  173. tgi/resonance.py +114 -0
  174. tgi/response_eligibility.py +46 -0
  175. tgi/response_eligibility_check.py +55 -0
  176. tgi/response_interval_order.py +59 -0
  177. tgi/response_interval_order_check.py +92 -0
  178. tgi/response_readout.py +53 -0
  179. tgi/response_readout_check.py +73 -0
  180. tgi/role_bundle.py +31 -0
  181. tgi/role_bundle_check.py +20 -0
  182. tgi/role_candidates.py +47 -0
  183. tgi/role_candidates_check.py +57 -0
  184. tgi/role_eligibility.py +34 -0
  185. tgi/role_eligibility_check.py +46 -0
  186. tgi/role_feedback.py +15 -0
  187. tgi/role_feedback_check.py +23 -0
  188. tgi/role_history.py +37 -0
  189. tgi/role_history_check.py +51 -0
  190. tgi/role_projection.py +16 -0
  191. tgi/role_projection_check.py +85 -0
  192. tgi/role_proposal.py +39 -0
  193. tgi/role_proposal_check.py +51 -0
  194. tgi/role_readout.py +31 -0
  195. tgi/role_readout_check.py +51 -0
  196. tgi/role_response.py +34 -0
  197. tgi/role_response_check.py +40 -0
  198. tgi/role_work.py +54 -0
  199. tgi/scoped_sequence_experiment.py +20 -0
  200. tgi/scoped_sequence_experiment_check.py +33 -0
  201. tgi/separation.py +74 -0
  202. tgi/separation_check.py +87 -0
  203. tgi/sequence_prefix_feedback.py +57 -0
  204. tgi/sequence_prefix_feedback_check.py +79 -0
  205. tgi/source_intervals.py +85 -0
  206. tgi/source_intervals_check.py +97 -0
  207. tgi/spatial.py +109 -0
  208. tgi/spatial_action_observation.py +88 -0
  209. tgi/spatial_action_relation.py +53 -0
  210. tgi/spatial_action_relation_check.py +45 -0
  211. tgi/spatial_action_relation_cycle.py +84 -0
  212. tgi/spatial_action_relation_migration.py +41 -0
  213. tgi/spatial_action_relation_migration_bundle.py +60 -0
  214. tgi/spatial_contextual_action_cycle.py +130 -0
  215. tgi/spatial_contextual_action_local.py +79 -0
  216. tgi/spatial_contextual_action_phase.py +81 -0
  217. tgi/spatial_contextual_action_phase_check.py +81 -0
  218. tgi/spatial_direct_action_cycle.py +98 -0
  219. tgi/spatial_direct_action_relation.py +60 -0
  220. tgi/spatial_direct_action_relation_check.py +67 -0
  221. tgi/spatial_local_action_cycle.py +62 -0
  222. tgi/spatial_local_action_relation.py +66 -0
  223. tgi/spatial_temporal_action_cycle.py +116 -0
  224. tgi/spatial_temporal_action_local.py +77 -0
  225. tgi/spatial_temporal_action_phase.py +89 -0
  226. tgi/spatial_temporal_action_phase_check.py +94 -0
  227. tgi/structural_append_closure.py +99 -0
  228. tgi/structural_append_closure_check.py +106 -0
  229. tgi/structural_continuation_closure.py +93 -0
  230. tgi/structural_continuation_closure_check.py +108 -0
  231. tgi/temporal_candidate_spectrum.py +57 -0
  232. tgi/temporal_candidate_spectrum_check.py +94 -0
  233. tgi/temporal_correspondence.py +41 -0
  234. tgi/temporal_correspondence_check.py +56 -0
  235. tgi/temporal_projection.py +41 -0
  236. tgi/temporal_projection_check.py +56 -0
  237. tgi/transport.py +294 -0
  238. tgi/transport_cli.py +27 -0
  239. tgi/unlabeled_port_classes.py +199 -0
  240. tgi/unlabeled_port_classes_check.py +113 -0
  241. tgi/unobserved_action_selection.py +47 -0
  242. tgi/unobserved_action_selection_check.py +40 -0
  243. tgi/verification.py +98 -0
  244. tgi/witness_function.py +62 -0
  245. tgi/witness_probe.py +50 -0
  246. tgi/witness_probe_feedback.py +106 -0
  247. tgi/witness_response.py +67 -0
  248. tgi_foundation-0.2.0.dev0.dist-info/METADATA +30 -0
  249. tgi_foundation-0.2.0.dev0.dist-info/RECORD +252 -0
  250. tgi_foundation-0.2.0.dev0.dist-info/WHEEL +5 -0
  251. tgi_foundation-0.2.0.dev0.dist-info/licenses/LICENSE +3 -0
  252. tgi_foundation-0.2.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,199 @@
1
+ """Native chronological formation of unlabeled sensor response classes."""
2
+ import copy
3
+ import hashlib
4
+ import json
5
+ import os
6
+ import re
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ from tgi.frame_engine import canonical, unique_object
11
+ from tgi.identity import REGISTRY_ID, encode
12
+ from tgi.organization import OMEGA_CRIT
13
+ from tgi.intervention_edit_contrast import contrast
14
+
15
+ REVISION = 'TGI-M1-UNLABELED-PORT-CLASSES-RESEARCH-V2'
16
+
17
+
18
+ def _sha(data):
19
+ return hashlib.sha256(data).hexdigest()
20
+
21
+
22
+ def _event(source, action, ports):
23
+ if type(source) is not str or not source or type(action) is not str or not action:
24
+ raise ValueError('Original source and action required')
25
+ if type(ports) is not dict or len(ports) < 3:
26
+ raise ValueError('At least three original port streams required')
27
+ rows = {}
28
+ for name, pair in sorted(ports.items()):
29
+ if (type(name) is not str or not name or type(pair) not in (tuple, list) or
30
+ len(pair) != 2):
31
+ raise ValueError('Raw port address and frame pair required')
32
+ rows[name] = contrast(*pair)
33
+ return {'source': source, 'action_nmu': list(encode(action)), 'ports': rows}
34
+
35
+
36
+ def _apply(stats, members, event, names):
37
+ action = tuple(event['action_nmu'])
38
+ changed = all(event['ports'][name]['changed'] for name in members)
39
+ stable = all(not event['ports'][name]['changed'] for name in members)
40
+ if changed:
41
+ context = tuple(tuple(event['ports'][name]['before_nmu']) for name in members)
42
+ stats['support'].setdefault(action, set()).add(context)
43
+ elif stable:
44
+ stats['controls'][action] = stats['controls'].get(action, 0) + 1
45
+ if any(event['ports'][name]['changed'] for name in names if name not in members):
46
+ stats['comparator'].add(action)
47
+
48
+
49
+ def form(events):
50
+ if type(events) is not list:
51
+ raise ValueError('Original chronological event list required')
52
+ phases = []
53
+ seen = set()
54
+ names = None
55
+ previous_class = None
56
+ epoch_start = 0
57
+ traces = {}
58
+ previous_stats = {}
59
+ actions_seen = set()
60
+ for index, event in enumerate(events):
61
+ if (type(event) is not dict or set(event) != {'source', 'action_nmu', 'ports'} or
62
+ event['source'] in seen or type(event['ports']) is not dict):
63
+ raise ValueError('Unique original event required')
64
+ seen.add(event['source'])
65
+ current = list(event['ports'])
66
+ if names is None:
67
+ names = current
68
+ traces = {name: () for name in names}
69
+ if current != names:
70
+ raise ValueError('Fixed port inventory required')
71
+ for name in names:
72
+ row = event['ports'][name]
73
+ traces[name] += ((row['kind'], tuple(row['positions'])),)
74
+ grouped = {}
75
+ for name in names:
76
+ grouped.setdefault(traces[name], []).append(name)
77
+ classes = sorted(tuple(group) for group in grouped.values())
78
+ actions_seen.add(tuple(event['action_nmu']))
79
+ actions = sorted(actions_seen)
80
+ cells = []
81
+ eligible = []
82
+ current_stats = {}
83
+ for members in classes:
84
+ if members in previous_stats:
85
+ stats = previous_stats[members]
86
+ _apply(stats, members, event, names)
87
+ else:
88
+ stats = {'support': {}, 'controls': {}, 'comparator': set()}
89
+ for row in events[epoch_start:index + 1]:
90
+ _apply(stats, members, row, names)
91
+ current_stats[members] = stats
92
+ counts = []
93
+ for action in actions:
94
+ omega = len(stats['support'].get(action, ()))
95
+ controls = stats['controls'].get(action, 0)
96
+ comparator = any(other != action for other in stats['comparator'])
97
+ counts.append({'action_nmu': list(action), 'omega': omega,
98
+ 'controls': controls, 'comparator': comparator})
99
+ if omega >= OMEGA_CRIT and controls and comparator:
100
+ eligible.append((action, members))
101
+ cells.append({'members': list(members), 'C_L_by_action': counts, 'xi': 0})
102
+ fractured = previous_class is not None and previous_class not in classes
103
+ if fractured:
104
+ result = {'status': 'REVOKED', 'action_nmu': None, 'members': None}
105
+ previous_class = None
106
+ epoch_start = index + 1
107
+ current_stats = {}
108
+ elif len(eligible) == 1:
109
+ action, members = eligible[0]
110
+ result = {'status': 'CRYSTAL', 'action_nmu': list(action), 'members': list(members)}
111
+ previous_class = members
112
+ for cell in cells:
113
+ cell['xi'] = int(tuple(cell['members']) == members)
114
+ elif previous_class is not None and not any(members == previous_class for _, members in eligible):
115
+ result = {'status': 'REVOKED', 'action_nmu': None, 'members': None}
116
+ else:
117
+ result = {'status': 'NO_PATH', 'action_nmu': None, 'members': None}
118
+ phases.append({'event_index': index, 'source': event['source'],
119
+ 'epoch_start': epoch_start,
120
+ 'classes': cells, 'result': result})
121
+ previous_stats = current_stats
122
+ result = phases[-1]['result'] if phases else {'status': 'NO_PATH', 'action_nmu': None, 'members': None}
123
+ return {'revision': REVISION, 'registry': REGISTRY_ID,
124
+ 'events': list(events), 'phases': phases, 'result': result}
125
+
126
+
127
+ class OwnedPortClasses:
128
+ def __init__(self, events=None):
129
+ from tgi.unlabeled_port_classes_check import verify
130
+ self._events = copy.deepcopy([] if events is None else events)
131
+ self._certificate = form(self._events)
132
+ if not verify(self._events, self._certificate):
133
+ raise ValueError('Independent original port replay failed')
134
+
135
+ @property
136
+ def certificate(self):
137
+ return copy.deepcopy(self._certificate)
138
+
139
+ def observe(self, source, action, ports):
140
+ from tgi.unlabeled_port_classes_check import verify
141
+ if any(row['source'] == source for row in self._events):
142
+ raise ValueError('Duplicate original source')
143
+ event = _event(source, action, ports)
144
+ child = type(self).__new__(type(self))
145
+ child._events = self._events + [event]
146
+ child._certificate = form(child._events)
147
+ if not verify(child._events, child._certificate):
148
+ raise ValueError('Independent original port replay failed')
149
+ return child
150
+
151
+ def resolve(self, action):
152
+ result = self._certificate['result']
153
+ if type(action) is not str or not action:
154
+ raise ValueError('Original action trigger required')
155
+ if result['status'] != 'CRYSTAL' or result['action_nmu'] != list(encode(action)):
156
+ return None
157
+ return list(result['members'])
158
+
159
+ def commit(self, path):
160
+ from tgi.unlabeled_port_classes_check import verify
161
+ if not verify(self._events, self._certificate):
162
+ raise ValueError('Independent complete replay failed')
163
+ payload = {'revision': REVISION, 'registry': REGISTRY_ID,
164
+ 'events': self._events, 'certificate': self._certificate}
165
+ envelope = {'payload': payload, 'sha256': _sha(canonical(payload).encode('utf8'))}
166
+ data = canonical(envelope).encode('utf8')
167
+ destination = Path(path)
168
+ temporary = None
169
+ try:
170
+ with tempfile.NamedTemporaryFile(mode='wb', dir=destination.parent, delete=False) as stream:
171
+ temporary = Path(stream.name)
172
+ stream.write(data)
173
+ stream.flush()
174
+ os.fsync(stream.fileno())
175
+ os.replace(temporary, destination)
176
+ temporary = None
177
+ finally:
178
+ if temporary is not None and temporary.exists():
179
+ temporary.unlink()
180
+ return {'path': str(destination), 'sha256': _sha(data), 'bytes': len(data)}
181
+
182
+ @classmethod
183
+ def recover(cls, path, expected_sha256):
184
+ from tgi.unlabeled_port_classes_check import verify
185
+ if type(expected_sha256) is not str or re.fullmatch(r'[0-9a-f]{64}', expected_sha256) is None:
186
+ raise ValueError('External checkpoint digest required')
187
+ data = Path(path).read_bytes()
188
+ if _sha(data) != expected_sha256:
189
+ raise ValueError('Checkpoint digest mismatch')
190
+ envelope = json.loads(data.decode('utf8'), object_pairs_hook=unique_object)
191
+ if type(envelope) is not dict or set(envelope) != {'payload', 'sha256'}:
192
+ raise ValueError('Invalid checkpoint envelope')
193
+ payload = envelope['payload']
194
+ if (type(payload) is not dict or set(payload) != {'revision', 'registry', 'events', 'certificate'} or
195
+ payload['revision'] != REVISION or payload['registry'] != REGISTRY_ID or
196
+ _sha(canonical(payload).encode('utf8')) != envelope['sha256'] or
197
+ not verify(payload['events'], payload['certificate'])):
198
+ raise ValueError('Invalid original port checkpoint')
199
+ return cls(payload['events'])
@@ -0,0 +1,113 @@
1
+ """Independent complete chronological replay of class and epoch certificates."""
2
+ from tgi.frame_engine import canonical
3
+ from tgi.identity import REGISTRY_ID, decode
4
+ from tgi.organization import OMEGA_CRIT
5
+ from tgi.intervention_edit_contrast_check import verify as valid_contrast
6
+
7
+ REVISION = 'TGI-M1-UNLABELED-PORT-CLASSES-RESEARCH-V2'
8
+
9
+
10
+ def _measure(data, members, row, names):
11
+ action = tuple(row['action_nmu'])
12
+ stable = all(row['ports'][port]['kind'] == 'STABLE' for port in members)
13
+ changed = all(row['ports'][port]['kind'] != 'STABLE' for port in members)
14
+ if changed:
15
+ key = tuple(tuple(row['ports'][port]['before_nmu']) for port in members)
16
+ data['contexts'].setdefault(action, set()).add(key)
17
+ if stable:
18
+ data['controls'][action] = data['controls'].get(action, 0) + 1
19
+ if any(row['ports'][port]['kind'] != 'STABLE' for port in names if port not in members):
20
+ data['separators'].add(action)
21
+
22
+
23
+ def verify(events, certificate):
24
+ try:
25
+ if (type(events) is not list or type(certificate) is not dict or
26
+ set(certificate) != {'revision', 'registry', 'events', 'phases', 'result'} or
27
+ certificate['revision'] != REVISION or certificate['registry'] != REGISTRY_ID or
28
+ canonical(certificate['events']) != canonical(events) or
29
+ type(certificate['phases']) is not list or len(certificate['phases']) != len(events)):
30
+ return False
31
+ names = None
32
+ seen = set()
33
+ traces = {}
34
+ actions = set()
35
+ epoch_rows = []
36
+ old_groups = {}
37
+ prior_crystal = None
38
+ epoch_start = 0
39
+ result = {'status': 'NO_PATH', 'action_nmu': None, 'members': None}
40
+ for index, event in enumerate(events):
41
+ if (type(event) is not dict or set(event) != {'source', 'action_nmu', 'ports'} or
42
+ type(event['source']) is not str or not event['source'] or event['source'] in seen or
43
+ type(event['action_nmu']) is not list or not event['action_nmu'] or
44
+ type(event['ports']) is not dict):
45
+ return False
46
+ seen.add(event['source'])
47
+ decode(event['action_nmu'])
48
+ current_names = list(event['ports'])
49
+ if (len(current_names) < 3 or current_names != sorted(current_names) or
50
+ any(type(name) is not str or not name for name in current_names)):
51
+ return False
52
+ if names is None:
53
+ names = current_names
54
+ traces = {name: [] for name in names}
55
+ elif current_names != names:
56
+ return False
57
+ for name in names:
58
+ contrast = event['ports'][name]
59
+ if not valid_contrast(contrast):
60
+ return False
61
+ traces[name].append((contrast['kind'], tuple(contrast['positions'])))
62
+ epoch_rows.append(event)
63
+ actions.add(tuple(event['action_nmu']))
64
+ partition = {}
65
+ for name in names:
66
+ partition.setdefault(tuple(traces[name]), []).append(name)
67
+ groups = sorted(tuple(members) for members in partition.values())
68
+ new_groups = {}
69
+ cells = []
70
+ winners = []
71
+ for members in groups:
72
+ if members in old_groups:
73
+ data = old_groups[members]
74
+ _measure(data, members, event, names)
75
+ else:
76
+ data = {'contexts': {}, 'controls': {}, 'separators': set()}
77
+ for row in epoch_rows:
78
+ _measure(data, members, row, names)
79
+ new_groups[members] = data
80
+ counts = []
81
+ for action in sorted(actions):
82
+ omega = len(data['contexts'].get(action, ()))
83
+ controls = data['controls'].get(action, 0)
84
+ comparison = any(other != action for other in data['separators'])
85
+ counts.append({'action_nmu': list(action), 'omega': omega,
86
+ 'controls': controls, 'comparator': comparison})
87
+ if omega >= OMEGA_CRIT and controls and comparison:
88
+ winners.append((action, members))
89
+ cells.append({'members': list(members), 'C_L_by_action': counts, 'xi': 0})
90
+ if prior_crystal is not None and prior_crystal not in groups:
91
+ result = {'status': 'REVOKED', 'action_nmu': None, 'members': None}
92
+ prior_crystal = None
93
+ epoch_start = index + 1
94
+ epoch_rows = []
95
+ new_groups = {}
96
+ elif len(winners) == 1:
97
+ action, members = winners[0]
98
+ result = {'status': 'CRYSTAL', 'action_nmu': list(action), 'members': list(members)}
99
+ prior_crystal = members
100
+ for cell in cells:
101
+ cell['xi'] = int(tuple(cell['members']) == members)
102
+ elif prior_crystal is not None and not any(members == prior_crystal for _, members in winners):
103
+ result = {'status': 'REVOKED', 'action_nmu': None, 'members': None}
104
+ else:
105
+ result = {'status': 'NO_PATH', 'action_nmu': None, 'members': None}
106
+ expected = {'event_index': index, 'source': event['source'],
107
+ 'epoch_start': epoch_start, 'classes': cells, 'result': result}
108
+ if canonical(certificate['phases'][index]) != canonical(expected):
109
+ return False
110
+ old_groups = new_groups
111
+ return canonical(certificate['result']) == canonical(result)
112
+ except (ValueError, TypeError, KeyError, IndexError, AttributeError):
113
+ return False
@@ -0,0 +1,47 @@
1
+ """Source-complete acquisition coverage choice before a separator is observable."""
2
+ from copy import deepcopy
3
+ from hashlib import sha256
4
+ from tgi.frame_engine import canonical
5
+ from tgi.identity import encode
6
+ from tgi.role_work import RoleSearchBudget
7
+ from tgi.guarded_word_goal_frontier_check import verify_guarded_word_goal_frontier
8
+ from tgi.guarded_word_goal_workflow import GuardedWordGoalWorkflow
9
+
10
+ def certify_unobserved_action_selection(*args,frontier,max_search_steps=None):
11
+ b=RoleSearchBudget(max_search_steps)
12
+ with b.scope():
13
+ b.consume();frontier=deepcopy(frontier)
14
+ if not verify_guarded_word_goal_frontier(*args,goal=frontier['goal'],certificate=frontier,max_depth=frontier['max_depth']):raise ValueError('Complete original current task frontier required')
15
+ d=frontier['discovery'];coverage=[];bindings=[]
16
+ for action in d['available_actions']:
17
+ witnesses=[]
18
+ for li,item in enumerate(d['laws']):
19
+ if item['action']!=action:continue
20
+ c=item['certificate'];c=c['law'] if c['policy']=='observation_context_laws_v1' else c;matching=[]
21
+ for ri,row in enumerate(c['rows']):
22
+ b.consume()
23
+ if row['cells'][item['port']]['frames'][1]==action:matching.append(ri)
24
+ witnesses.append({'law_index':li,'port':item['port'],'matching_original_rows':matching})
25
+ unobserved=bool(witnesses) and len(witnesses)==len(d['state_certificate']['ports']) and all(not w['matching_original_rows'] for w in witnesses)
26
+ coverage.append({'action':action,'action_ids':list(encode(action)),'source_coverage':witnesses,'unobserved':unobserved})
27
+ indexes=[]
28
+ for ci,choice in enumerate(frontier['choices']):
29
+ b.consume()
30
+ if unobserved and choice['kind']=='FRONTIER_EXPERIENCE' and choice['actions']==[action] and len(choice['edges'])==1 and frontier['frontier']['edges'][choice['edges'][0]]['source']==0:indexes.append(ci)
31
+ if unobserved:bindings.append({'action':action,'choice_indexes':indexes})
32
+ ready=d['result']['catalogue_current'] and d['state_certificate']['result']['status']=='JOINT_STATE_READY'
33
+ eligible=[row for row in bindings if len(row['choice_indexes'])==1];unique=ready and len(bindings)==1 and len(eligible)==1
34
+ status='UNIQUE_UNOBSERVED_ACTION_SELECTION_READY' if unique else 'UNOBSERVED_ACTION_ALTERNATIVES_RETAINED' if bindings else 'NO_CURRENT_UNOBSERVED_ACTION'
35
+ return {'policy':'unobserved_action_selection_v1','frontier_sha256':sha256(canonical(frontier).encode()).hexdigest(),'coverage':coverage,'bindings':bindings,'result':{'status':status,'selected_action':eligible[0]['action'] if unique else None,'selected_choice_index':eligible[0]['choice_indexes'][0] if unique else None,'effect_predicted':False,'separation_claimed':False}}
36
+
37
+ def select_unique_unobserved_action(workflow,*,max_search_steps=None):
38
+ from tgi.unobserved_action_selection_check import verify_unobserved_action_selection
39
+ b=RoleSearchBudget(max_search_steps)
40
+ with b.scope():
41
+ b.consume()
42
+ if type(workflow) is not GuardedWordGoalWorkflow or workflow.pending()['status']!='SELECTION_REQUIRED':raise ValueError('Owned unselected current workflow required')
43
+ args=workflow.context();f=workflow.frontier();c=certify_unobserved_action_selection(*args,frontier=f)
44
+ if not verify_unobserved_action_selection(*args,frontier=f,certificate=c) or c['result']['status']!='UNIQUE_UNOBSERVED_ACTION_SELECTION_READY':raise ValueError('Unique independently checked current acquisition obligation required')
45
+ result=workflow.select(c['result']['selected_choice_index'])
46
+ if result.pending()['action']!=c['result']['selected_action']:raise ValueError('Actual selection binding mismatch')
47
+ return result,c
@@ -0,0 +1,40 @@
1
+ """Independent complete original-row coverage and owned frontier selection verifier."""
2
+ from hashlib import sha256
3
+ from tgi.frame_engine import canonical
4
+ from tgi.identity import encode
5
+ from tgi.role_work import RoleSearchBudget
6
+ from tgi.guarded_word_goal_frontier_check import verify_guarded_word_goal_frontier
7
+
8
+ def verify_unobserved_action_selection(*args,frontier,certificate,max_search_steps=None):
9
+ b=RoleSearchBudget(max_search_steps)
10
+ with b.scope():
11
+ b.consume()
12
+ try:
13
+ if type(certificate) is not dict or set(certificate)!={'policy','frontier_sha256','coverage','bindings','result'} or certificate['policy']!='unobserved_action_selection_v1':return False
14
+ if not verify_guarded_word_goal_frontier(*args,goal=frontier['goal'],certificate=frontier,max_depth=frontier['max_depth']):return False
15
+ if certificate['frontier_sha256']!=sha256(canonical(frontier).encode()).hexdigest():return False
16
+ discovery=frontier['discovery'];coverage=[];bindings=[]
17
+ for action in discovery['available_actions']:
18
+ sources=[]
19
+ for li in range(len(discovery['laws'])):
20
+ law=discovery['laws'][li]
21
+ if law['action']!=action:continue
22
+ c=law['certificate'];c=c['law'] if c['policy']=='observation_context_laws_v1' else c;indexes=[]
23
+ for ri in range(len(c['rows'])):
24
+ b.consume()
25
+ if c['rows'][ri]['cells'][law['port']]['frames'][1]==action:indexes.append(ri)
26
+ sources.append({'law_index':li,'port':law['port'],'matching_original_rows':indexes})
27
+ missing=len(sources)==len(discovery['state_certificate']['ports']) and bool(sources) and not any(row['matching_original_rows'] for row in sources)
28
+ coverage.append({'action':action,'action_ids':list(encode(action)),'source_coverage':sources,'unobserved':missing})
29
+ choices=[]
30
+ for ci in range(len(frontier['choices'])):
31
+ b.consume();choice=frontier['choices'][ci]
32
+ if not missing or choice['kind']!='FRONTIER_EXPERIENCE' or choice['actions']!=[action] or len(choice['edges'])!=1:continue
33
+ if frontier['frontier']['edges'][choice['edges'][0]]['source']==0:choices.append(ci)
34
+ if missing:bindings.append({'action':action,'choice_indexes':choices})
35
+ eligible=[row for row in bindings if len(row['choice_indexes'])==1]
36
+ unique=discovery['result']['catalogue_current'] and discovery['state_certificate']['result']['status']=='JOINT_STATE_READY' and len(bindings)==1 and len(eligible)==1
37
+ status='UNIQUE_UNOBSERVED_ACTION_SELECTION_READY' if unique else 'UNOBSERVED_ACTION_ALTERNATIVES_RETAINED' if bindings else 'NO_CURRENT_UNOBSERVED_ACTION'
38
+ expected={'status':status,'selected_action':eligible[0]['action'] if unique else None,'selected_choice_index':eligible[0]['choice_indexes'][0] if unique else None,'effect_predicted':False,'separation_claimed':False}
39
+ return canonical(certificate['coverage'])==canonical(coverage) and canonical(certificate['bindings'])==canonical(bindings) and canonical(certificate['result'])==canonical(expected)
40
+ except (ValueError,KeyError,TypeError,IndexError,AttributeError):return False
tgi/verification.py ADDED
@@ -0,0 +1,98 @@
1
+ """Instrumented verification with complete-or-unverified matcher work."""
2
+ from .incremental import SearchBudget, FormationSearchLimit, ACTIVE_SEARCH_BUDGET
3
+ from .organization_check import verify_resolution, verify_refusal
4
+ from .organization_snapshot import snapshot
5
+
6
+
7
+ class _VerificationBudget(SearchBudget):
8
+ def __init__(self,limit,parent):
9
+ super().__init__(limit);self.parent=parent
10
+
11
+ def match_step(self):
12
+ # Preflight the child before charging any ancestor. Ancestors perform
13
+ # the same preflight, so rejected work is charged nowhere in the chain.
14
+ if self.limit is not None and self.used>=self.limit:
15
+ raise FormationSearchLimit(self.limit,self.used)
16
+ if self.parent is not None:self.parent.match_step()
17
+ super().match_step()
18
+
19
+
20
+ def check_resolution(engine,prefix,result,*,terminal_only=False,max_match_steps=None):
21
+ budget=_VerificationBudget(max_match_steps,ACTIVE_SEARCH_BUDGET.get())
22
+ report={"status":"NOT_VERIFIED","verified":False,"match_steps":0,"max_match_steps":max_match_steps}
23
+ try:
24
+ with budget.scope():
25
+ if engine._dirty:return report
26
+ kind=result.get("status")
27
+ if kind not in ("RESOLVED","NO_PATH","AMBIGUOUS"):return report
28
+ frozen=snapshot(engine)
29
+ checker=verify_resolution if kind=="RESOLVED" else verify_refusal
30
+ verified=checker(frozen,prefix,result,terminal_only=terminal_only)
31
+ report.update(status="VERIFIED" if verified else "NOT_VERIFIED",verified=verified)
32
+ except FormationSearchLimit as error:
33
+ report.update(status="WORK_LIMIT",verified=False,exhausted_limit=error.limit)
34
+ except (KeyError,IndexError,TypeError,ValueError,AttributeError):
35
+ report.update(status="NOT_VERIFIED",verified=False)
36
+ finally:
37
+ report["match_steps"]=budget.matcher_used
38
+ return report
39
+
40
+
41
+ def check_ask_certificate(engine, certificate, *, max_match_steps=None):
42
+ """Check complete search scope; exhausted work never proves absence."""
43
+ from .ask_certificate import verify_ask_certificate
44
+ budget = _VerificationBudget(max_match_steps, ACTIVE_SEARCH_BUDGET.get())
45
+ report = {"status": "NOT_VERIFIED", "verified": False,
46
+ "match_steps": 0, "max_match_steps": max_match_steps}
47
+ try:
48
+ with budget.scope():
49
+ if engine._dirty:
50
+ return report
51
+ verified = verify_ask_certificate(engine, certificate)
52
+ report.update(status="VERIFIED" if verified else "NOT_VERIFIED",
53
+ verified=verified)
54
+ except FormationSearchLimit as error:
55
+ report.update(status="WORK_LIMIT", verified=False,
56
+ exhausted_limit=error.limit)
57
+ except (KeyError, IndexError, TypeError, ValueError, AttributeError):
58
+ report.update(status="NOT_VERIFIED", verified=False)
59
+ finally:
60
+ report["match_steps"] = budget.matcher_used
61
+ return report
62
+
63
+
64
+ class TemporalCandidateLimit(FormationSearchLimit):
65
+ """Separate work unit: pool candidate or Cartesian tuple examination."""
66
+
67
+
68
+ class _TemporalCandidateBudget(SearchBudget):
69
+ def __init__(self,limit,parent):
70
+ super().__init__(limit);self.parent=parent
71
+
72
+ def consume(self):
73
+ if self.limit is not None and self.used>=self.limit:
74
+ raise TemporalCandidateLimit(self.limit,self.used)
75
+ if self.parent is not None:self.parent.consume()
76
+ super().consume()
77
+
78
+
79
+ def check_recent_certificate(engine,certificate,*,max_match_steps=None,max_candidates=None):
80
+ """Verify temporal completeness under independent matcher/candidate ceilings."""
81
+ from .recent_observation import verify_recent_at,ACTIVE_TEMPORAL_CANDIDATES
82
+ matches=_VerificationBudget(max_match_steps,ACTIVE_SEARCH_BUDGET.get())
83
+ candidates=_TemporalCandidateBudget(max_candidates,ACTIVE_TEMPORAL_CANDIDATES.get())
84
+ report={'status':'NOT_VERIFIED','verified':False,'match_steps':0,'candidate_steps':0,
85
+ 'max_match_steps':max_match_steps,'max_candidates':max_candidates}
86
+ token=ACTIVE_TEMPORAL_CANDIDATES.set(candidates)
87
+ try:
88
+ with matches.scope():
89
+ verified=verify_recent_at(engine,certificate)
90
+ report.update(status='VERIFIED' if verified else 'NOT_VERIFIED',verified=verified)
91
+ except TemporalCandidateLimit as error:
92
+ report.update(status='WORK_LIMIT',verified=False,exhausted_kind='candidates',exhausted_limit=error.limit)
93
+ except FormationSearchLimit as error:
94
+ report.update(status='WORK_LIMIT',verified=False,exhausted_kind='matcher',exhausted_limit=error.limit)
95
+ finally:
96
+ ACTIVE_TEMPORAL_CANDIDATES.reset(token)
97
+ report.update(match_steps=matches.matcher_used,candidate_steps=candidates.used)
98
+ return report
@@ -0,0 +1,62 @@
1
+ """Conditional functional consensus over all measured candidate spans."""
2
+ from .identity import encode,decode
3
+ from .frame_engine import canonical
4
+ from .organization_snapshot import snapshot
5
+ from .role_work import RoleSearchBudget
6
+ from .acquisition_witness import certify_acquisition_witness,verify_acquisition_witness
7
+ from .response_eligibility import certify_response_eligibility
8
+ from .functional_transition import certify_functional_eligibility,certify_functional_readout
9
+ from .functional_transition_check import verify_functional_eligibility,_verify_functional_readout_contents
10
+
11
+ from .witness_response import _root_index
12
+
13
+ def certify_witness_function(model,measurements,root,query,*,max_search_steps=None):
14
+ budget=RoleSearchBudget(max_search_steps)
15
+ with budget.scope():
16
+ view=snapshot(model);w=certify_acquisition_witness(view,measurements);i=_root_index(w['measurements'],root,query);neighbors=set()
17
+ for pair in w['pairs']:
18
+ budget.consume()
19
+ if i in (pair['left'],pair['right']):neighbors.add(pair['right'] if pair['left']==i else pair['left'])
20
+ eligibility=certify_functional_eligibility(view,certify_response_eligibility(view))
21
+ branches=[]
22
+ for j in sorted(neighbors):
23
+ budget.consume();r=w['measurements'][j];context=decode(tuple(w['occurrences'][j]['identities']))
24
+ branches.append({'measurement':dict(r),'response':certify_functional_readout(view,eligibility,[context,query])})
25
+ alternatives=sorted({x for b in branches for x in b['response']['result']['output']});complete=bool(branches) and all(b['response']['result']['status'] in ('OBSERVED_RESPONSE','CONDITIONAL_FUNCTION') for b in branches)
26
+ observed_conflict=any(b['response']['result']['basis']=='observed_conflict' for b in branches)
27
+ status='NO_CANDIDATES' if not branches else 'CONFLICT' if len(alternatives)>1 or observed_conflict else 'CONDITIONAL_FUNCTION_CONSENSUS' if complete and len(alternatives)==1 else 'UNRESOLVED'
28
+ return {'policy':'witness_function_response_v1','root':dict(root),'query':query,'witness':w,'branches':branches,'result':{'status':status,'output':alternatives if status=='CONDITIONAL_FUNCTION_CONSENSUS' else [],'alternatives':alternatives}}
29
+
30
+ def verify_witness_function(model,measurements,root,query,certificate,*,max_search_steps=None):
31
+ budget=RoleSearchBudget(max_search_steps)
32
+ with budget.scope():
33
+ try:
34
+ view=snapshot(model)
35
+ if set(certificate)!={'policy','root','query','witness','branches','result'} or certificate['policy']!='witness_function_response_v1':return False
36
+ if canonical(certificate['root'])!=canonical(root) or certificate['query']!=query:return False
37
+ if not verify_acquisition_witness(view,measurements,certificate['witness']):return False
38
+ records=certificate['witness']['measurements'];i=_root_index(records,root,query);r=records[i];selected=[]
39
+ for j,other in enumerate(records):
40
+ budget.consume()
41
+ if other['source']!=r['source'] and other['clock']==r['clock'] and other['lower']<=r['upper'] and r['lower']<=other['upper']:selected.append(other)
42
+ branches=certificate['branches']
43
+ if not isinstance(branches,list) or len(branches)!=len(selected):return False
44
+ values=set();all_agree=True;conflict=False;verified_eligibility=None
45
+ for other,b in zip(selected,branches):
46
+ budget.consume()
47
+ if set(b)!={'measurement','response'} or canonical(b['measurement'])!=canonical(other):return False
48
+ raw=view.episodes[other['source']][0][other['frame']][other['start']:other['stop']]
49
+ eligibility=b['response']['eligibility']
50
+ if verified_eligibility is None:
51
+ if not verify_functional_eligibility(view,eligibility):return False
52
+ verified_eligibility=canonical(eligibility)
53
+ elif canonical(eligibility)!=verified_eligibility:return False
54
+ if b['response']['readout']['prefix']!=[decode(raw),query] or not _verify_functional_readout_contents(view,b['response']):return False
55
+ values.update(b['response']['result']['output']);all_agree &= b['response']['result']['status'] in ('OBSERVED_RESPONSE','CONDITIONAL_FUNCTION')
56
+ conflict |= b['response']['result']['basis']=='observed_conflict'
57
+ status='UNRESOLVED';output=[]
58
+ if not selected:status='NO_CANDIDATES'
59
+ elif len(values)>1 or conflict:status='CONFLICT'
60
+ elif all_agree and len(values)==1:status='CONDITIONAL_FUNCTION_CONSENSUS';output=sorted(values)
61
+ return canonical(certificate['result'])==canonical({'status':status,'output':output,'alternatives':sorted(values)})
62
+ except (ValueError,TypeError,KeyError,IndexError,AttributeError):return False
tgi/witness_probe.py ADDED
@@ -0,0 +1,50 @@
1
+ """Complete conditional probe planning over measured candidate occurrences."""
2
+ from .identity import decode
3
+ from .frame_engine import canonical
4
+ from .organization_snapshot import snapshot
5
+ from .role_work import RoleSearchBudget
6
+ from .acquisition_witness import certify_acquisition_witness,verify_acquisition_witness
7
+ from .witness_response import _measured_root_index
8
+ from .witness_function import certify_witness_function,verify_witness_function
9
+
10
+ def certify_witness_probe(model,measurements,root,*,max_search_steps=None):
11
+ budget=RoleSearchBudget(max_search_steps)
12
+ with budget.scope():
13
+ view=snapshot(model);w=certify_acquisition_witness(view,measurements);_measured_root_index(w['measurements'],root);rows=[];admitted=[]
14
+ queries=sorted({decode(frames[-2]) for frames,_ in view.episodes.values() if len(frames)>=3})
15
+ for query in queries:
16
+ budget.consume();c=certify_witness_function(view,measurements,root,query);groups={};complete=True
17
+ for i,b in enumerate(c['branches']):
18
+ r=b['response']['result'];complete &= r['status'] in ('CONDITIONAL_FUNCTION','OBSERVED_RESPONSE')
19
+ if r['output']:groups.setdefault(tuple(r['output']),[]).append(i)
20
+ count=len(c['branches']);separates=count>1 and complete and all(len(ids)<count for ids in groups.values())
21
+ rows.append({'query':query,'certificate':c,'groups':[{'response':list(v),'branches':ids} for v,ids in sorted(groups.items())],'separates':separates})
22
+ if separates:admitted.append(query)
23
+ return {'policy':'measured_probe_plan_v1','root':dict(root),'witness':w,'requests':rows,'result':{'status':'SEPARATING' if admitted else 'NO_SEPARATOR','requests':admitted}}
24
+
25
+ def verify_witness_probe(model,measurements,root,certificate,*,max_search_steps=None):
26
+ budget=RoleSearchBudget(max_search_steps)
27
+ with budget.scope():
28
+ try:
29
+ view=snapshot(model)
30
+ if set(certificate)!={'policy','root','witness','requests','result'} or certificate['policy']!='measured_probe_plan_v1' or canonical(certificate['root'])!=canonical(root):return False
31
+ w=certificate['witness']
32
+ if not verify_acquisition_witness(view,measurements,w):return False
33
+ _measured_root_index(w['measurements'],root)
34
+ queries=sorted(set(decode(frames[-2]) for frames,_ in view.episodes.values() if len(frames)>=3));rows=certificate['requests']
35
+ if not isinstance(rows,list) or len(rows)!=len(queries):return False
36
+ admitted=[]
37
+ for query,row in zip(queries,rows):
38
+ budget.consume()
39
+ if set(row)!={'query','certificate','groups','separates'} or row['query']!=query:return False
40
+ c=row['certificate']
41
+ if canonical(c['witness'])!=canonical(w) or not verify_witness_function(view,measurements,root,query,c):return False
42
+ answers=[];complete=bool(c['branches'])
43
+ for b in c['branches']:
44
+ r=b['response']['result'];complete &= r['status'] in ('CONDITIONAL_FUNCTION','OBSERVED_RESPONSE');answers.append(r['output'])
45
+ values=sorted({tuple(a) for a in answers if a});groups=[{'response':list(v),'branches':[i for i,a in enumerate(answers) if tuple(a)==v]} for v in values]
46
+ separates=len(answers)>1 and complete and len(values)>1 and all(len(g['branches'])<len(answers) for g in groups)
47
+ if canonical(row['groups'])!=canonical(groups) or type(row['separates']) is not bool or row['separates']!=separates:return False
48
+ if separates:admitted.append(query)
49
+ return canonical(certificate['result'])==canonical({'status':'SEPARATING' if admitted else 'NO_SEPARATOR','requests':admitted})
50
+ except (KeyError,IndexError,TypeError,ValueError,AttributeError):return False