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,42 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/data_artifact.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.data_artifact import (
11
+ DataArtifactCorePM,
12
+ DocRefPM,
13
+ SrcCodeRefPM,
14
+ )
15
+
16
+ from speclogician.data.refs import DocRef, SrcCodeRef # adjust if needed
17
+
18
+
19
+ def _build_core(art_id: str) -> DataArtifactCorePM:
20
+ return DataArtifactCorePM(art_id=art_id)
21
+
22
+
23
+ def build_doc_ref_pm(d: DocRef, ctx: RenderCtx) -> DocRefPM:
24
+ # Text is the artifact; keep it unless you want a separate ctx flag for "heavy text"
25
+ text = d.text if getattr(ctx, "show_code", True) else d.text
26
+ return DocRefPM(
27
+ core=_build_core(d.art_id),
28
+ text=text,
29
+ meta=d.meta,
30
+ )
31
+
32
+
33
+ def build_src_code_ref_pm(s: SrcCodeRef, ctx: RenderCtx) -> SrcCodeRefPM:
34
+ show = getattr(ctx, "show_code", True)
35
+ return SrcCodeRefPM(
36
+ core=_build_core(s.art_id),
37
+ src_code=s.src_code if show else "",
38
+ language=s.language,
39
+ file_path=s.file_path,
40
+ iml_code=s.iml_code if show else None,
41
+ meta=s.meta,
42
+ )
@@ -0,0 +1,54 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/domain.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.modeling.domain import DomainModel
10
+ from speclogician.presentation.ctx import RenderCtx
11
+ from speclogician.presentation.models.domain import (
12
+ DomainModelPM,
13
+ DomainCountsPM,
14
+ DomainCodePM,
15
+ )
16
+
17
+
18
+ def build_domain_model_pm(dm: DomainModel, ctx: RenderCtx) -> DomainModelPM:
19
+ counts = DomainCountsPM(
20
+ base_status=dm.base_status,
21
+ base_has_state=dm.base_has_state,
22
+ base_has_action=dm.base_has_action,
23
+ num_state_preds_total=dm.num_state_preds_total,
24
+ num_state_preds_valid_logic=dm.num_state_preds_valid_logic,
25
+ num_state_preds_matched=dm.num_state_preds_matched,
26
+ num_action_preds_total=dm.num_action_preds_total,
27
+ num_action_preds_valid_logic=dm.num_action_preds_valid_logic,
28
+ num_action_preds_matched=dm.num_action_preds_matched,
29
+ num_preds_total=dm.num_preds_total,
30
+ num_preds_valid_logic=dm.num_preds_valid_logic,
31
+ num_preds_matched=dm.num_preds_matched,
32
+ num_trans_total=dm.num_trans_total,
33
+ num_trans_valid_logic=dm.num_trans_valid_logic,
34
+ num_trans_matched=dm.num_trans_matched,
35
+ )
36
+
37
+ # Always construct code PM for stable JSON; keep fields empty unless requested.
38
+ code = DomainCodePM()
39
+
40
+ if not ctx.show_stats_only and ctx.show_code:
41
+ # Base
42
+ if getattr(ctx, "show_base", True):
43
+ code.base = dm.base or ""
44
+
45
+ # Predicates
46
+ if getattr(ctx, "show_predicates", True):
47
+ code.state_preds_iml = "\n\n".join(p.to_iml() for p in dm.state_preds).strip()
48
+ code.action_preds_iml = "\n\n".join(p.to_iml() for p in dm.action_preds).strip()
49
+
50
+ # Transitions
51
+ if getattr(ctx, "show_transitions", True):
52
+ code.transitions_iml = "\n\n".join(t.to_iml() for t in dm.transitions).strip()
53
+
54
+ return DomainModelPM(counts=counts, code=code)
@@ -0,0 +1,38 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/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 InstancesListPM, InstancesListRowPM
11
+ from speclogician.presentation.builders.state_instance_summary import build_state_instance_summary_pm
12
+ from speclogician.state.inst import StateInstance
13
+
14
+
15
+ def _changes_str(si: StateInstance) -> str:
16
+ if not (si.changes or []):
17
+ return "-"
18
+
19
+ parts: list[str] = []
20
+ for ch in si.changes:
21
+ short = getattr(ch, "short_str", None)
22
+ parts.append(short() if callable(short) else ch.__class__.__name__)
23
+ return ", ".join(parts)
24
+
25
+
26
+ def build_instances_list_pm(states: list[StateInstance], ctx: RenderCtx) -> InstancesListPM:
27
+ rows: list[InstancesListRowPM] = []
28
+ for idx, si in enumerate(states):
29
+ state_label = "latest" if idx == 0 else str(idx)
30
+ rows.append(
31
+ InstancesListRowPM(
32
+ state_label=state_label,
33
+ changes=_changes_str(si),
34
+ summary=build_state_instance_summary_pm(si, ctx=ctx),
35
+ )
36
+ )
37
+
38
+ return InstancesListPM(rows=rows)
@@ -0,0 +1,51 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/predicate.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.modeling.predicates import ActionPredicate, PredicateType, StatePredicate, Transition
10
+ from speclogician.presentation.ctx import RenderCtx
11
+ from speclogician.presentation.builders._links import build_links_pm
12
+ from speclogician.presentation.models.predicate import PredicatePM, TransitionPM
13
+
14
+
15
+ def build_predicate_pm(p: StatePredicate | ActionPredicate, ctx: RenderCtx) -> PredicatePM:
16
+ pred_type = PredicateType.STATE if isinstance(p, StatePredicate) else PredicateType.ACTION
17
+ signature = f"{p.name} s" if pred_type == PredicateType.STATE else f"{p.name} s a"
18
+
19
+ # Prefer to_iml if present (yours is)
20
+ iml = p.to_iml() if hasattr(p, "to_iml") else ""
21
+
22
+ return PredicatePM(
23
+ kind=p.kind, # "state_predicate" | "action_predicate"
24
+ comp_id=p.comp_id,
25
+ name=p.name,
26
+ last_updated=p.last_updated,
27
+ pred_type=pred_type,
28
+ signature=signature,
29
+ src_code=p.src_code or "",
30
+ iml=iml,
31
+ is_iml_valid=p.is_iml_valid,
32
+ iml_error=p.iml_error,
33
+ links=build_links_pm(p.comp_id, ctx),
34
+ )
35
+
36
+
37
+ def build_transition_pm(t: Transition, ctx: RenderCtx) -> TransitionPM:
38
+ signature = f"{t.name} s a"
39
+ iml = t.to_iml() if hasattr(t, "to_iml") else ""
40
+
41
+ return TransitionPM(
42
+ comp_id=t.comp_id,
43
+ name=t.name,
44
+ last_updated=t.last_updated,
45
+ signature=signature,
46
+ src_code=t.src_code or "",
47
+ iml=iml,
48
+ is_iml_valid=t.is_iml_valid,
49
+ iml_error=t.iml_error,
50
+ links=build_links_pm(t.comp_id, ctx),
51
+ )
@@ -0,0 +1,41 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/recommendations.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.recommendations import (
11
+ RecommendationsPM,
12
+ RecommendationPM,
13
+ )
14
+ from speclogician.state.recommender import Recommendation
15
+
16
+
17
+ def build_recommendations_pm(
18
+ recs: list[Recommendation] | None,
19
+ ctx: RenderCtx,
20
+ ) -> RecommendationsPM:
21
+ rs = list(recs or [])
22
+
23
+ # UI wants stable ordering: priority asc, then kind, then text
24
+ rs.sort(key=lambda r: (r.priority, r.kind, r.text))
25
+
26
+ items = [RecommendationPM(kind=r.kind, priority=r.priority, text=r.text) for r in rs]
27
+
28
+ # counts
29
+ n_error = sum(1 for r in items if r.kind == "error")
30
+ n_warn = sum(1 for r in items if r.kind == "warning")
31
+ n_next = sum(1 for r in items if r.kind == "next")
32
+ n_info = sum(1 for r in items if r.kind == "info")
33
+
34
+ return RecommendationsPM(
35
+ count=len(items),
36
+ items=items,
37
+ num_error=n_error,
38
+ num_warning=n_warn,
39
+ num_next=n_next,
40
+ num_info=n_info,
41
+ )
@@ -0,0 +1,41 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/scenario.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.modeling.scenario import Scenario, Missing, Inconsistent
11
+ from speclogician.presentation.models.scenario import (
12
+ ScenarioPM,
13
+ ScenarioStatusPM,
14
+ )
15
+
16
+
17
+ def build_scenario_pm(sc: Scenario, ctx: RenderCtx) -> ScenarioPM:
18
+ # status
19
+ st = sc.component_status
20
+ if isinstance(st, Missing):
21
+ status = ScenarioStatusPM(
22
+ kind="missing",
23
+ missing_preds=list(st.missing_preds),
24
+ missing_trans=list(st.missing_trans),
25
+ )
26
+ elif isinstance(st, Inconsistent):
27
+ status = ScenarioStatusPM(kind="inconsistent")
28
+ else:
29
+ status = ScenarioStatusPM(kind="valid")
30
+
31
+ return ScenarioPM(
32
+ comp_id=sc.comp_id,
33
+ name=sc.name,
34
+ status=status,
35
+ given=list(sc.given),
36
+ when=list(sc.when),
37
+ then=list(sc.then),
38
+ given_preds_consistent=sc.given_preds_consistent,
39
+ when_preds_consistent=sc.when_preds_consistent,
40
+ all_preds_consistent=sc.all_preds_consistent,
41
+ )
@@ -0,0 +1,82 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/scenario_complement.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.scenario_complement import (
11
+ ScenarioComplementPM,
12
+ ScenarioComplementRegionPM,
13
+ )
14
+
15
+ from speclogician.modeling.complement import ScenarioComplement, _fingerprint_region
16
+
17
+
18
+ def _safe_list(x) -> list:
19
+ try:
20
+ return list(x or [])
21
+ except Exception:
22
+ return []
23
+
24
+
25
+ def build_scenario_complement_pm(
26
+ comp: ScenarioComplement | None,
27
+ ctx: RenderCtx,
28
+ *,
29
+ preview_limit: int = 10,
30
+ constraints_preview_limit: int = 3,
31
+ ) -> ScenarioComplementPM:
32
+ if comp is None:
33
+ return ScenarioComplementPM(count_regions=0, regions_preview=[])
34
+
35
+ regs = _safe_list(getattr(comp, "regions", None))
36
+ count = len(regs)
37
+
38
+ # show only a few regions in the UI
39
+ preview_regs = regs[: max(0, int(preview_limit))]
40
+
41
+ items: list[ScenarioComplementRegionPM] = []
42
+ for r in preview_regs:
43
+ fp = _fingerprint_region(r)
44
+
45
+ constraints = _safe_list(getattr(r, "constraints_str", None))
46
+ invariant = (getattr(r, "invariant_str", None) or None)
47
+ model_eval = (getattr(r, "model_eval_str", None) or None)
48
+
49
+ # model_str can be dict-like; stringify for display
50
+ model_obj = getattr(r, "model_str", None)
51
+ if model_obj is None:
52
+ model_s = None
53
+ else:
54
+ try:
55
+ model_s = str(model_obj)
56
+ except Exception:
57
+ model_s = "<unprintable>"
58
+
59
+ # If ctx.show_code is False, keep it extra light.
60
+ if not ctx.show_code:
61
+ invariant = None
62
+ model_s = None
63
+ model_eval = None
64
+ constraints_preview: list[str] = []
65
+ else:
66
+ constraints_preview = [str(c) for c in constraints[: max(0, int(constraints_preview_limit))]]
67
+
68
+ items.append(
69
+ ScenarioComplementRegionPM(
70
+ fp=fp,
71
+ num_constraints=len(constraints),
72
+ invariant=invariant,
73
+ model=model_s,
74
+ model_eval=model_eval,
75
+ constraints_preview=constraints_preview,
76
+ )
77
+ )
78
+
79
+ return ScenarioComplementPM(
80
+ count_regions=count,
81
+ regions_preview=items,
82
+ )
@@ -0,0 +1,39 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/smart_find.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.data.container import SmartFindResults
10
+ from speclogician.presentation.ctx import RenderCtx
11
+ from speclogician.presentation.models.smart_find import (
12
+ SmartFindPM,
13
+ SmartFindCountsPM,
14
+ SmartFindItemsPM,
15
+ )
16
+
17
+ from speclogician.presentation.builders.trace import build_test_trace_pm, build_log_trace_pm
18
+ from speclogician.presentation.builders.data_artifact import build_doc_ref_pm, build_src_code_ref_pm
19
+
20
+
21
+ def build_smart_find_pm(res: SmartFindResults, ctx: RenderCtx) -> SmartFindPM:
22
+ counts = SmartFindCountsPM(
23
+ needle=res.needle,
24
+ num_test_traces=len(res.test_traces),
25
+ num_log_traces=len(res.log_traces),
26
+ num_doc_refs=len(res.doc_refs),
27
+ num_src_code_refs=len(res.src_code_refs),
28
+ )
29
+
30
+ items = SmartFindItemsPM()
31
+
32
+ # If stats-only, skip the heavy lists.
33
+ if not ctx.show_stats_only:
34
+ items.test_traces = [build_test_trace_pm(t, ctx) for t in res.test_traces]
35
+ items.log_traces = [build_log_trace_pm(t, ctx) for t in res.log_traces]
36
+ items.doc_refs = [build_doc_ref_pm(d, ctx) for d in res.doc_refs]
37
+ items.src_code_refs = [build_src_code_ref_pm(s, ctx) for s in res.src_code_refs]
38
+
39
+ return SmartFindPM(counts=counts, items=items)
@@ -0,0 +1,39 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/spec.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.modeling.spec import Spec
10
+ from speclogician.presentation.ctx import RenderCtx
11
+ from speclogician.presentation.builders.domain import build_domain_model_pm
12
+ from speclogician.presentation.builders.scenario_complement import (
13
+ build_scenario_complement_pm,
14
+ )
15
+ from speclogician.presentation.models.spec import SpecPM, SpecCountsPM
16
+
17
+
18
+ def build_spec_pm(spec: Spec, ctx: RenderCtx) -> SpecPM:
19
+ dm_pm = build_domain_model_pm(spec.domain_model, ctx)
20
+
21
+ counts = SpecCountsPM(
22
+ num_sc_total=spec.num_sc_total,
23
+ num_sc_missing=spec.num_sc_missing,
24
+ num_sc_matched=spec.num_sc_matched,
25
+ num_sc_inconsistent=spec.num_sc_inconsistent,
26
+ num_sc_conflicted=spec.num_sc_conflicted,
27
+ )
28
+
29
+ scenario_names = [s.name for s in (spec.scenarios or [])]
30
+
31
+ return SpecPM(
32
+ domain=dm_pm,
33
+ counts=counts,
34
+ scenario_names=scenario_names,
35
+ scenario_complement=build_scenario_complement_pm(
36
+ getattr(spec, "scenario_comp", None),
37
+ ctx,
38
+ ),
39
+ )
@@ -0,0 +1,150 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/state_diff.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from speclogician.presentation.ctx import RenderCtx
12
+ from speclogician.presentation.models.state_diff import (
13
+ StateDiffPM,
14
+ DiffSectionPM,
15
+ DiffRowPM,
16
+ )
17
+ from speclogician.state.diff import StateDiff, ValueDiff, ComparisonOutcome
18
+
19
+
20
+ def _outcome(vd: ValueDiff[Any]) -> ComparisonOutcome:
21
+ if vd.comp_func:
22
+ return vd.comp_func(vd.before, vd.after)
23
+ if vd.before == vd.after:
24
+ return ComparisonOutcome.NO_CHANGE
25
+ return ComparisonOutcome.UNKNOWN
26
+
27
+
28
+ def _message(vd: ValueDiff[Any]) -> str:
29
+ oc = _outcome(vd)
30
+ b = vd.before
31
+ a = vd.after
32
+
33
+ match oc:
34
+ case ComparisonOutcome.UNKNOWN:
35
+ return f"{vd.label}: Unknown: {b} -> {a}"
36
+ case ComparisonOutcome.NO_CHANGE:
37
+ return f"{vd.label}: No change: {b} -> {a}"
38
+ case ComparisonOutcome.NO_CHANGE_GOOD:
39
+ return f"{vd.label}: No change (good): {b} -> {a}"
40
+ case ComparisonOutcome.NO_CHANGE_BAD:
41
+ return f"{vd.label}: No change (bad): {b} -> {a}"
42
+ case ComparisonOutcome.IMPROVED:
43
+ return f"{vd.label}: Improvement: {b} -> {a}"
44
+ case ComparisonOutcome.DECLINED:
45
+ return f"{vd.label}: Decline: {b} -> {a}"
46
+ case _:
47
+ return f"{vd.label}: {b} -> {a}"
48
+
49
+
50
+ def _row(vd: ValueDiff[Any]) -> DiffRowPM:
51
+ return DiffRowPM(
52
+ label=vd.label,
53
+ before=vd.before,
54
+ after=vd.after,
55
+ outcome=_outcome(vd),
56
+ message=_message(vd),
57
+ )
58
+
59
+
60
+ def build_state_diff_pm(sd: StateDiff, ctx: RenderCtx) -> StateDiffPM:
61
+ sections: list[DiffSectionPM] = []
62
+
63
+ def add(title: str, vds: list[ValueDiff[Any]]) -> None:
64
+ rows = [_row(vd) for vd in vds if vd.is_different()]
65
+ if rows:
66
+ sections.append(DiffSectionPM(title=title, rows=rows))
67
+
68
+ # ---- Domain model ----
69
+ add("Domain Model: Base", [sd.base_status, sd.base_has_state, sd.base_has_action])
70
+
71
+ add(
72
+ "Domain Model: State predicates",
73
+ [
74
+ sd.num_state_preds_total,
75
+ sd.num_state_preds_matched,
76
+ sd.num_state_preds_valid_logic,
77
+ sd.num_state_preds_errored,
78
+ ],
79
+ )
80
+
81
+ add(
82
+ "Domain Model: Action predicates",
83
+ [
84
+ sd.num_action_preds_total,
85
+ sd.num_action_preds_matched,
86
+ sd.num_action_preds_valid_logic,
87
+ sd.num_action_preds_errored,
88
+ ],
89
+ )
90
+
91
+ add(
92
+ "Domain Model: Predicates (total)",
93
+ [
94
+ sd.num_preds_total,
95
+ sd.num_preds_matched,
96
+ sd.num_preds_valid_logic,
97
+ sd.num_preds_errored,
98
+ ],
99
+ )
100
+
101
+ add(
102
+ "Domain Model: Transitions",
103
+ [
104
+ sd.num_trans_total,
105
+ sd.num_trans_matched,
106
+ sd.num_trans_valid_logic,
107
+ sd.num_trans_errored,
108
+ ],
109
+ )
110
+
111
+ # ---- Scenarios + conflicts ----
112
+ add(
113
+ "Scenarios",
114
+ [
115
+ sd.num_sc_total,
116
+ sd.num_sc_missing,
117
+ sd.num_sc_matched,
118
+ sd.num_sc_inconsistent,
119
+ ],
120
+ )
121
+
122
+ add(
123
+ "Scenario conflicts",
124
+ [
125
+ sd.num_sc_conflicted,
126
+ sd.num_sc_overlap,
127
+ sd.num_sc_consumed,
128
+ ],
129
+ )
130
+
131
+ # ---- Artifacts ----
132
+ add(
133
+ "Test traces",
134
+ [
135
+ sd.num_test_traces_total,
136
+ sd.num_test_traces_logic_good,
137
+ sd.num_test_traces_matched,
138
+ ],
139
+ )
140
+
141
+ add(
142
+ "Log traces",
143
+ [
144
+ sd.num_log_traces_total,
145
+ sd.num_log_traces_logic_good,
146
+ sd.num_log_traces_matched,
147
+ ],
148
+ )
149
+
150
+ return StateDiffPM(has_changes=bool(sections), sections=sections)
@@ -0,0 +1,42 @@
1
+ #
2
+ # Imandra Inc.
3
+ #
4
+ # speclogician/presentation/builders/state_instance.py
5
+ #
6
+
7
+ from __future__ import annotations
8
+
9
+ from speclogician.presentation.ctx import RenderCtx
10
+ from speclogician.presentation.models.state_instance import StateInstancePM
11
+ from speclogician.presentation.builders.state_instance_summary import (
12
+ build_state_instance_summary_pm,
13
+ )
14
+
15
+ from speclogician.presentation.builders.spec import build_spec_pm
16
+ from speclogician.presentation.builders.container import build_artifact_container_pm
17
+ from speclogician.presentation.builders.state_diff import build_state_diff_pm
18
+ from speclogician.presentation.builders.recommendations import build_recommendations_pm
19
+
20
+ from speclogician.state.inst import StateInstance
21
+
22
+
23
+ def build_state_instance_pm(si: StateInstance, ctx: RenderCtx) -> StateInstancePM:
24
+ summary_pm = build_state_instance_summary_pm(si, ctx)
25
+
26
+ recommendations_pm = build_recommendations_pm(si.recommendations, ctx)
27
+
28
+ pm = StateInstancePM(summary=summary_pm, recommendations=recommendations_pm)
29
+
30
+ # -----------------
31
+ # Heavy parts (optional)
32
+ # -----------------
33
+ if not ctx.show_stats_only:
34
+ pm.spec = build_spec_pm(si.spec, ctx)
35
+ pm.artifacts = build_artifact_container_pm(si.art_container, ctx)
36
+
37
+ if si.state_diff is not None:
38
+ pm.state_diff = build_state_diff_pm(si.state_diff, ctx)
39
+ else:
40
+ pm.state_diff = None
41
+
42
+ return pm