dominus-sdk-python 3.0.5__tar.gz → 4.0.1__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 (58) hide show
  1. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/PKG-INFO +18 -2
  2. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/README.md +17 -1
  3. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/__init__.py +1 -1
  4. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/core.py +0 -3
  5. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/authority.py +60 -0
  6. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/logs.py +81 -0
  7. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus_sdk_python.egg-info/PKG-INFO +18 -2
  8. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/pyproject.toml +1 -1
  9. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_authority_public_vocabulary.py +44 -1
  10. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_logs.py +55 -0
  11. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/config/__init__.py +0 -0
  12. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/config/endpoints.py +0 -0
  13. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/errors.py +0 -0
  14. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/__init__.py +0 -0
  15. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/auth.py +0 -0
  16. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/cache.py +0 -0
  17. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/console_capture.py +0 -0
  18. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/crypto.py +0 -0
  19. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/sse.py +0 -0
  20. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/helpers/trace.py +0 -0
  21. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/__init__.py +0 -0
  22. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/admin.py +0 -0
  23. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/ai.py +0 -0
  24. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/artifacts.py +0 -0
  25. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/auth.py +0 -0
  26. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/courier.py +0 -0
  27. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/db.py +0 -0
  28. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/ddl.py +0 -0
  29. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/deployer.py +0 -0
  30. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/fastapi.py +0 -0
  31. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/files.py +0 -0
  32. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/health.py +0 -0
  33. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/jobs.py +0 -0
  34. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/portal.py +0 -0
  35. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/processor.py +0 -0
  36. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/redis.py +0 -0
  37. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/secrets.py +0 -0
  38. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/secure.py +0 -0
  39. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/sync.py +0 -0
  40. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/warden.py +0 -0
  41. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/namespaces/workflow.py +0 -0
  42. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/services/__init__.py +0 -0
  43. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus/start.py +0 -0
  44. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
  45. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
  46. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
  47. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
  48. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/setup.cfg +0 -0
  49. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_auth.py +0 -0
  50. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_control_plane_namespaces.py +0 -0
  51. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_errors.py +0 -0
  52. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_flat_commands.py +0 -0
  53. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_health.py +0 -0
  54. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_provisioning_parity.py +0 -0
  55. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_public_exports.py +0 -0
  56. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_transport_compat.py +0 -0
  57. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/tests/test_workflow_lifecycle.py +0 -0
  58. {dominus_sdk_python-3.0.5 → dominus_sdk_python-4.0.1}/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: 3.0.5
3
+ Version: 4.0.1
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
6
  License: Proprietary
@@ -41,7 +41,7 @@ Async Python SDK for the Dominus gateway-first service plane.
41
41
  - Namespace-first API with a small root shortcut surface
42
42
  - Gateway-scoped client mode for MCP and other user-JWT sessions
43
43
  - Local helpers for JWT verification, trace propagation, retries, and console capture
44
- - Current package version: `3.0.5`
44
+ - Current package version: `4.0.1`
45
45
 
46
46
  ## Install
47
47
 
@@ -74,6 +74,22 @@ timeline = await dominus.authority.get_run_timeline(
74
74
  since="2026-04-11T08:33:00Z",
75
75
  until="2026-04-11T09:33:00Z",
76
76
  )
77
+
78
+ timeline_archive = await dominus.authority.get_timeline_archive_status(
79
+ app_slug="carebridge-summit",
80
+ env="production",
81
+ )
82
+ logs_archive = await dominus.logs.get_archive_status(all_scopes=True, limit=5)
83
+
84
+ # Archive maintenance is explicit and dry-run first.
85
+ verify = await dominus.authority.verify_timeline_archive_manifests(
86
+ since="2026-04-01T00:00:00Z",
87
+ until="2026-04-02T00:00:00Z",
88
+ )
89
+ prune = await dominus.logs.prune_archive_retention(
90
+ retention_days=90,
91
+ dry_run=True,
92
+ )
77
93
  ```
78
94
 
79
95
  ## Session-Scoped Clients
@@ -8,7 +8,7 @@ Async Python SDK for the Dominus gateway-first service plane.
8
8
  - Namespace-first API with a small root shortcut surface
9
9
  - Gateway-scoped client mode for MCP and other user-JWT sessions
10
10
  - Local helpers for JWT verification, trace propagation, retries, and console capture
11
- - Current package version: `3.0.5`
11
+ - Current package version: `4.0.1`
12
12
 
13
13
  ## Install
14
14
 
@@ -41,6 +41,22 @@ timeline = await dominus.authority.get_run_timeline(
41
41
  since="2026-04-11T08:33:00Z",
42
42
  until="2026-04-11T09:33:00Z",
43
43
  )
44
+
45
+ timeline_archive = await dominus.authority.get_timeline_archive_status(
46
+ app_slug="carebridge-summit",
47
+ env="production",
48
+ )
49
+ logs_archive = await dominus.logs.get_archive_status(all_scopes=True, limit=5)
50
+
51
+ # Archive maintenance is explicit and dry-run first.
52
+ verify = await dominus.authority.verify_timeline_archive_manifests(
53
+ since="2026-04-01T00:00:00Z",
54
+ until="2026-04-02T00:00:00Z",
55
+ )
56
+ prune = await dominus.logs.prune_archive_retention(
57
+ retention_days=90,
58
+ dry_run=True,
59
+ )
44
60
  ```
