rocket-engine 0.0.2b1__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 (241) hide show
  1. rocket_engine/__init__.py +8 -0
  2. rocket_engine/__main__.py +12 -0
  3. rocket_engine/adapters/README.md +54 -0
  4. rocket_engine/adapters/__init__.py +7 -0
  5. rocket_engine/adapters/api.py +39 -0
  6. rocket_engine/adapters/base.py +46 -0
  7. rocket_engine/adapters/cace/__init__.py +163 -0
  8. rocket_engine/adapters/circulax/README.md +10 -0
  9. rocket_engine/adapters/circulax/__init__.py +112 -0
  10. rocket_engine/adapters/circulax/_driver.py +190 -0
  11. rocket_engine/adapters/circulax/adapter.py +538 -0
  12. rocket_engine/adapters/circulax/results.py +291 -0
  13. rocket_engine/adapters/cli/README.md +34 -0
  14. rocket_engine/adapters/cli/__init__.py +305 -0
  15. rocket_engine/adapters/connectivity/__init__.py +196 -0
  16. rocket_engine/adapters/connectivity/results.py +214 -0
  17. rocket_engine/adapters/cosim/README.md +9 -0
  18. rocket_engine/adapters/cosim/__init__.py +1390 -0
  19. rocket_engine/adapters/cosim/results.py +214 -0
  20. rocket_engine/adapters/devsim/__init__.py +171 -0
  21. rocket_engine/adapters/digital_flow/README.md +9 -0
  22. rocket_engine/adapters/digital_flow/__init__.py +1121 -0
  23. rocket_engine/adapters/digital_flow/results.py +227 -0
  24. rocket_engine/adapters/digital_flow/state.py +462 -0
  25. rocket_engine/adapters/drc/README.klayout.md +7 -0
  26. rocket_engine/adapters/drc/README.md +7 -0
  27. rocket_engine/adapters/drc/__init__.py +1 -0
  28. rocket_engine/adapters/drc/klayout.py +952 -0
  29. rocket_engine/adapters/drc/magic.py +287 -0
  30. rocket_engine/adapters/drc/results.py +184 -0
  31. rocket_engine/adapters/drc/results_kl.py +188 -0
  32. rocket_engine/adapters/elmer/__init__.py +163 -0
  33. rocket_engine/adapters/envelope.py +120 -0
  34. rocket_engine/adapters/equivalence/__init__.py +156 -0
  35. rocket_engine/adapters/errors.py +181 -0
  36. rocket_engine/adapters/extract_magic/README.md +15 -0
  37. rocket_engine/adapters/extract_magic/__init__.py +425 -0
  38. rocket_engine/adapters/extract_magic/results.py +209 -0
  39. rocket_engine/adapters/formal_sby/README.md +7 -0
  40. rocket_engine/adapters/formal_sby/__init__.py +773 -0
  41. rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
  42. rocket_engine/adapters/formal_sby/results.py +185 -0
  43. rocket_engine/adapters/gdsfactory/__init__.py +194 -0
  44. rocket_engine/adapters/gdsfactory/_driver.py +459 -0
  45. rocket_engine/adapters/gdsfactory/adapter.py +609 -0
  46. rocket_engine/adapters/gdsfactory/results.py +181 -0
  47. rocket_engine/adapters/hdl_sim/README.md +7 -0
  48. rocket_engine/adapters/hdl_sim/__init__.py +369 -0
  49. rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
  50. rocket_engine/adapters/hdl_sim/results.py +178 -0
  51. rocket_engine/adapters/hdl_sim/verilator.py +167 -0
  52. rocket_engine/adapters/http/README.md +15 -0
  53. rocket_engine/adapters/http/__init__.py +458 -0
  54. rocket_engine/adapters/http/events.py +63 -0
  55. rocket_engine/adapters/ipc/README.md +9 -0
  56. rocket_engine/adapters/ipc/__init__.py +111 -0
  57. rocket_engine/adapters/lvs_netgen/README.md +20 -0
  58. rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
  59. rocket_engine/adapters/lvs_netgen/results.py +168 -0
  60. rocket_engine/adapters/mcp/README.md +19 -0
  61. rocket_engine/adapters/mcp/__init__.py +85 -0
  62. rocket_engine/adapters/mcp/api-schema.json +6107 -0
  63. rocket_engine/adapters/mcp/pull.py +246 -0
  64. rocket_engine/adapters/mcp/server.py +794 -0
  65. rocket_engine/adapters/meep/__init__.py +167 -0
  66. rocket_engine/adapters/meow/README.md +7 -0
  67. rocket_engine/adapters/meow/__init__.py +190 -0
  68. rocket_engine/adapters/meow/_driver.py +227 -0
  69. rocket_engine/adapters/meow/_envelope.py +108 -0
  70. rocket_engine/adapters/meow/adapter.py +491 -0
  71. rocket_engine/adapters/meow/results.py +218 -0
  72. rocket_engine/adapters/mutation/__init__.py +192 -0
  73. rocket_engine/adapters/ngspice/README.md +28 -0
  74. rocket_engine/adapters/ngspice/__init__.py +627 -0
  75. rocket_engine/adapters/ngspice/results.py +215 -0
  76. rocket_engine/adapters/postlayout/README.md +28 -0
  77. rocket_engine/adapters/postlayout/__init__.py +17 -0
  78. rocket_engine/adapters/postlayout/adapter.py +1062 -0
  79. rocket_engine/adapters/postlayout/results.py +330 -0
  80. rocket_engine/adapters/postlayout/translate.py +71 -0
  81. rocket_engine/adapters/profile.py +220 -0
  82. rocket_engine/adapters/sax/README.md +23 -0
  83. rocket_engine/adapters/sax/__init__.py +516 -0
  84. rocket_engine/adapters/sax/_driver.py +148 -0
  85. rocket_engine/adapters/sax/results.py +291 -0
  86. rocket_engine/adapters/stdio_console.py +1151 -0
  87. rocket_engine/adapters/syntax/README.md +5 -0
  88. rocket_engine/adapters/syntax/__init__.py +366 -0
  89. rocket_engine/adapters/syntax/results.py +181 -0
  90. rocket_engine/adapters/synth_yosys/README.md +7 -0
  91. rocket_engine/adapters/synth_yosys/__init__.py +655 -0
  92. rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
  93. rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
  94. rocket_engine/adapters/synth_yosys/results.py +173 -0
  95. rocket_engine/adapters/timing_sta/README.md +9 -0
  96. rocket_engine/adapters/timing_sta/__init__.py +726 -0
  97. rocket_engine/adapters/view_sync/__init__.py +185 -0
  98. rocket_engine/adapters/view_sync/results.py +189 -0
  99. rocket_engine/adapters/vlsir/__init__.py +142 -0
  100. rocket_engine/adapters/xschem/__init__.py +440 -0
  101. rocket_engine/adapters/xschem/deck.py +266 -0
  102. rocket_engine/clients/__init__.py +5 -0
  103. rocket_engine/clients/runtime.py +255 -0
  104. rocket_engine/composition.py +2279 -0
  105. rocket_engine/execution/README.md +30 -0
  106. rocket_engine/execution/__init__.py +150 -0
  107. rocket_engine/execution/apig/__init__.py +1 -0
  108. rocket_engine/execution/apig/pool.py +463 -0
  109. rocket_engine/execution/apig/submit.py +124 -0
  110. rocket_engine/execution/block_runs_scope.py +479 -0
  111. rocket_engine/execution/coalesce.py +414 -0
  112. rocket_engine/execution/console/__init__.py +1 -0
  113. rocket_engine/execution/console/apply_session.py +318 -0
  114. rocket_engine/execution/console/lifecycle.py +265 -0
  115. rocket_engine/execution/dispatch.py +205 -0
  116. rocket_engine/execution/errors.py +31 -0
  117. rocket_engine/execution/ports.py +797 -0
  118. rocket_engine/execution/pyexec/__init__.py +1 -0
  119. rocket_engine/execution/pyexec/apply_context.py +547 -0
  120. rocket_engine/execution/pyexec/approvals.py +173 -0
  121. rocket_engine/execution/pyexec/runner.py +404 -0
  122. rocket_engine/execution/pyexec/support.py +464 -0
  123. rocket_engine/execution/python_exec.py +897 -0
  124. rocket_engine/execution/queue.py +85 -0
  125. rocket_engine/execution/replay_safe.py +601 -0
  126. rocket_engine/execution/routing/__init__.py +1 -0
  127. rocket_engine/execution/routing/calls.py +364 -0
  128. rocket_engine/execution/routing/namespace.py +785 -0
  129. rocket_engine/execution/routing/ports.py +102 -0
  130. rocket_engine/execution/routing/tools.py +346 -0
  131. rocket_engine/execution/runner.py +311 -0
  132. rocket_engine/execution/runtime.py +1149 -0
  133. rocket_engine/execution/service.py +279 -0
  134. rocket_engine/execution/worker.py +464 -0
  135. rocket_engine/interfaces/README.md +26 -0
  136. rocket_engine/interfaces/__init__.py +58 -0
  137. rocket_engine/interfaces/apig_assets.py +407 -0
  138. rocket_engine/interfaces/celllink.py +461 -0
  139. rocket_engine/interfaces/documents.py +92 -0
  140. rocket_engine/interfaces/envelopes.py +131 -0
  141. rocket_engine/interfaces/errors.py +16 -0
  142. rocket_engine/interfaces/layout.py +410 -0
  143. rocket_engine/interfaces/layout_schema.yaml +278 -0
  144. rocket_engine/interfaces/paths.py +223 -0
  145. rocket_engine/interfaces/pdks.py +550 -0
  146. rocket_engine/interfaces/ports.py +72 -0
  147. rocket_engine/interfaces/references.py +115 -0
  148. rocket_engine/interfaces/refs.py +85 -0
  149. rocket_engine/interfaces/runparams.py +304 -0
  150. rocket_engine/interfaces/setup.py +552 -0
  151. rocket_engine/interfaces/setup_catalogue.yaml +243 -0
  152. rocket_engine/interfaces/setup_probes.py +66 -0
  153. rocket_engine/interfaces/vocabulary.py +58 -0
  154. rocket_engine/interfaces/yaml_dump.py +31 -0
  155. rocket_engine/library/README.md +22 -0
  156. rocket_engine/library/__init__.py +13 -0
  157. rocket_engine/library/apig_catalog.py +371 -0
  158. rocket_engine/library/catalog.py +185 -0
  159. rocket_engine/library/errors.py +16 -0
  160. rocket_engine/mutate/__init__.py +776 -0
  161. rocket_engine/mutate/operators.py +700 -0
  162. rocket_engine/ops/__init__.py +1 -0
  163. rocket_engine/ops/deferred.py +279 -0
  164. rocket_engine/ops/editors.py +404 -0
  165. rocket_engine/ops/evidence.py +194 -0
  166. rocket_engine/ops/gates.py +262 -0
  167. rocket_engine/ops/ports.py +76 -0
  168. rocket_engine/ops/queries.py +417 -0
  169. rocket_engine/ops/runs.py +553 -0
  170. rocket_engine/ops/submit.py +614 -0
  171. rocket_engine/project/README.md +27 -0
  172. rocket_engine/project/__init__.py +6 -0
  173. rocket_engine/project/canvas/__init__.py +55 -0
  174. rocket_engine/project/canvas/compile.py +109 -0
  175. rocket_engine/project/canvas/integrate.py +48 -0
  176. rocket_engine/project/canvas/schema.py +241 -0
  177. rocket_engine/project/canvas/templates.py +46 -0
  178. rocket_engine/project/canvas/validate.py +603 -0
  179. rocket_engine/project/edit/__init__.py +5 -0
  180. rocket_engine/project/edit/cas.py +87 -0
  181. rocket_engine/project/edit/transactions.py +282 -0
  182. rocket_engine/project/errors.py +16 -0
  183. rocket_engine/project/frontend13.py +685 -0
  184. rocket_engine/project/geometry/__init__.py +23 -0
  185. rocket_engine/project/geometry/ops.py +451 -0
  186. rocket_engine/project/geometry/snap.py +103 -0
  187. rocket_engine/project/ip/__init__.py +1 -0
  188. rocket_engine/project/ip/blocks.py +195 -0
  189. rocket_engine/project/ip/entry.schema.json +78 -0
  190. rocket_engine/project/ip/lifecycle.py +402 -0
  191. rocket_engine/project/ip/verbs.py +123 -0
  192. rocket_engine/project/layers/__init__.py +6 -0
  193. rocket_engine/project/layers/bindings.py +115 -0
  194. rocket_engine/project/layers/catalog.py +228 -0
  195. rocket_engine/project/layers/presets.py +43 -0
  196. rocket_engine/project/ledger/__init__.py +1 -0
  197. rocket_engine/project/ledger/docs.py +167 -0
  198. rocket_engine/project/ledger/entities.py +176 -0
  199. rocket_engine/project/ledger/events.py +29 -0
  200. rocket_engine/project/ledger/runs.py +257 -0
  201. rocket_engine/project/ledger/shared.py +174 -0
  202. rocket_engine/project/ledger/snapshots.py +385 -0
  203. rocket_engine/project/ledger/wires.py +552 -0
  204. rocket_engine/project/process/__init__.py +14 -0
  205. rocket_engine/project/process/model.py +193 -0
  206. rocket_engine/project/process/recipes.py +121 -0
  207. rocket_engine/project/schematic/__init__.py +1 -0
  208. rocket_engine/project/schematic/hierarchy.py +312 -0
  209. rocket_engine/project/schematic/instances.py +393 -0
  210. rocket_engine/project/schematic/sheets.py +198 -0
  211. rocket_engine/project/service.py +2941 -0
  212. rocket_engine/project/symbols/__init__.py +1 -0
  213. rocket_engine/project/symbols/cells.py +324 -0
  214. rocket_engine/project/symbols/linkage.py +356 -0
  215. rocket_engine/project/symbols/symbols.py +352 -0
  216. rocket_engine/project/templates/__init__.py +303 -0
  217. rocket_engine/project/templates/errors.py +25 -0
  218. rocket_engine/project/testbench/__init__.py +49 -0
  219. rocket_engine/project/testbench/blocks.py +619 -0
  220. rocket_engine/project/testbench/templates.py +134 -0
  221. rocket_engine/stores/README.md +12 -0
  222. rocket_engine/stores/__init__.py +6 -0
  223. rocket_engine/stores/ecs.py +1508 -0
  224. rocket_engine/stores/layers.py +1023 -0
  225. rocket_engine/stores/settings.py +1188 -0
  226. rocket_engine/stores/wireplace.py +1324 -0
  227. rocket_engine/workflows/README.md +18 -0
  228. rocket_engine/workflows/__init__.py +53 -0
  229. rocket_engine/workflows/capability_plan.py +102 -0
  230. rocket_engine/workflows/completion.py +228 -0
  231. rocket_engine/workflows/connectgraph.py +734 -0
  232. rocket_engine/workflows/connectgraph_emit.py +336 -0
  233. rocket_engine/workflows/gates.py +144 -0
  234. rocket_engine/workflows/photonic_wire.py +76 -0
  235. rocket_engine/workflows/stages.py +324 -0
  236. rocket_engine/workflows/viewsync.py +399 -0
  237. rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
  238. rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
  239. rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
  240. rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
  241. rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,18 @@
