dominus-sdk-python 9.0.6__tar.gz → 9.0.8__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 (81) hide show
  1. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/PKG-INFO +4 -4
  2. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/README.md +3 -3
  3. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/__init__.py +1 -1
  4. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/authority.py +58 -1
  5. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus_sdk_python.egg-info/PKG-INFO +4 -4
  6. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/pyproject.toml +1 -1
  7. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_authority_public_vocabulary.py +127 -0
  8. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/config/__init__.py +0 -0
  9. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/config/endpoints.py +0 -0
  10. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/contract/__init__.py +0 -0
  11. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/contract/envelope.py +0 -0
  12. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/contract/projections.py +0 -0
  13. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/errors.py +0 -0
  14. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/__init__.py +0 -0
  15. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/auth.py +0 -0
  16. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/cache.py +0 -0
  17. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/console_capture.py +0 -0
  18. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/core.py +0 -0
  19. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/crypto.py +0 -0
  20. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/sse.py +0 -0
  21. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/helpers/trace.py +0 -0
  22. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/__init__.py +0 -0
  23. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/admin.py +0 -0
  24. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/ai.py +0 -0
  25. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/artifacts.py +0 -0
  26. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/auth.py +0 -0
  27. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/browser.py +0 -0
  28. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/coder.py +0 -0
  29. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/courier.py +0 -0
  30. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/db.py +0 -0
  31. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/ddl.py +0 -0
  32. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/deployer.py +0 -0
  33. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/fastapi.py +0 -0
  34. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/files.py +0 -0
  35. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/health.py +0 -0
  36. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/jobs.py +0 -0
  37. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/logs.py +0 -0
  38. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/platform.py +0 -0
  39. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/portal.py +0 -0
  40. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/processor.py +0 -0
  41. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/publisher.py +0 -0
  42. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/recipes.py +0 -0
  43. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/redis.py +0 -0
  44. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/secrets.py +0 -0
  45. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/secure.py +0 -0
  46. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/stash.py +0 -0
  47. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/sync.py +0 -0
  48. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/warden.py +0 -0
  49. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/namespaces/workflow.py +0 -0
  50. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/py.typed +0 -0
  51. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/services/__init__.py +0 -0
  52. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus/start.py +0 -0
  53. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
  54. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
  55. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus_sdk_python.egg-info/requires.txt +0 -0
  56. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/dominus_sdk_python.egg-info/top_level.txt +0 -0
  57. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/setup.cfg +0 -0
  58. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_auth.py +0 -0
  59. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_browser_namespace.py +0 -0
  60. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_contract.py +0 -0
  61. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_control_plane_namespaces.py +0 -0
  62. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_error_recovery_action.py +0 -0
  63. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_errors.py +0 -0
  64. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_errors_aliases.py +0 -0
  65. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_flat_commands.py +0 -0
  66. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_health.py +0 -0
  67. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_logs.py +0 -0
  68. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_namespace_barrel_exports.py +0 -0
  69. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_platform_coder_namespaces.py +0 -0
  70. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_provisioning_parity.py +0 -0
  71. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_public_exports.py +0 -0
  72. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_publisher_namespace.py +0 -0
  73. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_readme_quickstart_catalog.py +0 -0
  74. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_recipes_namespace.py +0 -0
  75. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_recipes_stash_routing.py +0 -0
  76. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_secrets_presence_contract.py +0 -0
  77. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_stash_artifact_facade.py +0 -0
  78. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_stash_tables_namespace.py +0 -0
  79. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_transport_compat.py +0 -0
  80. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_workflow_lifecycle.py +0 -0
  81. {dominus_sdk_python-9.0.6 → dominus_sdk_python-9.0.8}/tests/test_workflow_refs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 9.0.6
3
+ Version: 9.0.8
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -38,8 +38,8 @@ Async Python SDK for the Dominus gateway-first service plane.
38
38
  ## Agent Guide
39
39
 
40
40
  Start with `docs/agent-guide/INDEX.md`. The current snapshot is
41
- `docs/agent-guide/2026-08-04-sdk-python/00-reading-order.md`; the latest
42
- cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
41
+ `docs/agent-guide/2026-09-03-0942-sdk-python/00-reading-order.md`; the latest
42
+ cleanup audit is `docs/janitor/2026-09-03-0942-sdk-python-cleanup-audit.md`.
43
43
 
44
44
  ## What This Repo Ships
45
45
 
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
48
48
  - Gateway-scoped client mode for MCP and other user-JWT sessions
49
49
  - Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
50
50
  - Local helpers for JWT verification, trace propagation, retries, and console capture
51
- - Current package version: `9.0.5`
51
+ - Current package version: `9.0.6`
52
52
 
53
53
  ## Install
54
54
 
@@ -5,8 +5,8 @@ Async Python SDK for the Dominus gateway-first service plane.
5
5
  ## Agent Guide
