speclogician 0.0.0b1__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 (139) hide show
  1. speclogician/__init__.py +0 -0
  2. speclogician/commands/__init__.py +15 -0
  3. speclogician/commands/cmd_ch.py +616 -0
  4. speclogician/commands/cmd_find.py +256 -0
  5. speclogician/commands/cmd_view.py +202 -0
  6. speclogician/commands/runner.py +149 -0
  7. speclogician/commands/utils.py +101 -0
  8. speclogician/data/__init__.py +0 -0
  9. speclogician/data/artifact.py +63 -0
  10. speclogician/data/container.py +402 -0
  11. speclogician/data/mapping.py +88 -0
  12. speclogician/data/refs.py +24 -0
  13. speclogician/data/traces.py +26 -0
  14. speclogician/demos/.DS_Store +0 -0
  15. speclogician/demos/cmd_demo.py +278 -0
  16. speclogician/demos/loader.py +135 -0
  17. speclogician/demos/model.py +27 -0
  18. speclogician/demos/runner.py +51 -0
  19. speclogician/logic/__init__.py +11 -0
  20. speclogician/logic/api/__init__.py +29 -0
  21. speclogician/logic/api/client.py +606 -0
  22. speclogician/logic/api/decomp.py +67 -0
  23. speclogician/logic/api/scenario.py +102 -0
  24. speclogician/logic/api/traces.py +59 -0
  25. speclogician/logic/lib/__init__.py +19 -0
  26. speclogician/logic/lib/complement.py +107 -0
  27. speclogician/logic/lib/domain_model.py +59 -0
  28. speclogician/logic/lib/predicates.py +151 -0
  29. speclogician/logic/lib/scenarios.py +369 -0
  30. speclogician/logic/lib/traces.py +114 -0
  31. speclogician/logic/lib/transitions.py +104 -0
  32. speclogician/logic/main.py +246 -0
  33. speclogician/logic/strings.py +194 -0
  34. speclogician/logic/utils.py +135 -0
  35. speclogician/main.py +139 -0
  36. speclogician/modeling/__init__.py +31 -0
  37. speclogician/modeling/complement.py +104 -0
  38. speclogician/modeling/component.py +71 -0
  39. speclogician/modeling/conflict.py +26 -0
  40. speclogician/modeling/domain.py +349 -0
  41. speclogician/modeling/predicates.py +59 -0
  42. speclogician/modeling/scenario.py +162 -0
  43. speclogician/modeling/spec.py +306 -0
  44. speclogician/modeling/spec_stats.py +39 -0
  45. speclogician/presentation/__init__.py +0 -0
  46. speclogician/presentation/api.py +244 -0
  47. speclogician/presentation/builders/__init__.py +0 -0
  48. speclogician/presentation/builders/_links.py +44 -0
  49. speclogician/presentation/builders/container.py +53 -0
  50. speclogician/presentation/builders/data_artifact.py +42 -0
  51. speclogician/presentation/builders/domain.py +54 -0
  52. speclogician/presentation/builders/instances_list.py +38 -0
  53. speclogician/presentation/builders/predicate.py +51 -0
  54. speclogician/presentation/builders/recommendations.py +41 -0
  55. speclogician/presentation/builders/scenario.py +41 -0
  56. speclogician/presentation/builders/scenario_complement.py +82 -0
  57. speclogician/presentation/builders/smart_find.py +39 -0
  58. speclogician/presentation/builders/spec.py +39 -0
  59. speclogician/presentation/builders/state_diff.py +150 -0
  60. speclogician/presentation/builders/state_instance.py +42 -0
  61. speclogician/presentation/builders/state_instance_summary.py +84 -0
  62. speclogician/presentation/builders/trace.py +58 -0
  63. speclogician/presentation/ctx.py +38 -0
  64. speclogician/presentation/models/__init__.py +0 -0
  65. speclogician/presentation/models/container.py +44 -0
  66. speclogician/presentation/models/data_artifact.py +33 -0
  67. speclogician/presentation/models/domain.py +50 -0
  68. speclogician/presentation/models/instances_list.py +23 -0
  69. speclogician/presentation/models/predicate.py +60 -0
  70. speclogician/presentation/models/recommendations.py +34 -0
  71. speclogician/presentation/models/scenario.py +31 -0
  72. speclogician/presentation/models/scenario_complement.py +40 -0
  73. speclogician/presentation/models/smart_find.py +34 -0
  74. speclogician/presentation/models/spec.py +32 -0
  75. speclogician/presentation/models/state_diff.py +34 -0
  76. speclogician/presentation/models/state_instance.py +31 -0
  77. speclogician/presentation/models/state_instance_summary.py +102 -0
  78. speclogician/presentation/models/trace.py +42 -0
  79. speclogician/presentation/preview/__init__.py +13 -0
  80. speclogician/presentation/preview/cli.py +50 -0
  81. speclogician/presentation/preview/fixtures/__init__.py +205 -0
  82. speclogician/presentation/preview/fixtures/artifact_container.py +150 -0
  83. speclogician/presentation/preview/fixtures/data_artifact.py +144 -0
  84. speclogician/presentation/preview/fixtures/domain_model.py +162 -0
  85. speclogician/presentation/preview/fixtures/instances_list.py +162 -0
  86. speclogician/presentation/preview/fixtures/predicate.py +184 -0
  87. speclogician/presentation/preview/fixtures/scenario.py +84 -0
  88. speclogician/presentation/preview/fixtures/scenario_complement.py +81 -0
  89. speclogician/presentation/preview/fixtures/smart_find.py +140 -0
  90. speclogician/presentation/preview/fixtures/spec.py +95 -0
  91. speclogician/presentation/preview/fixtures/state_diff.py +158 -0
  92. speclogician/presentation/preview/fixtures/state_instance.py +128 -0
  93. speclogician/presentation/preview/fixtures/state_instance_summary.py +80 -0
  94. speclogician/presentation/preview/fixtures/trace.py +206 -0
  95. speclogician/presentation/preview/registry.py +42 -0
  96. speclogician/presentation/renderers/__init__.py +24 -0
  97. speclogician/presentation/renderers/container.py +136 -0
  98. speclogician/presentation/renderers/data_artifact.py +144 -0
  99. speclogician/presentation/renderers/domain.py +123 -0
  100. speclogician/presentation/renderers/instances_list.py +120 -0
  101. speclogician/presentation/renderers/predicate.py +180 -0
  102. speclogician/presentation/renderers/recommendations.py +90 -0
  103. speclogician/presentation/renderers/scenario.py +94 -0
  104. speclogician/presentation/renderers/scenario_complement.py +59 -0
  105. speclogician/presentation/renderers/smart_find.py +307 -0
  106. speclogician/presentation/renderers/spec.py +105 -0
  107. speclogician/presentation/renderers/state_diff.py +102 -0
  108. speclogician/presentation/renderers/state_instance.py +82 -0
  109. speclogician/presentation/renderers/state_instance_summary.py +143 -0
  110. speclogician/presentation/renderers/trace.py +122 -0
  111. speclogician/py.typed +0 -0
  112. speclogician/shell/app.py +170 -0
  113. speclogician/shell/shell_ch.py +263 -0
  114. speclogician/shell/shell_view.py +153 -0
  115. speclogician/state/__init__.py +0 -0
  116. speclogician/state/change.py +428 -0
  117. speclogician/state/change_result.py +32 -0
  118. speclogician/state/diff.py +191 -0
  119. speclogician/state/inst.py +574 -0
  120. speclogician/state/recommendation.py +13 -0
  121. speclogician/state/recommender.py +577 -0
  122. speclogician/state/state.py +465 -0
  123. speclogician/state/state_stats.py +133 -0
  124. speclogician/tui/__init__.py +0 -0
  125. speclogician/tui/app.py +257 -0
  126. speclogician/tui/app.tcss +160 -0
  127. speclogician/tui/demo.py +45 -0
  128. speclogician/tui/images/speclogician-full.png +0 -0
  129. speclogician/tui/images/speclogician-minimal.png +0 -0
  130. speclogician/tui/main_screen.py +454 -0
  131. speclogician/tui/splash_screen.py +51 -0
  132. speclogician/tui/stats_screen.py +125 -0
  133. speclogician/utils/__init__.py +78 -0
  134. speclogician/utils/load.py +166 -0
  135. speclogician/utils/prompt.md +325 -0
  136. speclogician/utils/testing.py +151 -0
  137. speclogician-0.0.0b1.dist-info/METADATA +116 -0
  138. speclogician-0.0.0b1.dist-info/RECORD +139 -0
  139. speclogician-0.0.0b1.dist-info/WHEEL +4 -0
