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.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
"""Engine ops mixin: Submit-immediate admission paths (CRUD, editors, place-cell, pyexec)."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import uuid
|
|
5
|
+
from ..execution.console.lifecycle import console_envelope
|
|
6
|
+
from ..interfaces.yaml_dump import dump_yaml_text
|
|
7
|
+
from ..library.errors import LibraryError
|
|
8
|
+
from ..project.errors import ProjectError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
#: Wave-3 editor operation -> required argument names.
|
|
12
|
+
_EDITOR_OPS = {
|
|
13
|
+
"apply_transaction": ("changes",),
|
|
14
|
+
"create_shape": ("shape_type", "layer", "data"),
|
|
15
|
+
"create_sheet": ("name",),
|
|
16
|
+
"place_instance": ("sheet", "symbol", "x", "y"),
|
|
17
|
+
"bind_block": ("parent_sheet", "child_sheet", "instance"),
|
|
18
|
+
"create_symbol": ("name", "pins"),
|
|
19
|
+
"create_cell": ("name", "ports"),
|
|
20
|
+
"map_pins": ("symbol", "cell", "pin_map"),
|
|
21
|
+
"ip_register": ("pdk", "name", "version", "symbol", "cell"),
|
|
22
|
+
"ip_instantiate": ("pdk", "name", "sheet", "x", "y"),
|
|
23
|
+
"ip_promote": ("pdk", "name", "version"),
|
|
24
|
+
"bind_layers": ("profile", "layers"),
|
|
25
|
+
"annotate": (),
|
|
26
|
+
"create_testbench": ("name", "kind", "targets"),
|
|
27
|
+
"regenerate_testbench": ("testbench",),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _SubmitMixin:
|
|
32
|
+
"""Submit-immediate admission paths (CRUD, editors, place-cell, pyexec)."""
|
|
33
|
+
|
|
34
|
+
def _submit_crud(self, name, arguments, command):
|
|
35
|
+
"""Execute one CRUD/rewind/editor mutation immediately (no queue)."""
|
|
36
|
+
if name in _EDITOR_OPS:
|
|
37
|
+
return self._submit_editor(name, arguments, command)
|
|
38
|
+
command_text = dump_yaml_text(
|
|
39
|
+
{"operation": name, "arguments": arguments}
|
|
40
|
+
)
|
|
41
|
+
request_id = str(command.get("request_id") or uuid.uuid4().hex)
|
|
42
|
+
if name == "create_project":
|
|
43
|
+
if "new_project_id" not in arguments:
|
|
44
|
+
raise ProjectError(
|
|
45
|
+
"invalid_command",
|
|
46
|
+
"create_project needs a new_project_id argument.",
|
|
47
|
+
)
|
|
48
|
+
return self._project.create_project(
|
|
49
|
+
arguments["new_project_id"], request_id, command_text
|
|
50
|
+
)
|
|
51
|
+
if name == "write_doc":
|
|
52
|
+
if "path" not in arguments or "content" not in arguments:
|
|
53
|
+
raise ProjectError(
|
|
54
|
+
"invalid_command",
|
|
55
|
+
"write_doc needs path and content arguments.",
|
|
56
|
+
)
|
|
57
|
+
return self._project.write_doc(
|
|
58
|
+
arguments["path"],
|
|
59
|
+
arguments["content"],
|
|
60
|
+
request_id,
|
|
61
|
+
command_text,
|
|
62
|
+
expected_revision=arguments.get("expected_revision"),
|
|
63
|
+
)
|
|
64
|
+
if name == "delete_doc":
|
|
65
|
+
if "path" not in arguments:
|
|
66
|
+
raise ProjectError(
|
|
67
|
+
"invalid_command", "delete_doc needs a path argument."
|
|
68
|
+
)
|
|
69
|
+
return self._project.delete_doc(
|
|
70
|
+
arguments["path"],
|
|
71
|
+
request_id,
|
|
72
|
+
command_text,
|
|
73
|
+
expected_revision=arguments.get("expected_revision"),
|
|
74
|
+
)
|
|
75
|
+
if name == "rewind":
|
|
76
|
+
if "target_revision" not in arguments:
|
|
77
|
+
raise ProjectError(
|
|
78
|
+
"invalid_command",
|
|
79
|
+
"rewind needs a target_revision argument.",
|
|
80
|
+
)
|
|
81
|
+
return self._project.rewind(
|
|
82
|
+
arguments["target_revision"],
|
|
83
|
+
request_id,
|
|
84
|
+
command_text,
|
|
85
|
+
expected_revision=arguments.get("expected_revision"),
|
|
86
|
+
)
|
|
87
|
+
if name == "write_settings":
|
|
88
|
+
if "kind" not in arguments or "settings" not in arguments:
|
|
89
|
+
raise ProjectError(
|
|
90
|
+
"invalid_command",
|
|
91
|
+
"write_settings needs kind and settings arguments.",
|
|
92
|
+
)
|
|
93
|
+
return self._project.write_settings(
|
|
94
|
+
arguments["kind"],
|
|
95
|
+
arguments["settings"],
|
|
96
|
+
request_id,
|
|
97
|
+
command_text,
|
|
98
|
+
expected_revision=arguments.get("expected_revision"),
|
|
99
|
+
)
|
|
100
|
+
if name == "create_entity":
|
|
101
|
+
return self._project.create_entity(
|
|
102
|
+
request_id,
|
|
103
|
+
command_text,
|
|
104
|
+
kind=arguments.get("kind", ""),
|
|
105
|
+
components=arguments.get("components"),
|
|
106
|
+
expected_revision=arguments.get("expected_revision"),
|
|
107
|
+
)
|
|
108
|
+
if name in ("set_component", "attach_component"):
|
|
109
|
+
if (
|
|
110
|
+
"entity" not in arguments
|
|
111
|
+
or "component" not in arguments
|
|
112
|
+
or "data" not in arguments
|
|
113
|
+
):
|
|
114
|
+
raise ProjectError(
|
|
115
|
+
"invalid_command",
|
|
116
|
+
f"{name} needs entity, component, and data arguments.",
|
|
117
|
+
)
|
|
118
|
+
method = getattr(self._project, name)
|
|
119
|
+
return method(
|
|
120
|
+
arguments["entity"],
|
|
121
|
+
arguments["component"],
|
|
122
|
+
arguments["data"],
|
|
123
|
+
request_id,
|
|
124
|
+
command_text,
|
|
125
|
+
expected_revision=arguments.get("expected_revision"),
|
|
126
|
+
expected_entity_revision=arguments.get(
|
|
127
|
+
"expected_entity_revision"
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
if name == "detach_component":
|
|
131
|
+
if "entity" not in arguments or "component" not in arguments:
|
|
132
|
+
raise ProjectError(
|
|
133
|
+
"invalid_command",
|
|
134
|
+
"detach_component needs entity and component arguments.",
|
|
135
|
+
)
|
|
136
|
+
return self._project.detach_component(
|
|
137
|
+
arguments["entity"],
|
|
138
|
+
arguments["component"],
|
|
139
|
+
request_id,
|
|
140
|
+
command_text,
|
|
141
|
+
expected_revision=arguments.get("expected_revision"),
|
|
142
|
+
expected_entity_revision=arguments.get(
|
|
143
|
+
"expected_entity_revision"
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
if name == "delete_entity":
|
|
147
|
+
if "entity" not in arguments:
|
|
148
|
+
raise ProjectError(
|
|
149
|
+
"invalid_command",
|
|
150
|
+
"delete_entity needs an entity argument.",
|
|
151
|
+
)
|
|
152
|
+
return self._project.delete_entity(
|
|
153
|
+
arguments["entity"],
|
|
154
|
+
request_id,
|
|
155
|
+
command_text,
|
|
156
|
+
expected_revision=arguments.get("expected_revision"),
|
|
157
|
+
expected_entity_revision=arguments.get(
|
|
158
|
+
"expected_entity_revision"
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
if name == "place_wire":
|
|
162
|
+
for key in ("segments", "layer", "endpoints"):
|
|
163
|
+
if key not in arguments:
|
|
164
|
+
raise ProjectError(
|
|
165
|
+
"invalid_command",
|
|
166
|
+
f"place_wire needs a {key} argument.",
|
|
167
|
+
)
|
|
168
|
+
return self._project.place_wire(
|
|
169
|
+
arguments["segments"],
|
|
170
|
+
arguments["layer"],
|
|
171
|
+
arguments["endpoints"],
|
|
172
|
+
request_id,
|
|
173
|
+
command_text,
|
|
174
|
+
expected_revision=arguments.get("expected_revision"),
|
|
175
|
+
)
|
|
176
|
+
if name == "place_junction":
|
|
177
|
+
for key in ("xy", "layer"):
|
|
178
|
+
if key not in arguments:
|
|
179
|
+
raise ProjectError(
|
|
180
|
+
"invalid_command",
|
|
181
|
+
f"place_junction needs a {key} argument.",
|
|
182
|
+
)
|
|
183
|
+
return self._project.place_junction(
|
|
184
|
+
arguments["xy"],
|
|
185
|
+
arguments["layer"],
|
|
186
|
+
request_id,
|
|
187
|
+
command_text,
|
|
188
|
+
expected_revision=arguments.get("expected_revision"),
|
|
189
|
+
)
|
|
190
|
+
if name == "connect_endpoint":
|
|
191
|
+
for key in ("wire_id", "end", "ref"):
|
|
192
|
+
if key not in arguments:
|
|
193
|
+
raise ProjectError(
|
|
194
|
+
"invalid_command",
|
|
195
|
+
f"connect_endpoint needs a {key} argument.",
|
|
196
|
+
)
|
|
197
|
+
return self._project.connect_endpoint(
|
|
198
|
+
arguments["wire_id"],
|
|
199
|
+
arguments["end"],
|
|
200
|
+
arguments["ref"],
|
|
201
|
+
request_id,
|
|
202
|
+
command_text,
|
|
203
|
+
expected_revision=arguments.get("expected_revision"),
|
|
204
|
+
)
|
|
205
|
+
if name == "move_wire":
|
|
206
|
+
for key in ("wire_id", "dx", "dy"):
|
|
207
|
+
if key not in arguments:
|
|
208
|
+
raise ProjectError(
|
|
209
|
+
"invalid_command",
|
|
210
|
+
f"move_wire needs a {key} argument.",
|
|
211
|
+
)
|
|
212
|
+
return self._project.move_wire(
|
|
213
|
+
arguments["wire_id"],
|
|
214
|
+
arguments["dx"],
|
|
215
|
+
arguments["dy"],
|
|
216
|
+
request_id,
|
|
217
|
+
command_text,
|
|
218
|
+
expected_revision=arguments.get("expected_revision"),
|
|
219
|
+
)
|
|
220
|
+
if name == "split_wire":
|
|
221
|
+
for key in ("wire_id", "at"):
|
|
222
|
+
if key not in arguments:
|
|
223
|
+
raise ProjectError(
|
|
224
|
+
"invalid_command",
|
|
225
|
+
f"split_wire needs a {key} argument.",
|
|
226
|
+
)
|
|
227
|
+
return self._project.split_wire(
|
|
228
|
+
arguments["wire_id"],
|
|
229
|
+
arguments["at"],
|
|
230
|
+
request_id,
|
|
231
|
+
command_text,
|
|
232
|
+
expected_revision=arguments.get("expected_revision"),
|
|
233
|
+
)
|
|
234
|
+
if name == "delete_wire":
|
|
235
|
+
if "wire_id" not in arguments:
|
|
236
|
+
raise ProjectError(
|
|
237
|
+
"invalid_command",
|
|
238
|
+
"delete_wire needs a wire_id argument.",
|
|
239
|
+
)
|
|
240
|
+
return self._project.delete_wire(
|
|
241
|
+
arguments["wire_id"],
|
|
242
|
+
request_id,
|
|
243
|
+
command_text,
|
|
244
|
+
expected_revision=arguments.get("expected_revision"),
|
|
245
|
+
)
|
|
246
|
+
if name == "delete_junction":
|
|
247
|
+
if "junction_id" not in arguments:
|
|
248
|
+
raise ProjectError(
|
|
249
|
+
"invalid_command",
|
|
250
|
+
"delete_junction needs a junction_id argument.",
|
|
251
|
+
)
|
|
252
|
+
return self._project.delete_junction(
|
|
253
|
+
arguments["junction_id"],
|
|
254
|
+
request_id,
|
|
255
|
+
command_text,
|
|
256
|
+
expected_revision=arguments.get("expected_revision"),
|
|
257
|
+
)
|
|
258
|
+
if name == "materialize_nets":
|
|
259
|
+
if "pins" not in arguments:
|
|
260
|
+
raise ProjectError(
|
|
261
|
+
"invalid_command",
|
|
262
|
+
"materialize_nets needs a pins argument.",
|
|
263
|
+
)
|
|
264
|
+
return self._project.materialize_nets(
|
|
265
|
+
arguments["pins"],
|
|
266
|
+
request_id,
|
|
267
|
+
command_text,
|
|
268
|
+
labels=arguments.get("labels"),
|
|
269
|
+
ports=arguments.get("ports"),
|
|
270
|
+
cell=arguments.get("cell", "top"),
|
|
271
|
+
named_refs=arguments.get("named_refs"),
|
|
272
|
+
expected_revision=arguments.get("expected_revision"),
|
|
273
|
+
)
|
|
274
|
+
if "mutations" not in arguments:
|
|
275
|
+
raise ProjectError(
|
|
276
|
+
"invalid_command",
|
|
277
|
+
"apply_mutations needs a mutations argument.",
|
|
278
|
+
)
|
|
279
|
+
return self._project.apply_mutations(
|
|
280
|
+
arguments["mutations"],
|
|
281
|
+
request_id,
|
|
282
|
+
command_text,
|
|
283
|
+
expected_revision=arguments.get("expected_revision"),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def _submit_editor(self, name, arguments, command):
|
|
287
|
+
"""Execute one Wave-3 editor/testbench op immediately.
|
|
288
|
+
|
|
289
|
+
Required arguments are refused loudly before anything is
|
|
290
|
+
admitted; the ProjectService verb owns revision gating,
|
|
291
|
+
idempotent replay, and I28 pins.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
name: Wave-3 editor operation name.
|
|
295
|
+
arguments: Operation arguments mapping.
|
|
296
|
+
command: Envelope carrying request_id.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Mutation mapping for the editor verb.
|
|
300
|
+
|
|
301
|
+
Raises:
|
|
302
|
+
ProjectError: invalid_command for missing arguments.
|
|
303
|
+
"""
|
|
304
|
+
missing = [
|
|
305
|
+
key for key in _EDITOR_OPS[name] if key not in arguments
|
|
306
|
+
]
|
|
307
|
+
if missing:
|
|
308
|
+
raise ProjectError(
|
|
309
|
+
"invalid_command",
|
|
310
|
+
f"{name} needs {', '.join(sorted(missing))}.",
|
|
311
|
+
)
|
|
312
|
+
request_id = str(command.get("request_id") or uuid.uuid4().hex)
|
|
313
|
+
command_text = dump_yaml_text(
|
|
314
|
+
{"operation": name, "arguments": arguments}
|
|
315
|
+
)
|
|
316
|
+
head = arguments.get("expected_revision")
|
|
317
|
+
project = self._project
|
|
318
|
+
if name == "apply_transaction":
|
|
319
|
+
return project.apply_transaction(
|
|
320
|
+
arguments["changes"], request_id, command_text,
|
|
321
|
+
expected_revision=head)
|
|
322
|
+
if name == "create_shape":
|
|
323
|
+
return project.create_shape(
|
|
324
|
+
arguments["shape_type"], arguments["layer"],
|
|
325
|
+
arguments["data"], request_id, command_text,
|
|
326
|
+
units=arguments.get("units", "nm"),
|
|
327
|
+
profile=arguments.get("profile"),
|
|
328
|
+
anchor=arguments.get("anchor"),
|
|
329
|
+
expected_revision=head)
|
|
330
|
+
if name == "create_sheet":
|
|
331
|
+
return project.create_sheet(
|
|
332
|
+
arguments["name"], request_id, command_text,
|
|
333
|
+
title=arguments.get("title", ""),
|
|
334
|
+
expected_revision=head)
|
|
335
|
+
if name == "place_instance":
|
|
336
|
+
return project.place_instance(
|
|
337
|
+
arguments["sheet"], arguments["symbol"],
|
|
338
|
+
arguments["x"], arguments["y"], request_id,
|
|
339
|
+
command_text,
|
|
340
|
+
rotation=arguments.get("rotation", 0),
|
|
341
|
+
mirror=arguments.get("mirror", False),
|
|
342
|
+
properties=arguments.get("properties"),
|
|
343
|
+
ref=arguments.get("ref", ""),
|
|
344
|
+
expected_revision=head)
|
|
345
|
+
if name == "bind_block":
|
|
346
|
+
return project.bind_block(
|
|
347
|
+
arguments["parent_sheet"],
|
|
348
|
+
arguments["child_sheet"], arguments["instance"],
|
|
349
|
+
request_id, command_text,
|
|
350
|
+
expected_revision=head)
|
|
351
|
+
if name == "create_symbol":
|
|
352
|
+
return project.create_symbol(
|
|
353
|
+
arguments["name"], arguments["pins"], request_id,
|
|
354
|
+
command_text, graphics=arguments.get("graphics"),
|
|
355
|
+
properties=arguments.get("properties"),
|
|
356
|
+
expected_revision=head)
|
|
357
|
+
if name == "create_cell":
|
|
358
|
+
return project.create_cell(
|
|
359
|
+
arguments["name"], arguments["ports"], request_id,
|
|
360
|
+
command_text,
|
|
361
|
+
properties=arguments.get("properties"),
|
|
362
|
+
expected_revision=head)
|
|
363
|
+
if name == "map_pins":
|
|
364
|
+
return project.map_pins(
|
|
365
|
+
arguments["symbol"], arguments["cell"],
|
|
366
|
+
arguments["pin_map"], request_id, command_text,
|
|
367
|
+
expected_revision=head)
|
|
368
|
+
if name == "ip_register":
|
|
369
|
+
return project.ip_register(
|
|
370
|
+
arguments["pdk"], arguments["name"],
|
|
371
|
+
arguments["version"], arguments["symbol"],
|
|
372
|
+
arguments["cell"], request_id, command_text,
|
|
373
|
+
pin_map=arguments.get("pin_map"),
|
|
374
|
+
scope=arguments.get("scope", "pdk"),
|
|
375
|
+
reason=arguments.get("reason", ""),
|
|
376
|
+
expected_revision=head)
|
|
377
|
+
if name == "ip_instantiate":
|
|
378
|
+
return project.ip_instantiate(
|
|
379
|
+
arguments["pdk"], arguments["name"],
|
|
380
|
+
arguments["sheet"], arguments["x"],
|
|
381
|
+
arguments["y"], request_id, command_text,
|
|
382
|
+
version=arguments.get("version"),
|
|
383
|
+
rotation=arguments.get("rotation", 0),
|
|
384
|
+
mirror=arguments.get("mirror", False),
|
|
385
|
+
properties=arguments.get("properties"),
|
|
386
|
+
expected_revision=head)
|
|
387
|
+
if name == "ip_promote":
|
|
388
|
+
return project.ip_promote(
|
|
389
|
+
arguments["pdk"], arguments["name"],
|
|
390
|
+
arguments["version"], request_id, command_text,
|
|
391
|
+
expected_revision=head)
|
|
392
|
+
if name == "bind_layers":
|
|
393
|
+
return project.bind_layers(
|
|
394
|
+
arguments["profile"], arguments["layers"],
|
|
395
|
+
request_id, command_text,
|
|
396
|
+
source=arguments.get("source", "profile"),
|
|
397
|
+
version=arguments.get("version", "unknown"),
|
|
398
|
+
expected_revision=head)
|
|
399
|
+
if name == "annotate":
|
|
400
|
+
return project.annotate(
|
|
401
|
+
request_id, command_text,
|
|
402
|
+
prefixes=arguments.get("prefixes"),
|
|
403
|
+
sheet=arguments.get("sheet"),
|
|
404
|
+
expected_revision=head)
|
|
405
|
+
if name == "create_testbench":
|
|
406
|
+
return project.create_testbench(
|
|
407
|
+
arguments["name"], arguments["kind"],
|
|
408
|
+
arguments["targets"], request_id, command_text,
|
|
409
|
+
probes=arguments.get("probes"),
|
|
410
|
+
harness=arguments.get("harness"),
|
|
411
|
+
template=arguments.get("template"),
|
|
412
|
+
expected_revision=head)
|
|
413
|
+
return project.regenerate_testbench(
|
|
414
|
+
arguments["testbench"], request_id, command_text,
|
|
415
|
+
expected_revision=head)
|
|
416
|
+
|
|
417
|
+
def _submit_place_cell(self, arguments, command):
|
|
418
|
+
"""Place a catalogue cell immediately (no queue, no solver).
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
arguments: place_cell arguments (cell plus the
|
|
422
|
+
optional pdk, instance_id, params, connections,
|
|
423
|
+
path_prefix, and expected_revision).
|
|
424
|
+
command: Envelope carrying request_id.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Mutation mapping for the placed instance.
|
|
428
|
+
|
|
429
|
+
Raises:
|
|
430
|
+
ProjectError: invalid_command for a missing cell, or
|
|
431
|
+
the catalogue codes (unknown_cell,
|
|
432
|
+
ambiguous_cell, unknown_param,
|
|
433
|
+
incompatible_port, instance_exists) for a bad
|
|
434
|
+
placement.
|
|
435
|
+
"""
|
|
436
|
+
if "cell" not in arguments:
|
|
437
|
+
raise ProjectError(
|
|
438
|
+
"invalid_command", "place_cell needs a cell argument."
|
|
439
|
+
)
|
|
440
|
+
request_id = str(command.get("request_id") or uuid.uuid4().hex)
|
|
441
|
+
try:
|
|
442
|
+
return self._cells.place_cell(
|
|
443
|
+
self._project,
|
|
444
|
+
arguments["cell"],
|
|
445
|
+
request_id,
|
|
446
|
+
expected_revision=arguments.get("expected_revision"),
|
|
447
|
+
pdk=arguments.get("pdk"),
|
|
448
|
+
instance_id=arguments.get("instance_id"),
|
|
449
|
+
params=arguments.get("params"),
|
|
450
|
+
connections=arguments.get("connections"),
|
|
451
|
+
path_prefix=arguments.get("path_prefix", "components"),
|
|
452
|
+
)
|
|
453
|
+
except LibraryError as exc:
|
|
454
|
+
raise ProjectError(exc.code, str(exc)) from exc
|
|
455
|
+
|
|
456
|
+
def apply_batch(self, batch):
|
|
457
|
+
"""Land one apply-context batch as a single transaction.
|
|
458
|
+
|
|
459
|
+
The session compiler turns staged sink intents into entity
|
|
460
|
+
changes; they submit through one ``apply_transaction`` on
|
|
461
|
+
the live head, so a console batch is atomic exactly like an
|
|
462
|
+
editor batch. The request id derives from the batch
|
|
463
|
+
envelope, so replaying the same batch is idempotent
|
|
464
|
+
(same receipt, no new revision) while a reused id with
|
|
465
|
+
different arguments conflicts loudly instead of landing
|
|
466
|
+
twice.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
batch: Emitted batch with label and intents.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
Receipt with the batch label, the new head revision,
|
|
473
|
+
landed kinds, declared-unlanded entries, and the
|
|
474
|
+
request id.
|
|
475
|
+
|
|
476
|
+
Raises:
|
|
477
|
+
ProjectError: invalid_command for a malformed batch
|
|
478
|
+
or an unknown sink kind, revision_conflict for a
|
|
479
|
+
head that moved under the batch, request_conflict
|
|
480
|
+
for a reused id with different arguments.
|
|
481
|
+
"""
|
|
482
|
+
from ..execution.pyexec.apply_context import compile_batch
|
|
483
|
+
|
|
484
|
+
label = batch.get("label", "")
|
|
485
|
+
intents = batch.get("intents", ())
|
|
486
|
+
envelope = batch.get("envelope") or dump_yaml_text(
|
|
487
|
+
{"operation": "apply_batch", "arguments": batch}
|
|
488
|
+
)
|
|
489
|
+
compiled = compile_batch(batch)
|
|
490
|
+
request_id = "apply-" + hashlib.sha256(
|
|
491
|
+
envelope.encode("utf-8")).hexdigest()[:32]
|
|
492
|
+
command_text = dump_yaml_text(
|
|
493
|
+
{"operation": "apply_batch",
|
|
494
|
+
"arguments": {"label": label, "intents": intents}}
|
|
495
|
+
)
|
|
496
|
+
head = self._project.snapshot()["current_revision"]
|
|
497
|
+
landed = self._project.apply_transaction(
|
|
498
|
+
compiled["changes"], request_id, command_text,
|
|
499
|
+
expected_revision=head)
|
|
500
|
+
return {
|
|
501
|
+
"ok": True,
|
|
502
|
+
"operation": "apply_batch",
|
|
503
|
+
"project_id": self._project_id,
|
|
504
|
+
"label": label,
|
|
505
|
+
"revision": landed["current_revision"],
|
|
506
|
+
"previous_revision": landed.get("previous_revision"),
|
|
507
|
+
"landed": compiled["landed"],
|
|
508
|
+
"unlanded": compiled["unlanded"],
|
|
509
|
+
"applied": len(compiled["changes"]),
|
|
510
|
+
"request_id": request_id,
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
def _submit_pyexec(self, arguments, command):
|
|
514
|
+
"""Admit a python_execute request: gate, then open an approval.
|
|
515
|
+
|
|
516
|
+
Shape validation runs before the revision gate so malformed
|
|
517
|
+
requests fail before anything is persisted or queued; the
|
|
518
|
+
gated submit then advances the revision and appends
|
|
519
|
+
job_queued like every other operation. The ack run_id IS
|
|
520
|
+
the execution id: approval admits it, run_job executes it.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
arguments: python_execute arguments (mode plus lines
|
|
524
|
+
or path, timeout_s, artifacts, expected_revision).
|
|
525
|
+
command: Envelope carrying request_id.
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
Admission mapping with the ack fields plus
|
|
529
|
+
execution_id, approval_id, and pending_approval
|
|
530
|
+
state. No run exists until the UI route approves.
|
|
531
|
+
|
|
532
|
+
Raises:
|
|
533
|
+
ProjectError: revision_conflict for a stale
|
|
534
|
+
expected_revision, request_conflict for a reused
|
|
535
|
+
envelope id with new text.
|
|
536
|
+
ExecutionError: invalid_command/invalid_cell/empty_cell
|
|
537
|
+
for malformed requests, path_escape/invalid_path/
|
|
538
|
+
cell_unreadable for bad project files.
|
|
539
|
+
"""
|
|
540
|
+
self._pyexec.validate_request(
|
|
541
|
+
arguments.get("mode"),
|
|
542
|
+
lines=arguments.get("lines"),
|
|
543
|
+
path=arguments.get("path"),
|
|
544
|
+
artifacts=arguments.get("artifacts", ()),
|
|
545
|
+
timeout_s=arguments.get("timeout_s"),
|
|
546
|
+
)
|
|
547
|
+
command_text = dump_yaml_text(
|
|
548
|
+
{"operation": "python_execute", "arguments": arguments})
|
|
549
|
+
request_id = command.get("request_id") or uuid.uuid4().hex
|
|
550
|
+
ack = self._project.submit(
|
|
551
|
+
command_text,
|
|
552
|
+
str(request_id),
|
|
553
|
+
expected_revision=arguments.get("expected_revision"),
|
|
554
|
+
)
|
|
555
|
+
handle = self._pyexec.request_execution(
|
|
556
|
+
arguments["mode"],
|
|
557
|
+
lines=arguments.get("lines"),
|
|
558
|
+
path=arguments.get("path"),
|
|
559
|
+
request_id=str(request_id),
|
|
560
|
+
run_id=ack["run_id"],
|
|
561
|
+
expected_revision=arguments.get("expected_revision"),
|
|
562
|
+
artifacts=arguments.get("artifacts", ()),
|
|
563
|
+
timeout_s=arguments.get("timeout_s"),
|
|
564
|
+
)
|
|
565
|
+
self._run_jobs.setdefault(
|
|
566
|
+
ack["run_id"],
|
|
567
|
+
{
|
|
568
|
+
"operation": "python_execute",
|
|
569
|
+
"arguments": dict(arguments),
|
|
570
|
+
"snapshot_id": ack["snapshot_id"],
|
|
571
|
+
"request_id": str(request_id),
|
|
572
|
+
},
|
|
573
|
+
)
|
|
574
|
+
return {**ack, **handle, "state": "pending_approval"}
|
|
575
|
+
|
|
576
|
+
def _submit_console(self, name, arguments, command):
|
|
577
|
+
"""Admit one console lifecycle op on the shared envelope.
|
|
578
|
+
|
|
579
|
+
Identity comes from the rendered console envelope, so
|
|
580
|
+
console history replays as command scripts by
|
|
581
|
+
construction. Consoles are Engine-memory and
|
|
582
|
+
workspace-bound — no ledger admission, no revision gate.
|
|
583
|
+
ExecutionError from the lifecycle (unknown_console)
|
|
584
|
+
passes through unwrapped, like python_execute refusals.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
name: python_open or python_reset.
|
|
588
|
+
arguments: Lifecycle arguments (workspace_root for
|
|
589
|
+
open, console_id for reset).
|
|
590
|
+
command: Envelope carrying request_id.
|
|
591
|
+
|
|
592
|
+
Returns:
|
|
593
|
+
Opening (open) or fresh generation (reset) plus
|
|
594
|
+
the request_id.
|
|
595
|
+
|
|
596
|
+
Raises:
|
|
597
|
+
ProjectError: invalid_command for an unknown console
|
|
598
|
+
operation name.
|
|
599
|
+
"""
|
|
600
|
+
if name not in ("python_open", "python_reset"):
|
|
601
|
+
raise ProjectError(
|
|
602
|
+
"invalid_command",
|
|
603
|
+
f"Not a console lifecycle operation: {name!r}.",
|
|
604
|
+
)
|
|
605
|
+
command_text = console_envelope(name, dict(arguments))
|
|
606
|
+
request_id = str(command.get("request_id") or uuid.uuid4().hex)
|
|
607
|
+
if name == "python_open":
|
|
608
|
+
out = self.open_console(
|
|
609
|
+
workspace_root=arguments.get("workspace_root"))
|
|
610
|
+
else:
|
|
611
|
+
out = self.reset_console(
|
|
612
|
+
console_id=arguments.get("console_id"))
|
|
613
|
+
return {**out, "request_id": request_id,
|
|
614
|
+
"command_text": command_text}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# project — revisioned project state and event ledger
|
|
2
|
+
|
|
3
|
+
`ProjectService` owns engine-mediated project state: documents, the project revision, idempotent mutation requests, snapshots, and the ordered event ledger. It also exposes the component/entity queries used by the engine's ECS-shaped design model.
|
|
4
|
+
|
|
5
|
+
## State and persistence
|
|
6
|
+
|
|
7
|
+
- Every mutation checks `expected_revision`. A stale head returns `revision_conflict`; changes are never merged silently.
|
|
8
|
+
- Accepted mutations advance the project revision and append an actor-attributed event. Reusing a `request_id` with the same command replays its stored result; reusing it with different command text is refused.
|
|
9
|
+
- With `engine_root` configured, project ledger files, events, managed documents, and rewind snapshots persist beneath `projects/<project_id>/`. Without an engine root, `ProjectService` keeps them in memory.
|
|
10
|
+
- Event reads are cursor-based and bounded by retention. A reader that requires missing history receives `resync_required` and reloads the snapshot.
|
|
11
|
+
- `rewind` restores engine-managed bytes under a new revision; it does not rewrite history or overwrite caller-owned files in a linked project.
|
|
12
|
+
- `ledger.yaml`, `events.yaml`, `runs/`, and `snapshots/` are engine-owned paths and cannot be written through document CRUD.
|
|
13
|
+
|
|
14
|
+
## Design entities
|
|
15
|
+
|
|
16
|
+
Entities and components are projected through the project document store. IDs are engine-minted; component changes use the same project mutation/event path. The service also handles settings and wire/junction operations through the corresponding interface stores. `get_world_snapshot`, `get_component`, `components_of`, and `entities_with` provide read surfaces for clients.
|
|
17
|
+
|
|
18
|
+
## Boundaries
|
|
19
|
+
|
|
20
|
+
`ProjectService` owns project revisions, documents, snapshots, idempotency, and event ordering. Solver output parsing and verdicts belong to [`execution/`](../execution/README.md); transport framing belongs to [`adapters/`](../adapters/README.md). `Engine` in [`composition.py`](../composition.py) routes operations into this service.
|
|
21
|
+
|
|
22
|
+
## Routing
|
|
23
|
+
|
|
24
|
+
- Contract shapes and path rules → [`interfaces/`](../interfaces/README.md).
|
|
25
|
+
- Run admission, results, and verdicts → [`execution/`](../execution/README.md).
|
|
26
|
+
- External behavior → [generated engine API](../../docs/engine-api.md) and [storage policy](../../docs/storage-policy.md).
|
|
27
|
+
- Product semantics → [PRD tooling spec](../../../PRD/tools-research/frontend-tooling-spec.md).
|