6
6
 
7
7
  Start with `docs/agent-guide/INDEX.md`. The current snapshot is
8
- `docs/agent-guide/2026-08-04-sdk-python/00-reading-order.md`; the latest
9
- cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
8
+ `docs/agent-guide/2026-09-03-0942-sdk-python/00-reading-order.md`; the latest
9
+ cleanup audit is `docs/janitor/2026-09-03-0942-sdk-python-cleanup-audit.md`.
10
10
 
11
11
  ## What This Repo Ships
12
12
 
@@ -15,7 +15,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
15
15
  - Gateway-scoped client mode for MCP and other user-JWT sessions
16
16
  - Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
17
17
  - Local helpers for JWT verification, trace propagation, retries, and console capture
18
- - Current package version: `9.0.5`
18
+ - Current package version: `9.0.6`
19
19
 
20
20
  ## Install
21
21
 
@@ -190,7 +190,7 @@ from .errors import (
190
190
  TimeoutError as DominusTimeoutError,
191
191
  )
192
192
 
193
- __version__ = "9.0.6"
193
+ __version__ = "9.0.8"
194
194
  __all__ = [
195
195
  # Main SDK instance
196
196
  "dominus",
@@ -48,6 +48,30 @@ def _is_provisioning_bootstrap_run_kind(run_kind: Optional[str]) -> bool:
48
48
  return _normalize_run_kind_token(run_kind) == "provisioning_bootstrap"
49
49
 
50
50
 
51
+ #: The only run kind ``GET /api/authority/runs`` surfaces. Authority rejects any
52
+ #: other value; ``bootstrap``/``deploy_rollout`` runs are read by run id.
53
+ LIST_RUN_KIND = "workflow"
54
+
55
+ #: Canonical control-result view. Authority still defaults to the legacy
56
+ #: top-level projection on the wire; this SDK never relies on that default.
57
+ RESULT_VIEW_BOUND = "bound"
58
+
59
+
60
+ def remote_command_outcome(payload: Mapping[str, Any]) -> Dict[str, Any]:
61
+ """Unwrap a canonical ``RemoteCommandResult`` to the remote outcome it carries.
62
+
63
+ The envelope's ``owner``/``bound_subject``/``provenance`` state who actually
64
+ performed the command; callers that only need the run summary use this, and
65
+ callers that need attribution read the envelope directly. A payload that is
66
+ not an envelope is returned unchanged so an upstream error body is never
67
+ replaced with an empty dict.
68
+ """
69
+ if isinstance(payload, Mapping) and "outcome" in payload and "bound_subject" in payload:
70
+ outcome = payload.get("outcome")
71
+ return dict(outcome) if isinstance(outcome, Mapping) else {}
72
+ return dict(payload)
73
+
74
+
51
75
  def _is_workflow_recipe_ref(value: Optional[str]) -> bool:
52
76
  return str(value or "").strip().startswith("recipe://workflow-recipe-v1/")
53
77
 
@@ -313,9 +337,22 @@ class AuthorityNamespace:
313
337
  timeout=self._http_timeout(timeout, 30.0),
314
338
  )
315
339
 
340
+ async def run_kinds(self, *, timeout: Optional[float] = None) -> Dict[str, Any]:
341
+ """Read the run-kind/capability matrix. ``GET /api/authority/run-kinds``.
342
+
343
+ Explicit capability discovery: callers ask which actions a kind supports
344
+ (and which kinds the run list surfaces) instead of guessing from run-id
345
+ prefixes.
346
+ """
347
+ return await self._get(
348
+ "/api/authority/run-kinds",
349
+ timeout=self._http_timeout(timeout, 30.0),
350
+ )
351
+
316
352
  async def list_runs(
317
353
  self,
318
354
  *,
355
+ kind: str = LIST_RUN_KIND,
319
356
  workflow_id: Optional[str] = None,
320
357
  status: Optional[str] = None,
321
358
  group: Optional[str] = None,
@@ -328,9 +365,20 @@ class AuthorityNamespace:
328
365
  offset: int = 0,
329
366
  timeout: Optional[float] = None,
330
367
  ) -> Dict[str, Any]:
331
- """List Authority-backed runs. ``GET /api/authority/runs``."""
368
+ """List Authority-backed workflow runs. ``GET /api/authority/runs``.
369
+
370
+ This list is workflow-kind only and says so: ``kind`` is sent explicitly
371
+ rather than inheriting a server default that reads like a universal
372
+ inventory. Use :meth:`run_kinds` to see which kinds are listable.
373
+ """
374
+ if str(kind).strip().lower() != LIST_RUN_KIND:
375
+ raise ValueError(
376
+ f"Authority lists only kind={LIST_RUN_KIND!r} runs; "
377
+ f"{kind!r} runs are read by run id via get_run()"
378
+ )
332
379
  tid, tev = self._target_query_params(target_org_id, target_env)
