openbox-temporal-sdk-python 1.1.1__tar.gz → 1.2.0__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.
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.gitignore +1 -0
- openbox_temporal_sdk_python-1.2.0/CHANGELOG.md +238 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/PKG-INFO +140 -101
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/README.md +138 -100
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/changelog-hook-level-governance.md +9 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/code-standards.md +58 -63
- openbox_temporal_sdk_python-1.2.0/docs/codebase-summary.md +632 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/configuration.md +45 -3
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/project-overview-pdr.md +42 -30
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/system-architecture.md +218 -511
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/docs/temporal-plugin-integration-guide.md +15 -11
- openbox_temporal_sdk_python-1.2.0/openbox/__init__.py +125 -0
- openbox_temporal_sdk_python-1.2.0/openbox/activities.py +278 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/activity_interceptor.py +123 -199
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/client.py +32 -8
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/config.py +166 -49
- openbox_temporal_sdk_python-1.2.0/openbox/core_adapter.py +279 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/errors.py +74 -30
- openbox_temporal_sdk_python-1.2.0/openbox/governance_state.py +101 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/hitl.py +8 -12
- openbox_temporal_sdk_python-1.2.0/openbox/multi_agent.py +136 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/plugin.py +57 -34
- openbox_temporal_sdk_python-1.2.0/openbox/request_signing.py +89 -0
- openbox_temporal_sdk_python-1.2.0/openbox/tracing.py +59 -0
- openbox_temporal_sdk_python-1.2.0/openbox/types.py +79 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/verdict_handler.py +0 -1
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/worker.py +60 -48
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/workflow_interceptor.py +126 -92
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/pyproject.toml +9 -1
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/sonar-project.properties +1 -4
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/__init__.py +0 -1
- openbox_temporal_sdk_python-1.2.0/tests/conftest.py +56 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_activities.py +7 -30
- openbox_temporal_sdk_python-1.2.0/tests/test_activity_interceptor.py +1869 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_approval_action_precedence.py +102 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_base_sdk_signing_parity.py +102 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_config.py +0 -31
- openbox_temporal_sdk_python-1.2.0/tests/test_core_conformance_suite.py +261 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_multi_agent.py +129 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_no_import_machinery_in_governed_open_paths.py +70 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_plugin.py +73 -46
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_plugin_integration.py +16 -21
- openbox_temporal_sdk_python-1.2.0/tests/test_public_api_compatibility.py +154 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_request_signing.py +280 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_signing_config.py +242 -0
- openbox_temporal_sdk_python-1.2.0/tests/test_temporal_hook_parity.py +129 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_types.py +0 -98
- openbox_temporal_sdk_python-1.2.0/tests/test_worker.py +1099 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/tests/test_workflow_interceptor.py +267 -142
- openbox_temporal_sdk_python-1.2.0/tests/test_workflow_sandbox_import_safety.py +67 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/uv.lock +309 -12
- openbox_temporal_sdk_python-1.1.1/CHANGELOG.md +0 -68
- openbox_temporal_sdk_python-1.1.1/docs/codebase-summary.md +0 -768
- openbox_temporal_sdk_python-1.1.1/openbox/__init__.py +0 -181
- openbox_temporal_sdk_python-1.1.1/openbox/activities.py +0 -201
- openbox_temporal_sdk_python-1.1.1/openbox/context_propagation.py +0 -56
- openbox_temporal_sdk_python-1.1.1/openbox/db_governance_hooks.py +0 -771
- openbox_temporal_sdk_python-1.1.1/openbox/file_governance_hooks.py +0 -396
- openbox_temporal_sdk_python-1.1.1/openbox/hook_governance.py +0 -427
- openbox_temporal_sdk_python-1.1.1/openbox/http_governance_hooks.py +0 -859
- openbox_temporal_sdk_python-1.1.1/openbox/otel_setup.py +0 -474
- openbox_temporal_sdk_python-1.1.1/openbox/span_processor.py +0 -240
- openbox_temporal_sdk_python-1.1.1/openbox/tracing.py +0 -297
- openbox_temporal_sdk_python-1.1.1/openbox/types.py +0 -188
- openbox_temporal_sdk_python-1.1.1/tests/conftest.py +0 -96
- openbox_temporal_sdk_python-1.1.1/tests/test_activity_interceptor.py +0 -2572
- openbox_temporal_sdk_python-1.1.1/tests/test_db_governance_hooks.py +0 -886
- openbox_temporal_sdk_python-1.1.1/tests/test_file_governance_hooks.py +0 -784
- openbox_temporal_sdk_python-1.1.1/tests/test_http_body_truncation.py +0 -75
- openbox_temporal_sdk_python-1.1.1/tests/test_otel_setup.py +0 -2255
- openbox_temporal_sdk_python-1.1.1/tests/test_psycopg2_hooks_verify.py +0 -60
- openbox_temporal_sdk_python-1.1.1/tests/test_span_processor.py +0 -706
- openbox_temporal_sdk_python-1.1.1/tests/test_tracing.py +0 -1288
- openbox_temporal_sdk_python-1.1.1/tests/test_worker.py +0 -1896
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.github/workflows/publish.yml +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.github/workflows/sonarqube.yaml +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.python-version +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/.repomixignore +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/LICENSE +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/openbox/py.typed +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/release-manifest.json +0 -0
- {openbox_temporal_sdk_python-1.1.1 → openbox_temporal_sdk_python-1.2.0}/repomix-output.xml +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to OpenBox SDK for Temporal Workflows.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.2.0] - 2026-07-20
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Depends on `openbox-sdk-python>=0.2.0`** (import package `openbox_core`) — the shared
|
|
12
|
+
base SDK owning contracts, the always-strict gate, identity/signing, the evaluate
|
|
13
|
+
client, context runtime, Core `SpanData` wire serialization, generic instrumentation,
|
|
14
|
+
and the conformance kit. The `0.2.0` floor adds Redis/MongoDB/urllib3/urllib
|
|
15
|
+
instrumentation in the base runtime so no hook coverage is lost in the flip.
|
|
16
|
+
- **Hook governance is now owned entirely by the base runtime.** The worker/plugin
|
|
17
|
+
build and own an `openbox_core` runtime and call `install_instrumentation()`;
|
|
18
|
+
HTTP/DB/file/function hooks, payload shape, evaluation, and enforcement all live in
|
|
19
|
+
`openbox_core`. LangGraph and Temporal now emit one identical flat hook interface.
|
|
20
|
+
- `request_signing` now shims over `openbox_core.identity`. Signed request bytes are
|
|
21
|
+
UNCHANGED — gated byte-for-byte by a golden fixture generated from the pre-migration
|
|
22
|
+
signer (`tests/test_base_sdk_signing_parity.py`).
|
|
23
|
+
- `Verdict`, `GuardrailsCheckResult`, and `WorkflowEventType` re-export the shared
|
|
24
|
+
contracts; `GovernanceVerdictResponse` subclasses the shared `EvaluationResult`
|
|
25
|
+
(same public surface, plus `fallback_used`/`diagnostics`/`raw`/`approval_expiration_time`).
|
|
26
|
+
- Workflow lifecycle events route through shared `EventEnvelope` factories
|
|
27
|
+
(sandbox-safe pure contracts; wire payloads unchanged).
|
|
28
|
+
- Activity execution binds the shared `ActivityContext` with a guaranteed
|
|
29
|
+
try/finally reset — governance context can no longer leak when an activity raises.
|
|
30
|
+
|
|
31
|
+
### Behavior changes (regression-gated)
|
|
32
|
+
|
|
33
|
+
- **Approval decision precedence:** approval poll responses parse via the shared
|
|
34
|
+
`ApprovalResult`; `action` now outranks `verdict` when both are present
|
|
35
|
+
(previously verdict-first), and a response with NEITHER field stays PENDING
|
|
36
|
+
(previously implicit ALLOW). Pinned by `tests/test_approval_action_precedence.py`.
|
|
37
|
+
|
|
38
|
+
### Known behavior differences in the core runtime
|
|
39
|
+
|
|
40
|
+
- **Completed-hook semantics:** core completed hooks never raise to the caller —
|
|
41
|
+
stop verdicts mark FUTURE execution blocked (abort/halt flags). Some legacy
|
|
42
|
+
completed hooks could raise after the operation ran (HTTP response hook, file
|
|
43
|
+
close); raising post-hoc cannot undo the operation, so the core model drops it
|
|
44
|
+
by design.
|
|
45
|
+
- **Fail-closed shaping:** core maps started-hook evaluation failures under
|
|
46
|
+
`on_api_error="fail_closed"` (and started-hook contract errors, always) to a
|
|
47
|
+
non-retryable `GovernanceHalt` via the adapter — same terminal effect as
|
|
48
|
+
legacy's HALT-shaped `GovernanceBlockedError`, different exception chain.
|
|
49
|
+
- **Redaction point for `activity_input`:** the core `ActivityContext` receives
|
|
50
|
+
the post-redaction input (what actually ran); the legacy buffer stored the
|
|
51
|
+
pre-redaction value. Core hook payloads therefore never leak redacted fields.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- `openbox.governance_state.TemporalGovernanceState` — run-scoped state carrying the
|
|
56
|
+
Temporal effects that must survive past a base hook callback: signal BLOCK/HALT
|
|
57
|
+
verdicts that fail the next activity, HITL pending-approval retry markers, and the
|
|
58
|
+
completed-hook stop bridge (completed BLOCK skips the duplicate completed event;
|
|
59
|
+
completed HALT reaches the terminate path). Consumed on every activity exit path.
|
|
60
|
+
- `openbox.core_adapter` — `TemporalFrameworkAdapter` (maps base-SDK verdicts to
|
|
61
|
+
native `ApplicationError` types and the retry-based HITL loop; REQUIRE_APPROVAL marks
|
|
62
|
+
the pending-approval retry marker and degrades to a non-retryable block when HITL is
|
|
63
|
+
disabled/skipped; completed BLOCK/HALT records run-scoped stop state with the resolved
|
|
64
|
+
`ActivityContext`), and `create_core_runtime(...)` which builds and owns the base
|
|
65
|
+
runtime the worker/plugin install. `core_activity_scope` is the sole hook-context
|
|
66
|
+
bridge (guaranteed try/finally reset + trace registration).
|
|
67
|
+
- Redis, MongoDB, urllib3, and urllib governance now flow through the base runtime.
|
|
68
|
+
- Gates: workflow-sandbox import-safety, base-SDK conformance suite driven by the
|
|
69
|
+
Temporal adapter, public-API compatibility suite, and a deterministic
|
|
70
|
+
`llm_completion` hook-parity test (flat interface via the Temporal runtime, no live
|
|
71
|
+
OpenAI credentials).
|
|
72
|
+
|
|
73
|
+
### Removed
|
|
74
|
+
|
|
75
|
+
- `WorkflowSpanProcessor`, `WorkflowSpanBuffer`, and the `setup_opentelemetry_for_governance`
|
|
76
|
+
entry point (public exports). Governance instrumentation is installed by the base
|
|
77
|
+
runtime; there is no Temporal-local OpenTelemetry setup to call.
|
|
78
|
+
- Temporal-local hook modules `otel_setup`, `hook_governance`, `http_governance_hooks`,
|
|
79
|
+
`db_governance_hooks`, `file_governance_hooks`, `span_processor`, and the dead
|
|
80
|
+
`context_propagation` helper (its ContextVar-to-executor propagation lives in the base
|
|
81
|
+
runtime). Their payload-shape/instrumentation coverage moved to the base SDK suite.
|
|
82
|
+
- `openbox.tracing.traced` now wraps `openbox_core.instrumentation.function.governed`;
|
|
83
|
+
`create_span` remains a plain span helper.
|
|
84
|
+
|
|
85
|
+
### Fixed (legacy file instrumentation — pre-existing upstream)
|
|
86
|
+
|
|
87
|
+
- **Runtime RecursionError under file instrumentation:** governance evaluation
|
|
88
|
+
itself opens files (httpx/ssl, package-metadata scans via importlib_metadata/
|
|
89
|
+
zipp) — governing those opens re-evaluated recursively until RecursionError.
|
|
90
|
+
traced_open now (1) passes through any open performed on a thread already
|
|
91
|
+
inside file-governance work (re-entrancy guard) and (2) bypasses
|
|
92
|
+
interpreter-owned trees (venv, stdlib, site-packages — sys.prefix/base_prefix
|
|
93
|
+
+ sysconfig paths): those reads are Python machinery, not application data
|
|
94
|
+
access. Application paths (./.env, data files, temp files) remain governed.
|
|
95
|
+
Same guard mirrored in the base SDK's file instrumentation.
|
|
96
|
+
|
|
97
|
+
### Fixed (HTTP hook payloads — pre-existing upstream)
|
|
98
|
+
|
|
99
|
+
- **Mangled method on httpx spans:** OTel's httpx instrumentation passes the
|
|
100
|
+
method as BYTES; `str(b"POST")` shipped `http_method: "b'POST'"` on every
|
|
101
|
+
httpx started span. Methods now decode bytes-safely at all client paths.
|
|
102
|
+
- **Credential leak in governance payloads:** raw request/response headers
|
|
103
|
+
(`authorization`, `cookie`, `set-cookie`, `x-api-key`, …) were sent to Core
|
|
104
|
+
verbatim — live API keys landed in Core logs. All header dicts are now
|
|
105
|
+
redacted at the span-data builder choke point. Same redaction added to the
|
|
106
|
+
base SDK instrumentation. **Rotate any API keys that appeared in payloads.**
|
|
107
|
+
|
|
108
|
+
### Migration notes
|
|
109
|
+
|
|
110
|
+
- Base-runtime instrumentation is the sole hook governance path
|
|
111
|
+
and the legacy in-repo hook stack is removed. Approval-retry, signal BLOCK/HALT, and
|
|
112
|
+
completed-hook BLOCK/HALT behavior are preserved (run-scoped, cleaned after use).
|
|
113
|
+
- Dropped coverage vs the legacy stack: direct raw-driver DB queries (psycopg2/mysql/
|
|
114
|
+
pymysql/sqlite3 used WITHOUT SQLAlchemy). The base runtime governs SQLAlchemy (all
|
|
115
|
+
backends), asyncpg, Redis, and MongoDB; activating raw dbapi driver instrumentors
|
|
116
|
+
interferes with SQLAlchemy's own dialect queries, so raw-driver-only governance is a
|
|
117
|
+
base-SDK follow-up.
|
|
118
|
+
|
|
119
|
+
## [1.1.2] - 2026-04-22
|
|
120
|
+
|
|
121
|
+
### Security
|
|
122
|
+
|
|
123
|
+
- **API key no longer flows through workflow history.** `send_governance_event` refactored into a `GovernanceActivities` class that holds credentials on `self`; activity inputs carry only `payload`, `timeout`, and `on_api_error`. Before this change, anyone with Describe permissions on the namespace could read the API key from the recorded activity input.
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
|
|
127
|
+
- W3C trace propagation through Temporal headers, on by default via `enable_trace_propagation=True` on `OpenBoxPlugin` and `create_openbox_worker()`. Uses `temporalio.contrib.opentelemetry.TracingInterceptor` so spans started by the caller stitch to workflow/activity spans on the worker side.
|
|
128
|
+
- `ApplicationError` type constants in `errors.py` (`GOVERNANCE_HALT_ERROR_TYPE`, `GOVERNANCE_BLOCK_ERROR_TYPE`, `GOVERNANCE_API_ERROR_TYPE`, `GOVERNANCE_STOP_ERROR_TYPE`) — single source of truth for governance error routing.
|
|
129
|
+
- `openbox.activities.GovernanceActivities` class + `build_governance_activities()` factory.
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
|
|
133
|
+
- **Workflow exception shadowing** — `WorkflowFailed` event send is now wrapped in `try/except`, so a `GovernanceHaltError` from `fail_closed + API down` no longer replaces the real workflow error.
|
|
134
|
+
- **String-matching on exception types** — `workflow_interceptor` now inspects `ApplicationError.type` via the exception chain instead of `"GovernanceHalt" in str(e)`. Eliminates false positives when a user workflow happens to emit an error message containing a governance keyword.
|
|
135
|
+
- **Race creating governance HTTP client** — `hook_governance._get_sync_client` / `_get_async_client` now use double-checked locking. Previously two concurrent activities could each create a client, with the losing instance getting garbage-collected while its connection pool leaked.
|
|
136
|
+
- **Replayer plugin coverage** — `test_plugin_integration.py` now passes `plugins=[plugin]` to `Replayer`, so replay tests validate interceptor determinism, not just user workflow code.
|
|
137
|
+
- Version-pin mismatch — comments in `openbox/__init__.py` said `temporalio >= 1.24.0` but pyproject/README pin `1.23.0`. Corrected to `1.23.0`.
|
|
138
|
+
|
|
139
|
+
### Changed
|
|
140
|
+
|
|
141
|
+
- `plugin.py` / `worker.py` now use `logging.getLogger(__name__)` for initialization status messages instead of `print()`.
|
|
142
|
+
|
|
143
|
+
## [1.1.1] - 2026-04-07
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- **OpenBoxPlugin** — drop-in `SimplePlugin` integration for Temporal Workers. Single-line setup: `plugins=[OpenBoxPlugin(openbox_url=..., openbox_api_key=...)]`. Auto-registers interceptors, OTel instrumentation, sandbox passthrough, and `send_governance_event` activity
|
|
148
|
+
- Plugin integration guide for Temporal AI Partner Ecosystem (`docs/temporal-plugin-integration-guide.md`)
|
|
149
|
+
- HTTP body truncation tests (`tests/test_http_body_truncation.py`)
|
|
150
|
+
- Plugin unit tests (`tests/test_plugin.py`) and integration/replay tests (`tests/test_plugin_integration.py`)
|
|
151
|
+
|
|
152
|
+
### Fixed
|
|
153
|
+
|
|
154
|
+
- **HTTP body truncation** — enforce `max_body_size` (default 64KB) on request/response bodies in governance spans
|
|
155
|
+
- **File I/O spans** — remove raw file content from governance payloads; only `bytes_read`/`bytes_written` metadata sent
|
|
156
|
+
- **error_type sanitization** — prevent serialized error objects from being sent as `error.cause.error_type` string in WorkflowFailed payloads
|
|
157
|
+
- Remove useless f-strings, redundant `(ImportError, Exception)` clauses, merge nested if statements
|
|
158
|
+
- Prefix unused `span` param in urllib hook
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
|
|
162
|
+
- `temporalio>=1.23.0` (from 1.8.0) for SimplePlugin support
|
|
163
|
+
- `GovernanceConfig.max_body_size` default changed from `None` (unlimited) to `65536` (64KB)
|
|
164
|
+
- `\w` regex shorthand in API key pattern
|
|
165
|
+
|
|
166
|
+
### Refactored
|
|
167
|
+
|
|
168
|
+
- Reduce cognitive complexity across 7 modules: `activity_interceptor.py` (126→split), `workflow_interceptor.py` (40→split), `activities.py` (20→split), `db_governance_hooks.py` (34→split), `otel_setup.py` (51→split), `tracing.py` (85→split), `verdict_handler.py` (16→15)
|
|
169
|
+
- Extract shared helpers: `_run_governed_query_sync/async`, `_build_error_dict`, `_extract_dbapi_context`, `_instrument_sqlalchemy`
|
|
170
|
+
|
|
171
|
+
### Dependencies
|
|
172
|
+
|
|
173
|
+
- Bump Pygments 2.19.2 → 2.20.0 (ReDoS fix, CVSS 1.9)
|
|
174
|
+
|
|
175
|
+
## [1.1.0] - 2026-03-09
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
|
|
179
|
+
- **Hook-level governance** — real-time, per-operation governance evaluation during activity execution
|
|
180
|
+
- Every HTTP request, database query, file operation, and traced function call is evaluated at `started` (before, can block) and `completed` (after, informational) stages
|
|
181
|
+
- Same `POST /api/v1/governance/evaluate` endpoint with new `hook_trigger` field in payload
|
|
182
|
+
- Automatically enabled when using `create_openbox_worker()`
|
|
183
|
+
- **Database query governance** — per-query started/completed evaluations for psycopg2, pymysql, mysql-connector, asyncpg, pymongo, redis, sqlalchemy
|
|
184
|
+
- **File I/O governance** — per-operation evaluations for open, read, write, readline, readlines, writelines, close (opt-in via `instrument_file_io=True`)
|
|
185
|
+
- **`@traced` decorator** (`openbox.tracing`) — function-level governance with OTel spans; zero overhead when governance not configured
|
|
186
|
+
- **`GovernanceBlockedError`** — new exception type for hook-level blocking with verdict, reason, and resource identifier
|
|
187
|
+
- **Abort propagation** — once one hook blocks, all subsequent hooks for the same activity short-circuit immediately
|
|
188
|
+
- **HALT workflow termination** from hook-level governance via `client.terminate()`
|
|
189
|
+
- **REQUIRE_APPROVAL** from hook-level governance enters the same HITL approval polling flow as activity-level approvals
|
|
190
|
+
- **`duration_ns`** computed for all hook span types (HTTP, file, function — DB already had it)
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
|
|
194
|
+
- **`hook_trigger` simplified to boolean** — was a dict with type/stage/data, now just `true`. All data moved to span root fields
|
|
195
|
+
- **Span data consolidation** — all type-specific fields at span root (`hook_type`, `http_method`, `db_system`, `file_path`, `function`, etc.)
|
|
196
|
+
- **`attributes` is OTel-original only** — no custom `openbox.*`, `http.request.*`, `db.result.*` fields injected
|
|
197
|
+
- Hook governance payloads send only the current span per evaluation (not accumulated history)
|
|
198
|
+
- Event-level payloads (ActivityStarted/Completed, Workflow events) no longer include spans
|
|
199
|
+
- Simplified `WorkflowSpanProcessor` — removed span buffering, governed span tracking, body data merging; `on_end()` now only forwards to fallback exporters
|
|
200
|
+
|
|
201
|
+
### Fixed
|
|
202
|
+
|
|
203
|
+
- HALT verdict from hooks now correctly terminates the workflow (previously only stopped the activity like BLOCK)
|
|
204
|
+
- REQUIRE_APPROVAL from hooks now enters the approval polling flow (previously raised unhandled error)
|
|
205
|
+
- Stale buffer/verdict from previous workflow run no longer carries over when workflow_id is reused
|
|
206
|
+
- Subsequent hooks no longer fire after the first hook blocks an activity
|
|
207
|
+
|
|
208
|
+
## [1.0.21] - 2026-03-04
|
|
209
|
+
|
|
210
|
+
### Added
|
|
211
|
+
|
|
212
|
+
- Human-in-the-loop approval with expiration handling
|
|
213
|
+
- Approval polling via `POST /api/v1/governance/approval`
|
|
214
|
+
- Guardrails: input/output validation and redaction
|
|
215
|
+
- `GovernanceVerdictResponse.from_dict()` with guardrails_result parsing
|
|
216
|
+
- Output redaction for activity results
|
|
217
|
+
- `_deep_update_dataclass()` for in-place dataclass field updates from redacted dicts
|
|
218
|
+
|
|
219
|
+
### Fixed
|
|
220
|
+
|
|
221
|
+
- Temporal Payload objects no longer slip through as non-serializable in governance payloads
|
|
222
|
+
- Stale buffer detection via run_id comparison
|
|
223
|
+
|
|
224
|
+
## [1.0.0] - 2026-02-15
|
|
225
|
+
|
|
226
|
+
### Added
|
|
227
|
+
|
|
228
|
+
- Initial release
|
|
229
|
+
- 6 event types: WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted
|
|
230
|
+
- 5-tier verdict system: ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT
|
|
231
|
+
- HTTP instrumentation via OpenTelemetry (httpx, requests, urllib3, urllib)
|
|
232
|
+
- Database instrumentation (psycopg2, pymysql, asyncpg, pymongo, redis, sqlalchemy)
|
|
233
|
+
- File I/O instrumentation (opt-in)
|
|
234
|
+
- Zero-code setup via `create_openbox_worker()` factory
|
|
235
|
+
- Workflow and activity interceptors for governance
|
|
236
|
+
- Span buffering and activity context tracking
|
|
237
|
+
- `fail_open` / `fail_closed` error policies
|
|
238
|
+
- v1.0 backward compatibility for legacy verdict strings
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openbox-temporal-sdk-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: OpenBox SDK - Governance and observability for Temporal workflows
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenBox-AI/temporal-sdk-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/OpenBox-AI/temporal-sdk-python#readme
|
|
@@ -24,6 +24,7 @@ Requires-Python: >=3.11
|
|
|
24
24
|
Requires-Dist: asyncpg>=0.29.0
|
|
25
25
|
Requires-Dist: httpx<1,>=0.28.0
|
|
26
26
|
Requires-Dist: mysql-connector-python>=8.0.0
|
|
27
|
+
Requires-Dist: openbox-sdk-python>=0.2.0
|
|
27
28
|
Requires-Dist: opentelemetry-api<1.40.0,>=1.38.0
|
|
28
29
|
Requires-Dist: opentelemetry-instrumentation-asyncpg<0.61b0,>=0.59b0
|
|
29
30
|
Requires-Dist: opentelemetry-instrumentation-httpx<0.61b0,>=0.59b0
|
|
@@ -51,7 +52,8 @@ Description-Content-Type: text/markdown
|
|
|
51
52
|
OpenBox SDK provides **governance and observability** for Temporal workflows by capturing workflow/activity lifecycle events, HTTP telemetry, database queries, and file operations, then sending them to OpenBox Core for policy evaluation.
|
|
52
53
|
|
|
53
54
|
**Key Features:**
|
|
54
|
-
-
|
|
55
|
+
- 7 event types (WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted, Handoff)
|
|
56
|
+
- Multi-agent sessions — propagate a shared `multi_agent_session_id` across workflow + activity events
|
|
55
57
|
- 5-tier verdict system (ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT)
|
|
56
58
|
- **Hook-level governance** — per-operation evaluation (HTTP requests, file I/O, database queries, function tracing) with started/completed stages
|
|
57
59
|
- HTTP/Database/File I/O instrumentation via OpenTelemetry
|
|
@@ -100,7 +102,15 @@ await worker.run()
|
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
The plugin automatically configures governance interceptors, OTel instrumentation,
|
|
103
|
-
sandbox passthrough,
|
|
105
|
+
sandbox passthrough, W3C trace propagation through Temporal headers (via
|
|
106
|
+
`temporalio.contrib.opentelemetry.TracingInterceptor`), and the
|
|
107
|
+
`send_governance_event` activity.
|
|
108
|
+
|
|
109
|
+
**Credentials never leave the plugin.** `openbox_api_key` is captured on the
|
|
110
|
+
governance activity instance itself — it does **not** flow through activity
|
|
111
|
+
inputs, so it is never written to workflow history. To opt out of trace
|
|
112
|
+
propagation (e.g., if you already wire `OpenTelemetryPlugin`), pass
|
|
113
|
+
`enable_trace_propagation=False`.
|
|
104
114
|
|
|
105
115
|
### Composing with Other Plugins
|
|
106
116
|
|
|
@@ -148,8 +158,10 @@ The factory automatically:
|
|
|
148
158
|
1. Validates the API key
|
|
149
159
|
2. Creates span processor
|
|
150
160
|
3. Sets up OpenTelemetry instrumentation
|
|
151
|
-
4. Creates governance interceptors
|
|
152
|
-
5.
|
|
161
|
+
4. Creates governance interceptors (incl. W3C trace propagation)
|
|
162
|
+
5. Builds the `GovernanceActivities` instance with credentials captured on
|
|
163
|
+
`self` and registers its `send_governance_event` method — the API key is
|
|
164
|
+
never passed through activity inputs / workflow history
|
|
153
165
|
6. Returns fully configured Worker
|
|
154
166
|
|
|
155
167
|
---
|
|
@@ -189,12 +201,15 @@ worker = create_openbox_worker(
|
|
|
189
201
|
|
|
190
202
|
# Database instrumentation
|
|
191
203
|
instrument_databases=True,
|
|
192
|
-
db_libraries={"psycopg2", "sqlalchemy"}, # None = all available
|
|
193
|
-
sqlalchemy_engine=engine, # pass pre-existing engine for query capture
|
|
194
204
|
|
|
195
205
|
# File I/O instrumentation
|
|
196
206
|
instrument_file_io=False, # disabled by default
|
|
197
207
|
|
|
208
|
+
# Header-based W3C trace propagation (client → workflow → activities).
|
|
209
|
+
# Default True. Set False if you already wire OpenTelemetryPlugin or a
|
|
210
|
+
# custom propagator.
|
|
211
|
+
enable_trace_propagation=True,
|
|
212
|
+
|
|
198
213
|
# Standard Worker options (all supported)
|
|
199
214
|
activity_executor=my_executor,
|
|
200
215
|
max_concurrent_activities=10,
|
|
@@ -232,6 +247,48 @@ OpenBox Core returns a verdict indicating what action the SDK should take.
|
|
|
232
247
|
| SignalReceived | Signal received | workflow_id, signal_name, signal_args |
|
|
233
248
|
| ActivityStarted | Activity begins | activity_id, activity_type, activity_input |
|
|
234
249
|
| ActivityCompleted | Activity ends | activity_id, activity_type, activity_input, activity_output, spans, status, duration |
|
|
250
|
+
| Handoff | Agent hands off to another agent | from_agent_did, multi_agent_session_id |
|
|
251
|
+
|
|
252
|
+
When a workflow runs as part of a multi-agent session (see below), every event
|
|
253
|
+
above also carries `multi_agent_session_id`. The field is omitted when no
|
|
254
|
+
session id is supplied.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Multi-Agent Sessions
|
|
259
|
+
|
|
260
|
+
Group the work of several agents under one shared `multi_agent_session_id`. The
|
|
261
|
+
SDK only **propagates** the id you supply — it never invents one, and it owns no
|
|
262
|
+
routing, session minting, or agent registry (those stay in your application).
|
|
263
|
+
|
|
264
|
+
**Supply the id** via the Temporal workflow memo at start time:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
await client.start_workflow(
|
|
268
|
+
MyWorkflow.run,
|
|
269
|
+
arg,
|
|
270
|
+
id="order-123",
|
|
271
|
+
task_queue="my-queue",
|
|
272
|
+
memo={"openbox_multi_agent_session_id": session_id},
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The SDK then tags every governance event (workflow, activity, and hook events)
|
|
277
|
+
with that id, propagating it from the workflow to its activities automatically.
|
|
278
|
+
|
|
279
|
+
**Emit an explicit handoff** from inside workflow code:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from openbox import emit_handoff
|
|
283
|
+
|
|
284
|
+
await emit_handoff(
|
|
285
|
+
multi_agent_session_id=session_id,
|
|
286
|
+
from_agent_did="did:aip:...", # the sending agent
|
|
287
|
+
)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The receiving agent is derived server-side from the signed identity and is never
|
|
291
|
+
sent. `emit_handoff` validates its arguments locally before any network call.
|
|
235
292
|
|
|
236
293
|
---
|
|
237
294
|
|
|
@@ -264,7 +321,7 @@ OpenBox Core can validate and redact sensitive data before/after activity execut
|
|
|
264
321
|
|
|
265
322
|
## Error Handling
|
|
266
323
|
|
|
267
|
-
Configure error policy via `
|
|
324
|
+
Configure error policy via the `governance_policy` parameter (`"fail_open"` / `"fail_closed"`):
|
|
268
325
|
|
|
269
326
|
| Policy | Behavior |
|
|
270
327
|
|--------|----------|
|
|
@@ -292,15 +349,12 @@ Configure error policy via `on_api_error` (constants available as `hook_governan
|
|
|
292
349
|
- `redis` — native OTel `request_hook`/`response_hook`
|
|
293
350
|
- `sqlalchemy` — `before/after_cursor_execute` event listeners
|
|
294
351
|
|
|
295
|
-
**SQLAlchemy Note:**
|
|
352
|
+
**SQLAlchemy Note:** Query-level governance works on pre-existing engines automatically — even engines created before `create_openbox_worker()` runs (e.g., at module import time) — because the base runtime governs SQLAlchemy via a global `Engine` event listener. No engine handle needs to be passed in. The `db_libraries` and `sqlalchemy_engine` parameters are still accepted for backward compatibility but are no longer required and have no effect: the base runtime installs every available database instrumentor best-effort.
|
|
296
353
|
|
|
297
354
|
```python
|
|
298
|
-
from db.engine import engine
|
|
299
|
-
|
|
300
355
|
worker = create_openbox_worker(
|
|
301
356
|
...,
|
|
302
|
-
|
|
303
|
-
sqlalchemy_engine=engine,
|
|
357
|
+
instrument_databases=True, # default; installs all available DB instrumentors
|
|
304
358
|
)
|
|
305
359
|
```
|
|
306
360
|
|
|
@@ -396,7 +450,7 @@ Every database operation is evaluated at `started` (pre-query, can block) and `c
|
|
|
396
450
|
|
|
397
451
|
### Function Tracing
|
|
398
452
|
|
|
399
|
-
Functions decorated with `@traced` are automatically governed when `
|
|
453
|
+
Functions decorated with `@traced` are automatically governed when a worker (or plugin) has installed the base runtime. `@traced` wraps the base SDK's `governed()` decorator:
|
|
400
454
|
|
|
401
455
|
| Stage | Trigger | Data Available |
|
|
402
456
|
|-------|---------|----------------|
|
|
@@ -405,15 +459,13 @@ Functions decorated with `@traced` are automatically governed when `hook_governa
|
|
|
405
459
|
|
|
406
460
|
**How it works (Function Tracing):**
|
|
407
461
|
|
|
408
|
-
1. `@traced`
|
|
409
|
-
2. →
|
|
410
|
-
3. If verdict is BLOCK/HALT →
|
|
462
|
+
1. `@traced` delegates to the base SDK's `governed()` decorator
|
|
463
|
+
2. → the base runtime sends a `started` FUNCTION_CALL evaluation with function name and module
|
|
464
|
+
3. If verdict is BLOCK/HALT → the call is blocked and the function never executes
|
|
411
465
|
4. Function executes normally
|
|
412
|
-
5. →
|
|
413
|
-
6. If verdict is BLOCK/HALT →
|
|
414
|
-
7. When
|
|
415
|
-
|
|
416
|
-
**Governed span tracking:** When hook-level governance is active, the SDK marks HTTP spans as "governed" so the OTel `on_end` processor skips buffering them — preventing duplicate spans.
|
|
466
|
+
5. → the base runtime sends a `completed` FUNCTION_CALL evaluation with result or error info
|
|
467
|
+
6. If verdict is BLOCK/HALT → the block is surfaced after execution
|
|
468
|
+
7. When no base runtime is installed → transparent passthrough, zero governance calls
|
|
417
469
|
|
|
418
470
|
---
|
|
419
471
|
|
|
@@ -424,104 +476,87 @@ See [System Architecture](./docs/system-architecture.md) for detailed component
|
|
|
424
476
|
**High-Level Flow:**
|
|
425
477
|
|
|
426
478
|
```
|
|
427
|
-
Workflow/Activity →
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
Hook-Level (per HTTP request):
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
Hook-Level (per file operation):
|
|
438
|
-
Activity open() → hook_governance → API (started) → Allow/Block
|
|
439
|
-
Activity read/write() → hook_governance → API (started) → Allow/Block
|
|
440
|
-
→ hook_governance → API (completed) → Allow/Block
|
|
441
|
-
Activity close() → hook_governance → API (completed) → Allow/Block
|
|
442
|
-
|
|
443
|
-
Hook-Level (per DB query):
|
|
444
|
-
Activity DB Call → db_governance_hooks → hook_governance → API (started) → Allow/Block
|
|
445
|
-
→ Query executes → hook_governance → API (completed)
|
|
446
|
-
|
|
447
|
-
Hook-Level (per @traced function):
|
|
448
|
-
@traced call → hook_governance → API (started) → Allow/Block
|
|
449
|
-
→ Function runs → hook_governance → API (completed)
|
|
479
|
+
Workflow / Activity lifecycle → Temporal SDK interceptors → OpenBox Core API
|
|
480
|
+
↓
|
|
481
|
+
Returns Verdict
|
|
482
|
+
↓
|
|
483
|
+
(ALLOW, BLOCK, HALT, REQUIRE_APPROVAL)
|
|
484
|
+
|
|
485
|
+
Hook-Level (per HTTP request / DB query / file op / @traced fn):
|
|
486
|
+
Operation → base-SDK instrumentation → OpenBox Core API (started) → Allow/Block
|
|
487
|
+
→ Operation runs → OpenBox Core API (completed) → Allow/Block
|
|
450
488
|
```
|
|
451
489
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
-
|
|
456
|
-
- `span_processor.py` — Span buffering, activity context tracking
|
|
457
|
-
- `activity_interceptor.py` — Activity lifecycle governance events
|
|
490
|
+
The Temporal worker/plugin builds and owns an `openbox_core` runtime that installs
|
|
491
|
+
all hook instrumentation. Hook payload building, evaluation, and enforcement are
|
|
492
|
+
owned entirely by the base SDK; the Temporal SDK maps the resulting verdicts onto
|
|
493
|
+
Temporal-native effects.
|
|
458
494
|
|
|
459
|
-
|
|
495
|
+
**Responsibility split:**
|
|
460
496
|
|
|
461
|
-
|
|
497
|
+
Base SDK (`openbox_core`):
|
|
498
|
+
- HTTP / DB / file / function hook instrumentation
|
|
499
|
+
- Hook payload shape, evaluation, and enforcement
|
|
462
500
|
|
|
463
|
-
|
|
501
|
+
Temporal SDK (this package):
|
|
502
|
+
- `workflow_interceptor.py` / `activity_interceptor.py` — workflow/activity/signal lifecycle governance events (via workflow-safe activities)
|
|
503
|
+
- `governance_state.py` — `TemporalGovernanceState`: signal-verdict bridging, HITL pending-approval markers, completed-hook stop/halt propagation
|
|
504
|
+
- `core_adapter.py` — builds the base runtime (`create_core_runtime`) and maps base verdicts to Temporal effects (`TemporalFrameworkAdapter`)
|
|
464
505
|
|
|
465
|
-
|
|
466
|
-
from openbox import (
|
|
467
|
-
initialize,
|
|
468
|
-
WorkflowSpanProcessor,
|
|
469
|
-
GovernanceInterceptor,
|
|
470
|
-
GovernanceConfig,
|
|
471
|
-
)
|
|
472
|
-
from openbox.otel_setup import setup_opentelemetry_for_governance
|
|
473
|
-
from openbox.activity_interceptor import ActivityGovernanceInterceptor
|
|
474
|
-
from openbox.activities import send_governance_event
|
|
475
|
-
from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
|
|
506
|
+
---
|
|
476
507
|
|
|
477
|
-
|
|
478
|
-
initialize(api_url="http://localhost:8086", api_key="obx_test_key_1")
|
|
508
|
+
## Advanced Usage
|
|
479
509
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
510
|
+
The supported integration is a single call. Both `create_openbox_worker(...)` and
|
|
511
|
+
`OpenBoxPlugin(...)` build and own the base `openbox_core` runtime, install all
|
|
512
|
+
hook instrumentation, register the governance interceptors, and wire the
|
|
513
|
+
`send_governance_event` activity for you — there is no manual OpenTelemetry or
|
|
514
|
+
span-processor setup to perform.
|
|
484
515
|
|
|
485
|
-
|
|
486
|
-
setup_opentelemetry_for_governance(
|
|
487
|
-
span_processor,
|
|
488
|
-
api_url="http://localhost:8086",
|
|
489
|
-
api_key="obx_test_key_1",
|
|
490
|
-
sqlalchemy_engine=engine, # optional: instrument pre-existing engine
|
|
491
|
-
)
|
|
516
|
+
Worker factory:
|
|
492
517
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
api_timeout=30.0,
|
|
497
|
-
)
|
|
518
|
+
```python
|
|
519
|
+
import os
|
|
520
|
+
from openbox import create_openbox_worker
|
|
498
521
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
522
|
+
worker = create_openbox_worker(
|
|
523
|
+
client=client,
|
|
524
|
+
task_queue="my-task-queue",
|
|
525
|
+
workflows=[MyWorkflow],
|
|
526
|
+
activities=[my_activity],
|
|
527
|
+
openbox_url=os.getenv("OPENBOX_URL"),
|
|
528
|
+
openbox_api_key=os.getenv("OPENBOX_API_KEY"),
|
|
529
|
+
governance_policy="fail_closed",
|
|
530
|
+
governance_timeout=30.0,
|
|
531
|
+
instrument_databases=True,
|
|
532
|
+
instrument_file_io=True,
|
|
505
533
|
)
|
|
506
534
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
config=config,
|
|
512
|
-
)
|
|
535
|
+
await worker.run()
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Plugin (Temporal >= 1.23.0) — attach to a plain `Worker`:
|
|
513
539
|
|
|
514
|
-
|
|
540
|
+
```python
|
|
515
541
|
from temporalio.worker import Worker
|
|
542
|
+
from openbox.plugin import OpenBoxPlugin
|
|
543
|
+
|
|
516
544
|
worker = Worker(
|
|
517
|
-
client
|
|
545
|
+
client,
|
|
518
546
|
task_queue="my-task-queue",
|
|
519
547
|
workflows=[MyWorkflow],
|
|
520
|
-
activities=[my_activity
|
|
521
|
-
|
|
548
|
+
activities=[my_activity],
|
|
549
|
+
plugins=[OpenBoxPlugin(
|
|
550
|
+
openbox_url=os.getenv("OPENBOX_URL"),
|
|
551
|
+
openbox_api_key=os.getenv("OPENBOX_API_KEY"),
|
|
552
|
+
governance_policy="fail_closed",
|
|
553
|
+
)],
|
|
522
554
|
)
|
|
523
555
|
```
|
|
524
556
|
|
|
557
|
+
Both entry points accept the same governance, HITL, signing, and instrumentation
|
|
558
|
+
options (see [Configuration](./docs/configuration.md)).
|
|
559
|
+
|
|
525
560
|
---
|
|
526
561
|
|
|
527
562
|
## Documentation
|
|
@@ -536,13 +571,17 @@ worker = Worker(
|
|
|
536
571
|
|
|
537
572
|
## Testing
|
|
538
573
|
|
|
539
|
-
The SDK includes comprehensive test coverage
|
|
574
|
+
The SDK includes comprehensive test coverage under `tests/`:
|
|
540
575
|
|
|
541
576
|
```bash
|
|
542
577
|
pytest tests/
|
|
543
578
|
```
|
|
544
579
|
|
|
545
|
-
|
|
580
|
+
Coverage spans the worker factory and plugin, workflow/activity/signal
|
|
581
|
+
interceptors, HITL approval flow, request signing, public-API compatibility, and
|
|
582
|
+
parity with the base SDK's hook governance (`test_temporal_hook_parity.py`,
|
|
583
|
+
`test_core_conformance_suite.py`). Hook instrumentation internals (HTTP/DB/file
|
|
584
|
+
payload shape) are verified in the base SDK's own conformance suite.
|
|
546
585
|
|
|
547
586
|
---
|
|
548
587
|
|