@@ -0,0 +1,162 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/preview/fixtures/domain_model.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.modeling.domain import BaseStatus
10
+ from speclogician.presentation.models.domain import (
11
+ DomainCountsPM,
12
+ DomainCodePM,
13
+ DomainModelPM,
14
+ )
15
+ from speclogician.presentation.ctx import RenderCtx
16
+
17
+
18
+ # -----------------------------------------------------------------------------
19
+ # PM builders (variants)
20
+ # -----------------------------------------------------------------------------
21
+
22
+ def make_domain_model_pm(ctx: RenderCtx, variant: str = "typical") -> DomainModelPM:
23
+ v = (variant or "typical").lower()
24
+ if v == "minimal":
25
+ return minimal_domain_model_pm()
26
+ if v == "edge":
27
+ return edge_domain_model_pm()
28
+ return typical_domain_model_pm()
29
+
30
+
31
+ def minimal_domain_model_pm() -> DomainModelPM:
32
+ # Minimal = almost empty, base unknown, no code.
33
+ counts = DomainCountsPM(
34
+ base_status=BaseStatus.UNKNOWN,
35
+ base_has_state=False,
36
+ base_has_action=False,
37
+ num_state_preds_total=0,
38
+ num_state_preds_valid_logic=0,
39
+ num_state_preds_matched=0,
40
+ num_action_preds_total=0,
41
+ num_action_preds_valid_logic=0,
42
+ num_action_preds_matched=0,
43
+ num_preds_total=0,
44
+ num_preds_valid_logic=0,
45
+ num_preds_matched=0,
46
+ num_trans_total=0,
47
+ num_trans_valid_logic=0,
48
+ num_trans_matched=0,
49
+ )
50
+ code = DomainCodePM(
51
+ base="",
52
+ state_preds_iml="",
53
+ action_preds_iml="",
54
+ transitions_iml="",
55
+ )
56
+ return DomainModelPM(counts=counts, code=code)
57
+
58
+
59
+ def typical_domain_model_pm() -> DomainModelPM:
60
+ # Typical = sane + readable, some matched/valid numbers.
61
+ counts = DomainCountsPM(
62
+ base_status=BaseStatus.VALID,
63
+ base_has_state=True,
64
+ base_has_action=True,
65
+ # state preds
66
+ num_state_preds_total=4,
67
+ num_state_preds_valid_logic=3,
68
+ num_state_preds_matched=2,
69
+ # action preds
70
+ num_action_preds_total=3,
71
+ num_action_preds_valid_logic=3,
72
+ num_action_preds_matched=1,
73
+ # totals
74
+ num_preds_total=7,
75
+ num_preds_valid_logic=6,
76
+ num_preds_matched=3,
77
+ # transitions
78
+ num_trans_total=2,
79
+ num_trans_valid_logic=2,
80
+ num_trans_matched=1,
81
+ )
82
+ code = DomainCodePM(
83
+ base=(
84
+ "type state = { x : int }\n"
85
+ "type action = int\n"
86
+ ),
87
+ state_preds_iml=(
88
+ "let pred_eq_ten (s: state) = s.x = 10\n"
89
+ "let pred_lt_ten (s: state) = s.x < 10\n"
90
+ "let pred_gt_zero (s: state) = s.x > 0\n"
91
+ ),
92
+ action_preds_iml=(
93
+ "let act_gt_x (s: state) (a: action) = a > s.x\n"
94
+ "let act_eq_zero (_s: state) (a: action) = a = 0\n"
95
+ ),
96
+ transitions_iml=(
97
+ "let step1 (s: state) (a: action) : state = { x = s.x + a }\n"
98
+ "let step2 (_s: state) (a: action) : state = { x = a }\n"
99
+ ),
100
+ )
101
+ return DomainModelPM(counts=counts, code=code)
102
+
103
+
104
+ def edge_domain_model_pm() -> DomainModelPM:
105
+ # Edge = stress the renderer: long code, weird counts, invalid base.
106
+ counts = DomainCountsPM(
107
+ base_status=BaseStatus.INVALID_IML,
108
+ base_has_state=True,
109
+ base_has_action=False,
110
+ # state preds
111
+ num_state_preds_total=12,
112
+ num_state_preds_valid_logic=7,
113
+ num_state_preds_matched=0,
114
+ # action preds
115
+ num_action_preds_total=0,
116
+ num_action_preds_valid_logic=0,
117
+ num_action_preds_matched=0,
118
+ # totals
119
+ num_preds_total=12,
120
+ num_preds_valid_logic=7,
121
+ num_preds_matched=0,
122
+ # transitions
123
+ num_trans_total=5,
124
+ num_trans_valid_logic=2,
125
+ num_trans_matched=0,
126
+ )
127
+ code = DomainCodePM(
128
+ base=(
129
+ "type state = {\n"
130
+ " x : int;\n"
131
+ " (* missing action type on purpose to simulate INVALID base *)\n"
132
+ "}\n"
133
+ ),
134
+ state_preds_iml=(
135
+ "(* many predicates, including a long one to wrap *)\n"
136
+ + "\n".join(
137
+ f"let pred_{i} (s: state) = s.x = {i}"
138
+ for i in range(1, 10)
139
+ )
140
+ + "\n"
141
+ "let pred_very_long (s: state) = "
142
+ "(s.x > 0 && s.x < 1000000 && (s.x mod 3 = 0 || s.x mod 5 = 0))\n"
143
+ ),
144
+ action_preds_iml="", # missing because base_has_action=False
145
+ transitions_iml=(
146
+ "(* some transitions, one intentionally sketchy *)\n"
147
+ "let step_ok (s: state) (_a: int) : state = { x = s.x }\n"
148
+ "let step_weird (s: state) (a: int) : state = { x = s.x + (a + 1) }\n"
149
+ "(* malformed-ish snippet *)\n"
150
+ "let step_bad (s: state) (a: int) : state = { x = s.x + }\n"
151
+ ),
152
+ )
153
+ return DomainModelPM(counts=counts, code=code)
154
+
155
+
156
+ # -----------------------------------------------------------------------------
157
+ # renderer adapter (matches other fixtures style)
158
+ # -----------------------------------------------------------------------------
159
+
160
+ def render(pm: DomainModelPM, ctx: RenderCtx):
161
+ from speclogician.presentation.renderers.domain import render_domain_model
162
+ return render_domain_model(pm, ctx=ctx)
@@ -0,0 +1,162 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/preview/fixtures/instances_list.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.instances_list import (
11
+ InstancesListPM,
12
+ InstancesListRowPM,
13
+ )
14
+ from speclogician.presentation.models.state_instance_summary import StateInstanceSummaryPM
15
+ from speclogician.utils import IMLValidity
16
+ from speclogician.modeling.domain import BaseStatus
17
+
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # helpers
21
+ # -----------------------------------------------------------------------------
22
+
23
+ def _summary(
24
+ *,
25
+ created_at: str,
26
+ num_changes: int,
27
+ base_status: BaseStatus = BaseStatus.UNKNOWN,
28
+ base_has_state: bool = False,
29
+ base_has_action: bool = False,
30
+ num_preds_total: int = 0,
31
+ num_preds_matched: int = 0,
32
+ num_trans_total: int = 0,
33
+ num_trans_valid_logic: int = 0,
34
+ num_sc_total: int = 0,
35
+ num_sc_missing: int = 0,
36
+ num_sc_inconsistent: int = 0,
37
+ num_sc_conflicted: int = 0,
38
+ num_test_traces_total: int = 0,
39
+ num_test_traces_logic_good: int = 0,
40
+ num_log_traces_total: int = 0,
41
+ num_log_traces_logic_good: int = 0,
42
+ ) -> StateInstanceSummaryPM:
43
+ return StateInstanceSummaryPM(
44
+ created_at=created_at,
45
+ num_changes=num_changes,
46
+ base_status=base_status,
47
+ base_has_state=base_has_state,
48
+ base_has_action=base_has_action,
49
+ num_preds_total=num_preds_total,
50
+ num_preds_matched=num_preds_matched,
51
+ num_trans_total=num_trans_total,
52
+ num_trans_valid_logic=num_trans_valid_logic,
53
+ num_sc_total=num_sc_total,
54
+ num_sc_missing=num_sc_missing,
55
+ num_sc_inconsistent=num_sc_inconsistent,
56
+ num_sc_conflicted=num_sc_conflicted,
57
+ num_test_traces_total=num_test_traces_total,
58
+ num_test_traces_logic_good=num_test_traces_logic_good,
59
+ num_log_traces_total=num_log_traces_total,
60
+ num_log_traces_logic_good=num_log_traces_logic_good,
61
+ )
62
+
63
+
64
+ # -----------------------------------------------------------------------------
65
+ # fixtures
66
+ # -----------------------------------------------------------------------------
67
+
68
+ def minimal_instances_list_pm() -> InstancesListPM:
69
+ return InstancesListPM(rows=[])
70
+
71
+
72
+ def typical_instances_list_pm() -> InstancesListPM:
73
+ return InstancesListPM(
74
+ rows=[
75
+ InstancesListRowPM(
76
+ state_label="latest",
77
+ changes="DomainModelBaseEdit, PredicateAdd",
78
+ summary=_summary(
79
+ created_at="2026-01-09T10:12:00Z",
80
+ num_changes=2,
81
+ base_status=BaseStatus.VALID,
82
+ base_has_state=True,
83
+ base_has_action=True,
84
+ num_preds_total=12,
85
+ num_preds_matched=7,
86
+ num_trans_total=4,
87
+ num_trans_valid_logic=3,
88
+ num_sc_total=9,
89
+ num_sc_missing=1,
90
+ num_test_traces_total=8,
91
+ num_test_traces_logic_good=6,
92
+ num_log_traces_total=4,
93
+ num_log_traces_logic_good=4,
94
+ ),
95
+ ),
96
+ InstancesListRowPM(
97
+ state_label="1",
98
+ changes="PredicateEdit",
99
+ summary=_summary(
100
+ created_at="2026-01-09T09:55:10Z",
101
+ num_changes=1,
102
+ base_status=BaseStatus.VALID,
103
+ base_has_state=True,
104
+ base_has_action=True,
105
+ num_preds_total=11,
106
+ num_preds_matched=6,
107
+ num_trans_total=4,
108
+ num_trans_valid_logic=3,
109
+ num_sc_total=8,
110
+ num_sc_missing=0,
111
+ num_test_traces_total=7,
112
+ num_test_traces_logic_good=6,
113
+ num_log_traces_total=3,
114
+ num_log_traces_logic_good=3,
115
+ ),
116
+ ),
117
+ ]
118
+ )
119
+
120
+
121
+ def edge_instances_list_pm() -> InstancesListPM:
122
+ return InstancesListPM(
123
+ rows=[
124
+ InstancesListRowPM(
125
+ state_label="latest",
126
+ changes="",
127
+ summary=_summary(
128
+ created_at="2026-01-09T10:12:00.123456Z",
129
+ num_changes=0,
130
+ base_status=BaseStatus.UNKNOWN,
131
+ base_has_state=False,
132
+ base_has_action=False,
133
+ num_preds_total=0,
134
+ num_preds_matched=0,
135
+ num_trans_total=0,
136
+ num_trans_valid_logic=0,
137
+ num_sc_total=0,
138
+ num_sc_missing=0,
139
+ num_test_traces_total=0,
140
+ num_test_traces_logic_good=0,
141
+ num_log_traces_total=0,
142
+ num_log_traces_logic_good=0,
143
+ ),
144
+ )
145
+ ]
146
+ )
147
+
148
+ def make_instances_list_pm(ctx: RenderCtx, variant: str = "typical") -> InstancesListPM:
149
+ v = (variant or "typical").strip().lower()
150
+
151
+ if v == "minimal":
152
+ return minimal_instances_list_pm()
153
+
154
+ if v == "edge":
155
+ return edge_instances_list_pm()
156
+
157
+ return typical_instances_list_pm()
158
+
159
+
160
+ def render(pm: InstancesListPM, ctx : RenderCtx):
161
+ from speclogician.presentation.renderers.instances_list import render_instances_list
162
+ return render_instances_list(pm, ctx=ctx)
@@ -0,0 +1,184 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/preview/fixtures/predicate.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime, timezone
10
+ from speclogician.presentation.ctx import RenderCtx
11
+ from speclogician.presentation.models.predicate import (
12
+ PredicatePM,
13
+ TransitionPM,
14
+ LinkedArtifactsPM,
15
+ )
16
+ from speclogician.modeling.predicates import PredicateType
17
+ from speclogician.utils import IMLValidity
18
+
19
+
20
+ NOW = datetime(2026, 1, 9, 10, 12, tzinfo=timezone.utc)
21
+
22
+
23
+ # -----------------------------------------------------------------------------
24
+ # Shared helpers
25
+ # -----------------------------------------------------------------------------
26
+
27
+ def _minimal_links() -> LinkedArtifactsPM:
28
+ return LinkedArtifactsPM()
29
+
30
+
31
+ def _typical_links() -> LinkedArtifactsPM:
32
+ return LinkedArtifactsPM(
33
+ total=4,
34
+ test_traces=2,
35
+ log_traces=1,
36
+ doc_refs=1,
37
+ src_code_refs=0,
38
+ unresolved_ids=0,
39
+ art_ids=["tt-1", "tt-2", "log-1", "doc-1"],
40
+ )
41
+
42
+
43
+ def _edge_links() -> LinkedArtifactsPM:
44
+ return LinkedArtifactsPM(
45
+ total=12,
46
+ test_traces=5,
47
+ log_traces=3,
48
+ doc_refs=2,
49
+ src_code_refs=1,
50
+ unresolved_ids=1,
51
+ art_ids=[f"art-{i}" for i in range(12)],
52
+ )
53
+
54
+
55
+ # -----------------------------------------------------------------------------
56
+ # PredicatePM variants
57
+ # -----------------------------------------------------------------------------
58
+
59
+ def _predicate_minimal() -> PredicatePM:
60
+ return PredicatePM(
61
+ kind="state_predicate",
62
+ comp_id="pred-min",
63
+ name="",
64
+ last_updated=NOW,
65
+ pred_type=PredicateType.STATE,
66
+ signature="",
67
+ src_code="",
68
+ iml="",
69
+ is_iml_valid=IMLValidity.UNKNOWN,
70
+ iml_error=None,
71
+ links=_minimal_links(),
72
+ )
73
+
74
+
75
+ def _predicate_typical() -> PredicatePM:
76
+ return PredicatePM(
77
+ kind="state_predicate",
78
+ comp_id="pred-001",
79
+ name="pred_eq_ten",
80
+ last_updated=NOW,
81
+ pred_type=PredicateType.STATE,
82
+ signature="state -> bool",
83
+ src_code="s.x = 10",
84
+ iml="let pred_eq_ten (s: state) = s.x = 10",
85
+ is_iml_valid=IMLValidity.VALID,
86
+ iml_error=None,
87
+ links=_typical_links(),
88
+ )
89
+
90
+
91
+ def _predicate_edge() -> PredicatePM:
92
+ return PredicatePM(
93
+ kind="action_predicate",
94
+ comp_id="pred-edge-999",
95
+ name="pred_complex_action_condition_with_long_name",
96
+ last_updated=NOW,
97
+ pred_type=PredicateType.ACTION,
98
+ signature="state -> action -> bool",
99
+ src_code="a.qty > s.max_qty &&\n(a.price > 0 || a.kind = Market)",
100
+ iml=(
101
+ "let pred_complex_action_condition_with_long_name (s: state) (a: action) =\n"
102
+ " a.qty > s.max_qty && (a.price > 0 || a.kind = Market)"
103
+ ),
104
+ is_iml_valid=IMLValidity.INVALID,
105
+ iml_error="Type error: cannot compare int and option<int> in expression a.price > 0",
106
+ links=_edge_links(),
107
+ )
108
+
109
+
110
+ def make_predicate_pm(ctx: RenderCtx, variant: str = "typical") -> PredicatePM:
111
+ v = (variant or "typical").strip().lower()
112
+ if v == "minimal":
113
+ return _predicate_minimal()
114
+ if v == "edge":
115
+ return _predicate_edge()
116
+ return _predicate_typical()
117
+
118
+ def render_predicate(pm: PredicatePM, ctx: RenderCtx):
119
+ from speclogician.presentation.renderers.predicate import render_predicate
120
+ return render_predicate(pm, ctx=ctx)
121
+
122
+ # -----------------------------------------------------------------------------
123
+ # TransitionPM variants
124
+ # -----------------------------------------------------------------------------
125
+
126
+ def _transition_minimal() -> TransitionPM:
127
+ return TransitionPM(
128
+ kind="transition",
129
+ comp_id="trans-min",
130
+ name="",
131
+ last_updated=NOW,
132
+ signature="",
133
+ src_code="",
134
+ iml="",
135
+ is_iml_valid=IMLValidity.UNKNOWN,
136
+ iml_error=None,
137
+ links=_minimal_links(),
138
+ )
139
+
140
+
141
+ def _transition_typical() -> TransitionPM:
142
+ return TransitionPM(
143
+ kind="transition",
144
+ comp_id="trans-001",
145
+ name="step1",
146
+ last_updated=NOW,
147
+ signature="state -> action -> state",
148
+ src_code="{ x = s.x + a }",
149
+ iml="let step1 (s: state) (a: action) : state =\n { x = s.x + a }",
150
+ is_iml_valid=IMLValidity.VALID,
151
+ iml_error=None,
152
+ links=_typical_links(),
153
+ )
154
+
155
+
156
+ def _transition_edge() -> TransitionPM:
157
+ return TransitionPM(
158
+ kind="transition",
159
+ comp_id="trans-edge-888",
160
+ name="step_with_very_long_and_descriptive_name",
161
+ last_updated=NOW,
162
+ signature="state -> action -> state",
163
+ src_code="{ x = if a > 0 then s.x + a else false }",
164
+ iml=(
165
+ "let step_with_very_long_and_descriptive_name (s: state) (a: action) : state =\n"
166
+ " { x = if a > 0 then s.x + a else false }"
167
+ ),
168
+ is_iml_valid=IMLValidity.INVALID,
169
+ iml_error="Type error: expected int, got bool in field x",
170
+ links=_edge_links(),
171
+ )
172
+
173
+
174
+ def make_transition_pm(ctx: RenderCtx, variant: str = "typical") -> TransitionPM:
175
+ v = (variant or "typical").strip().lower()
176
+ if v == "minimal":
177
+ return _transition_minimal()
178
+ if v == "edge":
179
+ return _transition_edge()
180
+ return _transition_typical()
181
+
182
+ def render_transition(pm: TransitionPM, ctx: RenderCtx):
183
+ from speclogician.presentation.renderers.predicate import render_transition
184
+ return render_transition(pm, ctx=ctx)
@@ -0,0 +1,84 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/preview/fixtures/scenario.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.scenario import (
11
+ ScenarioPM,
12
+ ScenarioStatusPM,
13
+ )
14
+
15
+ # -----------------------------------------------------------------------------
16
+ # variants
17
+ # -----------------------------------------------------------------------------
18
+
19
+ def _minimal() -> ScenarioPM:
20
+ return ScenarioPM(
21
+ comp_id="sc:0000",
22
+ name="",
23
+ status=ScenarioStatusPM(kind="valid"),
24
+ given=[],
25
+ when=[],
26
+ then=[],
27
+ given_preds_consistent=True,
28
+ when_preds_consistent=True,
29
+ all_preds_consistent=True,
30
+ )
31
+
32
+
33
+ def _typical() -> ScenarioPM:
34
+ return ScenarioPM(
35
+ comp_id="sc:0001",
36
+ name="scenario_1",
37
+ status=ScenarioStatusPM(kind="valid"),
38
+ given=["pred_eq_ten", "pred_ne_ten"],
39
+ when=["act_gt_x"],
40
+ then=["step1"],
41
+ given_preds_consistent=True,
42
+ when_preds_consistent=True,
43
+ all_preds_consistent=True,
44
+ )
45
+
46
+
47
+ def _edge() -> ScenarioPM:
48
+ return ScenarioPM(
49
+ comp_id="sc:9999",
50
+ name="scenario_" + ("very_long_name_" * 6) + "end",
51
+ status=ScenarioStatusPM(
52
+ kind="missing",
53
+ missing_preds=["pred_missing_1", "pred_missing_2"],
54
+ missing_trans=["step_missing"],
55
+ ),
56
+ given=["pred_eq_ten", "pred_missing_1"],
57
+ when=["act_gt_x", "act_weird_pred"],
58
+ then=["step1", "step_missing"],
59
+ given_preds_consistent=False,
60
+ when_preds_consistent=False,
61
+ all_preds_consistent=False,
62
+ )
63
+
64
+
65
+ # -----------------------------------------------------------------------------
66
+ # public fixture entrypoint (matches PreviewSpec.make)
67
+ # -----------------------------------------------------------------------------
68
+
69
+ def make_scenario_pm(ctx: RenderCtx, variant: str = "typical") -> ScenarioPM:
70
+ v = (variant or "typical").lower().strip()
71
+ if v == "minimal":
72
+ return _minimal()
73
+ if v == "edge":
74
+ return _edge()
75
+ return _typical()
76
+
77
+
78
+ # -----------------------------------------------------------------------------
79
+ # renderer adapter (matches other fixtures style)
80
+ # -----------------------------------------------------------------------------
81
+
82
+ def render(pm: ScenarioPM, ctx: RenderCtx):
83
+ from speclogician.presentation.renderers.scenario import render_scenario
84
+ return render_scenario(pm, ctx=ctx)
@@ -0,0 +1,81 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/preview/fixtures/scenario_complement.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.models.scenario_complement import (
10
+ ScenarioComplementPM,
11
+ ScenarioComplementRegionPM,
12
+ )
13
+
14
+
15
+ def make_scenario_complement_pm(variant: str = "typical") -> ScenarioComplementPM:
16
+ v = (variant or "typical").strip().lower()
17
+
18
+ if v == "empty":
19
+ return ScenarioComplementPM(
20
+ count_regions=0,
21
+ regions_preview=[],
22
+ regions_all=None,
23
+ )
24
+
25
+ if v == "edge":
26
+ preview = [
27
+ ScenarioComplementRegionPM(
28
+ fp="a3f91c0d12e9ab01",
29
+ num_constraints=12,
30
+ invariant="x >= 0",
31
+ model="{x = 0}",
32
+ model_eval="ok",
33
+ constraints_preview=["x >= 0", "y = 1", "z != 3"],
34
+ ),
35
+ ScenarioComplementRegionPM(
36
+ fp="bb92e44caa10fe88",
37
+ num_constraints=7,
38
+ invariant="balance < limit",
39
+ model="{balance = 5, limit = 10}",
40
+ model_eval="ok",
41
+ constraints_preview=["balance < limit", "limit = 10"],
42
+ ),
43
+ ScenarioComplementRegionPM(
44
+ fp="0f12caa903bc9e77",
45
+ num_constraints=3,
46
+ invariant=None,
47
+ model=None,
48
+ model_eval=None,
49
+ constraints_preview=["flag = true"],
50
+ ),
51
+ ]
52
+ return ScenarioComplementPM(
53
+ count_regions=42,
54
+ regions_preview=preview,
55
+ regions_all=None,
56
+ )
57
+
58
+ # typical
59
+ preview = [
60
+ ScenarioComplementRegionPM(
61
+ fp="e19caa903bc9e771",
62
+ num_constraints=4,
63
+ invariant="state_ok",
64
+ model="{s = {...}}",
65
+ model_eval="ok",
66
+ constraints_preview=["state_ok", "x != 0"],
67
+ ),
68
+ ScenarioComplementRegionPM(
69
+ fp="a2ff10d12e9ab013",
70
+ num_constraints=2,
71
+ invariant=None,
72
+ model="{x = 1}",
73
+ model_eval="ok",
74
+ constraints_preview=["x = 1"],
75
+ ),
76
+ ]
77
+ return ScenarioComplementPM(
78
+ count_regions=7,
79
+ regions_preview=preview,
80
+ regions_all=None,
81
+ )