speclogician 0.0.0b1__py3-none-any.whl → 0.0.0.dev1__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 (153) hide show
  1. speclogician/agent/funcs.py +29 -0
  2. speclogician/cmd/agent_cmd.py +89 -0
  3. speclogician/cmd/data_cmd.py +24 -0
  4. speclogician/cmd/model_cmd.py +42 -0
  5. speclogician/cmd/overlay_cmd.py +30 -0
  6. speclogician/cmd/scenario_cmd.py +61 -0
  7. speclogician/cmd/state_cmd.py +52 -0
  8. speclogician/data/artifact.py +8 -50
  9. speclogician/data/container.py +18 -384
  10. speclogician/data/mapping.py +18 -17
  11. speclogician/data/refs.py +12 -11
  12. speclogician/data/reports.py +11 -0
  13. speclogician/data/traces.py +15 -6
  14. speclogician/llms/llmtools.py +102 -0
  15. speclogician/llms/overlay.py +264 -0
  16. speclogician/main.py +36 -102
  17. speclogician/modeling/__init__.py +0 -31
  18. speclogician/modeling/component.py +4 -60
  19. speclogician/modeling/conflict.py +5 -19
  20. speclogician/modeling/domain.py +93 -280
  21. speclogician/modeling/model.py +206 -0
  22. speclogician/modeling/predicates.py +20 -22
  23. speclogician/modeling/report.py +33 -0
  24. speclogician/modeling/scenario.py +119 -87
  25. speclogician/sl_cmd.py +76 -0
  26. speclogician/state/change.py +98 -378
  27. speclogician/state/state.py +183 -399
  28. speclogician/tui/box.tcss +10 -0
  29. speclogician/tui/tui.py +131 -0
  30. speclogician/utils/__init__.py +1 -70
  31. speclogician/utils/imx.py +195 -0
  32. speclogician/utils/load.py +25 -147
  33. speclogician/utils/prompt.md +1 -325
  34. speclogician-0.0.0.dev1.dist-info/METADATA +21 -0
  35. speclogician-0.0.0.dev1.dist-info/RECORD +43 -0
  36. speclogician/commands/__init__.py +0 -15
  37. speclogician/commands/cmd_ch.py +0 -616
  38. speclogician/commands/cmd_find.py +0 -256
  39. speclogician/commands/cmd_view.py +0 -202
  40. speclogician/commands/runner.py +0 -149
  41. speclogician/commands/utils.py +0 -101
  42. speclogician/demos/.DS_Store +0 -0
  43. speclogician/demos/cmd_demo.py +0 -278
  44. speclogician/demos/loader.py +0 -135
  45. speclogician/demos/model.py +0 -27
  46. speclogician/demos/runner.py +0 -51
  47. speclogician/logic/__init__.py +0 -11
  48. speclogician/logic/api/__init__.py +0 -29
  49. speclogician/logic/api/client.py +0 -606
  50. speclogician/logic/api/decomp.py +0 -67
  51. speclogician/logic/api/scenario.py +0 -102
  52. speclogician/logic/api/traces.py +0 -59
  53. speclogician/logic/lib/__init__.py +0 -19
  54. speclogician/logic/lib/complement.py +0 -107
  55. speclogician/logic/lib/domain_model.py +0 -59
  56. speclogician/logic/lib/predicates.py +0 -151
  57. speclogician/logic/lib/scenarios.py +0 -369
  58. speclogician/logic/lib/traces.py +0 -114
  59. speclogician/logic/lib/transitions.py +0 -104
  60. speclogician/logic/main.py +0 -246
  61. speclogician/logic/strings.py +0 -194
  62. speclogician/logic/utils.py +0 -135
  63. speclogician/modeling/complement.py +0 -104
  64. speclogician/modeling/spec.py +0 -306
  65. speclogician/modeling/spec_stats.py +0 -39
  66. speclogician/presentation/api.py +0 -244
  67. speclogician/presentation/builders/_links.py +0 -44
  68. speclogician/presentation/builders/container.py +0 -53
  69. speclogician/presentation/builders/data_artifact.py +0 -42
  70. speclogician/presentation/builders/domain.py +0 -54
  71. speclogician/presentation/builders/instances_list.py +0 -38
  72. speclogician/presentation/builders/predicate.py +0 -51
  73. speclogician/presentation/builders/recommendations.py +0 -41
  74. speclogician/presentation/builders/scenario.py +0 -41
  75. speclogician/presentation/builders/scenario_complement.py +0 -82
  76. speclogician/presentation/builders/smart_find.py +0 -39
  77. speclogician/presentation/builders/spec.py +0 -39
  78. speclogician/presentation/builders/state_diff.py +0 -150
  79. speclogician/presentation/builders/state_instance.py +0 -42
  80. speclogician/presentation/builders/state_instance_summary.py +0 -84
  81. speclogician/presentation/builders/trace.py +0 -58
  82. speclogician/presentation/ctx.py +0 -38
  83. speclogician/presentation/models/container.py +0 -44
  84. speclogician/presentation/models/data_artifact.py +0 -33
  85. speclogician/presentation/models/domain.py +0 -50
  86. speclogician/presentation/models/instances_list.py +0 -23
  87. speclogician/presentation/models/predicate.py +0 -60
  88. speclogician/presentation/models/recommendations.py +0 -34
  89. speclogician/presentation/models/scenario.py +0 -31
  90. speclogician/presentation/models/scenario_complement.py +0 -40
  91. speclogician/presentation/models/smart_find.py +0 -34
  92. speclogician/presentation/models/spec.py +0 -32
  93. speclogician/presentation/models/state_diff.py +0 -34
  94. speclogician/presentation/models/state_instance.py +0 -31
  95. speclogician/presentation/models/state_instance_summary.py +0 -102
  96. speclogician/presentation/models/trace.py +0 -42
  97. speclogician/presentation/preview/__init__.py +0 -13
  98. speclogician/presentation/preview/cli.py +0 -50
  99. speclogician/presentation/preview/fixtures/__init__.py +0 -205
  100. speclogician/presentation/preview/fixtures/artifact_container.py +0 -150
  101. speclogician/presentation/preview/fixtures/data_artifact.py +0 -144
  102. speclogician/presentation/preview/fixtures/domain_model.py +0 -162
  103. speclogician/presentation/preview/fixtures/instances_list.py +0 -162
  104. speclogician/presentation/preview/fixtures/predicate.py +0 -184
  105. speclogician/presentation/preview/fixtures/scenario.py +0 -84
  106. speclogician/presentation/preview/fixtures/scenario_complement.py +0 -81
  107. speclogician/presentation/preview/fixtures/smart_find.py +0 -140
  108. speclogician/presentation/preview/fixtures/spec.py +0 -95
  109. speclogician/presentation/preview/fixtures/state_diff.py +0 -158
  110. speclogician/presentation/preview/fixtures/state_instance.py +0 -128
  111. speclogician/presentation/preview/fixtures/state_instance_summary.py +0 -80
  112. speclogician/presentation/preview/fixtures/trace.py +0 -206
  113. speclogician/presentation/preview/registry.py +0 -42
  114. speclogician/presentation/renderers/__init__.py +0 -24
  115. speclogician/presentation/renderers/container.py +0 -136
  116. speclogician/presentation/renderers/data_artifact.py +0 -144
  117. speclogician/presentation/renderers/domain.py +0 -123
  118. speclogician/presentation/renderers/instances_list.py +0 -120
  119. speclogician/presentation/renderers/predicate.py +0 -180
  120. speclogician/presentation/renderers/recommendations.py +0 -90
  121. speclogician/presentation/renderers/scenario.py +0 -94
  122. speclogician/presentation/renderers/scenario_complement.py +0 -59
  123. speclogician/presentation/renderers/smart_find.py +0 -307
  124. speclogician/presentation/renderers/spec.py +0 -105
  125. speclogician/presentation/renderers/state_diff.py +0 -102
  126. speclogician/presentation/renderers/state_instance.py +0 -82
  127. speclogician/presentation/renderers/state_instance_summary.py +0 -143
  128. speclogician/presentation/renderers/trace.py +0 -122
  129. speclogician/shell/app.py +0 -170
  130. speclogician/shell/shell_ch.py +0 -263
  131. speclogician/shell/shell_view.py +0 -153
  132. speclogician/state/change_result.py +0 -32
  133. speclogician/state/diff.py +0 -191
  134. speclogician/state/inst.py +0 -574
  135. speclogician/state/recommendation.py +0 -13
  136. speclogician/state/recommender.py +0 -577
  137. speclogician/state/state_stats.py +0 -133
  138. speclogician/tui/__init__.py +0 -0
  139. speclogician/tui/app.py +0 -257
  140. speclogician/tui/app.tcss +0 -160
  141. speclogician/tui/demo.py +0 -45
  142. speclogician/tui/images/speclogician-full.png +0 -0
  143. speclogician/tui/images/speclogician-minimal.png +0 -0
  144. speclogician/tui/main_screen.py +0 -454
  145. speclogician/tui/splash_screen.py +0 -51
  146. speclogician/tui/stats_screen.py +0 -125
  147. speclogician/utils/testing.py +0 -151
  148. speclogician-0.0.0b1.dist-info/METADATA +0 -116
  149. speclogician-0.0.0b1.dist-info/RECORD +0 -139
  150. /speclogician/{presentation → agent}/__init__.py +0 -0
  151. /speclogician/{presentation/builders → cmd}/__init__.py +0 -0
  152. /speclogician/{presentation/models → llms}/__init__.py +0 -0
  153. {speclogician-0.0.0b1.dist-info → speclogician-0.0.0.dev1.dist-info}/WHEEL +0 -0