45
61
 
46
62
  ## Session-Scoped Clients
@@ -163,7 +163,7 @@ from .errors import (
163
163
  TimeoutError as DominusTimeoutError,
164
164
  )
165
165
 
166
- __version__ = "3.0.5"
166
+ __version__ = "4.0.0"
167
167
  __all__ = [
168
168
  # Main SDK instance
169
169
  "dominus",
@@ -552,7 +552,6 @@ async def mint_selected_scope_jwt(
552
552
  target_org_id: str,
553
553
  target_app_slug: Optional[str] = None,
554
554
  target_env: Optional[str] = None,
555
- use_shared: bool = False
556
555
  ) -> str:
557
556
  """
558
557
  Mint a selected-scope JWT for a target org/app/env scope.
@@ -566,7 +565,6 @@ async def mint_selected_scope_jwt(
566
565
  target_app_slug: Optional descriptive slug for the target app. The live
567
566
  mint route keys selected-scope targeting off org/env.
568
567
  target_env: Environment (development, staging, production)
569
- use_shared: Deprecated no-op retained only for call-site simplicity
570
568
 
571
569
  Returns:
572
570
  Selected-scope JWT string
@@ -593,7 +591,6 @@ async def mint_selected_scope_jwt(
593
591
  "target_org_id": target_org_id,
594
592
  "target_env": target_env,
595
593
  }
596
- _ = use_shared
597
594
 
598
595
  body_b64 = _b64_encode(body_json)
599
596
 
@@ -1574,6 +1574,66 @@ class AuthorityNamespace:
1574
1574
  timeout=self._http_timeout(timeout, 15.0),
1575
1575
  )
1576
1576
 
1577
+ async def repair_timeline_archive_manifests(
1578
+ self,
1579
+ *,
1580
+ since: Optional[str] = None,
1581
+ until: Optional[str] = None,
1582
+ timeout: Optional[float] = None,
1583
+ ) -> Dict[str, Any]:
1584
+ """Rebuild Authority timeline daily archive manifests from cold buckets. ``POST /api/authority/timelines/archive-repair``."""
1585
+ body = _compact({
1586
+ "since": since,
1587
+ "until": until,
1588
+ })
1589
+ return await self._post(
1590
+ "/api/authority/timelines/archive-repair",
1591
+ body,
1592
+ timeout=self._http_timeout(timeout, 30.0),
1593
+ )
1594
+
1595
+ async def verify_timeline_archive_manifests(
1596
+ self,
1597
+ *,
1598
+ since: Optional[str] = None,
1599
+ until: Optional[str] = None,
1600
+ timeout: Optional[float] = None,
1601
+ ) -> Dict[str, Any]:
1602
+ """Verify Authority timeline daily archive manifests against cold buckets. ``POST /api/authority/timelines/archive-verify``."""
1603
+ body = _compact({
1604
+ "since": since,
1605
+ "until": until,
1606
+ })
1607
+ return await self._post(
1608
+ "/api/authority/timelines/archive-verify",
1609
+ body,
1610
+ timeout=self._http_timeout(timeout, 30.0),
1611
+ )
1612
+
1613
+ async def prune_timeline_archive_retention(
1614
+ self,
1615
+ *,
1616
+ since: Optional[str] = None,
1617
+ until: Optional[str] = None,
1618
+ retention_days: Optional[int] = None,
1619
+ dry_run: bool = True,
1620
+ confirm: Optional[str] = None,
1621
+ timeout: Optional[float] = None,
1622
+ ) -> Dict[str, Any]:
1623
+ """Dry-run or enforce Authority timeline cold archive retention. ``POST /api/authority/timelines/archive-prune``."""
1624
+ body = _compact({
1625
+ "since": since,
1626
+ "until": until,
1627
+ "retention_days": retention_days,
1628
+ "dry_run": dry_run,
1629
+ "confirm": confirm,
1630
+ })
1631
+ return await self._post(
1632
+ "/api/authority/timelines/archive-prune",
1633
+ body,
1634
+ timeout=self._http_timeout(timeout, 30.0),
1635
+ )
1636
+
1577
1637
  # ==================================================================
1578
1638
  # Context
1579
1639
  # ==================================================================
@@ -429,6 +429,87 @@ class LogsNamespace:
429
429
  use_gateway=True,
430
430
  )
431
431
 
432
+ async def repair_archive_manifests(
433
+ self,
434
+ *,
435
+ since: Optional[str] = None,
436
+ until: Optional[str] = None,
437
+ target_org_id: Optional[str] = None,
438
+ target_env: Optional[str] = None,
439
+ use_shared: Optional[bool] = None,
440
+ ) -> Dict[str, Any]:
441
+ """Rebuild Logs Worker daily archive manifests from existing cold buckets. ``POST /api/logs/archive-repair``."""
442
+ body = {
443
+ "since": since,
444
+ "until": until,
445
+ "target_org_id": target_org_id,
446
+ "target_env": target_env,
447
+ }
448
+ if use_shared is not None:
449
+ body["use_shared"] = use_shared
450
+ return await self._client._request(
451
+ endpoint="/api/logs/archive-repair",
452
+ method="POST",
453
+ body={k: v for k, v in body.items() if v is not None and v != ""},
454
+ use_gateway=True,
455
+ )
456
+
457
+ async def verify_archive_manifests(
458
+ self,
459
+ *,
460
+ since: Optional[str] = None,
461
+ until: Optional[str] = None,
462
+ target_org_id: Optional[str] = None,
463
+ target_env: Optional[str] = None,
464
+ use_shared: Optional[bool] = None,
465
+ ) -> Dict[str, Any]:
466
+ """Verify Logs Worker daily archive manifests against existing cold buckets. ``POST /api/logs/archive-verify``."""
467
+ body = {
468
+ "since": since,
469
+ "until": until,
470
+ "target_org_id": target_org_id,
471
+ "target_env": target_env,
472
+ }
473
+ if use_shared is not None:
474
+ body["use_shared"] = use_shared
475
+ return await self._client._request(
476
+ endpoint="/api/logs/archive-verify",
477
+ method="POST",
478
+ body={k: v for k, v in body.items() if v is not None and v != ""},
479
+ use_gateway=True,
480
+ )
481
+
482
+ async def prune_archive_retention(
483
+ self,
484
+ *,
485
+ since: Optional[str] = None,
486
+ until: Optional[str] = None,
487
+ retention_days: Optional[int] = None,
488
+ dry_run: bool = True,
489
+ confirm: Optional[str] = None,
490
+ target_org_id: Optional[str] = None,
491
+ target_env: Optional[str] = None,
492
+ use_shared: Optional[bool] = None,
493
+ ) -> Dict[str, Any]:
494
+ """Dry-run or enforce Logs Worker cold archive retention. ``POST /api/logs/archive-prune``."""
495
+ body = {
496
+ "since": since,
497
+ "until": until,
498
+ "retention_days": retention_days,
499
+ "dry_run": dry_run,
500
+ "confirm": confirm,
501
+ "target_org_id": target_org_id,
502
+ "target_env": target_env,
503
+ }
504
+ if use_shared is not None:
505
+ body["use_shared"] = use_shared
506
+ return await self._client._request(
507
+ endpoint="/api/logs/archive-prune",
508
+ method="POST",
509
+ body={k: v for k, v in body.items() if v is not None and v != ""},
510
+ use_gateway=True,
511
+ )
512
+
432
513
  async def query(
433
514
  self,
434
515
  minutes: int = 10,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dominus-sdk-python
3
- Version: 3.0.5
3
+ Version: 4.0.1
4
4
  Summary: Python SDK for the Dominus gateway-first platform
5
5
  Author-email: CareBridge Systems <dev@carebridge.io>
6
6
  License: Proprietary
@@ -41,7 +41,7 @@ Async Python SDK for the Dominus gateway-first service plane.
41
41
  - Namespace-first API with a small root shortcut surface
42
42
  - Gateway-scoped client mode for MCP and other user-JWT sessions
43
43
  - Local helpers for JWT verification, trace propagation, retries, and console capture
44
- - Current package version: `3.0.5`
44
+ - Current package version: `4.0.1`
45
45
 
46
46
  ## Install
47
47
 
@@ -74,6 +74,22 @@ timeline = await dominus.authority.get_run_timeline(
74
74
  since="2026-04-11T08:33:00Z",
75
75
  until="2026-04-11T09:33:00Z",
76
76
  )
77
+
78
+ timeline_archive = await dominus.authority.get_timeline_archive_status(
79
+ app_slug="carebridge-summit",
80
+ env="production",
81
+ )
82
+ logs_archive = await dominus.logs.get_archive_status(all_scopes=True, limit=5)
83
+
84
+ # Archive maintenance is explicit and dry-run first.
85
+ verify = await dominus.authority.verify_timeline_archive_manifests(
86
+ since="2026-04-01T00:00:00Z",
87
+ until="2026-04-02T00:00:00Z",
88
+ )
89
+ prune = await dominus.logs.prune_archive_retention(
90
+ retention_days=90,
91
+ dry_run=True,
92
+ )
77
93
  ```
78
94
 
79
95
  ## Session-Scoped Clients
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dominus-sdk-python"
7
- version = "3.0.5"
7
+ version = "4.0.1"
8
8
  description = "Python SDK for the Dominus gateway-first platform"
9
9
  readme = "README.md"
10
10
  license = {text = "Proprietary"}
@@ -79,6 +79,9 @@ def test_authority_scope_signatures_use_new_public_vocabulary():
79
79
  timeline_sig = inspect.signature(AuthorityNamespace.get_run_timeline)
80
80
  query_timelines_sig = inspect.signature(AuthorityNamespace.query_timelines)
81
81
  archive_status_sig = inspect.signature(AuthorityNamespace.get_timeline_archive_status)
82
+ archive_repair_sig = inspect.signature(AuthorityNamespace.repair_timeline_archive_manifests)
83
+ archive_verify_sig = inspect.signature(AuthorityNamespace.verify_timeline_archive_manifests)
84
+ archive_prune_sig = inspect.signature(AuthorityNamespace.prune_timeline_archive_retention)
82
85
  mint_sig = inspect.signature(core_helpers.mint_selected_scope_jwt)
83
86
 
84
87
  for name in ("app_slug", "env", "target_org_id", "target_env", "run_kind", "target_app_slug", "bootstrap_profile"):
@@ -104,10 +107,16 @@ def test_authority_scope_signatures_use_new_public_vocabulary():
104
107
  assert name in timeline_sig.parameters
105
108
  assert name in query_timelines_sig.parameters
106
109
  assert "all_scopes" in archive_status_sig.parameters
110
+ for name in ("since", "until"):
111
+ assert name in archive_repair_sig.parameters
112
+ assert name in archive_verify_sig.parameters
113
+ assert name in archive_prune_sig.parameters
114
+ for name in ("retention_days", "dry_run", "confirm"):
115
+ assert name in archive_prune_sig.parameters
107
116
 
108
117
  for name in ("target_org_id", "target_app_slug", "target_env"):
109
118
  assert name in mint_sig.parameters
110
- for legacy in ("target_project_id", "target_project_slug", "target_environment"):
119
+ for legacy in ("target_project_id", "target_project_slug", "target_environment", "use_shared"):
111
120
  assert legacy not in mint_sig.parameters
112
121
 
113
122
 
@@ -197,6 +206,21 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
197
206
  env="production",
198
207
  limit=5,
199
208
  )
209
+ await namespace.repair_timeline_archive_manifests(
210
+ since="2026-04-01T00:00:00Z",
211
+ until="2026-04-02T00:00:00Z",
212
+ )
213
+ await namespace.verify_timeline_archive_manifests(
214
+ since="2026-04-01T00:00:00Z",
215
+ until="2026-04-02T00:00:00Z",
216
+ )
217
+ await namespace.prune_timeline_archive_retention(
218
+ since="2026-01-01T00:00:00Z",
219
+ until="2026-01-02T00:00:00Z",
220
+ retention_days=365,
221
+ dry_run=False,
222
+ confirm="DELETE_EXPIRED_ARCHIVE_OBJECTS",
223
+ )
200
224
 
201
225
  ensure_body = client.calls[0]["body"]
202
226
  assert ensure_body["app_slug"] == "carebridge"
@@ -255,6 +279,25 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
255
279
  "/api/authority/timelines/archive-status?"
256
280
  "all_scopes=true&app_slug=carebridge&env=production&limit=5"
257
281
  )
282
+ assert client.calls[10]["endpoint"] == "/api/authority/timelines/archive-repair"
283
+ assert client.calls[10]["method"] == "POST"
284
+ assert client.calls[10]["body"] == {
285
+ "since": "2026-04-01T00:00:00Z",
286
+ "until": "2026-04-02T00:00:00Z",
287
+ }
288
+ assert client.calls[11]["endpoint"] == "/api/authority/timelines/archive-verify"
289
+ assert client.calls[11]["body"] == {
290
+ "since": "2026-04-01T00:00:00Z",
291
+ "until": "2026-04-02T00:00:00Z",
292
+ }
293
+ assert client.calls[12]["endpoint"] == "/api/authority/timelines/archive-prune"
294
+ assert client.calls[12]["body"] == {
295
+ "since": "2026-01-01T00:00:00Z",
296
+ "until": "2026-01-02T00:00:00Z",
297
+ "retention_days": 365,
298
+ "dry_run": False,
299
+ "confirm": "DELETE_EXPIRED_ARCHIVE_OBJECTS",
300
+ }
258
301
 
259
302
 
260
303
  @pytest.mark.asyncio
@@ -91,3 +91,58 @@ async def test_logs_get_archive_status_forwards_filters():
91
91
  )
92
92
  assert client.calls[0]["method"] == "GET"
93
93
  assert client.calls[0]["use_gateway"] is True
94
+
95
+
96
+ @pytest.mark.asyncio
97
+ async def test_logs_archive_maintenance_helpers_forward_canonical_body():
98
+ client = FakeClient()
99
+ namespace = LogsNamespace(client)
100
+
101
+ await namespace.repair_archive_manifests(
102
+ since="2026-04-01T00:00:00Z",
103
+ until="2026-04-02T00:00:00Z",
104
+ target_org_id="org-123",
105
+ target_env="production",
106
+ use_shared=True,
107
+ )
108
+ await namespace.verify_archive_manifests(
109
+ since="2026-04-01T00:00:00Z",
110
+ until="2026-04-02T00:00:00Z",
111
+ target_org_id="org-123",
112
+ target_env="production",
113
+ use_shared=True,
114
+ )
115
+ await namespace.prune_archive_retention(
116
+ since="2026-01-01T00:00:00Z",
117
+ until="2026-01-02T00:00:00Z",
118
+ retention_days=90,
119
+ dry_run=False,
120
+ confirm="DELETE_EXPIRED_ARCHIVE_OBJECTS",
121
+ target_org_id="org-123",
122
+ target_env="production",
123
+ use_shared=True,
124
+ )
125
+
126
+ assert client.calls[0]["endpoint"] == "/api/logs/archive-repair"
127
+ assert client.calls[0]["method"] == "POST"
128
+ assert client.calls[0]["body"] == {
129
+ "since": "2026-04-01T00:00:00Z",
130
+ "until": "2026-04-02T00:00:00Z",
131
+ "target_org_id": "org-123",
132
+ "target_env": "production",
133
+ "use_shared": True,
134
+ }
135
+ assert client.calls[1]["endpoint"] == "/api/logs/archive-verify"
136
+ assert client.calls[1]["body"]["target_org_id"] == "org-123"
137
+ assert client.calls[1]["body"]["use_shared"] is True
138
+ assert client.calls[2]["endpoint"] == "/api/logs/archive-prune"
139
+ assert client.calls[2]["body"] == {
140
+ "since": "2026-01-01T00:00:00Z",
141
+ "until": "2026-01-02T00:00:00Z",
142
+ "retention_days": 90,
143
+ "dry_run": False,
144
+ "confirm": "DELETE_EXPIRED_ARCHIVE_OBJECTS",
145
+ "target_org_id": "org-123",
146
+ "target_env": "production",
147
+ "use_shared": True,
148
+ }