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,785 @@
1
+ """Host-side console namespace over ProjectService verbs."""
2
+
3
+ import uuid
4
+
5
+ from ...library.errors import LibraryError
6
+ from ...project.errors import ProjectError
7
+ from .calls import admission_envelope
8
+
9
+
10
+ class ConsoleNamespace:
11
+ """Host-side console namespace: tier-2 verbs through admission.
12
+
13
+ One thin wrapper per tier-2 verb, named identically to the
14
+ CRUD/MCP verbs and calling the matching ProjectService verb
15
+ with the same call shape the Engine uses (verb arguments, then
16
+ the envelope id plus identity text, revision bound by keyword).
17
+ Every wrapper mints an envelope id when the caller omits one,
18
+ builds the admission identity text over its arguments, and
19
+ lets admission judge: stale revisions conflict, resubmits
20
+ replay, and accepted mutations advance once with one
21
+ actor-attributed ledger record. ECS and wire verbs fail loudly
22
+ until their peer patches land; place_cell needs a catalogue.
23
+
24
+ Args:
25
+ project: Project port serving the public admission verbs
26
+ (ProjectService serves).
27
+ catalog: Component catalogue place_cell resolves through,
28
+ or None when cell placement stays unwired.
29
+ """
30
+
31
+ def __init__(self, project, catalog=None):
32
+ self._project = project
33
+ self._catalog = catalog
34
+
35
+ def _admit(self, verb, arguments, request_id, expected_revision):
36
+ """Envelope id plus identity text for one verb call."""
37
+ rid = request_id or uuid.uuid4().hex
38
+ payload = dict(arguments)
39
+ if expected_revision is not None:
40
+ payload["expected_revision"] = expected_revision
41
+ return rid, admission_envelope(verb, payload)
42
+
43
+ def _wired(self, verb, owner):
44
+ """One ECS/wire project verb, or loud pre-patch refusal."""
45
+ method = getattr(self._project, verb, None)
46
+ if method is None:
47
+ raise ProjectError(
48
+ "unsupported_operation",
49
+ f"Console tier-2 verb {verb!r} needs the {owner} "
50
+ "wiring, which is not applied yet.",
51
+ )
52
+ return method
53
+
54
+ def write_doc(self, path, content, request_id=None,
55
+ expected_revision=None):
56
+ """Write one document through admission.
57
+
58
+ Args:
59
+ path: Document path to create or overwrite.
60
+ content: UTF-8 text to store.
61
+ request_id: Client envelope id; resubmitting the
62
+ identical call returns the identical response
63
+ without advancing.
64
+ expected_revision: Revision the writer read.
65
+
66
+ Returns:
67
+ Mutation mapping with path, revision transition, new
68
+ head, actor, diff summary, rewind handle, the
69
+ appended record, cursor, and request id.
70
+
71
+ Raises:
72
+ ProjectError: request_conflict for a reused id with
73
+ new arguments; invalid_path or path_escape for a
74
+ bad path; invalid_command for non-text content;
75
+ revision_conflict for a stale head.
76
+
77
+ Caution: Every accepted write advances the head — a
78
+ second writer at the old head fails instead of
79
+ merging.
80
+ """
81
+ rid, text = self._admit(
82
+ "write_doc", {"path": path, "content": content},
83
+ request_id, expected_revision,
84
+ )
85
+ return self._project.write_doc(
86
+ path, content, rid, text,
87
+ expected_revision=expected_revision,
88
+ )
89
+
90
+ def delete_doc(self, path, request_id=None, expected_revision=None):
91
+ """Delete one document through admission.
92
+
93
+ Args:
94
+ path: Document path to delete.
95
+ request_id: Client envelope id for idempotent replay.
96
+ expected_revision: Revision the writer read.
97
+
98
+ Returns:
99
+ Mutation mapping with path, revision transition, new
100
+ head, actor, diff summary, rewind handle, the
101
+ appended record, cursor, and request id.
102
+
103
+ Raises:
104
+ ProjectError: request_conflict for a reused id with
105
+ new arguments; invalid_path or path_escape for a
106
+ bad path; unknown_document for a missing document;
107
+ revision_conflict for a stale head.
108
+
109
+ Caution: Deleting advances the head like any mutation —
110
+ stale writers fail instead of merging.
111
+ """
112
+ rid, text = self._admit(
113
+ "delete_doc", {"path": path}, request_id,
114
+ expected_revision,
115
+ )
116
+ return self._project.delete_doc(
117
+ path, rid, text, expected_revision=expected_revision
118
+ )
119
+
120
+ def apply_mutations(self, mutations, request_id=None,
121
+ expected_revision=None):
122
+ """Apply a batch atomically under one new head.
123
+
124
+ Args:
125
+ mutations: Non-empty list of write/delete mappings.
126
+ request_id: Client envelope id for idempotent replay.
127
+ expected_revision: Revision the writer read.
128
+
129
+ Returns:
130
+ Mutation mapping with revision transition, new head,
131
+ actor, joined diff summary, rewind handle, the
132
+ single appended record, per-mutation results,
133
+ cursor, and request id.
134
+
135
+ Raises:
136
+ ProjectError: request_conflict for a reused id with
137
+ new arguments; invalid_command for a malformed
138
+ batch; unknown_document for a delete of a missing
139
+ document; revision_conflict for a stale head.
140
+
141
+ Caution: All-or-nothing means one head for the whole
142
+ batch — a half-landed batch is a bug, never a state.
143
+ """
144
+ rid, text = self._admit(
145
+ "apply_mutations", {"mutations": mutations}, request_id,
146
+ expected_revision,
147
+ )
148
+ return self._project.apply_mutations(
149
+ mutations, rid, text, expected_revision=expected_revision
150
+ )
151
+
152
+ def place_cell(self, cell, request_id=None, expected_revision=None,
153
+ pdk=None, instance_id=None, params=None,
154
+ connections=None):
155
+ """Place a catalogue cell through batch admission.
156
+
157
+ Args:
158
+ cell: Catalogue cell name.
159
+ request_id: Client envelope id for idempotent replay.
160
+ expected_revision: Revision the writer read.
161
+ pdk: PDK label disambiguating same-named cells.
162
+ instance_id: Instance name; derived when omitted.
163
+ params: Override values for the cell defaults.
164
+ connections: Connection mappings over cell ports.
165
+
166
+ Returns:
167
+ Mutation mapping with the revision transition, new
168
+ head, actor, record, and request id.
169
+
170
+ Raises:
171
+ ProjectError: unsupported_operation without a
172
+ catalogue; unknown_cell, ambiguous_cell,
173
+ unknown_param, incompatible_port, or
174
+ instance_exists for a bad placement (catalogue
175
+ codes surface unchanged); revision_conflict for
176
+ a stale head; request_conflict for a reused id
177
+ with new arguments.
178
+ """
179
+ if self._catalog is None:
180
+ raise ProjectError(
181
+ "unsupported_operation",
182
+ "place_cell needs a catalogue; this namespace was "
183
+ "built without one.",
184
+ )
185
+ try:
186
+ return self._catalog.place_cell(
187
+ self._project, cell, request_id or uuid.uuid4().hex,
188
+ expected_revision=expected_revision, pdk=pdk,
189
+ instance_id=instance_id, params=params,
190
+ connections=connections,
191
+ )
192
+ except LibraryError as exc:
193
+ raise ProjectError(exc.code, str(exc)) from exc
194
+
195
+ def create_entity(self, kind="", components=None, request_id=None,
196
+ expected_revision=None):
197
+ """Create one entity with an engine-minted id.
198
+
199
+ Args:
200
+ kind: Kind label carried beside the components.
201
+ components: Optional initial component mapping.
202
+ request_id: Client envelope id for idempotent replay.
203
+ expected_revision: Revision the writer read.
204
+
205
+ Returns:
206
+ Mutation mapping with the minted entity, kind, storage
207
+ path, revision transition, new head, actor, diff
208
+ summary, rewind handle, the entity_created record,
209
+ cursor, and request id.
210
+
211
+ Raises:
212
+ ProjectError: unsupported_operation until the
213
+ ENGINE-ECS wiring lands; request_conflict for a
214
+ reused id with new arguments; invalid_kind,
215
+ invalid_component, or invalid_data for malformed
216
+ parts; revision_conflict for a stale head.
217
+
218
+ Caution: Every accepted create advances the head — a
219
+ second writer at the old head fails instead of
220
+ merging.
221
+ """
222
+ arguments = {"kind": kind}
223
+ if components is not None:
224
+ arguments["components"] = components
225
+ rid, text = self._admit(
226
+ "create_entity", arguments, request_id, expected_revision
227
+ )
228
+ return self._wired("create_entity", "ENGINE-ECS")(
229
+ rid, text, kind=kind, components=components,
230
+ expected_revision=expected_revision,
231
+ )
232
+
233
+ def set_component(self, entity, component, data, request_id=None,
234
+ expected_revision=None,
235
+ expected_entity_revision=None):
236
+ """Upsert one component payload through admission.
237
+
238
+ Args:
239
+ entity: Entity id holding the component.
240
+ component: Component name to write.
241
+ data: JSON payload to store.
242
+ request_id: Client envelope id for idempotent replay.
243
+ expected_revision: Revision the writer read.
244
+ expected_entity_revision: Entity revision the writer
245
+ read (ENTITY-CAS).
246
+
247
+ Returns:
248
+ Mutation mapping with entity, component, canonical
249
+ address, storage path, revision transition, new
250
+ head, actor, diff summary, rewind handle, the
251
+ component_changed record, cursor, and request id.
252
+
253
+ Raises:
254
+ ProjectError: unsupported_operation until the
255
+ ENGINE-ECS wiring lands; unknown_entity for a
256
+ missing entity; backfill_readonly for a
257
+ backfilled id; revision_conflict for a stale
258
+ head; request_conflict for a reused id with new
259
+ arguments.
260
+
261
+ Caution: Every accepted write advances the head — a
262
+ second writer at the old head fails instead of
263
+ merging.
264
+ """
265
+ arguments = {"entity": entity, "component": component, "data": data}
266
+ if expected_entity_revision is not None:
267
+ arguments["expected_entity_revision"] = expected_entity_revision
268
+ rid, text = self._admit(
269
+ "set_component", arguments, request_id, expected_revision,
270
+ )
271
+ return self._wired("set_component", "ENGINE-ECS")(
272
+ entity, component, data, rid, text,
273
+ expected_revision=expected_revision,
274
+ expected_entity_revision=expected_entity_revision,
275
+ )
276
+
277
+ def attach_component(self, entity, component, data, request_id=None,
278
+ expected_revision=None,
279
+ expected_entity_revision=None):
280
+ """Attach one new component through admission.
281
+
282
+ Args:
283
+ entity: Entity id holding the component.
284
+ component: Component name to attach.
285
+ data: JSON payload to store.
286
+ request_id: Client envelope id for idempotent replay.
287
+ expected_revision: Revision the writer read.
288
+ expected_entity_revision: Entity revision the writer
289
+ read (ENTITY-CAS).
290
+
291
+ Returns:
292
+ Mutation mapping with entity, component, canonical
293
+ address, storage path, revision transition, new
294
+ head, actor, diff summary, rewind handle, the
295
+ component_changed record, cursor, and request id.
296
+
297
+ Raises:
298
+ ProjectError: unsupported_operation until the
299
+ ENGINE-ECS wiring lands; component_exists when
300
+ the component already lives on the entity;
301
+ unknown_entity for a missing entity;
302
+ revision_conflict for a stale head.
303
+
304
+ Caution: Every accepted attach advances the head — a
305
+ second writer at the old head fails instead of
306
+ merging.
307
+ """
308
+ arguments = {"entity": entity, "component": component, "data": data}
309
+ if expected_entity_revision is not None:
310
+ arguments["expected_entity_revision"] = expected_entity_revision
311
+ rid, text = self._admit(
312
+ "attach_component", arguments, request_id, expected_revision,
313
+ )
314
+ return self._wired("attach_component", "ENGINE-ECS")(
315
+ entity, component, data, rid, text,
316
+ expected_revision=expected_revision,
317
+ expected_entity_revision=expected_entity_revision,
318
+ )
319
+
320
+ def detach_component(self, entity, component, request_id=None,
321
+ expected_revision=None,
322
+ expected_entity_revision=None):
323
+ """Detach one component through admission.
324
+
325
+ Args:
326
+ entity: Entity id holding the component.
327
+ component: Component name to remove.
328
+ request_id: Client envelope id for idempotent replay.
329
+ expected_revision: Revision the writer read.
330
+ expected_entity_revision: Entity revision the writer
331
+ read (ENTITY-CAS).
332
+
333
+ Returns:
334
+ Mutation mapping with entity, component, canonical
335
+ address, storage path, revision transition, new
336
+ head, actor, diff summary, rewind handle, the
337
+ component_changed record, cursor, and request id.
338
+
339
+ Raises:
340
+ ProjectError: unsupported_operation until the
341
+ ENGINE-ECS wiring lands; unknown_component for a
342
+ missing component; revision_conflict for a stale
343
+ head; request_conflict for a reused id with new
344
+ arguments.
345
+
346
+ Caution: Every accepted detach advances the head — a
347
+ second writer at the old head fails instead of
348
+ merging.
349
+ """
350
+ arguments = {"entity": entity, "component": component}
351
+ if expected_entity_revision is not None:
352
+ arguments["expected_entity_revision"] = expected_entity_revision
353
+ rid, text = self._admit(
354
+ "detach_component", arguments, request_id, expected_revision,
355
+ )
356
+ return self._wired("detach_component", "ENGINE-ECS")(
357
+ entity, component, rid, text,
358
+ expected_revision=expected_revision,
359
+ expected_entity_revision=expected_entity_revision,
360
+ )
361
+
362
+ def delete_entity(self, entity, request_id=None,
363
+ expected_revision=None,
364
+ expected_entity_revision=None):
365
+ """Delete one entity through admission.
366
+
367
+ Args:
368
+ entity: Entity id to delete.
369
+ request_id: Client envelope id for idempotent replay.
370
+ expected_revision: Revision the writer read.
371
+ expected_entity_revision: Entity revision the writer
372
+ read (ENTITY-CAS).
373
+
374
+ Returns:
375
+ Mutation mapping with entity, storage path, revision
376
+ transition, new head, actor, diff summary, rewind
377
+ handle, the entity_deleted record, cursor, and
378
+ request id.
379
+
380
+ Raises:
381
+ ProjectError: unsupported_operation until the
382
+ ENGINE-ECS wiring lands; unknown_entity for a
383
+ missing entity; backfill_readonly for a
384
+ backfilled id; revision_conflict for a stale
385
+ head.
386
+
387
+ Caution: Deleting advances the head like any mutation —
388
+ stale writers fail instead of merging.
389
+ """
390
+ arguments = {"entity": entity}
391
+ if expected_entity_revision is not None:
392
+ arguments["expected_entity_revision"] = expected_entity_revision
393
+ rid, text = self._admit(
394
+ "delete_entity", arguments, request_id, expected_revision,
395
+ )
396
+ return self._wired("delete_entity", "ENGINE-ECS")(
397
+ entity, rid, text, expected_revision=expected_revision,
398
+ expected_entity_revision=expected_entity_revision,
399
+ )
400
+
401
+ def place_wire(self, segments, layer, endpoints, request_id=None,
402
+ expected_revision=None):
403
+ """Place one wire through admission.
404
+
405
+ Args:
406
+ segments: Non-empty list of integer polylines.
407
+ layer: Layer name for every segment.
408
+ endpoints: Exactly two endpoint mappings with xy plus
409
+ a pin/port/junction/net id or null when dangling.
410
+ request_id: Client envelope id for idempotent replay.
411
+ expected_revision: Revision the writer read.
412
+
413
+ Returns:
414
+ Mutation mapping with the wire receipt, revision
415
+ transition, actor, diff summary, rewind handle,
416
+ appended record, cursor, and request id.
417
+
418
+ Raises:
419
+ ProjectError: unsupported_operation until the
420
+ WIRE-PLACE wiring lands; invalid_wire for a
421
+ malformed wire; unknown_ref for a dangling id
422
+ naming no entity; revision_conflict for a stale
423
+ head.
424
+
425
+ Caution: Every accepted place advances the head — a
426
+ second writer at the old head fails instead of
427
+ merging.
428
+ """
429
+ rid, text = self._admit(
430
+ "place_wire",
431
+ {"segments": segments, "layer": layer,
432
+ "endpoints": endpoints},
433
+ request_id, expected_revision,
434
+ )
435
+ return self._wired("place_wire", "WIRE-PLACE")(
436
+ segments, layer, endpoints, rid, text,
437
+ expected_revision=expected_revision,
438
+ )
439
+
440
+ def place_junction(self, xy, layer, request_id=None,
441
+ expected_revision=None):
442
+ """Place one junction dot through admission.
443
+
444
+ Args:
445
+ xy: Integer dot position.
446
+ layer: Layer name for the dot.
447
+ request_id: Client envelope id for idempotent replay.
448
+ expected_revision: Revision the writer read.
449
+
450
+ Returns:
451
+ Mutation mapping with the junction receipt, revision
452
+ transition, actor, diff summary, rewind handle,
453
+ appended record, cursor, and request id.
454
+
455
+ Raises:
456
+ ProjectError: unsupported_operation until the
457
+ WIRE-PLACE wiring lands; invalid_junction for a
458
+ malformed dot; revision_conflict for a stale
459
+ head.
460
+
461
+ Caution: Every accepted place advances the head — a
462
+ second writer at the old head fails instead of
463
+ merging.
464
+ """
465
+ rid, text = self._admit(
466
+ "place_junction", {"xy": xy, "layer": layer},
467
+ request_id, expected_revision,
468
+ )
469
+ return self._wired("place_junction", "WIRE-PLACE")(
470
+ xy, layer, rid, text, expected_revision=expected_revision
471
+ )
472
+
473
+ def connect_endpoint(self, wire_id, end, ref, request_id=None,
474
+ expected_revision=None):
475
+ """Terminate one wire endpoint on an id through admission.
476
+
477
+ Args:
478
+ wire_id: Wire entity to connect.
479
+ end: Endpoint selector (a or b).
480
+ ref: Pin/port/junction/net entity id, or null to
481
+ leave the end dangling.
482
+ request_id: Client envelope id for idempotent replay.
483
+ expected_revision: Revision the writer read.
484
+
485
+ Returns:
486
+ Mutation mapping with the connect receipt, revision
487
+ transition, actor, diff summary, rewind handle,
488
+ appended record, cursor, and request id.
489
+
490
+ Raises:
491
+ ProjectError: unsupported_operation until the
492
+ WIRE-PLACE wiring lands; unknown_wire for a
493
+ missing wire; unknown_ref for an id naming no
494
+ entity; revision_conflict for a stale head.
495
+
496
+ Caution: Every accepted connect advances the head — a
497
+ second writer at the old head fails instead of
498
+ merging.
499
+ """
500
+ rid, text = self._admit(
501
+ "connect_endpoint",
502
+ {"wire_id": wire_id, "end": end, "ref": ref},
503
+ request_id, expected_revision,
504
+ )
505
+ return self._wired("connect_endpoint", "WIRE-PLACE")(
506
+ wire_id, end, ref, rid, text,
507
+ expected_revision=expected_revision,
508
+ )
509
+
510
+ def move_wire(self, wire_id, dx, dy, request_id=None,
511
+ expected_revision=None):
512
+ """Shift one wire by a delta through admission.
513
+
514
+ Args:
515
+ wire_id: Wire entity to move.
516
+ dx: Integer x shift.
517
+ dy: Integer y shift.
518
+ request_id: Client envelope id for idempotent replay.
519
+ expected_revision: Revision the writer read.
520
+
521
+ Returns:
522
+ Mutation mapping with the move receipt, revision
523
+ transition, actor, diff summary, rewind handle,
524
+ appended record, cursor, and request id.
525
+
526
+ Raises:
527
+ ProjectError: unsupported_operation until the
528
+ WIRE-PLACE wiring lands; unknown_wire for a
529
+ missing wire; revision_conflict for a stale
530
+ head.
531
+
532
+ Caution: Every accepted move advances the head — a
533
+ second writer at the old head fails instead of
534
+ merging.
535
+ """
536
+ rid, text = self._admit(
537
+ "move_wire", {"wire_id": wire_id, "dx": dx, "dy": dy},
538
+ request_id, expected_revision,
539
+ )
540
+ return self._wired("move_wire", "WIRE-PLACE")(
541
+ wire_id, dx, dy, rid, text,
542
+ expected_revision=expected_revision,
543
+ )
544
+
545
+ def split_wire(self, wire_id, at, request_id=None,
546
+ expected_revision=None):
547
+ """Split one wire at a point through admission.
548
+
549
+ Args:
550
+ wire_id: Wire entity to split.
551
+ at: Integer point on the wire marking the split.
552
+ request_id: Client envelope id for idempotent replay.
553
+ expected_revision: Revision the writer read.
554
+
555
+ Returns:
556
+ Mutation mapping with the split receipt, revision
557
+ transition, actor, diff summary, rewind handle,
558
+ appended record, cursor, and request id.
559
+
560
+ Raises:
561
+ ProjectError: unsupported_operation until the
562
+ WIRE-PLACE wiring lands; unknown_wire for a
563
+ missing wire; invalid_split for a point off the
564
+ wire; revision_conflict for a stale head.
565
+
566
+ Caution: Every accepted split advances the head — a
567
+ second writer at the old head fails instead of
568
+ merging.
569
+ """
570
+ rid, text = self._admit(
571
+ "split_wire", {"wire_id": wire_id, "at": at},
572
+ request_id, expected_revision,
573
+ )
574
+ return self._wired("split_wire", "WIRE-PLACE")(
575
+ wire_id, at, rid, text, expected_revision=expected_revision
576
+ )
577
+
578
+ def delete_wire(self, wire_id, request_id=None,
579
+ expected_revision=None):
580
+ """Delete one wire through admission.
581
+
582
+ Args:
583
+ wire_id: Wire entity to delete.
584
+ request_id: Client envelope id for idempotent replay.
585
+ expected_revision: Revision the writer read.
586
+
587
+ Returns:
588
+ Mutation mapping with the delete receipt, revision
589
+ transition, actor, diff summary, rewind handle,
590
+ appended record, cursor, and request id.
591
+
592
+ Raises:
593
+ ProjectError: unsupported_operation until the
594
+ WIRE-PLACE wiring lands; unknown_wire for a
595
+ missing wire; revision_conflict for a stale
596
+ head.
597
+
598
+ Caution: Deleting advances the head like any mutation —
599
+ stale writers fail instead of merging.
600
+ """
601
+ rid, text = self._admit(
602
+ "delete_wire", {"wire_id": wire_id}, request_id,
603
+ expected_revision,
604
+ )
605
+ return self._wired("delete_wire", "WIRE-PLACE")(
606
+ wire_id, rid, text, expected_revision=expected_revision
607
+ )
608
+
609
+ def delete_junction(self, junction_id, request_id=None,
610
+ expected_revision=None):
611
+ """Delete one junction through admission.
612
+
613
+ Args:
614
+ junction_id: Junction entity to delete.
615
+ request_id: Client envelope id for idempotent replay.
616
+ expected_revision: Revision the writer read.
617
+
618
+ Returns:
619
+ Mutation mapping with the delete receipt, revision
620
+ transition, actor, diff summary, rewind handle,
621
+ appended record, cursor, and request id.
622
+
623
+ Raises:
624
+ ProjectError: unsupported_operation until the
625
+ WIRE-PLACE wiring lands; unknown_junction for a
626
+ missing junction; junction_in_use for a dot still
627
+ referenced; revision_conflict for a stale head.
628
+
629
+ Caution: Deleting advances the head like any mutation —
630
+ stale writers fail instead of merging.
631
+ """
632
+ rid, text = self._admit(
633
+ "delete_junction", {"junction_id": junction_id},
634
+ request_id, expected_revision,
635
+ )
636
+ return self._wired("delete_junction", "WIRE-PLACE")(
637
+ junction_id, rid, text,
638
+ expected_revision=expected_revision,
639
+ )
640
+
641
+ def materialize_nets(self, pins, request_id=None,
642
+ expected_revision=None, labels=None,
643
+ ports=None, cell="top", named_refs=None):
644
+ """Materialize net entities through admission.
645
+
646
+ Args:
647
+ pins: Caller pin records mapping entities to their
648
+ schematic triples.
649
+ request_id: Client envelope id for idempotent replay.
650
+ expected_revision: Revision the writer read.
651
+ labels: Optional label records for extraction.
652
+ ports: Optional port records for extraction.
653
+ cell: Cell name extraction runs under.
654
+ named_refs: Optional net-ref taps by name.
655
+
656
+ Returns:
657
+ Mutation mapping with the materialize receipt,
658
+ revision transition, actor, diff summary, rewind
659
+ handle, appended record, cursor, and request id.
660
+
661
+ Raises:
662
+ ProjectError: unsupported_operation until the
663
+ WIRE-PLACE wiring lands; invalid_pin and sibling
664
+ contract codes for malformed inputs;
665
+ revision_conflict for a stale head.
666
+
667
+ Caution: Every accepted materialize advances the head —
668
+ a second writer at the old head fails instead of
669
+ merging.
670
+ """
671
+ arguments = {"pins": pins, "cell": cell}
672
+ if labels is not None:
673
+ arguments["labels"] = labels
674
+ if ports is not None:
675
+ arguments["ports"] = ports
676
+ if named_refs is not None:
677
+ arguments["named_refs"] = named_refs
678
+ rid, text = self._admit(
679
+ "materialize_nets", arguments, request_id,
680
+ expected_revision,
681
+ )
682
+ return self._wired("materialize_nets", "WIRE-PLACE")(
683
+ pins, rid, text, labels=labels, ports=ports, cell=cell,
684
+ named_refs=named_refs, expected_revision=expected_revision,
685
+ )
686
+
687
+ def create_project(self, new_project_id, request_id=None):
688
+ """Create one managed project at its first head.
689
+
690
+ New ledgers start clean, so creation binds no revision —
691
+ the envelope id alone carries idempotency.
692
+
693
+ Args:
694
+ new_project_id: Slug for the project to create.
695
+ request_id: Client envelope id; resubmitting the
696
+ identical call returns the identical response.
697
+
698
+ Returns:
699
+ Creation mapping with the new project id, first head,
700
+ actor, diff summary, revision transition, rewind
701
+ handle, empty records, and request id.
702
+
703
+ Raises:
704
+ ProjectError: invalid_command without an engine root;
705
+ invalid_project for a non-slug id;
706
+ project_exists for a duplicate; request_conflict
707
+ for a reused id with new arguments.
708
+ """
709
+ rid, text = self._admit(
710
+ "create_project", {"new_project_id": new_project_id},
711
+ request_id, None,
712
+ )
713
+ return self._project.create_project(new_project_id, rid, text)
714
+
715
+ def rewind(self, target_revision, request_id=None,
716
+ expected_revision=None):
717
+ """Restore a prior head under a new head through admission.
718
+
719
+ Args:
720
+ target_revision: Stored head to restore.
721
+ request_id: Client envelope id for idempotent replay.
722
+ expected_revision: Revision the writer read.
723
+
724
+ Returns:
725
+ Mutation mapping with revision transition, target
726
+ revision, new head, actor, diff summary,
727
+ external-file report, rewind handle, the appended
728
+ record, cursor, and request id.
729
+
730
+ Raises:
731
+ ProjectError: request_conflict for a reused id with
732
+ new arguments; invalid_command for a missing
733
+ target or a target equal to the live head;
734
+ unknown_revision for a head with no stored
735
+ bytes; revision_conflict for a stale head.
736
+
737
+ Caution: Rewinding advances — the new head is not the
738
+ target id, and stale writers fail instead of merging.
739
+ """
740
+ rid, text = self._admit(
741
+ "rewind", {"target_revision": target_revision},
742
+ request_id, expected_revision,
743
+ )
744
+ return self._project.rewind(
745
+ target_revision, rid, text,
746
+ expected_revision=expected_revision,
747
+ )
748
+
749
+ def write_settings(self, kind, settings, request_id=None,
750
+ expected_revision=None):
751
+ """Write one settings kind through admission.
752
+
753
+ Args:
754
+ kind: Settings kind to replace wholesale.
755
+ settings: Complete settings mapping at the head
756
+ version.
757
+ request_id: Client envelope id for idempotent replay.
758
+ expected_revision: Revision the writer read.
759
+
760
+ Returns:
761
+ Mutation mapping with path, revision transition, new
762
+ head, actor, diff summary, rewind handle, the
763
+ appended record, cursor, request id, written
764
+ flag, and settings version.
765
+
766
+ Raises:
767
+ ProjectError: request_conflict for a reused id with
768
+ new arguments; invalid_command for an unknown
769
+ kind or settings breaking the kind schema;
770
+ revision_conflict for a stale head;
771
+ future_format when the stored file is newer
772
+ than the head.
773
+
774
+ Caution: Every accepted write advances the head — a
775
+ second writer at the old head fails instead of
776
+ merging.
777
+ """
778
+ rid, text = self._admit(
779
+ "write_settings", {"kind": kind, "settings": settings},
780
+ request_id, expected_revision,
781
+ )
782
+ return self._project.write_settings(
783
+ kind, settings, rid, text,
784
+ expected_revision=expected_revision,
785
+ )