astraform-remote-domain-author-kit 0.1.2__tar.gz

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 (26) hide show
  1. astraform_remote_domain_author_kit-0.1.2/PKG-INFO +329 -0
  2. astraform_remote_domain_author_kit-0.1.2/README.md +307 -0
  3. astraform_remote_domain_author_kit-0.1.2/pyproject.toml +58 -0
  4. astraform_remote_domain_author_kit-0.1.2/setup.cfg +4 -0
  5. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/__init__.py +63 -0
  6. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/fastapi.py +233 -0
  7. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/protocol.py +757 -0
  8. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/README.md +115 -0
  9. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/pyproject.toml +31 -0
  10. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/scripts/bootstrap-local-sdk.sh +37 -0
  11. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/src/acme_remote_domain/__init__.py +1 -0
  12. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/src/acme_remote_domain/main.py +12 -0
  13. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/src/acme_remote_domain/population_catalog.json +69 -0
  14. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/src/acme_remote_domain/service.py +332 -0
  15. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/src/acme_remote_domain/validate_population_catalog.py +49 -0
  16. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates/fastapi-minimal/tests/test_app.py +59 -0
  17. astraform_remote_domain_author_kit-0.1.2/src/astraform/remote_domain/author_kit/templates.py +90 -0
  18. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/PKG-INFO +329 -0
  19. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/SOURCES.txt +24 -0
  20. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/dependency_links.txt +1 -0
  21. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/entry_points.txt +2 -0
  22. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/requires.txt +8 -0
  23. astraform_remote_domain_author_kit-0.1.2/src/astraform_remote_domain_author_kit.egg-info/top_level.txt +1 -0
  24. astraform_remote_domain_author_kit-0.1.2/tests/test_fastapi.py +325 -0
  25. astraform_remote_domain_author_kit-0.1.2/tests/test_protocol.py +364 -0
  26. astraform_remote_domain_author_kit-0.1.2/tests/test_templates.py +55 -0
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: astraform-remote-domain-author-kit
3
+ Version: 0.1.2
4
+ Summary: Python author kit for Astraform remote-domain.v1 services
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Documentation, https://github.com/astraform/platform/blob/main/docs/guides/remote-domain-authoring-guide.md
7
+ Project-URL: Source, https://github.com/astraform/platform
8
+ Project-URL: Publishing, https://github.com/astraform/platform/blob/main/docs/platform/remote-domain-sdk-publishing.md
9
+ Keywords: astraform,remote-domain,simulation,conformance
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+ Provides-Extra: fastapi
17
+ Requires-Dist: fastapi<1.0,>=0.115; extra == "fastapi"
18
+ Provides-Extra: test
19
+ Requires-Dist: fastapi<1.0,>=0.115; extra == "test"
20
+ Requires-Dist: httpx<1.0,>=0.28; extra == "test"
21
+ Requires-Dist: pytest<9.0,>=8.3; extra == "test"
22
+
23
+ # astraform-remote-domain-author-kit
24
+
25
+ This is the Python author kit for `remote-domain.v1`.
26
+
27
+ Brutal truth: an "SDK" alone is not enough. If teams still have to reverse
28
+ engineer request envelopes, invent their own starter layout, or guess how to
29
+ prove conformance, you did not ship onboarding. You shipped homework.
30
+
31
+ This author kit is broader than a thin SDK. It includes:
32
+
33
+ - reusable protocol constants and envelope builders
34
+ - request validation helpers
35
+ - an optional FastAPI app factory
36
+ - a starter template inside the package
37
+
38
+ If you only want helper functions, that is the SDK-like layer. The author kit
39
+ is the whole package around it.
40
+
41
+ ## Install
42
+
43
+ Current status: the published PyPI baseline is
44
+ `astraform-remote-domain-author-kit==0.1.0`. The workspace release target is
45
+ `0.1.2`, which adds the current Population Builder catalog/readiness starter
46
+ shape. Do not claim external Python catalog authoring support until `0.1.2` is
47
+ actually published to PyPI.
48
+
49
+ Protocol helpers only:
50
+
51
+ ```bash
52
+ pip install astraform-remote-domain-author-kit==0.1.0
53
+ ```
54
+
55
+ FastAPI helper included:
56
+
57
+ ```bash
58
+ pip install 'astraform-remote-domain-author-kit[fastapi]==0.1.0'
59
+ ```
60
+
61
+ For catalog-enabled `0.1.2` work before publication, use the monorepo editable
62
+ install or a private pre-release wheel.
63
+
64
+ Repo-local development:
65
+
66
+ ```bash
67
+ pip install -e './remote-domain-author-kit-python[fastapi,test]'
68
+ ```
69
+
70
+ Starter-template validation is part of the release gate. It still runs the
71
+ template's `scripts/bootstrap-local-sdk.sh` path against the repo-local SDK
72
+ source, then separately runs an external-consumer smoke from the exact built
73
+ local author-kit and conformance wheels. That smoke creates a clean project,
74
+ copies the starter through the installed `astraform-remote-domain-copy-starter`
75
+ command, installs the copied starter with `--no-deps`, runs
76
+ `validate-population-catalog`, and passes
77
+ `remote-domain-conformance --population-catalog`. That keeps the source
78
+ bootstrap and release artifact paths honest before the matching SDK version is
79
+ visible on PyPI.
80
+
81
+ ## Core Usage
82
+
83
+ ```python
84
+ from astraform.remote_domain.author_kit.protocol import build_manifest
85
+ from astraform.remote_domain.author_kit.protocol import build_projection
86
+ from astraform.remote_domain.author_kit.protocol import opaque_state
87
+ from astraform.remote_domain.author_kit.protocol import projection_envelope
88
+ from astraform.remote_domain.author_kit.protocol import success_envelope
89
+ from astraform.remote_domain.author_kit.protocol import validate_request_envelope
90
+
91
+
92
+ def manifest() -> dict:
93
+ return build_manifest(
94
+ domain_id="acme-ops",
95
+ display_name="Acme Operations Domain",
96
+ description="Remote proof domain",
97
+ schema_version="acme-ops.state.v1",
98
+ supported_agent_types=["Operator"],
99
+ supported_interaction_modes=["SIMULATION", "HYBRID"],
100
+ tools=[
101
+ {
102
+ "name": "lookup_case",
103
+ "description": "Look up a case in the remote domain.",
104
+ "inputSchema": {"type": "object", "additionalProperties": False},
105
+ }
106
+ ],
107
+ )
108
+
109
+
110
+ def prepare(request: dict) -> dict:
111
+ validate_request_envelope(
112
+ request,
113
+ expected_domain_id="acme-ops",
114
+ expected_operation="prepare",
115
+ require_idempotency=True,
116
+ )
117
+ state = opaque_state(
118
+ "acme-ops.state.v1",
119
+ {"personaName": "Taylor", "completedWorkCount": 0},
120
+ )
121
+ projection = build_projection(
122
+ runtime_metadata={"domainProfile": "acme"},
123
+ status_view={"completedWorkCount": 0},
124
+ inspection_view={"tasks": []},
125
+ )
126
+ return success_envelope(
127
+ request,
128
+ runtime_identity="acme-ops::Taylor",
129
+ next_state=state,
130
+ projection=projection,
131
+ )
132
+
133
+
134
+ def status(request: dict) -> dict:
135
+ validate_request_envelope(
136
+ request,
137
+ expected_domain_id="acme-ops",
138
+ expected_operation="status",
139
+ require_state=True,
140
+ )
141
+ return projection_envelope(
142
+ request,
143
+ projection=build_projection(
144
+ runtime_metadata={"domainProfile": "acme"},
145
+ status_view={"completedWorkCount": 0},
146
+ inspection_view={"tasks": []},
147
+ ),
148
+ )
149
+ ```
150
+
151
+ Domain providers may add optional `evidence_events=[...]` to
152
+ `success_envelope(...)` or `projection_envelope(...)`. Use that lane for
153
+ provider-internal evidence that Astraform cannot observe directly, such as a
154
+ private third-party call or domain-owned policy check.
155
+
156
+ ## FastAPI App Factory
157
+
158
+ ```python
159
+ from astraform.remote_domain.author_kit.fastapi import create_fastapi_app
160
+
161
+ app = create_fastapi_app(
162
+ service=my_remote_domain_service,
163
+ policy_wind_tunnel_service=my_policy_wind_tunnel_service,
164
+ )
165
+ ```
166
+
167
+ The service object must implement:
168
+
169
+ - `manifest()`
170
+ - optional `population_catalog()` for
171
+ `GET /api/remote-domain/v1/population/catalog`
172
+ - `prepare(payload)`
173
+ - `execute_work(payload)`
174
+ - `status(payload)`
175
+ - `inspection(payload)`
176
+ - `shutdown(payload)`
177
+
178
+ `population_catalog()` must return `domain_population_catalog.v1`. The FastAPI
179
+ factory validates the catalog before returning it, so missing archetype fields,
180
+ bad recipe numbers, unknown archetype references, and malformed eligibility
181
+ fail before Population Builder treats the provider as usable.
182
+
183
+ Prefer a file-backed catalog over an inline Python dict. That keeps the domain
184
+ authoring surface reviewable and conformance-testable:
185
+
186
+ ```python
187
+ from importlib import resources
188
+
189
+ from astraform.remote_domain.author_kit import load_population_catalog
190
+
191
+
192
+ def population_catalog() -> dict:
193
+ catalog_file = resources.files("my_remote_domain").joinpath("population_catalog.json")
194
+ return load_population_catalog(catalog_file)
195
+ ```
196
+
197
+ `load_population_catalog(...)` reads JSON and runs the same
198
+ `validate_population_catalog(...)` checks used by the FastAPI route.
199
+
200
+ The bundled `fastapi-minimal` starter also installs a provider-local preflight:
201
+
202
+ ```bash
203
+ validate-population-catalog
204
+ ```
205
+
206
+ That command validates `src/acme_remote_domain/population_catalog.json` before
207
+ the provider starts and prints the exact rejected field when the catalog is
208
+ malformed. Treat it as the first command after editing archetypes, segment
209
+ templates, validation rules, or run-path eligibility.
210
+
211
+ The optional `policy_wind_tunnel_service` object exposes the remote Wind Tunnel
212
+ provider routes:
213
+
214
+ - `GET /policy-wind-tunnel/pack`
215
+ - `GET /policy-wind-tunnel/presets`
216
+ - `POST /policy-wind-tunnel/runs`
217
+ - `GET /policy-wind-tunnel/runs/{runId}`
218
+ - `POST /policy-wind-tunnel/runs/{runId}/lifecycle`
219
+ - `GET /policy-wind-tunnel/runs/{runId}/bundle`
220
+ - `GET /policy-wind-tunnel/runs/{runId}/artifacts/{artifactType}`
221
+ - `GET /policy-wind-tunnel/runs/{runId}/artifacts/{artifactType}/readiness`
222
+ - `GET /policy-wind-tunnel/runs/{runId}/evidence-pack`
223
+ - `GET /policy-wind-tunnel/runs/{runId}/evidence-pack/readiness`
224
+
225
+ The readiness routes are metadata-only checks for dashboard download UX. They
226
+ must prove the same provider-owned artifact/evidence-pack path is readable
227
+ without materializing the JSON body or ZIP archive.
228
+
229
+ ## Policy Expressions
230
+
231
+ When a domain exposes Policy Wind Tunnel behavior, CEL-compatible
232
+ `PolicyExpression` definitions are the portable decision contract. They are for
233
+ decision gates, eligibility checks, thresholds, and conformance-testable public
234
+ rules.
235
+
236
+ They are not the execution engine. Keep state mutation, external calls,
237
+ proprietary scoring, and side effects inside your service implementation or
238
+ domain-owned execution target. Use the expression's `executionTarget` as a
239
+ stable provider-owned target name.
240
+
241
+ Python providers should publish the same `/policy-wind-tunnel/pack` metadata as
242
+ Java providers. The helper below builds the portable parts of that response:
243
+
244
+ ```python
245
+ from astraform.remote_domain.author_kit import build_policy_expression
246
+ from astraform.remote_domain.author_kit import build_policy_wind_tunnel_pack_metadata
247
+
248
+
249
+ def wind_tunnel_pack() -> dict:
250
+ return build_policy_wind_tunnel_pack_metadata(
251
+ capabilities={"remoteProviderReady": True, "evidenceExport": True},
252
+ control_schema={
253
+ "type": "object",
254
+ "properties": {
255
+ "blockerCount": {"type": "integer", "minimum": 0},
256
+ },
257
+ },
258
+ ui_schema={
259
+ "fields": [
260
+ {"name": "blockerCount", "widget": "number", "label": "Blockers"},
261
+ ],
262
+ },
263
+ outcome_schema={
264
+ "schemaVersion": "domain_outcome_schema.v1",
265
+ "metricDefinitions": [
266
+ {
267
+ "metricId": "blockerCount",
268
+ "label": "Blockers",
269
+ "unit": "count",
270
+ "evidence": "domain-state",
271
+ },
272
+ ],
273
+ },
274
+ domain_labels={
275
+ "actorSingular": "campaign",
276
+ "actorPlural": "campaigns",
277
+ "simulatedActorsLabel": "Simulated campaigns",
278
+ },
279
+ policy_expressions=[
280
+ build_policy_expression(
281
+ expression_id="acme.blockers.acceptable",
282
+ expression="metrics.blockerCount <= 10",
283
+ execution_target="acme-policy-service",
284
+ description="Blockers must stay under launch threshold.",
285
+ ),
286
+ ],
287
+ )
288
+ ```
289
+
290
+ See
291
+ [`docs/architecture/scenario-lab-policy-expressions.md`](../docs/architecture/scenario-lab-policy-expressions.md)
292
+ for the contract shape and CEL subset.
293
+
294
+ ## Starter Template
295
+
296
+ Copy the bundled FastAPI starter from the installed author kit:
297
+
298
+ ```bash
299
+ astraform-remote-domain-copy-starter acme-remote-domain
300
+ cd acme-remote-domain
301
+ pip install --no-deps -e .
302
+ validate-population-catalog
303
+ remote-domain-conformance --app acme_remote_domain.main:app --domain-id acme-remote --population-catalog
304
+ ```
305
+
306
+ The underlying template resource lives under:
307
+
308
+ - `astraform/remote_domain/author_kit/templates/fastapi-minimal`
309
+
310
+ It is intentionally boring. That is a feature. Teams need a truthful starting
311
+ point, not a framework demo that hides the protocol.
312
+
313
+ ## Publishing Status
314
+
315
+ This package is released through the shared Python + Java SDK publishing process
316
+ documented in
317
+ [`docs/platform/remote-domain-sdk-publishing.md`](../docs/platform/remote-domain-sdk-publishing.md).
318
+
319
+ Release guardrail:
320
+
321
+ ```bash
322
+ SDK_VERSION=0.1.2 scripts/check-remote-domain-sdk-release-gates.sh
323
+ ```
324
+
325
+ PyPI artifacts are immutable. Do not rerun a publish for an already published
326
+ version; run smoke-only verification or bump the SDK version.
327
+
328
+ Public package note: PyPI distributions expose this SDK implementation. Keep
329
+ host runtime internals and domain-private logic out of this package.
@@ -0,0 +1,307 @@
1
+ # astraform-remote-domain-author-kit
2
+
3
+ This is the Python author kit for `remote-domain.v1`.
4
+
5
+ Brutal truth: an "SDK" alone is not enough. If teams still have to reverse
6
+ engineer request envelopes, invent their own starter layout, or guess how to
7
+ prove conformance, you did not ship onboarding. You shipped homework.
8
+
9
+ This author kit is broader than a thin SDK. It includes:
10
+
11
+ - reusable protocol constants and envelope builders
12
+ - request validation helpers
13
+ - an optional FastAPI app factory
14
+ - a starter template inside the package
15
+
16
+ If you only want helper functions, that is the SDK-like layer. The author kit
17
+ is the whole package around it.
18
+
19
+ ## Install
20
+
21
+ Current status: the published PyPI baseline is
22
+ `astraform-remote-domain-author-kit==0.1.0`. The workspace release target is
23
+ `0.1.2`, which adds the current Population Builder catalog/readiness starter
24
+ shape. Do not claim external Python catalog authoring support until `0.1.2` is
25
+ actually published to PyPI.
26
+
27
+ Protocol helpers only:
28
+
29
+ ```bash
30
+ pip install astraform-remote-domain-author-kit==0.1.0
31
+ ```
32
+
33
+ FastAPI helper included:
34
+
35
+ ```bash
36
+ pip install 'astraform-remote-domain-author-kit[fastapi]==0.1.0'
37
+ ```
38
+
39
+ For catalog-enabled `0.1.2` work before publication, use the monorepo editable
40
+ install or a private pre-release wheel.
41
+
42
+ Repo-local development:
43
+
44
+ ```bash
45
+ pip install -e './remote-domain-author-kit-python[fastapi,test]'
46
+ ```
47
+
48
+ Starter-template validation is part of the release gate. It still runs the
49
+ template's `scripts/bootstrap-local-sdk.sh` path against the repo-local SDK
50
+ source, then separately runs an external-consumer smoke from the exact built
51
+ local author-kit and conformance wheels. That smoke creates a clean project,
52
+ copies the starter through the installed `astraform-remote-domain-copy-starter`
53
+ command, installs the copied starter with `--no-deps`, runs
54
+ `validate-population-catalog`, and passes
55
+ `remote-domain-conformance --population-catalog`. That keeps the source
56
+ bootstrap and release artifact paths honest before the matching SDK version is
57
+ visible on PyPI.
58
+
59
+ ## Core Usage
60
+
61
+ ```python
62
+ from astraform.remote_domain.author_kit.protocol import build_manifest
63
+ from astraform.remote_domain.author_kit.protocol import build_projection
64
+ from astraform.remote_domain.author_kit.protocol import opaque_state
65
+ from astraform.remote_domain.author_kit.protocol import projection_envelope
66
+ from astraform.remote_domain.author_kit.protocol import success_envelope
67
+ from astraform.remote_domain.author_kit.protocol import validate_request_envelope
68
+
69
+
70
+ def manifest() -> dict:
71
+ return build_manifest(
72
+ domain_id="acme-ops",
73
+ display_name="Acme Operations Domain",
74
+ description="Remote proof domain",
75
+ schema_version="acme-ops.state.v1",
76
+ supported_agent_types=["Operator"],
77
+ supported_interaction_modes=["SIMULATION", "HYBRID"],
78
+ tools=[
79
+ {
80
+ "name": "lookup_case",
81
+ "description": "Look up a case in the remote domain.",
82
+ "inputSchema": {"type": "object", "additionalProperties": False},
83
+ }
84
+ ],
85
+ )
86
+
87
+
88
+ def prepare(request: dict) -> dict:
89
+ validate_request_envelope(
90
+ request,
91
+ expected_domain_id="acme-ops",
92
+ expected_operation="prepare",
93
+ require_idempotency=True,
94
+ )
95
+ state = opaque_state(
96
+ "acme-ops.state.v1",
97
+ {"personaName": "Taylor", "completedWorkCount": 0},
98
+ )
99
+ projection = build_projection(
100
+ runtime_metadata={"domainProfile": "acme"},
101
+ status_view={"completedWorkCount": 0},
102
+ inspection_view={"tasks": []},
103
+ )
104
+ return success_envelope(
105
+ request,
106
+ runtime_identity="acme-ops::Taylor",
107
+ next_state=state,
108
+ projection=projection,
109
+ )
110
+
111
+
112
+ def status(request: dict) -> dict:
113
+ validate_request_envelope(
114
+ request,
115
+ expected_domain_id="acme-ops",
116
+ expected_operation="status",
117
+ require_state=True,
118
+ )
119
+ return projection_envelope(
120
+ request,
121
+ projection=build_projection(
122
+ runtime_metadata={"domainProfile": "acme"},
123
+ status_view={"completedWorkCount": 0},
124
+ inspection_view={"tasks": []},
125
+ ),
126
+ )
127
+ ```
128
+
129
+ Domain providers may add optional `evidence_events=[...]` to
130
+ `success_envelope(...)` or `projection_envelope(...)`. Use that lane for
131
+ provider-internal evidence that Astraform cannot observe directly, such as a
132
+ private third-party call or domain-owned policy check.
133
+
134
+ ## FastAPI App Factory
135
+
136
+ ```python
137
+ from astraform.remote_domain.author_kit.fastapi import create_fastapi_app
138
+
139
+ app = create_fastapi_app(
140
+ service=my_remote_domain_service,
141
+ policy_wind_tunnel_service=my_policy_wind_tunnel_service,
142
+ )
143
+ ```
144
+
145
+ The service object must implement:
146
+
147
+ - `manifest()`
148
+ - optional `population_catalog()` for
149
+ `GET /api/remote-domain/v1/population/catalog`
150
+ - `prepare(payload)`
151
+ - `execute_work(payload)`
152
+ - `status(payload)`
153
+ - `inspection(payload)`
154
+ - `shutdown(payload)`
155
+
156
+ `population_catalog()` must return `domain_population_catalog.v1`. The FastAPI
157
+ factory validates the catalog before returning it, so missing archetype fields,
158
+ bad recipe numbers, unknown archetype references, and malformed eligibility
159
+ fail before Population Builder treats the provider as usable.
160
+
161
+ Prefer a file-backed catalog over an inline Python dict. That keeps the domain
162
+ authoring surface reviewable and conformance-testable:
163
+
164
+ ```python
165
+ from importlib import resources
166
+
167
+ from astraform.remote_domain.author_kit import load_population_catalog
168
+
169
+
170
+ def population_catalog() -> dict:
171
+ catalog_file = resources.files("my_remote_domain").joinpath("population_catalog.json")
172
+ return load_population_catalog(catalog_file)
173
+ ```
174
+
175
+ `load_population_catalog(...)` reads JSON and runs the same
176
+ `validate_population_catalog(...)` checks used by the FastAPI route.
177
+
178
+ The bundled `fastapi-minimal` starter also installs a provider-local preflight:
179
+
180
+ ```bash
181
+ validate-population-catalog
182
+ ```
183
+
184
+ That command validates `src/acme_remote_domain/population_catalog.json` before
185
+ the provider starts and prints the exact rejected field when the catalog is
186
+ malformed. Treat it as the first command after editing archetypes, segment
187
+ templates, validation rules, or run-path eligibility.
188
+
189
+ The optional `policy_wind_tunnel_service` object exposes the remote Wind Tunnel
190
+ provider routes:
191
+
192
+ - `GET /policy-wind-tunnel/pack`
193
+ - `GET /policy-wind-tunnel/presets`
194
+ - `POST /policy-wind-tunnel/runs`
195
+ - `GET /policy-wind-tunnel/runs/{runId}`
196
+ - `POST /policy-wind-tunnel/runs/{runId}/lifecycle`
197
+ - `GET /policy-wind-tunnel/runs/{runId}/bundle`
198
+ - `GET /policy-wind-tunnel/runs/{runId}/artifacts/{artifactType}`
199
+ - `GET /policy-wind-tunnel/runs/{runId}/artifacts/{artifactType}/readiness`
200
+ - `GET /policy-wind-tunnel/runs/{runId}/evidence-pack`
201
+ - `GET /policy-wind-tunnel/runs/{runId}/evidence-pack/readiness`
202
+
203
+ The readiness routes are metadata-only checks for dashboard download UX. They
204
+ must prove the same provider-owned artifact/evidence-pack path is readable
205
+ without materializing the JSON body or ZIP archive.
206
+
207
+ ## Policy Expressions
208
+
209
+ When a domain exposes Policy Wind Tunnel behavior, CEL-compatible
210
+ `PolicyExpression` definitions are the portable decision contract. They are for
211
+ decision gates, eligibility checks, thresholds, and conformance-testable public
212
+ rules.
213
+
214
+ They are not the execution engine. Keep state mutation, external calls,
215
+ proprietary scoring, and side effects inside your service implementation or
216
+ domain-owned execution target. Use the expression's `executionTarget` as a
217
+ stable provider-owned target name.
218
+
219
+ Python providers should publish the same `/policy-wind-tunnel/pack` metadata as
220
+ Java providers. The helper below builds the portable parts of that response:
221
+
222
+ ```python
223
+ from astraform.remote_domain.author_kit import build_policy_expression
224
+ from astraform.remote_domain.author_kit import build_policy_wind_tunnel_pack_metadata
225
+
226
+
227
+ def wind_tunnel_pack() -> dict:
228
+ return build_policy_wind_tunnel_pack_metadata(
229
+ capabilities={"remoteProviderReady": True, "evidenceExport": True},
230
+ control_schema={
231
+ "type": "object",
232
+ "properties": {
233
+ "blockerCount": {"type": "integer", "minimum": 0},
234
+ },
235
+ },
236
+ ui_schema={
237
+ "fields": [
238
+ {"name": "blockerCount", "widget": "number", "label": "Blockers"},
239
+ ],
240
+ },
241
+ outcome_schema={
242
+ "schemaVersion": "domain_outcome_schema.v1",
243
+ "metricDefinitions": [
244
+ {
245
+ "metricId": "blockerCount",
246
+ "label": "Blockers",
247
+ "unit": "count",
248
+ "evidence": "domain-state",
249
+ },
250
+ ],
251
+ },
252
+ domain_labels={
253
+ "actorSingular": "campaign",
254
+ "actorPlural": "campaigns",
255
+ "simulatedActorsLabel": "Simulated campaigns",
256
+ },
257
+ policy_expressions=[
258
+ build_policy_expression(
259
+ expression_id="acme.blockers.acceptable",
260
+ expression="metrics.blockerCount <= 10",
261
+ execution_target="acme-policy-service",
262
+ description="Blockers must stay under launch threshold.",
263
+ ),
264
+ ],
265
+ )
266
+ ```
267
+
268
+ See
269
+ [`docs/architecture/scenario-lab-policy-expressions.md`](../docs/architecture/scenario-lab-policy-expressions.md)
270
+ for the contract shape and CEL subset.
271
+
272
+ ## Starter Template
273
+
274
+ Copy the bundled FastAPI starter from the installed author kit:
275
+
276
+ ```bash
277
+ astraform-remote-domain-copy-starter acme-remote-domain
278
+ cd acme-remote-domain
279
+ pip install --no-deps -e .
280
+ validate-population-catalog
281
+ remote-domain-conformance --app acme_remote_domain.main:app --domain-id acme-remote --population-catalog
282
+ ```
283
+
284
+ The underlying template resource lives under:
285
+
286
+ - `astraform/remote_domain/author_kit/templates/fastapi-minimal`
287
+
288
+ It is intentionally boring. That is a feature. Teams need a truthful starting
289
+ point, not a framework demo that hides the protocol.
290
+
291
+ ## Publishing Status
292
+
293
+ This package is released through the shared Python + Java SDK publishing process
294
+ documented in
295
+ [`docs/platform/remote-domain-sdk-publishing.md`](../docs/platform/remote-domain-sdk-publishing.md).
296
+
297
+ Release guardrail:
298
+
299
+ ```bash
300
+ SDK_VERSION=0.1.2 scripts/check-remote-domain-sdk-release-gates.sh
301
+ ```
302
+
303
+ PyPI artifacts are immutable. Do not rerun a publish for an already published
304
+ version; run smoke-only verification or bump the SDK version.
305
+
306
+ Public package note: PyPI distributions expose this SDK implementation. Keep
307
+ host runtime internals and domain-private logic out of this package.
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "astraform-remote-domain-author-kit"
7
+ version = "0.1.2"
8
+ description = "Python author kit for Astraform remote-domain.v1 services"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "Apache-2.0"
12
+ keywords = ["astraform", "remote-domain", "simulation", "conformance"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Framework :: FastAPI",
17
+ "Typing :: Typed",
18
+ ]
19
+ dependencies = []
20
+
21
+ [project.urls]
22
+ Documentation = "https://github.com/astraform/platform/blob/main/docs/guides/remote-domain-authoring-guide.md"
23
+ Source = "https://github.com/astraform/platform"
24
+ Publishing = "https://github.com/astraform/platform/blob/main/docs/platform/remote-domain-sdk-publishing.md"
25
+
26
+ [project.optional-dependencies]
27
+ fastapi = [
28
+ "fastapi>=0.115,<1.0",
29
+ ]
30
+ test = [
31
+ "fastapi>=0.115,<1.0",
32
+ "httpx>=0.28,<1.0",
33
+ "pytest>=8.3,<9.0",
34
+ ]
35
+
36
+ [project.scripts]
37
+ astraform-remote-domain-copy-starter = "astraform.remote_domain.author_kit.templates:main"
38
+
39
+ [tool.setuptools]
40
+ package-dir = {"" = "src"}
41
+ include-package-data = true
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+ namespaces = true
46
+
47
+ [tool.setuptools.package-data]
48
+ "astraform.remote_domain.author_kit" = [
49
+ "templates/fastapi-minimal/README.md",
50
+ "templates/fastapi-minimal/pyproject.toml",
51
+ "templates/fastapi-minimal/scripts/*.sh",
52
+ "templates/fastapi-minimal/src/acme_remote_domain/*.json",
53
+ "templates/fastapi-minimal/src/acme_remote_domain/*.py",
54
+ "templates/fastapi-minimal/tests/*.py",
55
+ ]
56
+
57
+ [tool.pytest.ini_options]
58
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+