1
+ # workflows — process inventories and completion gates
2
+
3
+ `workflows/` holds the digital, analog, photonic, and mixed-domain verification stages as data. It plans checks and reports whether the requested flow has enough available operations and passing evidence. It never launches a solver.
4
+
5
+ ## Current behavior
6
+
7
+ - `stages.py` contains the D/A/P/M inventories and their stage-to-operation mappings.
8
+ - `capability_plan.py` marks stages ready or blocked based on available operations.
9
+ - `completion.py` combines the plan with the latest check records. Missing operations, checks that have not run, and non-PASS verdicts remain blockers; only a blocker-free flow can report fully verified.
10
+ - The stage `when` expressions are descriptive today. The planner currently treats planned stages as required instead of evaluating those expressions to narrow applicability.
11
+
12
+ The same plan and completion reports are served by `plan_checks` and `get_design_completion`, so clients can show the engine's reasons and blockers directly.
13
+
14
+ ## Boundaries and routing
15
+
16
+ This module owns stage order, applicability metadata, and completion rules. `composition.Engine` supplies registered operations and check results. Solver inputs and evidence belong to [`adapters/`](../adapters/README.md) and [`execution/`](../execution/README.md); command and result shapes belong to [`interfaces/`](../interfaces/README.md).
17
+
18
+ For the product process source, see the [PRD verification process](../../../PRD/tools-research/verification-process.md) and [workflow catalog](../../../PRD/tools-research/verification-workflows.yaml). For current engine gaps, see the [sprint board](../../docs/sprint-board.md).
@@ -0,0 +1,53 @@
1
+ """D/A/P/M step order and check plans as data. Never calls solvers directly."""
2
+
3
+ from .capability_plan import plan_from_capabilities
4
+ from .completion import design_completion_report, plan_checks_report
5
+ from .connectgraph import (
6
+ RULE_DANGLING_NET,
7
+ RULE_ORPHAN_LABEL,
8
+ RULE_SHORT,
9
+ RULE_UNCONNECTED_PIN,
10
+ Connectivity,
11
+ ConnectGraphError,
12
+ Net,
13
+ NetlistStructs,
14
+ ResolvedInstance,
15
+ erc_findings,
16
+ prepare_netlist_structs,
17
+ )
18
+ from .connectgraph_emit import (
19
+ LANGUAGES,
20
+ emit_netlists,
21
+ emit_spice,
22
+ emit_verilog,
23
+ emit_vhdl,
24
+ resolve_format,
25
+ to_raw_ir,
26
+ )
27
+ from .stages import STAGES, applicable_stages
28
+
29
+ __all__ = [
30
+ "STAGES",
31
+ "applicable_stages",
32
+ "plan_from_capabilities",
33
+ "design_completion_report",
34
+ "plan_checks_report",
35
+ "Connectivity",
36
+ "ConnectGraphError",
37
+ "Net",
38
+ "NetlistStructs",
39
+ "ResolvedInstance",
40
+ "erc_findings",
41
+ "prepare_netlist_structs",
42
+ "RULE_SHORT",
43
+ "RULE_UNCONNECTED_PIN",
44
+ "RULE_DANGLING_NET",
45
+ "RULE_ORPHAN_LABEL",
46
+ "LANGUAGES",
47
+ "emit_netlists",
48
+ "emit_spice",
49
+ "emit_verilog",
50
+ "emit_vhdl",
51
+ "resolve_format",
52
+ "to_raw_ir",
53
+ ]
@@ -0,0 +1,102 @@
1
+ """Capability-aware stage planning: readiness from tool truth, not wiring.
2
+
3
+ `applicable_stages` plans against a caller-supplied backend set,
4
+ which reads ready whenever an operation is merely wired — M11
5
+ (`run_timing`) planned ready while `sta` was absent. This module
6
+ plans against a `report_capabilities` operations mapping instead:
7
+ a stage is ready only when every tool entrypoint it needs reports
8
+ ready, and a blocked stage carries which entrypoints block it plus
9
+ their reason, missing items, and remedy.
10
+ """
11
+
12
+ from .stages import _SCRIPTED, STAGES, covering_decks
13
+
14
+ _FALLBACK_REMEDY = "Provision the missing tooling (see docs/provision-plan.md)."
15
+
16
+
17
+ def _stage_decks(stage, operations, solvers):
18
+ """Covering deck cites for one stage's tool entrypoints.
19
+
20
+ Args:
21
+ stage: Stage inventory entry with entrypoints.
22
+ operations: Per-operation readiness mapping.
23
+ solvers: Operation -> deck-tool cites; None means the
24
+ curated DECKS table.
25
+
26
+ Returns:
27
+ Deck cites across the stage's tool entrypoints.
28
+ """
29
+ cites = []
30
+ for entrypoint in stage["entrypoints"]:
31
+ cites.extend(covering_decks(entrypoint, solvers, operations))
32
+ return cites
33
+
34
+
35
+ def plan_from_capabilities(flow, operations, solvers=None):
36
+ """Plan a flow against a capabilities report's operations mapping.
37
+
38
+ Scripted entrypoints need no backend. A stage whose tool
39
+ entrypoints all read ready in the report is ready; otherwise it
40
+ stays required-but-blocked (never silently dropped) and names
41
+ the blocking entrypoints with their report reason, missing
42
+ items, and remedy. Entrypoints with no report row block loudly
43
+ with a no-report reason instead of a tool-blind ready. Every
44
+ stage also cites its covering decks (WORKFLOW-COVERAGE, ADR-007)
45
+ so blockers resolve to a deck, a probe-gated deck, or a named
46
+ gap — the completion report reads the same cites per blocker.
47
+
48
+ Args:
49
+ flow: Flow inventory to plan (digital/analog/photonic/mixed).
50
+ operations: Per-operation readiness mapping as reported by
51
+ `report_capabilities` (operation -> readiness plus
52
+ reason/missing/remedy detail).
53
+ solvers: Operation -> deck-tool cites (tool plus needs).
54
+ Defaults to the curated DECKS table.
55
+
56
+ Returns:
57
+ Stage list with per-stage readiness (ready/blocked) plus
58
+ deck cites; blocked stages also carry blocked_by,
59
+ reason, missing, and remedy keys.
60
+
61
+ Raises:
62
+ ValueError: Unknown flow name.
63
+ """
64
+ try:
65
+ stages = STAGES[flow]
66
+ except KeyError:
67
+ raise ValueError(f"Unknown flow: {flow}.") from None
68
+ planned = []
69
+ for stage in stages:
70
+ needed = [e for e in stage["entrypoints"] if e not in _SCRIPTED]
71
+ decks = _stage_decks(stage, operations, solvers)
72
+ blocking = [
73
+ e for e in needed
74
+ if operations.get(e, {}).get("readiness") != "ready"
75
+ ]
76
+ if not blocking:
77
+ planned.append({**stage, "readiness": "ready", "decks": decks})
78
+ continue
79
+ reasons, missing, remedies = [], [], []
80
+ for entry in blocking:
81
+ row = operations.get(entry, {})
82
+ if entry not in operations:
83
+ reasons.append(
84
+ f"{entry}: no capability report on this machine."
85
+ )
86
+ missing.append(entry)
87
+ continue
88
+ reasons.append(f"{entry}: {row.get('reason', 'not ready.')}")
89
+ missing.extend(row.get("missing") or [])
90
+ remedy = row.get("remedy")
91
+ if remedy and remedy not in remedies:
92
+ remedies.append(remedy)
93
+ planned.append({
94
+ **stage,
95
+ "readiness": "blocked",
96
+ "blocked_by": blocking,
97
+ "reason": "; ".join(reasons),
98
+ "missing": sorted(set(missing)),
99
+ "remedy": " ".join(remedies) if remedies else _FALLBACK_REMEDY,
100
+ "decks": decks,
101
+ })
102
+ return planned
@@ -0,0 +1,228 @@
1
+ """Design-completion gate: stage plans plus fully-verified claims (I27).
2
+
3
+ `plan_checks_report` wires the stage inventory to caller backends so
4
+ required-but-blocked stages surface instead of vanishing; every
5
+ planned stage stays required and reports ready or blocked with its
6
+ missing operations. `design_completion_report` layers the latest
7
+ check verdicts over that plan: any blocked, unverified, or failing
8
+ stage becomes a named blocker (mixed M14 cites run_thermal, and the
9
+ vocab-only run_thermal operation appears as its own blocker), the
10
+ report refuses the fully-verified claim while any stand, and only
11
+ an all-satisfied plan claims with per-operation evidence run ids.
12
+ """
13
+
14
+ from .gates import gate_plan_for_target
15
+ from .stages import applicable_stages
16
+
17
+ # Scripted entrypoints need no backend (mirrors stages._SCRIPTED).
18
+ _SCRIPTED = frozenset({"project scripting", "get_capabilities", "ip_instantiate"})
19
+
20
+
21
+ def _tool_ops(stage):
22
+ """Tool entrypoints one stage needs (scripted entries excluded)."""
23
+ return [e for e in stage["entrypoints"] if e not in _SCRIPTED]
24
+
25
+
26
+ def _missing_ops(stage, available):
27
+ """Tool entrypoints one stage needs but no backend serves."""
28
+ return [e for e in _tool_ops(stage) if e not in available]
29
+
30
+
31
+ def _check_status(record):
32
+ """Verdict status for one check record (None when never ran)."""
33
+ if record is None:
34
+ return None
35
+ if not isinstance(record, dict):
36
+ return "unknown"
37
+ status = record.get("status")
38
+ if status is None:
39
+ verdict = record.get("verdict")
40
+ if isinstance(verdict, dict):
41
+ status = verdict.get("status")
42
+ return status if status is not None else "unknown"
43
+
44
+
45
+ def _run_id(record):
46
+ """String run id for one check record (None when absent)."""
47
+ if isinstance(record, dict) and isinstance(record.get("run_id"), str):
48
+ return record["run_id"]
49
+ return None
50
+
51
+
52
+ def plan_checks_report(flow, available_backends, target=None, canvas=None):
53
+ """Plan one flow against installed backends with named blockers.
54
+
55
+ Thin report over `applicable_stages`: every planned stage keeps
56
+ its required flag and gains its readiness plus the exact missing
57
+ operations, so a caller can name required-but-blocked stages
58
+ (mixed M14 cites run_thermal) instead of silently dropping them.
59
+ With a target (plus an optional canvas) the plan is additionally
60
+ gated per `gates.gate_plan_for_target`, joining deck tools,
61
+ canvas settings, and metric budgets to every stage.
62
+
63
+ Args:
64
+ flow: Flow inventory to plan (digital/analog/photonic/mixed).
65
+ available_backends: Installed backend entrypoint names.
66
+ target: v1 target the plan serves (kind required); None
67
+ skips gating and returns the backend-only plan.
68
+ canvas: Canvas mapping with targets/tools; None means an
69
+ empty canvas (a canvas without a target refuses).
70
+
71
+ Returns:
72
+ Mapping with the flow, per-stage plans (each carrying
73
+ readiness plus missing_operations), blocked_stages and
74
+ ready_stages id lists in stage order, the sorted
75
+ unique missing_operations across blocked stages, and —
76
+ when gated — the target/canvas join.
77
+
78
+ Raises:
79
+ ValueError: Unknown flow name, a canvas without a target,
80
+ or a bad target/canvas shape.
81
+ """
82
+ available = set() if available_backends is None else set(available_backends)
83
+ planned = applicable_stages(flow, available)
84
+ stages = []
85
+ for stage in planned:
86
+ stages.append({**stage, "missing_operations": _missing_ops(stage, available)})
87
+ blocked = [s["id"] for s in stages if s["readiness"] == "blocked"]
88
+ ready = [s["id"] for s in stages if s["readiness"] == "ready"]
89
+ missing = sorted({op for stage in stages for op in stage["missing_operations"]})
90
+ plan = {
91
+ "flow": flow,
92
+ "stages": stages,
93
+ "blocked_stages": blocked,
94
+ "ready_stages": ready,
95
+ "missing_operations": missing,
96
+ }
97
+ if target is None and canvas is None:
98
+ return plan
99
+ if target is None:
100
+ raise ValueError("plan_checks canvas needs a target.")
101
+ return gate_plan_for_target(plan, target, canvas)
102
+
103
+
104
+ def design_completion_report(flow, available_backends, checks, current_revision=None):
105
+ """Judge one flow's completion: blockers stand or evidence claims.
106
+
107
+ Combines the stage plan with the latest check verdicts. A stage
108
+ whose backends are missing blocks as blocked_stage; a ready tool
109
+ stage whose operation never ran blocks as unverified_stage; a
110
+ ready stage whose latest verdict is not PASS blocks as
111
+ failing_check with its run id. Missing operations additionally
112
+ appear as missing_operation blockers so vocab-only names such as
113
+ run_thermal never vanish silently. Scripted-only stages need no
114
+ solver evidence and never block. While any blocker stands the
115
+ report refuses the claim (fully_verified False, claim None);
116
+ only a blocker-free plan claims fully verified with evidence.
117
+
118
+ Args:
119
+ flow: Flow inventory to judge (digital/analog/photonic/mixed).
120
+ available_backends: Installed backend entrypoint names.
121
+ checks: Mapping of operation to its latest check record
122
+ (the checks value from Engine.check_results: each with
123
+ status plus run_id); absent operations read never_run.
124
+ current_revision: Project revision to echo; None when unknown.
125
+
126
+ Returns:
127
+ Mapping with the flow, fully_verified flag, claim (fully
128
+ verified) or refusal text, blockers in stage order with
129
+ missing-operation entries after, sorted unique
130
+ missing_operations, evidence mapping each PASS operation
131
+ to its run id, per-stage plans carrying stage_status plus
132
+ run_ids, and the echoed current_revision.
133
+
134
+ Raises:
135
+ ValueError: Unknown flow name.
136
+ """
137
+ available = set() if available_backends is None else set(available_backends)
138
+ seen = dict(checks) if checks is not None else {}
139
+ plan = plan_checks_report(flow, available)
140
+ blockers = []
141
+ stages_out = []
142
+ for stage in plan["stages"]:
143
+ tool_ops = _tool_ops(stage)
144
+ missing = stage["missing_operations"]
145
+ if missing:
146
+ blockers.append(
147
+ {
148
+ "kind": "blocked_stage",
149
+ "stage": stage["id"],
150
+ "step": stage["step"],
151
+ "missing_operations": list(missing),
152
+ "required": stage["required"],
153
+ }
154
+ )
155
+ stages_out.append({**stage, "stage_status": "blocked", "run_ids": {}})
156
+ continue
157
+ if not tool_ops:
158
+ stages_out.append({**stage, "stage_status": "verified", "run_ids": {}})
159
+ continue
160
+ run_ids = {}
161
+ failing = []
162
+ unverified = []
163
+ for op in tool_ops:
164
+ record = seen.get(op)
165
+ if op not in seen:
166
+ unverified.append(
167
+ {
168
+ "kind": "unverified_stage",
169
+ "stage": stage["id"],
170
+ "step": stage["step"],
171
+ "operation": op,
172
+ "status": "never_run",
173
+ }
174
+ )
175
+ continue
176
+ status = _check_status(record)
177
+ rid = _run_id(record)
178
+ if rid is not None:
179
+ run_ids[op] = rid
180
+ if status == "PASS":
181
+ continue
182
+ failing.append(
183
+ {
184
+ "kind": "failing_check",
185
+ "stage": stage["id"],
186
+ "step": stage["step"],
187
+ "operation": op,
188
+ "status": status,
189
+ "run_id": rid,
190
+ }
191
+ )
192
+ blockers.extend(failing)
193
+ blockers.extend(unverified)
194
+ if failing:
195
+ stage_status = "failing"
196
+ elif unverified:
197
+ stage_status = "unverified"
198
+ else:
199
+ stage_status = "verified"
200
+ stages_out.append({**stage, "stage_status": stage_status, "run_ids": run_ids})
201
+ for op in plan["missing_operations"]:
202
+ stages_needing = sorted(
203
+ s["id"] for s in plan["stages"] if op in s["missing_operations"]
204
+ )
205
+ blockers.append(
206
+ {"kind": "missing_operation", "operation": op, "stages": stages_needing}
207
+ )
208
+ evidence = {}
209
+ for op in sorted(available):
210
+ record = seen.get(op)
211
+ if _check_status(record) == "PASS":
212
+ rid = _run_id(record)
213
+ if rid is not None:
214
+ evidence[op] = rid
215
+ fully_verified = not blockers
216
+ return {
217
+ "flow": flow,
218
+ "fully_verified": fully_verified,
219
+ "claim": "fully verified" if fully_verified else None,
220
+ "refusal": None
221
+ if fully_verified
222
+ else f"{len(blockers)} blocker(s) stand; not fully verified.",
223
+ "blockers": blockers,
224
+ "missing_operations": plan["missing_operations"],
225
+ "evidence": evidence,
226
+ "stages": stages_out,
227
+ "current_revision": current_revision,
228
+ }