@@ -1,574 +0,0 @@
1
- #
2
- # Imandra Inc.
3
- #
4
- # speclogician/state/inst.py
5
- #
6
-
7
- from __future__ import annotations
8
-
9
- from datetime import datetime, timezone
10
- from typing import Optional
11
-
12
- from pydantic import BaseModel, Field
13
-
14
- from .change import StateChange
15
- from .diff import (
16
- StateDiff,
17
- ValueDiff,
18
- base_status_comp,
19
- bool_true_is_good,
20
- numeric_increase_bad,
21
- numeric_increase_good,
22
- )
23
- from .recommendation import Recommendation
24
- from ..data.container import ArtifactContainer
25
- from ..data.mapping import ArtifactMap
26
- from ..modeling.domain import BaseStatus
27
- from ..modeling.spec import Spec
28
- from ..utils import JSONObject
29
-
30
- def _safe_iso(dt: Optional[datetime]) -> str | None:
31
- if dt is None:
32
- return None
33
- return dt.isoformat()
34
-
35
-
36
- def _comp_regions(comp: object | None) -> list[object]:
37
- """
38
- Best-effort extraction of complement regions.
39
-
40
- Supports a couple of plausible layouts:
41
- - comp.regions
42
- - comp.cover_regions
43
- """
44
- if comp is None:
45
- return []
46
- regs = getattr(comp, "regions", None)
47
- if regs is None:
48
- regs = getattr(comp, "cover_regions", None)
49
- try:
50
- return list(regs or [])
51
- except Exception:
52
- return []
53
-
54
-
55
- class StateInstance(BaseModel):
56
- """
57
- State Instance class
58
- """
59
-
60
- created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
61
-
62
- spec: Spec = Field(default_factory=Spec) # `Spec` is the domain model + scenarios
63
- art_container: ArtifactContainer = Field(default_factory=ArtifactContainer)
64
- art_map: ArtifactMap = Field(default_factory=ArtifactMap)
65
-
66
- # These describe the changes from the previous state
67
- changes: list[StateChange] = Field(default_factory=list[StateChange])
68
- state_diff: StateDiff | None = None
69
-
70
- # The generated recommendations
71
- recommendations: list[Recommendation] = Field(default_factory=list[Recommendation])
72
-
73
- def update_created_at(self) -> None:
74
- """Update the `created_at` timestamp field"""
75
- self.created_at = datetime.now(timezone.utc)
76
-
77
- def json_summary(self) -> JSONObject:
78
- """
79
- JSON snapshot of the *inputs* used by calc_diff() to compute StateDiff.
80
-
81
- Goal: stable, machine-readable summary suitable for --json output and tests.
82
- """
83
- dm = self.spec.domain_model
84
- spec = self.spec
85
- art = self.art_container
86
-
87
- comp = getattr(spec, "scenario_comp", None)
88
- regions = _comp_regions(comp)
89
-
90
- return {
91
- "created_at": _safe_iso(self.created_at),
92
- "changes": [c.model_dump(mode="json") for c in (self.changes or [])],
93
- # ---- Domain model (base) ----
94
- "base_status": getattr(dm, "base_status", None),
95
- "base_has_state": getattr(dm, "base_has_state", None),
96
- "base_has_action": getattr(dm, "base_has_action", None),
97
- # ---- Domain model (state predicates) ----
98
- "num_state_preds_total": getattr(dm, "num_state_preds_total", None),
99
- "num_state_preds_matched": getattr(dm, "num_state_preds_matched", None),
100
- "num_state_preds_valid_logic": getattr(dm, "num_state_preds_valid_logic", None),
101
- "num_state_preds_errored": getattr(dm, "num_state_preds_errored", None),
102
- # ---- Domain model (action predicates) ----
103
- "num_action_preds_total": getattr(dm, "num_action_preds_total", None),
104
- "num_action_preds_matched": getattr(dm, "num_action_preds_matched", None),
105
- "num_action_preds_valid_logic": getattr(dm, "num_action_preds_valid_logic", None),
106
- "num_action_preds_errored": getattr(dm, "num_action_preds_errored", None),
107
- # ---- Domain model (predicates totals) ----
108
- "num_preds_total": getattr(dm, "num_preds_total", None),
109
- "num_preds_matched": getattr(dm, "num_preds_matched", None),
110
- "num_preds_valid_logic": getattr(dm, "num_preds_valid_logic", None),
111
- "num_preds_errored": getattr(dm, "num_preds_errored", None),
112
- # ---- Domain model (transitions) ----
113
- "num_trans_total": getattr(dm, "num_trans_total", None),
114
- "num_trans_matched": getattr(dm, "num_trans_matched", None),
115
- "num_trans_valid_logic": getattr(dm, "num_trans_valid_logic", None),
116
- "num_trans_errored": getattr(dm, "num_trans_errored", None),
117
- # ---- Scenarios (Spec-level) ----
118
- "num_sc_total": getattr(spec, "num_sc_total", None),
119
- "num_sc_missing": getattr(spec, "num_sc_missing", None),
120
- "num_sc_matched": getattr(spec, "num_sc_matched", None),
121
- "num_sc_inconsistent": getattr(spec, "num_sc_inconsistent", None),
122
- # Conflicts (total + by kind).
123
- "num_sc_conflicted": getattr(spec, "num_sc_conflicted", None),
124
- "num_sc_overlap": getattr(spec, "num_sc_overlap", 0),
125
- "num_sc_consumed": getattr(spec, "num_sc_consumed", 0),
126
- # ---- Complement (Spec-level) ----
127
- # NOTE: match StateDiff field names (new ones).
128
- "scenario_comp_present": comp is not None,
129
- "num_comp_regions_total": len(regions),
130
- # ---- ArtifactContainer: test traces ----
131
- "num_test_traces_total": getattr(art, "num_test_traces_total", None),
132
- "num_test_traces_logic_good": getattr(art, "num_test_traces_logic_good", None),
133
- "num_test_traces_matched": getattr(art, "num_test_traces_matched", None),
134
- # ---- ArtifactContainer: log traces ----
135
- "num_log_traces_total": getattr(art, "num_log_traces_total", None),
136
- "num_log_traces_logic_good": getattr(art, "num_log_traces_logic_good", None),
137
- "num_log_traces_matched": getattr(art, "num_log_traces_matched", None),
138
- }
139
-
140
-
141
- class InstancesList(BaseModel):
142
- """A helper container for pretty printing a list of instances"""
143
-
144
- states: list[StateInstance]
145
-
146
-
147
- def calc_diff(new: StateInstance, old: StateInstance) -> StateDiff:
148
- """
149
- Generate a diff between two StateInstances.
150
-
151
- Assumes `old` and `new` have fully computed stats on:
152
- - new.spec.domain_model
153
- - new.spec (scenario stats)
154
- - new.art_container
155
- - new.spec.scenario_comp (optional)
156
- """
157
- vals: list[tuple[str, ValueDiff]] = []
158
-
159
- dm_old = old.spec.domain_model
160
- dm_new = new.spec.domain_model
161
- spec_old = old.spec
162
- spec_new = new.spec
163
- art_old = old.art_container
164
- art_new = new.art_container
165
-
166
- # -------------------------------------------------------------------------
167
- # Helper: only emit diffs for fields that actually exist on StateDiff.
168
- # This makes calc_diff resilient while you evolve StateDiff.
169
- # -------------------------------------------------------------------------
170
- state_diff_fields = getattr(StateDiff, "model_fields", {}) # pydantic v2
171
-
172
- def _emit(key: str, vd: ValueDiff) -> None:
173
- if key in state_diff_fields:
174
- vals.append((key, vd))
175
-
176
- # >>> Base
177
- _emit(
178
- "base_status",
179
- ValueDiff[BaseStatus](
180
- label="Base code IML status",
181
- before=dm_old.base_status,
182
- after=dm_new.base_status,
183
- comp_func=base_status_comp,
184
- hint={},
185
- ),
186
- )
187
-
188
- _emit(
189
- "base_has_state",
190
- ValueDiff[bool](
191
- label="State type is present",
192
- before=dm_old.base_has_state,
193
- after=dm_new.base_has_state,
194
- comp_func=bool_true_is_good,
195
- hint={},
196
- ),
197
- )
198
-
199
- _emit(
200
- "base_has_action",
201
- ValueDiff[bool](
202
- label="Action type is present",
203
- before=dm_old.base_has_action,
204
- after=dm_new.base_has_action,
205
- comp_func=bool_true_is_good,
206
- hint={},
207
- ),
208
- )
209
-
210
- # >>> State predicates
211
- _emit(
212
- "num_state_preds_total",
213
- ValueDiff[int](
214
- label="Number of state predicates",
215
- before=dm_old.num_state_preds_total,
216
- after=dm_new.num_state_preds_total,
217
- comp_func=numeric_increase_good,
218
- hint={},
219
- ),
220
- )
221
-
222
- _emit(
223
- "num_state_preds_matched",
224
- ValueDiff[int](
225
- label="Number of matched state predicates",
226
- before=dm_old.num_state_preds_matched,
227
- after=dm_new.num_state_preds_matched,
228
- comp_func=numeric_increase_good,
229
- hint={},
230
- ),
231
- )
232
-
233
- _emit(
234
- "num_state_preds_valid_logic",
235
- ValueDiff[int](
236
- label="Number of state predicates with valid logic",
237
- before=dm_old.num_state_preds_valid_logic,
238
- after=dm_new.num_state_preds_valid_logic,
239
- comp_func=numeric_increase_good,
240
- hint={},
241
- ),
242
- )
243
-
244
- _emit(
245
- "num_state_preds_errored",
246
- ValueDiff[int](
247
- label="Number of state predicates with errors",
248
- before=dm_old.num_state_preds_errored,
249
- after=dm_new.num_state_preds_errored,
250
- comp_func=numeric_increase_bad,
251
- hint={},
252
- ),
253
- )
254
-
255
- # >>> Action predicates
256
- _emit(
257
- "num_action_preds_total",
258
- ValueDiff[int](
259
- label="Number of action predicates",
260
- before=dm_old.num_action_preds_total,
261
- after=dm_new.num_action_preds_total,
262
- comp_func=numeric_increase_good,
263
- hint={},
264
- ),
265
- )
266
-
267
- _emit(
268
- "num_action_preds_matched",
269
- ValueDiff[int](
270
- label="Number of matched action predicates",
271
- before=dm_old.num_action_preds_matched,
272
- after=dm_new.num_action_preds_matched,
273
- comp_func=numeric_increase_good,
274
- hint={},
275
- ),
276
- )
277
-
278
- _emit(
279
- "num_action_preds_valid_logic",
280
- ValueDiff[int](
281
- label="Number of action predicates with valid logic",
282
- before=dm_old.num_action_preds_valid_logic,
283
- after=dm_new.num_action_preds_valid_logic,
284
- comp_func=numeric_increase_good,
285
- hint={},
286
- ),
287
- )
288
-
289
- _emit(
290
- "num_action_preds_errored",
291
- ValueDiff[int](
292
- label="Number of action predicates with errors",
293
- before=dm_old.num_action_preds_errored,
294
- after=dm_new.num_action_preds_errored,
295
- comp_func=numeric_increase_bad,
296
- hint={},
297
- ),
298
- )
299
-
300
- # >>> Predicates (totals)
301
- _emit(
302
- "num_preds_total",
303
- ValueDiff[int](
304
- label="Number of predicates (total)",
305
- before=dm_old.num_preds_total,
306
- after=dm_new.num_preds_total,
307
- comp_func=numeric_increase_good,
308
- hint={},
309
- ),
310
- )
311
-
312
- _emit(
313
- "num_preds_matched",
314
- ValueDiff[int](
315
- label="Number of matched predicates (total)",
316
- before=dm_old.num_preds_matched,
317
- after=dm_new.num_preds_matched,
318
- comp_func=numeric_increase_good,
319
- hint={},
320
- ),
321
- )
322
-
323
- _emit(
324
- "num_preds_valid_logic",
325
- ValueDiff[int](
326
- label="Number of predicates with valid logic (total)",
327
- before=dm_old.num_preds_valid_logic,
328
- after=dm_new.num_preds_valid_logic,
329
- comp_func=numeric_increase_good,
330
- hint={},
331
- ),
332
- )
333
-
334
- _emit(
335
- "num_preds_errored",
336
- ValueDiff[int](
337
- label="Number of predicates with errors (total)",
338
- before=dm_old.num_preds_errored,
339
- after=dm_new.num_preds_errored,
340
- comp_func=numeric_increase_bad,
341
- hint={},
342
- ),
343
- )
344
-
345
- # >>> Transitions
346
- _emit(
347
- "num_trans_total",
348
- ValueDiff[int](
349
- label="Number of transitions",
350
- before=dm_old.num_trans_total,
351
- after=dm_new.num_trans_total,
352
- comp_func=numeric_increase_good,
353
- hint={},
354
- ),
355
- )
356
-
357
- _emit(
358
- "num_trans_matched",
359
- ValueDiff[int](
360
- label="Number of matched transitions",
361
- before=dm_old.num_trans_matched,
362
- after=dm_new.num_trans_matched,
363
- comp_func=numeric_increase_good,
364
- hint={},
365
- ),
366
- )
367
-
368
- _emit(
369
- "num_trans_valid_logic",
370
- ValueDiff[int](
371
- label="Number of transitions with valid logic",
372
- before=dm_old.num_trans_valid_logic,
373
- after=dm_new.num_trans_valid_logic,
374
- comp_func=numeric_increase_good,
375
- hint={},
376
- ),
377
- )
378
-
379
- _emit(
380
- "num_trans_errored",
381
- ValueDiff[int](
382
- label="Number of transitions with errors",
383
- before=dm_old.num_trans_errored,
384
- after=dm_new.num_trans_errored,
385
- comp_func=numeric_increase_bad,
386
- hint={},
387
- ),
388
- )
389
-
390
- # >>> Scenarios
391
- _emit(
392
- "num_sc_total",
393
- ValueDiff[int](
394
- label="Number of scenarios",
395
- before=spec_old.num_sc_total,
396
- after=spec_new.num_sc_total,
397
- comp_func=numeric_increase_good,
398
- hint={},
399
- ),
400
- )
401
-
402
- _emit(
403
- "num_sc_missing",
404
- ValueDiff[int](
405
- label="Number of scenarios with missing model components",
406
- before=spec_old.num_sc_missing,
407
- after=spec_new.num_sc_missing,
408
- comp_func=numeric_increase_bad,
409
- hint={},
410
- ),
411
- )
412
-
413
- _emit(
414
- "num_sc_matched",
415
- ValueDiff[int](
416
- label="Number of scenarios matched",
417
- before=spec_old.num_sc_matched,
418
- after=spec_new.num_sc_matched,
419
- comp_func=numeric_increase_good,
420
- hint={},
421
- ),
422
- )
423
-
424
- _emit(
425
- "num_sc_inconsistent",
426
- ValueDiff[int](
427
- label="Number of inconsistent scenarios",
428
- before=spec_old.num_sc_inconsistent,
429
- after=spec_new.num_sc_inconsistent,
430
- comp_func=numeric_increase_bad,
431
- hint={},
432
- ),
433
- )
434
-
435
- # >>> Conflicts
436
- _emit(
437
- "num_sc_conflicted",
438
- ValueDiff[int](
439
- label="Number of scenario conflicts (total)",
440
- before=spec_old.num_sc_conflicted,
441
- after=spec_new.num_sc_conflicted,
442
- comp_func=numeric_increase_bad,
443
- hint={},
444
- ),
445
- )
446
-
447
- _emit(
448
- "num_sc_overlap",
449
- ValueDiff[int](
450
- label="Number of scenario overlap conflicts",
451
- before=getattr(spec_old, "num_sc_overlap", 0),
452
- after=getattr(spec_new, "num_sc_overlap", 0),
453
- comp_func=numeric_increase_bad,
454
- hint={},
455
- ),
456
- )
457
-
458
- _emit(
459
- "num_sc_consumed",
460
- ValueDiff[int](
461
- label="Number of scenario consumed conflicts",
462
- before=getattr(spec_old, "num_sc_consumed", 0),
463
- after=getattr(spec_new, "num_sc_consumed", 0),
464
- comp_func=numeric_increase_bad,
465
- hint={},
466
- ),
467
- )
468
-
469
- # -------------------------------------------------------------------------
470
- # >>> Complement (Spec-level): scenario_comp
471
- # NOTE: match the new StateDiff field names you mentioned:
472
- # - scenario_comp_present
473
- # - num_comp_regions_total
474
- # -------------------------------------------------------------------------
475
- comp_old = getattr(spec_old, "scenario_comp", None)
476
- comp_new = getattr(spec_new, "scenario_comp", None)
477
-
478
- old_regions = _comp_regions(comp_old)
479
- new_regions = _comp_regions(comp_new)
480
-
481
- # Presence: having a computed complement is generally "good" (enables coverage reasoning).
482
- _emit(
483
- "scenario_comp_present",
484
- ValueDiff[bool](
485
- label="Scenario complement is present",
486
- before=(comp_old is not None),
487
- after=(comp_new is not None),
488
- comp_func=bool_true_is_good,
489
- hint={},
490
- ),
491
- )
492
-
493
- # Region count: smaller complement is better (more coverage), so increases are "bad".
494
- _emit(
495
- "num_comp_regions_total",
496
- ValueDiff[int](
497
- label="Complement region count",
498
- before=len(old_regions),
499
- after=len(new_regions),
500
- comp_func=numeric_increase_bad,
501
- hint={},
502
- ),
503
- )
504
-
505
- # -------------------------------------------------------------------------
506
- # >>> Traces
507
- # -------------------------------------------------------------------------
508
- _emit(
509
- "num_test_traces_total",
510
- ValueDiff[int](
511
- label="Number of test traces",
512
- before=art_old.num_test_traces_total,
513
- after=art_new.num_test_traces_total,
514
- comp_func=numeric_increase_good,
515
- hint={},
516
- ),
517
- )
518
-
519
- _emit(
520
- "num_test_traces_logic_good",
521
- ValueDiff[int](
522
- label="Number of test traces logically valid",
523
- before=art_old.num_test_traces_logic_good,
524
- after=art_new.num_test_traces_logic_good,
525
- comp_func=numeric_increase_good,
526
- hint={},
527
- ),
528
- )
529
-
530
- _emit(
531
- "num_test_traces_matched",
532
- ValueDiff[int](
533
- label="Number of test traces matched",
534
- before=art_old.num_test_traces_matched,
535
- after=art_new.num_test_traces_matched,
536
- comp_func=numeric_increase_good,
537
- hint={},
538
- ),
539
- )
540
-
541
- _emit(
542
- "num_log_traces_total",
543
- ValueDiff[int](
544
- label="Number of log traces",
545
- before=art_old.num_log_traces_total,
546
- after=art_new.num_log_traces_total,
547
- comp_func=numeric_increase_good,
548
- hint={},
549
- ),
550
- )
551
-
552
- _emit(
553
- "num_log_traces_logic_good",
554
- ValueDiff[int](
555
- label="Number of log traces logically valid",
556
- before=art_old.num_log_traces_logic_good,
557
- after=art_new.num_log_traces_logic_good,
558
- comp_func=numeric_increase_good,
559
- hint={},
560
- ),
561
- )
562
-
563
- _emit(
564
- "num_log_traces_matched",
565
- ValueDiff[int](
566
- label="Number of log traces matched",
567
- before=art_old.num_log_traces_matched,
568
- after=art_new.num_log_traces_matched,
569
- comp_func=numeric_increase_good,
570
- hint={},
571
- ),
572
- )
573
-
574
- return StateDiff(**dict(vals))
@@ -1,13 +0,0 @@
1
- #
2
- # Imandra Inc.
3
- #
4
- # speclogician/state/recommendation.py
5
- #
6
-
7
- from pydantic import BaseModel
8
- from typing import Literal
9
-
10
- class Recommendation(BaseModel):
11
- text: str
12
- kind: Literal["error", "warning", "next", "info"] = "next"
13
- priority: int = 50 # lower = higher priority