333
380
  qs = _query_string({
381
+ "kind": LIST_RUN_KIND,
334
382
  "workflow_id": workflow_id,
335
383
  "status": status,
336
384
  "group": group,
@@ -378,6 +426,7 @@ class AuthorityNamespace:
378
426
  "metadata": metadata,
379
427
  **self._initiator(initiator_type, initiator_id, idempotency_key),
380
428
  **self._scope(app_slug, env, target_org_id, target_env),
429
+ "result_view": RESULT_VIEW_BOUND,
381
430
  })
382
431
  return await self._post(
383
432
  f"/api/authority/runs/{quote(run_id, safe='')}/cancel",
@@ -422,6 +471,7 @@ class AuthorityNamespace:
422
471
  "metadata": metadata,
423
472
  **self._initiator(initiator_type, initiator_id, idempotency_key),
424
473
  **self._scope(app_slug, env, target_org_id, target_env),
474
+ "result_view": RESULT_VIEW_BOUND,
425
475
  })
426
476
  return await self._post(
427
477
  f"/api/authority/runs/{quote(run_id, safe='')}/retry",
@@ -459,6 +509,7 @@ class AuthorityNamespace:
459
509
  "metadata": metadata,
460
510
  **self._initiator(initiator_type, initiator_id, idempotency_key),
461
511
  **self._scope(app_slug, env, target_org_id, target_env),
512
+ "result_view": RESULT_VIEW_BOUND,
462
513
  })
