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.
- speclogician/agent/funcs.py +29 -0
- speclogician/cmd/agent_cmd.py +89 -0
- speclogician/cmd/data_cmd.py +24 -0
- speclogician/cmd/model_cmd.py +42 -0
- speclogician/cmd/overlay_cmd.py +30 -0
- speclogician/cmd/scenario_cmd.py +61 -0
- speclogician/cmd/state_cmd.py +52 -0
- speclogician/data/artifact.py +8 -50
- speclogician/data/container.py +18 -384
- speclogician/data/mapping.py +18 -17
- speclogician/data/refs.py +12 -11
- speclogician/data/reports.py +11 -0
- speclogician/data/traces.py +15 -6
- speclogician/llms/llmtools.py +102 -0
- speclogician/llms/overlay.py +264 -0
- speclogician/main.py +36 -102
- speclogician/modeling/__init__.py +0 -31
- speclogician/modeling/component.py +4 -60
- speclogician/modeling/conflict.py +5 -19
- speclogician/modeling/domain.py +93 -280
- speclogician/modeling/model.py +206 -0
- speclogician/modeling/predicates.py +20 -22
- speclogician/modeling/report.py +33 -0
- speclogician/modeling/scenario.py +119 -87
- speclogician/sl_cmd.py +76 -0
- speclogician/state/change.py +98 -378
- speclogician/state/state.py +183 -399
- speclogician/tui/box.tcss +10 -0
- speclogician/tui/tui.py +131 -0
- speclogician/utils/__init__.py +1 -70
- speclogician/utils/imx.py +195 -0
- speclogician/utils/load.py +25 -147
- speclogician/utils/prompt.md +1 -325
- speclogician-0.0.0.dev1.dist-info/METADATA +21 -0
- speclogician-0.0.0.dev1.dist-info/RECORD +43 -0
- speclogician/commands/__init__.py +0 -15
- speclogician/commands/cmd_ch.py +0 -616
- speclogician/commands/cmd_find.py +0 -256
- speclogician/commands/cmd_view.py +0 -202
- speclogician/commands/runner.py +0 -149
- speclogician/commands/utils.py +0 -101
- speclogician/demos/.DS_Store +0 -0
- speclogician/demos/cmd_demo.py +0 -278
- speclogician/demos/loader.py +0 -135
- speclogician/demos/model.py +0 -27
- speclogician/demos/runner.py +0 -51
- speclogician/logic/__init__.py +0 -11
- speclogician/logic/api/__init__.py +0 -29
- speclogician/logic/api/client.py +0 -606
- speclogician/logic/api/decomp.py +0 -67
- speclogician/logic/api/scenario.py +0 -102
- speclogician/logic/api/traces.py +0 -59
- speclogician/logic/lib/__init__.py +0 -19
- speclogician/logic/lib/complement.py +0 -107
- speclogician/logic/lib/domain_model.py +0 -59
- speclogician/logic/lib/predicates.py +0 -151
- speclogician/logic/lib/scenarios.py +0 -369
- speclogician/logic/lib/traces.py +0 -114
- speclogician/logic/lib/transitions.py +0 -104
- speclogician/logic/main.py +0 -246
- speclogician/logic/strings.py +0 -194
- speclogician/logic/utils.py +0 -135
- speclogician/modeling/complement.py +0 -104
- speclogician/modeling/spec.py +0 -306
- speclogician/modeling/spec_stats.py +0 -39
- speclogician/presentation/api.py +0 -244
- speclogician/presentation/builders/_links.py +0 -44
- speclogician/presentation/builders/container.py +0 -53
- speclogician/presentation/builders/data_artifact.py +0 -42
- speclogician/presentation/builders/domain.py +0 -54
- speclogician/presentation/builders/instances_list.py +0 -38
- speclogician/presentation/builders/predicate.py +0 -51
- speclogician/presentation/builders/recommendations.py +0 -41
- speclogician/presentation/builders/scenario.py +0 -41
- speclogician/presentation/builders/scenario_complement.py +0 -82
- speclogician/presentation/builders/smart_find.py +0 -39
- speclogician/presentation/builders/spec.py +0 -39
- speclogician/presentation/builders/state_diff.py +0 -150
- speclogician/presentation/builders/state_instance.py +0 -42
- speclogician/presentation/builders/state_instance_summary.py +0 -84
- speclogician/presentation/builders/trace.py +0 -58
- speclogician/presentation/ctx.py +0 -38
- speclogician/presentation/models/container.py +0 -44
- speclogician/presentation/models/data_artifact.py +0 -33
- speclogician/presentation/models/domain.py +0 -50
- speclogician/presentation/models/instances_list.py +0 -23
- speclogician/presentation/models/predicate.py +0 -60
- speclogician/presentation/models/recommendations.py +0 -34
- speclogician/presentation/models/scenario.py +0 -31
- speclogician/presentation/models/scenario_complement.py +0 -40
- speclogician/presentation/models/smart_find.py +0 -34
- speclogician/presentation/models/spec.py +0 -32
- speclogician/presentation/models/state_diff.py +0 -34
- speclogician/presentation/models/state_instance.py +0 -31
- speclogician/presentation/models/state_instance_summary.py +0 -102
- speclogician/presentation/models/trace.py +0 -42
- speclogician/presentation/preview/__init__.py +0 -13
- speclogician/presentation/preview/cli.py +0 -50
- speclogician/presentation/preview/fixtures/__init__.py +0 -205
- speclogician/presentation/preview/fixtures/artifact_container.py +0 -150
- speclogician/presentation/preview/fixtures/data_artifact.py +0 -144
- speclogician/presentation/preview/fixtures/domain_model.py +0 -162
- speclogician/presentation/preview/fixtures/instances_list.py +0 -162
- speclogician/presentation/preview/fixtures/predicate.py +0 -184
- speclogician/presentation/preview/fixtures/scenario.py +0 -84
- speclogician/presentation/preview/fixtures/scenario_complement.py +0 -81
- speclogician/presentation/preview/fixtures/smart_find.py +0 -140
- speclogician/presentation/preview/fixtures/spec.py +0 -95
- speclogician/presentation/preview/fixtures/state_diff.py +0 -158
- speclogician/presentation/preview/fixtures/state_instance.py +0 -128
- speclogician/presentation/preview/fixtures/state_instance_summary.py +0 -80
- speclogician/presentation/preview/fixtures/trace.py +0 -206
- speclogician/presentation/preview/registry.py +0 -42
- speclogician/presentation/renderers/__init__.py +0 -24
- speclogician/presentation/renderers/container.py +0 -136
- speclogician/presentation/renderers/data_artifact.py +0 -144
- speclogician/presentation/renderers/domain.py +0 -123
- speclogician/presentation/renderers/instances_list.py +0 -120
- speclogician/presentation/renderers/predicate.py +0 -180
- speclogician/presentation/renderers/recommendations.py +0 -90
- speclogician/presentation/renderers/scenario.py +0 -94
- speclogician/presentation/renderers/scenario_complement.py +0 -59
- speclogician/presentation/renderers/smart_find.py +0 -307
- speclogician/presentation/renderers/spec.py +0 -105
- speclogician/presentation/renderers/state_diff.py +0 -102
- speclogician/presentation/renderers/state_instance.py +0 -82
- speclogician/presentation/renderers/state_instance_summary.py +0 -143
- speclogician/presentation/renderers/trace.py +0 -122
- speclogician/shell/app.py +0 -170
- speclogician/shell/shell_ch.py +0 -263
- speclogician/shell/shell_view.py +0 -153
- speclogician/state/change_result.py +0 -32
- speclogician/state/diff.py +0 -191
- speclogician/state/inst.py +0 -574
- speclogician/state/recommendation.py +0 -13
- speclogician/state/recommender.py +0 -577
- speclogician/state/state_stats.py +0 -133
- speclogician/tui/__init__.py +0 -0
- speclogician/tui/app.py +0 -257
- speclogician/tui/app.tcss +0 -160
- speclogician/tui/demo.py +0 -45
- speclogician/tui/images/speclogician-full.png +0 -0
- speclogician/tui/images/speclogician-minimal.png +0 -0
- speclogician/tui/main_screen.py +0 -454
- speclogician/tui/splash_screen.py +0 -51
- speclogician/tui/stats_screen.py +0 -125
- speclogician/utils/testing.py +0 -151
- speclogician-0.0.0b1.dist-info/METADATA +0 -116
- speclogician-0.0.0b1.dist-info/RECORD +0 -139
- /speclogician/{presentation → agent}/__init__.py +0 -0
- /speclogician/{presentation/builders → cmd}/__init__.py +0 -0
- /speclogician/{presentation/models → llms}/__init__.py +0 -0
- {speclogician-0.0.0b1.dist-info → speclogician-0.0.0.dev1.dist-info}/WHEEL +0 -0
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/presentation/renderers/smart_find.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from rich import box
|
|
10
|
-
from rich.console import Group
|
|
11
|
-
from rich.panel import Panel
|
|
12
|
-
from rich.rule import Rule
|
|
13
|
-
from rich.table import Table, Column
|
|
14
|
-
from rich.text import Text
|
|
15
|
-
|
|
16
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
17
|
-
from speclogician.presentation.models.smart_find import SmartFindPM
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# -----------------------------------------------------------------------------
|
|
21
|
-
# helpers
|
|
22
|
-
# -----------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
def _snip(s: str | None, n: int = 140) -> str:
|
|
25
|
-
x = (s or "").strip()
|
|
26
|
-
if not x:
|
|
27
|
-
return ""
|
|
28
|
-
x = x.replace("\n", " ")
|
|
29
|
-
return (x[: n - 1] + "…") if len(x) > n else x
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _validity_text(iml_valid: str) -> Text:
|
|
33
|
-
v = (iml_valid or "").lower()
|
|
34
|
-
if v == "valid":
|
|
35
|
-
return Text("✓ valid", style="bold green")
|
|
36
|
-
if v == "invalid":
|
|
37
|
-
return Text("✗ invalid", style="bold red")
|
|
38
|
-
return Text("? unknown", style="bold yellow")
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _counts_table(pm: SmartFindPM, *, ctx: RenderCtx) -> Table:
|
|
42
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
43
|
-
t = Table(
|
|
44
|
-
show_header=True,
|
|
45
|
-
header_style="bold",
|
|
46
|
-
box=b,
|
|
47
|
-
show_edge=False,
|
|
48
|
-
pad_edge=False,
|
|
49
|
-
expand=False, # keep count column close
|
|
50
|
-
)
|
|
51
|
-
t.add_column("Type", style="dim", no_wrap=True)
|
|
52
|
-
t.add_column("Count", justify="right", no_wrap=True)
|
|
53
|
-
|
|
54
|
-
c = pm.counts
|
|
55
|
-
t.add_row("Test traces", str(c.num_test_traces))
|
|
56
|
-
t.add_row("Log traces", str(c.num_log_traces))
|
|
57
|
-
t.add_row("Doc refs", str(c.num_doc_refs))
|
|
58
|
-
t.add_row("Src code refs", str(c.num_src_code_refs))
|
|
59
|
-
return t
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def _want_art_id(ctx: RenderCtx) -> bool:
|
|
63
|
-
return bool(getattr(ctx, "show_art_ids", False) or getattr(ctx, "show_ids", False))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _rule(title: str, n: int) -> Rule:
|
|
67
|
-
return Rule(f"[bold]{title}[/bold] ({n})", style="dim")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# -----------------------------------------------------------------------------
|
|
71
|
-
# per-kind tables
|
|
72
|
-
# -----------------------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
def _test_traces_table(pm: SmartFindPM, *, ctx: RenderCtx) -> Table:
|
|
75
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
76
|
-
show_art_id = _want_art_id(ctx)
|
|
77
|
-
|
|
78
|
-
cols: list[Column] = []
|
|
79
|
-
if show_art_id:
|
|
80
|
-
cols.append(Column("art_id", style="dim", no_wrap=True, width=12))
|
|
81
|
-
cols += [
|
|
82
|
-
Column("name", no_wrap=False, width=34, overflow="ellipsis"),
|
|
83
|
-
Column("iml", no_wrap=True, width=9),
|
|
84
|
-
Column("time", style="dim", no_wrap=True, width=20),
|
|
85
|
-
Column("snippet", no_wrap=False, width=68, overflow="ellipsis"),
|
|
86
|
-
]
|
|
87
|
-
|
|
88
|
-
t = Table(
|
|
89
|
-
*cols,
|
|
90
|
-
show_header=True,
|
|
91
|
-
header_style="bold",
|
|
92
|
-
box=b,
|
|
93
|
-
show_edge=False,
|
|
94
|
-
pad_edge=False,
|
|
95
|
-
expand=False,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
for tt in pm.items.test_traces:
|
|
99
|
-
core = tt.core
|
|
100
|
-
label = tt.name or tt.filepath or "-"
|
|
101
|
-
snippet = (
|
|
102
|
-
_snip(tt.contents, 180)
|
|
103
|
-
or _snip(core.given, 180)
|
|
104
|
-
or _snip(core.when, 180)
|
|
105
|
-
or _snip(core.then, 180)
|
|
106
|
-
or "—"
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
row = []
|
|
110
|
-
if show_art_id:
|
|
111
|
-
row.append(Text(core.art_id or "-", style="dim"))
|
|
112
|
-
row += [
|
|
113
|
-
Text(label, style="bold"),
|
|
114
|
-
_validity_text(core.iml_validity.iml_valid),
|
|
115
|
-
Text(core.time or "—", style="dim"),
|
|
116
|
-
Text(snippet, style="dim" if snippet == "—" else ""),
|
|
117
|
-
]
|
|
118
|
-
t.add_row(*row)
|
|
119
|
-
|
|
120
|
-
if not pm.items.test_traces:
|
|
121
|
-
if show_art_id:
|
|
122
|
-
t.add_row(Text("-", style="dim"), Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("", style="dim"))
|
|
123
|
-
else:
|
|
124
|
-
t.add_row(Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("", style="dim"), Text("", style="dim"))
|
|
125
|
-
|
|
126
|
-
return t
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def _log_traces_table(pm: SmartFindPM, *, ctx: RenderCtx) -> Table:
|
|
130
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
131
|
-
show_art_id = _want_art_id(ctx)
|
|
132
|
-
|
|
133
|
-
cols: list[Column] = []
|
|
134
|
-
if show_art_id:
|
|
135
|
-
cols.append(Column("art_id", style="dim", no_wrap=True, width=12))
|
|
136
|
-
cols += [
|
|
137
|
-
Column("filename", no_wrap=False, width=34, overflow="ellipsis"),
|
|
138
|
-
Column("iml", no_wrap=True, width=9),
|
|
139
|
-
Column("time", style="dim", no_wrap=True, width=20),
|
|
140
|
-
Column("snippet", no_wrap=False, width=68, overflow="ellipsis"),
|
|
141
|
-
]
|
|
142
|
-
|
|
143
|
-
t = Table(
|
|
144
|
-
*cols,
|
|
145
|
-
show_header=True,
|
|
146
|
-
header_style="bold",
|
|
147
|
-
box=b,
|
|
148
|
-
show_edge=False,
|
|
149
|
-
pad_edge=False,
|
|
150
|
-
expand=False,
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
for lt in pm.items.log_traces:
|
|
154
|
-
core = lt.core
|
|
155
|
-
label = lt.filename or "-"
|
|
156
|
-
snippet = (
|
|
157
|
-
_snip(lt.contents, 180)
|
|
158
|
-
or _snip(core.given, 180)
|
|
159
|
-
or _snip(core.when, 180)
|
|
160
|
-
or _snip(core.then, 180)
|
|
161
|
-
or "—"
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
row = []
|
|
165
|
-
if show_art_id:
|
|
166
|
-
row.append(Text(core.art_id or "-", style="dim"))
|
|
167
|
-
row += [
|
|
168
|
-
Text(label, style="bold"),
|
|
169
|
-
_validity_text(core.iml_validity.iml_valid),
|
|
170
|
-
Text(core.time or "—", style="dim"),
|
|
171
|
-
Text(snippet, style="dim" if snippet == "—" else ""),
|
|
172
|
-
]
|
|
173
|
-
t.add_row(*row)
|
|
174
|
-
|
|
175
|
-
if not pm.items.log_traces:
|
|
176
|
-
if show_art_id:
|
|
177
|
-
t.add_row(Text("-", style="dim"), Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("", style="dim"))
|
|
178
|
-
else:
|
|
179
|
-
t.add_row(Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("", style="dim"), Text("", style="dim"))
|
|
180
|
-
|
|
181
|
-
return t
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def _doc_refs_table(pm: SmartFindPM, *, ctx: RenderCtx) -> Table:
|
|
185
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
186
|
-
show_art_id = _want_art_id(ctx)
|
|
187
|
-
|
|
188
|
-
cols: list[Column] = []
|
|
189
|
-
if show_art_id:
|
|
190
|
-
cols.append(Column("art_id", style="dim", no_wrap=True, width=12))
|
|
191
|
-
cols += [
|
|
192
|
-
Column("meta", no_wrap=False, width=46, overflow="ellipsis"),
|
|
193
|
-
Column("snippet", no_wrap=False, width=92, overflow="ellipsis"),
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
t = Table(
|
|
197
|
-
*cols,
|
|
198
|
-
show_header=True,
|
|
199
|
-
header_style="bold",
|
|
200
|
-
box=b,
|
|
201
|
-
show_edge=False,
|
|
202
|
-
pad_edge=False,
|
|
203
|
-
expand=False,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
for dr in pm.items.doc_refs:
|
|
207
|
-
label = dr.meta or "DocRef"
|
|
208
|
-
snippet = _snip(dr.text, 220) or "—"
|
|
209
|
-
row = []
|
|
210
|
-
if show_art_id:
|
|
211
|
-
row.append(Text(dr.core.art_id or "-", style="dim"))
|
|
212
|
-
row += [
|
|
213
|
-
Text(label, style="bold"),
|
|
214
|
-
Text(snippet, style="dim" if snippet == "—" else ""),
|
|
215
|
-
]
|
|
216
|
-
t.add_row(*row)
|
|
217
|
-
|
|
218
|
-
if not pm.items.doc_refs:
|
|
219
|
-
if show_art_id:
|
|
220
|
-
t.add_row(Text("-", style="dim"), Text("(none)", style="dim"), Text("—", style="dim"))
|
|
221
|
-
else:
|
|
222
|
-
t.add_row(Text("(none)", style="dim"), Text("—", style="dim"))
|
|
223
|
-
|
|
224
|
-
return t
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def _src_code_refs_table(pm: SmartFindPM, *, ctx: RenderCtx) -> Table:
|
|
228
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
229
|
-
show_art_id = _want_art_id(ctx)
|
|
230
|
-
|
|
231
|
-
cols: list[Column] = []
|
|
232
|
-
if show_art_id:
|
|
233
|
-
cols.append(Column("art_id", style="dim", no_wrap=True, width=12))
|
|
234
|
-
cols += [
|
|
235
|
-
Column("file", no_wrap=False, width=44, overflow="ellipsis"),
|
|
236
|
-
Column("lang", no_wrap=True, width=10),
|
|
237
|
-
Column("iml", no_wrap=True, width=7),
|
|
238
|
-
Column("snippet", no_wrap=False, width=77, overflow="ellipsis"),
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
t = Table(
|
|
242
|
-
*cols,
|
|
243
|
-
show_header=True,
|
|
244
|
-
header_style="bold",
|
|
245
|
-
box=b,
|
|
246
|
-
show_edge=False,
|
|
247
|
-
pad_edge=False,
|
|
248
|
-
expand=False,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
for sr in pm.items.src_code_refs:
|
|
252
|
-
file_label = sr.file_path or "SrcCodeRef"
|
|
253
|
-
lang = sr.language or "—"
|
|
254
|
-
has_iml = "✓" if (sr.iml_code or "").strip() else "—"
|
|
255
|
-
snippet = _snip(sr.src_code, 220) or _snip(sr.iml_code, 220) or "—"
|
|
256
|
-
|
|
257
|
-
row = []
|
|
258
|
-
if show_art_id:
|
|
259
|
-
row.append(Text(sr.core.art_id or "-", style="dim"))
|
|
260
|
-
row += [
|
|
261
|
-
Text(file_label, style="bold"),
|
|
262
|
-
Text(lang, style="cyan" if lang != "—" else "dim"),
|
|
263
|
-
Text(has_iml, style="green" if has_iml == "✓" else "dim"),
|
|
264
|
-
Text(snippet, style="dim" if snippet == "—" else ""),
|
|
265
|
-
]
|
|
266
|
-
t.add_row(*row)
|
|
267
|
-
|
|
268
|
-
if not pm.items.src_code_refs:
|
|
269
|
-
if show_art_id:
|
|
270
|
-
t.add_row(Text("-", style="dim"), Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("—", style="dim"))
|
|
271
|
-
else:
|
|
272
|
-
t.add_row(Text("(none)", style="dim"), Text("—", style="dim"), Text("—", style="dim"), Text("—", style="dim"))
|
|
273
|
-
|
|
274
|
-
return t
|
|
275
|
-
|
|
276
|
-
def _maybe_section(title: str, n: int, body: object, *, ctx: RenderCtx) -> list[object]:
|
|
277
|
-
# Default: do not render empty sections
|
|
278
|
-
show_empty = bool(getattr(ctx, "show_empty_sections", False))
|
|
279
|
-
if n == 0 and not show_empty:
|
|
280
|
-
return []
|
|
281
|
-
return [Text(""), _rule(title, n), body]
|
|
282
|
-
|
|
283
|
-
# -----------------------------------------------------------------------------
|
|
284
|
-
# renderer
|
|
285
|
-
# -----------------------------------------------------------------------------
|
|
286
|
-
|
|
287
|
-
def render_smart_find(pm: SmartFindPM, *, ctx: RenderCtx):
|
|
288
|
-
needle = (pm.counts.needle or "").strip().replace("\n", " ")
|
|
289
|
-
header = Text.assemble(
|
|
290
|
-
("Smart find", "bold"),
|
|
291
|
-
(" ", ""),
|
|
292
|
-
("needle: ", "dim"),
|
|
293
|
-
(repr(needle) if needle else "(empty)", "cyan" if needle else "dim"),
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
blocks: list[object] = [
|
|
297
|
-
header,
|
|
298
|
-
Text(""),
|
|
299
|
-
_counts_table(pm, ctx=ctx),
|
|
300
|
-
]
|
|
301
|
-
|
|
302
|
-
blocks += _maybe_section("Test traces", len(pm.items.test_traces), _test_traces_table(pm, ctx=ctx), ctx=ctx)
|
|
303
|
-
blocks += _maybe_section("Log traces", len(pm.items.log_traces), _log_traces_table(pm, ctx=ctx), ctx=ctx)
|
|
304
|
-
blocks += _maybe_section("Doc refs", len(pm.items.doc_refs), _doc_refs_table(pm, ctx=ctx), ctx=ctx)
|
|
305
|
-
blocks += _maybe_section("Src code refs", len(pm.items.src_code_refs), _src_code_refs_table(pm, ctx=ctx), ctx=ctx)
|
|
306
|
-
|
|
307
|
-
return Panel(Group(*blocks), title="[bold]Smart find[/bold]", border_style="magenta")
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/presentation/renderers/spec.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from rich.console import Group
|
|
10
|
-
from rich.panel import Panel
|
|
11
|
-
from rich.rule import Rule
|
|
12
|
-
from rich.table import Table
|
|
13
|
-
from rich.text import Text
|
|
14
|
-
from rich import box
|
|
15
|
-
|
|
16
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
17
|
-
from speclogician.presentation.models.spec import SpecPM
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _scenario_stats_table(pm: SpecPM, *, ctx: RenderCtx) -> Table:
|
|
21
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
22
|
-
t = Table(show_header=False, box=b, show_edge=False, pad_edge=False)
|
|
23
|
-
t.add_column(style="italic dim", no_wrap=True)
|
|
24
|
-
t.add_column(justify="right", no_wrap=True)
|
|
25
|
-
|
|
26
|
-
c = pm.counts
|
|
27
|
-
|
|
28
|
-
t.add_row(Text("Scenarios", style="bold magenta"), Text(""))
|
|
29
|
-
t.add_row("Total", Text(str(c.num_sc_total), style="bold"))
|
|
30
|
-
t.add_row("Missing components", Text(str(c.num_sc_missing)))
|
|
31
|
-
t.add_row("Matched", Text(str(c.num_sc_matched)))
|
|
32
|
-
t.add_row("Inconsistent", Text(str(c.num_sc_inconsistent)))
|
|
33
|
-
t.add_row("Conflicted", Text(str(c.num_sc_conflicted)))
|
|
34
|
-
|
|
35
|
-
return t
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _scenario_list_table(pm: SpecPM, *, ctx: RenderCtx) -> Table:
|
|
39
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
40
|
-
t = Table(title="Scenario list", box=b, show_edge=False, pad_edge=False, expand=False)
|
|
41
|
-
t.add_column("Name", no_wrap=True)
|
|
42
|
-
|
|
43
|
-
if not pm.scenario_names:
|
|
44
|
-
t.add_row(Text("(none)", style="dim"))
|
|
45
|
-
return t
|
|
46
|
-
|
|
47
|
-
for name in pm.scenario_names:
|
|
48
|
-
t.add_row(Text(name))
|
|
49
|
-
|
|
50
|
-
return t
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _scenario_complement_table(pm: SpecPM, *, ctx: RenderCtx) -> Table:
|
|
54
|
-
"""
|
|
55
|
-
Lightweight complement summary (counts + optional preview list).
|
|
56
|
-
Full regions are intentionally not rendered here (they can be huge).
|
|
57
|
-
"""
|
|
58
|
-
b = box.MINIMAL if ctx.compact else box.SIMPLE
|
|
59
|
-
t = Table(show_header=False, box=b, show_edge=False, pad_edge=False)
|
|
60
|
-
t.add_column(style="italic dim", no_wrap=True)
|
|
61
|
-
t.add_column(justify="right", no_wrap=True)
|
|
62
|
-
|
|
63
|
-
sc = getattr(pm, "scenario_complement", None)
|
|
64
|
-
if sc is None:
|
|
65
|
-
t.add_row(Text("Complement", style="bold magenta"), Text(""))
|
|
66
|
-
t.add_row(Text("(none)", style="dim"), Text(""))
|
|
67
|
-
return t
|
|
68
|
-
|
|
69
|
-
t.add_row(Text("Complement", style="bold magenta"), Text(""))
|
|
70
|
-
t.add_row("Regions", Text(str(getattr(sc, "num_regions", 0)), style="bold"))
|
|
71
|
-
|
|
72
|
-
# Optional “preview” identifiers if you included them in the PM
|
|
73
|
-
preview = list(getattr(sc, "region_fps_preview", []) or [])
|
|
74
|
-
if preview:
|
|
75
|
-
t.add_row(Text("Preview", style="dim"), Text(""))
|
|
76
|
-
for fp in preview[:3]:
|
|
77
|
-
t.add_row("•", Text(str(fp), style="dim"))
|
|
78
|
-
|
|
79
|
-
return t
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def render_spec(pm: SpecPM, *, ctx: RenderCtx):
|
|
83
|
-
# Domain model block (reuse your existing renderer, but DON'T wrap it in extra panels here)
|
|
84
|
-
from speclogician.presentation.renderers.domain import render_domain_model
|
|
85
|
-
|
|
86
|
-
blocks: list[object] = []
|
|
87
|
-
|
|
88
|
-
# ---- Domain ----
|
|
89
|
-
blocks.append(Text("Domain", style="bold magenta"))
|
|
90
|
-
blocks.append(render_domain_model(pm.domain, ctx=ctx))
|
|
91
|
-
|
|
92
|
-
# ---- Scenarios ----
|
|
93
|
-
blocks.append(Rule(style="dim"))
|
|
94
|
-
blocks.append(Text("Scenarios", style="bold magenta"))
|
|
95
|
-
blocks.append(_scenario_stats_table(pm, ctx=ctx))
|
|
96
|
-
blocks.append(Text("")) # small spacer
|
|
97
|
-
blocks.append(_scenario_list_table(pm, ctx=ctx))
|
|
98
|
-
|
|
99
|
-
# ---- Complement ----
|
|
100
|
-
sc = getattr(pm, "scenario_complement", None)
|
|
101
|
-
if sc is not None or ctx.show_empty_sections:
|
|
102
|
-
blocks.append(Rule(style="dim"))
|
|
103
|
-
blocks.append(_scenario_complement_table(pm, ctx=ctx))
|
|
104
|
-
|
|
105
|
-
return Panel(Group(*blocks), title="[bold]Spec[/bold]", border_style="magenta")
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/presentation/renderers/state_diff.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from rich.console import Group, RenderableType
|
|
10
|
-
from rich.rule import Rule
|
|
11
|
-
from rich.text import Text
|
|
12
|
-
|
|
13
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
14
|
-
from speclogician.presentation.models.state_diff import StateDiffPM, DiffRowPM
|
|
15
|
-
from speclogician.state.diff import ComparisonOutcome
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _icon_style_label(oc: ComparisonOutcome) -> tuple[str, str, str]:
|
|
19
|
-
"""
|
|
20
|
-
Returns: (icon, rich_style, human_label)
|
|
21
|
-
"""
|
|
22
|
-
match oc:
|
|
23
|
-
case ComparisonOutcome.IMPROVED:
|
|
24
|
-
return ("⬆", "green", "improved")
|
|
25
|
-
case ComparisonOutcome.DECLINED:
|
|
26
|
-
return ("⬇", "red", "declined")
|
|
27
|
-
case ComparisonOutcome.NO_CHANGE_GOOD:
|
|
28
|
-
return ("🟢", "green", "no change (good)")
|
|
29
|
-
case ComparisonOutcome.NO_CHANGE_BAD:
|
|
30
|
-
return ("🔴", "red", "no change (bad)")
|
|
31
|
-
case ComparisonOutcome.NO_CHANGE:
|
|
32
|
-
return ("🟰", "yellow", "no change")
|
|
33
|
-
case ComparisonOutcome.UNKNOWN:
|
|
34
|
-
return ("🤷", "dim", "unknown")
|
|
35
|
-
case _:
|
|
36
|
-
# fallback for future enum additions
|
|
37
|
-
return ("•", "dim", getattr(oc, "value", str(oc)))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _render_row(r: DiffRowPM, *, label_width: int) -> RenderableType:
|
|
41
|
-
icon, style, human = _icon_style_label(r.outcome)
|
|
42
|
-
|
|
43
|
-
# Keep alignment stable for monospace-ish terminals
|
|
44
|
-
before = str(r.before)
|
|
45
|
-
after = str(r.after)
|
|
46
|
-
|
|
47
|
-
# Example:
|
|
48
|
-
# ⬆ base_status UNKNOWN → VALID (improved)
|
|
49
|
-
txt = Text()
|
|
50
|
-
txt.append(f"{icon} ", style=style)
|
|
51
|
-
txt.append(f"{r.label:<{label_width}}", style="bold")
|
|
52
|
-
txt.append(f" {before} → {after}")
|
|
53
|
-
txt.append(f" ({human})", style="dim")
|
|
54
|
-
return txt
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _render_section(title: str, rows: list[DiffRowPM], *, ctx: RenderCtx) -> RenderableType:
|
|
58
|
-
if not rows:
|
|
59
|
-
return Text("")
|
|
60
|
-
|
|
61
|
-
# Compute a reasonable label alignment width for this section (cap to avoid huge padding)
|
|
62
|
-
max_len = max((len(r.label) for r in rows), default=len("Metric"))
|
|
63
|
-
label_width = min(max(max_len, 18), 34)
|
|
64
|
-
|
|
65
|
-
parts: list[RenderableType] = []
|
|
66
|
-
# Bracketed header style like your desired output
|
|
67
|
-
parts.append(Text(f"[{title}]", style="bold"))
|
|
68
|
-
|
|
69
|
-
for r in rows:
|
|
70
|
-
parts.append(_render_row(r, label_width=label_width))
|
|
71
|
-
|
|
72
|
-
return Group(*parts)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def render_state_diff(pm: StateDiffPM, *, ctx: RenderCtx) -> RenderableType:
|
|
76
|
-
# Plain-text-ish layout, no panels/tables.
|
|
77
|
-
# Desired style:
|
|
78
|
-
# State diff
|
|
79
|
-
# ──────────
|
|
80
|
-
#
|
|
81
|
-
# [Section]
|
|
82
|
-
# ⬆ metric before → after (improved)
|
|
83
|
-
|
|
84
|
-
if not pm.has_changes:
|
|
85
|
-
return Group(
|
|
86
|
-
Text("State diff", style="bold"),
|
|
87
|
-
Rule(style="dim"),
|
|
88
|
-
Text("No differences detected", style="italic dim"),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
blocks: list[RenderableType] = [Text("State diff", style="bold"), Rule(style="dim")]
|
|
92
|
-
|
|
93
|
-
first = True
|
|
94
|
-
for sec in pm.sections:
|
|
95
|
-
if sec.empty():
|
|
96
|
-
continue
|
|
97
|
-
if not first and not ctx.compact:
|
|
98
|
-
blocks.append(Text("")) # blank line between sections
|
|
99
|
-
first = False
|
|
100
|
-
blocks.append(_render_section(sec.title, sec.rows, ctx=ctx))
|
|
101
|
-
|
|
102
|
-
return Group(*blocks)
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Imandra Inc.
|
|
3
|
-
#
|
|
4
|
-
# speclogician/presentation/renderers/state_instance.py
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from rich.console import Group, RenderableType
|
|
10
|
-
from rich.panel import Panel
|
|
11
|
-
|
|
12
|
-
from speclogician.presentation.ctx import RenderCtx
|
|
13
|
-
from speclogician.presentation.models.state_instance import StateInstancePM
|
|
14
|
-
|
|
15
|
-
from speclogician.presentation.renderers.spec import render_spec
|
|
16
|
-
from speclogician.presentation.renderers.state_diff import render_state_diff
|
|
17
|
-
from speclogician.presentation.renderers.container import render_artifact_container
|
|
18
|
-
from speclogician.presentation.renderers.state_instance_summary import (
|
|
19
|
-
render_state_instance_summary,
|
|
20
|
-
)
|
|
21
|
-
from speclogician.presentation.renderers.recommendations import render_recommendations
|
|
22
|
-
|
|
23
|
-
def render_state_instance(pm: StateInstancePM, *, ctx: RenderCtx) -> RenderableType:
|
|
24
|
-
summary_panel = render_state_instance_summary(pm.summary, ctx=ctx)
|
|
25
|
-
|
|
26
|
-
# Always show recommendations first if present (cheap + high-signal).
|
|
27
|
-
rec_panel: RenderableType | None = None
|
|
28
|
-
if getattr(pm, "recommendations", None) is not None:
|
|
29
|
-
rec_panel = render_recommendations(pm.recommendations, ctx=ctx)
|
|
30
|
-
|
|
31
|
-
# Stats-only: summary (and recs if available)
|
|
32
|
-
if ctx.show_stats_only:
|
|
33
|
-
blocks: list[RenderableType] = []
|
|
34
|
-
if rec_panel is not None:
|
|
35
|
-
blocks.append(rec_panel)
|
|
36
|
-
blocks.append(summary_panel)
|
|
37
|
-
return Panel(
|
|
38
|
-
Group(*blocks),
|
|
39
|
-
title="[bold]State instance[/bold]",
|
|
40
|
-
border_style="magenta",
|
|
41
|
-
expand=True,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
blocks: list[RenderableType] = []
|
|
45
|
-
if rec_panel is not None:
|
|
46
|
-
blocks.append(rec_panel)
|
|
47
|
-
|
|
48
|
-
blocks.append(summary_panel)
|
|
49
|
-
|
|
50
|
-
if pm.state_diff is not None and getattr(ctx, "show_diff", True):
|
|
51
|
-
blocks.append(
|
|
52
|
-
Panel(
|
|
53
|
-
render_state_diff(pm.state_diff, ctx=ctx),
|
|
54
|
-
title="[bold]Diff[/bold]",
|
|
55
|
-
border_style="yellow",
|
|
56
|
-
)
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
if pm.spec is not None:
|
|
60
|
-
blocks.append(
|
|
61
|
-
Panel(
|
|
62
|
-
render_spec(pm.spec, ctx=ctx),
|
|
63
|
-
title="[bold]Spec[/bold]",
|
|
64
|
-
border_style="cyan",
|
|
65
|
-
)
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
if pm.artifacts is not None:
|
|
69
|
-
blocks.append(
|
|
70
|
-
Panel(
|
|
71
|
-
render_artifact_container(pm.artifacts, ctx=ctx),
|
|
72
|
-
title="[bold]Artifacts[/bold]",
|
|
73
|
-
border_style="green",
|
|
74
|
-
)
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
return Panel(
|
|
78
|
-
Group(*blocks),
|
|
79
|
-
title="[bold]State instance[/bold]",
|
|
80
|
-
border_style="magenta",
|
|
81
|
-
expand=True,
|
|
82
|
-
)
|