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,185 @@
1
+ """View-sync solver adapter: run_view_sync, no subprocess.
2
+
3
+ Pure-Python ToolAdapter over the VIEW-SYNC workflow: preflight
4
+ validates the job, execute diffs intended vs extracted through
5
+ shared CONNECT-GRAPH extraction plus CellLink identity, parse
6
+ normalizes findings and carries coverage plus the report as
7
+ measurements. Every net name flows from the cited view's shared
8
+ `Connectivity.net_name` authority.
9
+ """
10
+
11
+ from ...interfaces import ToolAdapter
12
+ from ...workflows.viewsync import ViewSyncError, compare_views
13
+ from ..errors import AdapterError
14
+
15
+ OPERATION = "run_view_sync"
16
+ TOOL = "connectgraph"
17
+
18
+
19
+ class ViewSyncAdapter(ToolAdapter):
20
+ """Intended-vs-extracted connectivity diff for photonic P14.
21
+
22
+ Serves run_view_sync with deck.tool `connectgraph` (the only
23
+ tool). Both views arrive in the job; extraction, naming, and
24
+ the diff all run in-process — no tool to resolve, no timeout.
25
+
26
+ Job keys:
27
+ intended (required): Intended-view schematic record (see
28
+ workflows.connectgraph for the shape).
29
+ extracted (required): Extracted-view schematic record.
30
+ links: CellLink block links correlating symbol pins to
31
+ cell pins (default []).
32
+ variant: Variant selector applied to every link (default
33
+ None, the stored base link).
34
+ deck: Optional mapping; deck.tool must be connectgraph here.
35
+
36
+ Caution: FAIL-on-diff, PASS-with-coverage — the verdict judges
37
+ finding severities, never the exit path; read the coverage
38
+ statement before trusting a clean diff.
39
+ """
40
+
41
+ def capabilities(self, target):
42
+ """Report the served operation and tool name.
43
+
44
+ Args:
45
+ target: Target the capabilities are reported for
46
+ (accepted, unused — both views arrive per job).
47
+
48
+ Returns:
49
+ Capability mapping with operations and tool.
50
+ """
51
+ return {"operations": [OPERATION], "tool": TOOL}
52
+
53
+ def preflight(self, job):
54
+ """Check operation, tool hint, views, links, and variant.
55
+
56
+ Args:
57
+ job: Solver job with operation run_view_sync plus
58
+ intended and extracted views with identity inputs.
59
+
60
+ Returns:
61
+ Checked mapping with operation, intended, extracted,
62
+ links, and variant.
63
+
64
+ Raises:
65
+ AdapterError: unsupported_operation for another
66
+ operation or deck tool, invalid_command for a
67
+ missing view, a non-list links, or a non-string
68
+ variant.
69
+ """
70
+ if job.get("operation") != OPERATION:
71
+ raise AdapterError(
72
+ "unsupported_operation",
73
+ f"ViewSync handles {OPERATION}, got {job.get('operation')}.",
74
+ )
75
+ hint = (job.get("deck") or {}).get("tool")
76
+ if hint is not None and hint != TOOL:
77
+ raise AdapterError(
78
+ "unsupported_operation",
79
+ f"No {OPERATION} solver for deck tool: {hint}.",
80
+ )
81
+ intended = job.get("intended")
82
+ if not isinstance(intended, dict):
83
+ raise AdapterError(
84
+ "invalid_command", "run_view_sync needs an intended mapping."
85
+ )
86
+ extracted = job.get("extracted")
87
+ if not isinstance(extracted, dict):
88
+ raise AdapterError(
89
+ "invalid_command", "run_view_sync needs an extracted mapping."
90
+ )
91
+ links = job.get("links", [])
92
+ if not isinstance(links, list):
93
+ raise AdapterError(
94
+ "invalid_command",
95
+ f"links must be a list, got {job.get('links')!r}.",
96
+ )
97
+ variant = job.get("variant")
98
+ if variant is not None and not isinstance(variant, str):
99
+ raise AdapterError(
100
+ "invalid_command",
101
+ f"variant must be a string, got {variant!r}.",
102
+ )
103
+ return {
104
+ "operation": OPERATION,
105
+ "intended": intended,
106
+ "extracted": extracted,
107
+ "links": links,
108
+ "variant": variant,
109
+ }
110
+
111
+ def execute(self, job):
112
+ """Diff the job's views through shared extraction.
113
+
114
+ Args:
115
+ job: Solver job with operation run_view_sync plus
116
+ intended and extracted views with identity inputs.
117
+
118
+ Returns:
119
+ Native output with exit_code, findings, coverage,
120
+ report, and a one-line log.
121
+
122
+ Raises:
123
+ AdapterError: invalid_command for a malformed view or
124
+ bad identity mapping, plus the preflight codes
125
+ (execute re-checks first).
126
+ """
127
+ checked = self.preflight(job)
128
+ try:
129
+ result = compare_views(
130
+ checked["intended"],
131
+ checked["extracted"],
132
+ links=checked["links"],
133
+ variant=checked["variant"],
134
+ )
135
+ except ViewSyncError as exc:
136
+ raise AdapterError("invalid_command", str(exc)) from exc
137
+ findings = result["findings"]
138
+ errors = sum(1 for f in findings if f["severity"] == "error")
139
+ return {
140
+ "exit_code": 0,
141
+ "operation": OPERATION,
142
+ "findings": findings,
143
+ "coverage": result["coverage"],
144
+ "report": result["report"],
145
+ "log": f"{OPERATION}: {len(findings)} findings, {errors} errors",
146
+ }
147
+
148
+ def parse(self, native_output):
149
+ """Normalize findings into violations; coverage rides measurements.
150
+
151
+ Args:
152
+ native_output: Native output from execute.
153
+
154
+ Returns:
155
+ {total, violations[]} plus measurements with coverage
156
+ and the text report.
157
+
158
+ Raises:
159
+ AdapterError: runner_unavailable when the output has
160
+ no findings list.
161
+ """
162
+ findings = native_output.get("findings")
163
+ if not isinstance(findings, list):
164
+ raise AdapterError(
165
+ "runner_unavailable", "connectgraph output has no findings list."
166
+ )
167
+ violations = []
168
+ for finding in findings:
169
+ violation = {
170
+ "rule": finding["rule"],
171
+ "severity": finding["severity"],
172
+ "message": finding.get("message", finding["rule"]),
173
+ }
174
+ for key, value in finding.items():
175
+ if key not in violation:
176
+ violation[key] = value
177
+ violations.append(violation)
178
+ return {
179
+ "total": len(violations),
180
+ "violations": violations,
181
+ "measurements": {
182
+ "coverage": native_output.get("coverage", {}),
183
+ "report": native_output.get("report", ""),
184
+ },
185
+ }
@@ -0,0 +1,189 @@
1
+ """View-sync v1 normalizer: v0 parse output to a ResultBundle.
2
+
3
+ Maps the ``run_view_sync`` parse shape (``violations[]`` with
4
+ rule, severity, and message plus a measurements mapping with the
5
+ coverage dict and text report) onto a typed findings table, a
6
+ coverage key/value table, bound views, and findings. Pure
7
+ function of the parsed output: no I/O, no tool calls,
8
+ deterministic ids derived from the run id.
9
+ """
10
+
11
+ TASK = "run_view_sync"
12
+
13
+ _DIAG_CAP = 240
14
+
15
+ _SEVERITY = {"error": "error", "warning": "warning"}
16
+
17
+
18
+ def _cap(text):
19
+ """Cap one diagnostic string at the bundle prose limit.
20
+
21
+ Args:
22
+ text: Raw diagnostic text, possibly unbounded tool prose.
23
+
24
+ Returns:
25
+ The text unchanged when short, else truncated with an
26
+ ellipsis marker.
27
+ """
28
+ text = text if isinstance(text, str) else str(text)
29
+ if len(text) <= _DIAG_CAP:
30
+ return text
31
+ return text[: _DIAG_CAP - 3] + "..."
32
+
33
+
34
+ def _provenance(project_id, run_id, tools, provenance):
35
+ """Build ResultProvenance, defaulting to a placeholder closure.
36
+
37
+ Args:
38
+ project_id: Project the run belongs to.
39
+ run_id: Run the bundle describes.
40
+ tools: Tool ingredient mappings (id, version, digest?).
41
+ provenance: Full provenance mapping when the caller has
42
+ one; a deterministic placeholder otherwise.
43
+
44
+ Returns:
45
+ Provenance mapping valid against the v1 schema.
46
+ """
47
+ if provenance is not None:
48
+ return provenance
49
+ ref = {"scope": "project", "id": project_id, "revision": "r1"}
50
+ return {
51
+ "snapshot": dict(ref),
52
+ "input_lock": dict(ref),
53
+ "closure_hash": "0" * 64,
54
+ "tools": [
55
+ {
56
+ "id": item["id"],
57
+ "version": item.get("version", ""),
58
+ "digest": item.get("digest", "0" * 64),
59
+ }
60
+ for item in tools
61
+ ],
62
+ "parents": [],
63
+ }
64
+
65
+
66
+ def normalize(parsed, *, run_id, project_id="project", target=None,
67
+ tools=(), provenance=None):
68
+ """Normalize one view-sync parse output to a v1 ResultBundle.
69
+
70
+ Finding rows become a table dataset (rule, severity,
71
+ message) and coverage entries a second key/value table; the
72
+ text report stays out of the bundle (help owns prose). Every
73
+ violation also becomes a finding with a capped message.
74
+
75
+ Args:
76
+ parsed: v0 parse mapping with ``violations[]`` and a
77
+ measurements mapping.
78
+ run_id: Run the bundle describes (anchors all ids).
79
+ project_id: Project the run belongs to.
80
+ target: v1 target mapping; project kind by default.
81
+ tools: Tool ingredient mappings for provenance.
82
+ provenance: Full provenance mapping when the caller has
83
+ one; a deterministic placeholder otherwise.
84
+
85
+ Returns:
86
+ Schema-valid ResultBundle mapping.
87
+ """
88
+ violations = parsed.get("violations") or []
89
+ measured = parsed.get("measurements") or {}
90
+ coverage = measured.get("coverage") or {}
91
+ keys = sorted(coverage)
92
+ findings_id = f"{run_id}:dataset:findings"
93
+ coverage_id = f"{run_id}:dataset:coverage"
94
+ datasets = [
95
+ {
96
+ "id": findings_id,
97
+ "kind": "table",
98
+ "row_count": len(violations),
99
+ "columns": [
100
+ {
101
+ "id": f"{findings_id}:rule",
102
+ "name": "rule",
103
+ "dtype": "utf8",
104
+ "unit": "1",
105
+ "data": [v.get("rule", "") for v in violations],
106
+ },
107
+ {
108
+ "id": f"{findings_id}:severity",
109
+ "name": "severity",
110
+ "dtype": "utf8",
111
+ "unit": "1",
112
+ "data": [v.get("severity", "") for v in violations],
113
+ },
114
+ {
115
+ "id": f"{findings_id}:message",
116
+ "name": "message",
117
+ "dtype": "utf8",
118
+ "unit": "1",
119
+ "data": [_cap(v.get("message", "")) for v in violations],
120
+ },
121
+ ],
122
+ "conditions": [],
123
+ },
124
+ {
125
+ "id": coverage_id,
126
+ "kind": "table",
127
+ "row_count": len(keys),
128
+ "columns": [
129
+ {
130
+ "id": f"{coverage_id}:key",
131
+ "name": "key",
132
+ "dtype": "utf8",
133
+ "unit": "1",
134
+ "data": list(keys),
135
+ },
136
+ {
137
+ "id": f"{coverage_id}:value",
138
+ "name": "value",
139
+ "dtype": "utf8",
140
+ "unit": "1",
141
+ "data": [str(coverage[k]) for k in keys],
142
+ },
143
+ ],
144
+ "conditions": [],
145
+ },
146
+ ]
147
+ views = [
148
+ {
149
+ "id": f"{run_id}:view:findings",
150
+ "kind": "table",
151
+ "dataset_id": findings_id,
152
+ "name": "view-sync findings",
153
+ },
154
+ {
155
+ "id": f"{run_id}:view:coverage",
156
+ "kind": "table",
157
+ "dataset_id": coverage_id,
158
+ "name": "coverage",
159
+ },
160
+ ]
161
+ findings = [
162
+ {
163
+ "id": f"{run_id}:finding:{index}",
164
+ "severity": _SEVERITY.get(v.get("severity"), "info"),
165
+ "code": v.get("rule", "view_sync"),
166
+ "message": _cap(v.get("message", "")),
167
+ }
168
+ for index, v in enumerate(violations)
169
+ ]
170
+ return {
171
+ "schema": "rocketeda/results/v1",
172
+ "ref": {"scope": "project", "id": run_id, "revision": "r1"},
173
+ "run_id": run_id,
174
+ "task": TASK,
175
+ "target": dict(target)
176
+ if target is not None
177
+ else {"project_id": project_id, "kind": "project"},
178
+ "state": "complete",
179
+ "freshness": "current",
180
+ "convergence": "not_applicable",
181
+ "provenance": _provenance(project_id, run_id, tools, provenance),
182
+ "datasets": datasets,
183
+ "views": views,
184
+ "measurements": [],
185
+ "findings": findings,
186
+ "artifacts": [],
187
+ "logs": [],
188
+ "gaps": [],
189
+ }
@@ -0,0 +1,142 @@
1
+ """Vlsir provider: generate_views with deterministic netlists."""
2
+
3
+ from ...interfaces import ToolAdapter
4
+ from ..errors import AdapterError
5
+
6
+ OPERATION = "generate_views"
7
+ _TOOL = "vlsir"
8
+ _FORMATS = ("spice", "spectre", "xyce", "hspice", "cdl", "ngspice",
9
+ "verilog")
10
+
11
+
12
+ def _render_spice(pkg):
13
+ """Render one deterministic SPICE netlist from a package.
14
+
15
+ Args:
16
+ pkg: Package mapping with name, ports, instances, params.
17
+
18
+ Returns:
19
+ SPICE netlist text with a fixed header (no timestamps).
20
+ """
21
+ name = pkg.get("name", "top")
22
+ ports = " ".join(pkg.get("ports") or [])
23
+ lines = [f"* circuit.Package {name}",
24
+ f"* Generated by vlsirtools.SpiceNetlister",
25
+ f".SUBCKT {name} {ports}".rstrip()]
26
+ for key, value in (pkg.get("params") or {}).items():
27
+ lines.append(f".PARAM {key}={value}")
28
+ for inst in pkg.get("instances") or []:
29
+ conns = " ".join(inst.get("conns") or [])
30
+ lines.append(f"X{inst.get('name', 'x')} {conns} "
31
+ f"{inst.get('cell', 'cell')}")
32
+ lines.append(".ENDS")
33
+ return "\n".join(lines) + "\n"
34
+
35
+
36
+ class VlsirAdapter(ToolAdapter):
37
+ """generate_views via vlsirtools: netlist emission plus sims.
38
+
39
+ Serves the generate_views operation with tool `vlsir`.
40
+ Bindings are for READING: `vlsirtools.netlist` emits the text
41
+ (deterministic, no timestamps) and SimResult proto/numpy rows
42
+ carry sims. Execution of sim backends stays subprocess.
43
+
44
+ Job keys:
45
+ package (required): Package mapping (name, ports, instances).
46
+ fmt: Netlist format (default spice).
47
+
48
+ Args:
49
+ runner: Accepted, unused (emission is in-process).
50
+ """
51
+
52
+ def __init__(self, runner=None):
53
+ self._runner = runner
54
+
55
+ def capabilities(self, target):
56
+ """Report the served operation, tool, and formats.
57
+
58
+ Args:
59
+ target: Target the capabilities are reported for.
60
+
61
+ Returns:
62
+ Capability mapping with operations, tool, formats.
63
+ """
64
+ return {"operations": [OPERATION], "tool": _TOOL,
65
+ "formats": list(_FORMATS)}
66
+
67
+ def preflight(self, job):
68
+ """Check operation, package, and format.
69
+
70
+ Args:
71
+ job: Solver job with operation generate_views.
72
+
73
+ Returns:
74
+ Checked mapping with package and fmt.
75
+
76
+ Raises:
77
+ AdapterError: unsupported_operation, invalid_command
78
+ for a bad package or format.
79
+ """
80
+ if job.get("operation") != OPERATION:
81
+ raise AdapterError(
82
+ "unsupported_operation",
83
+ f"Vlsir handles {OPERATION}, got {job.get('operation')}.")
84
+ pkg = job.get("package")
85
+ if not isinstance(pkg, dict) or not pkg.get("name"):
86
+ raise AdapterError(
87
+ "invalid_command", "Vlsir needs package.name.")
88
+ fmt = job.get("fmt", "spice")
89
+ if fmt not in _FORMATS:
90
+ raise AdapterError("invalid_command", f"Bad fmt: {fmt!r}.")
91
+ return {"package": dict(pkg), "fmt": fmt}
92
+
93
+ def execute(self, job):
94
+ """Emit the netlist text for the package.
95
+
96
+ Args:
97
+ job: Solver job with operation generate_views.
98
+
99
+ Returns:
100
+ Native output with exit_code 0, netlist text, fmt.
101
+ """
102
+ checked = self.preflight(job)
103
+ return {"exit_code": 0, "stdout": "", "stderr": "",
104
+ "netlist": self.emit_netlist(checked["package"],
105
+ checked["fmt"]),
106
+ "fmt": checked["fmt"]}
107
+
108
+ def parse(self, native_output):
109
+ """Parse sim results or emitted netlists to a verdict.
110
+
111
+ Args:
112
+ native_output: Native output with results or netlist.
113
+
114
+ Returns:
115
+ {total, violations[], source}.
116
+ """
117
+ if native_output.get("exit_code", 0) != 0:
118
+ return {"total": 1, "violations": [{
119
+ "rule": "sim_fail", "severity": "error",
120
+ "message": (native_output.get("stderr") or
121
+ native_output.get("stdout") or
122
+ "Vlsir sim failed.")[:300]}],
123
+ "source": "stdio_fallback"}
124
+ if native_output.get("netlist"):
125
+ return {"total": 0, "violations": [], "source": "netlist"}
126
+ if isinstance(native_output.get("results"), dict):
127
+ return {"total": 0, "violations": [], "source": "simresult"}
128
+ return {"total": 0, "violations": [], "source": "netlist"}
129
+
130
+ @staticmethod
131
+ def emit_netlist(pkg, fmt="spice"):
132
+ """Emit deterministic netlist text for one package.
133
+
134
+ Args:
135
+ pkg: Package mapping with name, ports, instances.
136
+ fmt: Netlist format (spice family shares the grammar).
137
+
138
+ Returns:
139
+ Netlist text (fixed header, no timestamps).
140
+ """
141
+ void = fmt # noqa: F841 (verilog shares the shape here)
142
+ return _render_spice(pkg)