463
514
  return await self._post(
464
515
  f"/api/authority/runs/{quote(run_id, safe='')}/nudge",
@@ -898,6 +949,9 @@ class AuthorityNamespace:
898
949
  region: Optional[str] = None,
899
950
  system: Optional[str] = None,
900
951
  github_mode: Optional[str] = None,
952
+ repo_template: Optional[str] = None,
953
+ standalone: Optional[bool] = None,
954
+ root_role: Optional[str] = None,
901
955
  overwrite_existing: Optional[bool] = None,
902
956
  owner_email: Optional[str] = None,
903
957
  execution_mode: Optional[str] = None,
@@ -928,6 +982,9 @@ class AuthorityNamespace:
928
982
  "region": region,
929
983
  "system": system,
930
984
  "github_mode": github_mode,
985
+ "repo_template": repo_template,
986
+ "standalone": standalone,
987
+ "root_role": root_role,
931
988
  "overwrite_existing": overwrite_existing,
932
989
  "owner_email": owner_email,
933
990
  "execution_mode": execution_mode,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 9.0.6
3
+ Version: 9.0.8
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -38,8 +38,8 @@ Async Python SDK for the Dominus gateway-first service plane.
38
38
  ## Agent Guide
39
39
 
40
40
  Start with `docs/agent-guide/INDEX.md`. The current snapshot is
41
- `docs/agent-guide/2026-08-04-sdk-python/00-reading-order.md`; the latest
42
- cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
41
+ `docs/agent-guide/2026-09-03-0942-sdk-python/00-reading-order.md`; the latest
42
+ cleanup audit is `docs/janitor/2026-09-03-0942-sdk-python-cleanup-audit.md`.
43
43
 
44
44
  ## What This Repo Ships
45
45
 
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-08-04-sdk-python-cleanup-audit.md`.
48
48
  - Gateway-scoped client mode for MCP and other user-JWT sessions
49
49
  - Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
50
50
  - Local helpers for JWT verification, trace propagation, retries, and console capture
51
- - Current package version: `9.0.5`
51
+ - Current package version: `9.0.6`
52
52
 
53
53
  ## Install
54
54
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dominus-sdk-python"
7
- version = "9.0.6"
7
+ version = "9.0.8"
8
8
  description = "Python SDK for the Dominus gateway-first platform"
9
9
  readme = "README.md"
10
10
  license = "LicenseRef-Proprietary"
@@ -515,3 +515,130 @@ async def test_authority_ensure_run_bootstrap_requires_provisioning_target_slug(
515
515
  env="production",
516
516
  )
517
517
  assert client.calls == []
518
+
519
+
520
+ # --- Task 21: canonical run-kind discovery, honest list, bound control results --
521
+
522
+
523
+ @pytest.mark.asyncio
524
+ async def test_run_kinds_reads_the_explicit_capability_surface():
525
+ client = FakeClient()
526
+ ns = AuthorityNamespace(client)
527
+
528
+ await ns.run_kinds()
529
+
530
+ assert client.calls[0]["endpoint"] == "/api/authority/run-kinds"
531
+ assert client.calls[0]["method"] == "GET"
532
+
533
+
534
+ @pytest.mark.asyncio
535
+ async def test_list_runs_states_the_workflow_kind_it_actually_gets():
536
+ client = FakeClient()
537
+ ns = AuthorityNamespace(client)
538
+
539
+ await ns.list_runs(limit=5)
540
+
541
+ assert "kind=workflow" in client.calls[0]["endpoint"]
542
+
543
+
544
+ @pytest.mark.asyncio
545
+ async def test_list_runs_rejects_a_kind_authority_does_not_list():
546
+ client = FakeClient()
547
+ ns = AuthorityNamespace(client)
548
+
549
+ with pytest.raises(ValueError):
550
+ await ns.list_runs(kind="bootstrap")
551
+
552
+ assert client.calls == []
553
+
554
+
555
+ @pytest.mark.asyncio
556
+ async def test_run_control_calls_request_the_canonical_bound_result():
557
+ client = FakeClient()
558
+ ns = AuthorityNamespace(client)
559
+
560
+ await ns.cancel_run("run-1", reason="operator")
561
+ await ns.retry_run("run-1", reason="operator")
562
+ await ns.nudge_run("run-1", reason="operator")
563
+
564
+ assert [call["body"]["result_view"] for call in client.calls] == ["bound", "bound", "bound"]
565
+
566
+
567
+ def test_run_control_outcome_helper_preserves_owner_and_provenance():
568
+ from dominus.namespaces.authority import remote_command_outcome
569
+
570
+ envelope = {
571
+ "owner": "workflow-manager",
572
+ "bound_subject": {"kind": "workflow", "canonical_id": "wf-1"},
573
+ "operation": "cancel",
574
+ "outcome": {"run_id": "run-1", "status": "cancelled"},
575
+ "observed_at": "2026-08-24T00:00:00Z",
576
+ "provenance": {"request_id": "req_1", "trace_id": "tr_1"},
577
+ }
578
+
579
+ assert remote_command_outcome(envelope) == {"run_id": "run-1", "status": "cancelled"}
580
+ # A non-envelope payload is returned untouched rather than silently emptied.
581
+ assert remote_command_outcome({"run_id": "run-1"}) == {"run_id": "run-1"}
582
+
583
+
584
+ def test_bootstrap_signature_carries_repo_template():
585
+ bootstrap_sig = inspect.signature(AuthorityNamespace.bootstrap_provisioning_target)
586
+ assert "repo_template" in bootstrap_sig.parameters
587
+
588
+
589
+ @pytest.mark.asyncio
590
+ async def test_bootstrap_provisioning_target_carries_repo_template():
591
+ client = FakeClient()
592
+ namespace = AuthorityNamespace(client)
593
+ await namespace.bootstrap_provisioning_target(
594
+ "summit-radiology",
595
+ shared_app_slug="shared-core",
596
+ app_slug="carebridge",
597
+ env="production",
598
+ repo_template="dominus-fastapi-template",
599
+ )
600
+ assert client.calls[-1]["body"]["repo_template"] == "dominus-fastapi-template"
601
+
602
+ absent_client = FakeClient()
603
+ absent_namespace = AuthorityNamespace(absent_client)
604
+ await absent_namespace.bootstrap_provisioning_target(
605
+ "summit-radiology",
606
+ shared_app_slug="shared-core",
607
+ app_slug="carebridge",
608
+ env="production",
609
+ )
610
+ # Absent field keeps the legacy wire body byte-identical.
611
+ assert "repo_template" not in absent_client.calls[-1]["body"]
612
+
613
+
614
+ def test_bootstrap_signature_carries_standalone_and_root_role():
615
+ bootstrap_sig = inspect.signature(AuthorityNamespace.bootstrap_provisioning_target)
616
+ assert "standalone" in bootstrap_sig.parameters
617
+ assert "root_role" in bootstrap_sig.parameters
618
+
619
+
620
+ @pytest.mark.asyncio
621
+ async def test_bootstrap_provisioning_target_carries_standalone_and_root_role():
622
+ client = FakeClient()
623
+ namespace = AuthorityNamespace(client)
624
+ await namespace.bootstrap_provisioning_target(
625
+ "summit-radiology",
626
+ app_slug="carebridge",
627
+ env="production",
628
+ standalone=True,
629
+ root_role="root",
630
+ )
631
+ assert client.calls[-1]["body"]["standalone"] is True
632
+ assert client.calls[-1]["body"]["root_role"] == "root"
633
+
634
+ absent_client = FakeClient()
635
+ absent_namespace = AuthorityNamespace(absent_client)
636
+ await absent_namespace.bootstrap_provisioning_target(
637
+ "summit-radiology",
638
+ shared_app_slug="shared-core",
639
+ app_slug="carebridge",
640
+ env="production",
641
+ )
642
+ # Absent fields keep the legacy wire body byte-identical.
643
+ assert "standalone" not in absent_client.calls[-1]["body"]
644
+ assert "root_role" not in absent_client.calls[-1]["body"]