openbox-temporal-sdk-python 1.0.21__tar.gz → 1.1.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.
Files changed (58) hide show
  1. openbox_temporal_sdk_python-1.1.0/CHANGELOG.md +68 -0
  2. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/PKG-INFO +152 -11
  3. openbox_temporal_sdk_python-1.1.0/README.md +465 -0
  4. openbox_temporal_sdk_python-1.1.0/docs/changelog-hook-level-governance.md +146 -0
  5. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/docs/code-standards.md +43 -19
  6. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/docs/codebase-summary.md +306 -42
  7. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/docs/configuration.md +11 -2
  8. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/docs/project-overview-pdr.md +99 -19
  9. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/docs/system-architecture.md +425 -67
  10. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/__init__.py +61 -2
  11. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/activities.py +62 -28
  12. openbox_temporal_sdk_python-1.1.0/openbox/activity_interceptor.py +626 -0
  13. openbox_temporal_sdk_python-1.1.0/openbox/client.py +181 -0
  14. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/config.py +18 -29
  15. openbox_temporal_sdk_python-1.1.0/openbox/context_propagation.py +53 -0
  16. openbox_temporal_sdk_python-1.1.0/openbox/db_governance_hooks.py +874 -0
  17. openbox_temporal_sdk_python-1.1.0/openbox/errors.py +164 -0
  18. openbox_temporal_sdk_python-1.1.0/openbox/file_governance_hooks.py +354 -0
  19. openbox_temporal_sdk_python-1.1.0/openbox/hitl.py +120 -0
  20. openbox_temporal_sdk_python-1.1.0/openbox/hook_governance.py +376 -0
  21. openbox_temporal_sdk_python-1.1.0/openbox/http_governance_hooks.py +749 -0
  22. openbox_temporal_sdk_python-1.1.0/openbox/otel_setup.py +487 -0
  23. openbox_temporal_sdk_python-1.1.0/openbox/span_processor.py +212 -0
  24. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/tracing.py +92 -0
  25. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/types.py +13 -3
  26. openbox_temporal_sdk_python-1.1.0/openbox/verdict_handler.py +93 -0
  27. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/worker.py +21 -1
  28. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/workflow_interceptor.py +19 -8
  29. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/pyproject.toml +2 -1
  30. openbox_temporal_sdk_python-1.1.0/tests/conftest.py +91 -0
  31. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_activities.py +92 -50
  32. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_activity_interceptor.py +294 -18
  33. openbox_temporal_sdk_python-1.1.0/tests/test_db_governance_hooks.py +808 -0
  34. openbox_temporal_sdk_python-1.1.0/tests/test_file_governance_hooks.py +716 -0
  35. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_otel_setup.py +570 -286
  36. openbox_temporal_sdk_python-1.1.0/tests/test_psycopg2_hooks_verify.py +53 -0
  37. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_span_processor.py +17 -469
  38. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_tracing.py +384 -1
  39. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_worker.py +10 -6
  40. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_workflow_interceptor.py +7 -7
  41. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/uv.lock +1 -1
  42. openbox_temporal_sdk_python-1.0.21/README.md +0 -325
  43. openbox_temporal_sdk_python-1.0.21/openbox/activity_interceptor.py +0 -761
  44. openbox_temporal_sdk_python-1.0.21/openbox/otel_setup.py +0 -1007
  45. openbox_temporal_sdk_python-1.0.21/openbox/span_processor.py +0 -361
  46. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/.github/workflows/publish.yml +0 -0
  47. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/.github/workflows/sonarqube.yaml +0 -0
  48. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/.gitignore +0 -0
  49. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/.python-version +0 -0
  50. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/.repomixignore +0 -0
  51. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/LICENSE +0 -0
  52. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/openbox/py.typed +0 -0
  53. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/release-manifest.json +0 -0
  54. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/repomix-output.xml +0 -0
  55. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/sonar-project.properties +0 -0
  56. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/__init__.py +0 -0
  57. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_config.py +0 -0
  58. {openbox_temporal_sdk_python-1.0.21 → openbox_temporal_sdk_python-1.1.0}/tests/test_types.py +0 -0
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to OpenBox SDK for Temporal Workflows.
4
+
5
+ ## [1.1.0] - 2026-03-09
6
+
7
+ ### Added
8
+
9
+ - **Hook-level governance** — real-time, per-operation governance evaluation during activity execution
10
+ - Every HTTP request, database query, file operation, and traced function call is evaluated at `started` (before, can block) and `completed` (after, informational) stages
11
+ - Same `POST /api/v1/governance/evaluate` endpoint with new `hook_trigger` field in payload
12
+ - Automatically enabled when using `create_openbox_worker()`
13
+ - **Database query governance** — per-query started/completed evaluations for psycopg2, pymysql, mysql-connector, asyncpg, pymongo, redis, sqlalchemy
14
+ - **File I/O governance** — per-operation evaluations for open, read, write, readline, readlines, writelines, close (opt-in via `instrument_file_io=True`)
15
+ - **`@traced` decorator** (`openbox.tracing`) — function-level governance with OTel spans; zero overhead when governance not configured
16
+ - **`GovernanceBlockedError`** — new exception type for hook-level blocking with verdict, reason, and resource identifier
17
+ - **Abort propagation** — once one hook blocks, all subsequent hooks for the same activity short-circuit immediately
18
+ - **HALT workflow termination** from hook-level governance via `client.terminate()`
19
+ - **REQUIRE_APPROVAL** from hook-level governance enters the same HITL approval polling flow as activity-level approvals
20
+ - **`duration_ns`** computed for all hook span types (HTTP, file, function — DB already had it)
21
+
22
+ ### Changed
23
+
24
+ - **`hook_trigger` simplified to boolean** — was a dict with type/stage/data, now just `true`. All data moved to span root fields
25
+ - **Span data consolidation** — all type-specific fields at span root (`hook_type`, `http_method`, `db_system`, `file_path`, `function`, etc.)
26
+ - **`attributes` is OTel-original only** — no custom `openbox.*`, `http.request.*`, `db.result.*` fields injected
27
+ - Hook governance payloads send only the current span per evaluation (not accumulated history)
28
+ - Event-level payloads (ActivityStarted/Completed, Workflow events) no longer include spans
29
+ - Simplified `WorkflowSpanProcessor` — removed span buffering, governed span tracking, body data merging; `on_end()` now only forwards to fallback exporters
30
+
31
+ ### Fixed
32
+
33
+ - HALT verdict from hooks now correctly terminates the workflow (previously only stopped the activity like BLOCK)
34
+ - REQUIRE_APPROVAL from hooks now enters the approval polling flow (previously raised unhandled error)
35
+ - Stale buffer/verdict from previous workflow run no longer carries over when workflow_id is reused
36
+ - Subsequent hooks no longer fire after the first hook blocks an activity
37
+
38
+ ## [1.0.21] - 2026-03-04
39
+
40
+ ### Added
41
+
42
+ - Human-in-the-loop approval with expiration handling
43
+ - Approval polling via `POST /api/v1/governance/approval`
44
+ - Guardrails: input/output validation and redaction
45
+ - `GovernanceVerdictResponse.from_dict()` with guardrails_result parsing
46
+ - Output redaction for activity results
47
+ - `_deep_update_dataclass()` for in-place dataclass field updates from redacted dicts
48
+
49
+ ### Fixed
50
+
51
+ - Temporal Payload objects no longer slip through as non-serializable in governance payloads
52
+ - Stale buffer detection via run_id comparison
53
+
54
+ ## [1.0.0] - 2026-02-15
55
+
56
+ ### Added
57
+
58
+ - Initial release
59
+ - 6 event types: WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted
60
+ - 5-tier verdict system: ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT
61
+ - HTTP instrumentation via OpenTelemetry (httpx, requests, urllib3, urllib)
62
+ - Database instrumentation (psycopg2, pymysql, asyncpg, pymongo, redis, sqlalchemy)
63
+ - File I/O instrumentation (opt-in)
64
+ - Zero-code setup via `create_openbox_worker()` factory
65
+ - Workflow and activity interceptors for governance
66
+ - Span buffering and activity context tracking
67
+ - `fail_open` / `fail_closed` error policies
68
+ - 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.0.21
3
+ Version: 1.1.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
@@ -36,6 +36,7 @@ Requires-Dist: opentelemetry-instrumentation-pymysql<0.61b0,>=0.59b0
36
36
  Requires-Dist: opentelemetry-instrumentation-redis<0.61b0,>=0.59b0
37
37
  Requires-Dist: opentelemetry-instrumentation-requests<0.61b0,>=0.59b0
38
38
  Requires-Dist: opentelemetry-instrumentation-sqlalchemy<0.61b0,>=0.59b0
39
+ Requires-Dist: opentelemetry-instrumentation-sqlite3<0.61b0,>=0.59b0
39
40
  Requires-Dist: opentelemetry-instrumentation-urllib3<0.61b0,>=0.59b0
40
41
  Requires-Dist: opentelemetry-instrumentation-urllib<0.61b0,>=0.59b0
41
42
  Requires-Dist: opentelemetry-sdk<1.40.0,>=1.38.0
@@ -54,6 +55,7 @@ OpenBox SDK provides **governance and observability** for Temporal workflows by
54
55
  **Key Features:**
55
56
  - 6 event types (WorkflowStarted, WorkflowCompleted, WorkflowFailed, SignalReceived, ActivityStarted, ActivityCompleted)
56
57
  - 5-tier verdict system (ALLOW, CONSTRAIN, REQUIRE_APPROVAL, BLOCK, HALT)
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
58
60
  - Guardrails: Input/output validation and redaction
59
61
  - Human-in-the-loop approval with expiration handling
@@ -215,7 +217,7 @@ OpenBox Core can validate and redact sensitive data before/after activity execut
215
217
 
216
218
  ## Error Handling
217
219
 
218
- Configure error policy via `on_api_error`:
220
+ Configure error policy via `on_api_error` (constants available as `hook_governance.FAIL_OPEN` / `FAIL_CLOSED`):
219
221
 
220
222
  | Policy | Behavior |
221
223
  |--------|----------|
@@ -233,11 +235,15 @@ Configure error policy via `on_api_error`:
233
235
  - `urllib` - request body only
234
236
 
235
237
  ### Databases
236
- - PostgreSQL: `psycopg2`, `asyncpg`
237
- - MySQL: `mysql-connector-python`, `pymysql`
238
- - MongoDB: `pymongo`
239
- - Redis: `redis`
240
- - ORM: `sqlalchemy`
238
+
239
+ **Fully supported** — any dbapi-compatible library using OTel's `CursorTracer.traced_execution()`:
240
+ - `psycopg2`, `pymysql`, `mysql-connector-python`, and other dbapi-compliant drivers
241
+
242
+ **Custom hooks (best-effort)** — these libraries use non-dbapi instrumentation paths; governance hooks may not work correctly in all scenarios:
243
+ - `asyncpg` — wrapt wrapper on Connection methods (governance runs outside OTel span context)
244
+ - `pymongo` — CommandListener monitoring + wrapt Collection wrappers (dedup via thread-local flag; some internal commands like `endSessions` only produce `completed` stage)
245
+ - `redis` — native OTel `request_hook`/`response_hook`
246
+ - `sqlalchemy` — `before/after_cursor_execute` event listeners
241
247
 
242
248
  **SQLAlchemy Note:** If your SQLAlchemy engine is created before `create_openbox_worker()` runs (e.g., at module import time), you must pass it via the `sqlalchemy_engine` parameter. Without this, `SQLAlchemyInstrumentor` only patches future `create_engine()` calls and won't capture queries on pre-existing engines.
243
249
 
@@ -257,6 +263,113 @@ worker = create_openbox_worker(
257
263
 
258
264
  ---
259
265
 
266
+ ## Hook-Level Governance
267
+
268
+ Every HTTP request, file operation, and database query made during an activity is evaluated by OpenBox Core in real-time at two stages:
269
+
270
+ ### HTTP Requests
271
+
272
+ | Stage | Trigger | Data Available |
273
+ |-------|---------|----------------|
274
+ | `started` | Before request is sent | Method, URL, request headers, request body |
275
+ | `completed` | After response received | All of above + response headers, response body, status code |
276
+
277
+ ### File Operations
278
+
279
+ Per-operation governance evaluates **every** `read()`/`write()`/`readline()`/`readlines()`/`writelines()` call, not just open/close:
280
+
281
+ | Operation | Stage | Trigger | Data Available |
282
+ |-----------|-------|---------|----------------|
283
+ | `open` | `started` | Before file is opened | File path, open mode |
284
+ | `read` | `started` | Before read executes | File path, mode |
285
+ | `read` | `completed` | After read returns | data (content read), bytes_read |
286
+ | `readline` | `started` | Before readline executes | File path, mode |
287
+ | `readline` | `completed` | After readline returns | data (line read), bytes_read |
288
+ | `readlines` | `started` | Before readlines executes | File path, mode |
289
+ | `readlines` | `completed` | After readlines returns | data (lines read), bytes_read, lines_count |
290
+ | `write` | `started` | Before write executes | File path, mode |
291
+ | `write` | `completed` | After write returns | data (content written), bytes_written |
292
+ | `writelines` | `started` | Before writelines executes | File path, mode |
293
+ | `writelines` | `completed` | After writelines returns | data (lines written), bytes_written, lines_count |
294
+ | `close` | `completed` | After file is closed | bytes_read, bytes_written, operations list |
295
+
296
+ **How it works (HTTP):**
297
+
298
+ 1. OTel httpx instrumentation fires a **request hook** → SDK sends `started` governance evaluation with request data
299
+ 2. If verdict is BLOCK/HALT → request is aborted before it leaves the process
300
+ 3. After response arrives → SDK sends `completed` governance evaluation with full request+response data
301
+ 4. If verdict is BLOCK/HALT → `GovernanceBlockedError` is raised, activity fails with `GovernanceStop`
302
+
303
+ **How it works (File I/O):**
304
+
305
+ 1. Activity calls `open()` → SDK sends `started` governance evaluation with file path and mode
306
+ 2. If verdict is BLOCK/HALT → file is never opened, `GovernanceBlockedError` is raised
307
+ 3. Each `read()`/`write()`/`readline()`/`readlines()`/`writelines()` call sends `started` (before) and `completed` (after) governance evaluations — enabling content-based policy enforcement
308
+ 4. After file is closed → SDK sends `completed` governance with lifecycle summary (total bytes, operations list)
309
+ 5. File governance requires `instrument_file_io=True` (disabled by default)
310
+
311
+ A simple open-read-close produces **4 governance evaluations**: open(started) → read(started) → read(completed) → close(completed).
312
+
313
+ ### Database Queries
314
+
315
+ Every database operation is evaluated at `started` (pre-query, can block) and `completed` (post-query, reports outcome):
316
+
317
+ | Field | Started | Completed |
318
+ |-------|---------|-----------|
319
+ | `type` | `"db_query"` | `"db_query"` |
320
+ | `stage` | `"started"` | `"completed"` |
321
+ | `db_system` | postgresql, mysql, mongodb, redis, sqlite | same |
322
+ | `db_name` | Database name | same |
323
+ | `db_operation` | SQL verb or command (SELECT, INSERT, GET, etc.) | same |
324
+ | `db_statement` | Query string or command | same |
325
+ | `server_address` | Host | same |
326
+ | `server_port` | Port | same |
327
+ | `duration_ms` | — | Query duration in ms |
328
+ | `error` | — | Error message or None |
329
+
330
+ **How it works:**
331
+
332
+ 1. Activity executes a DB query (via any supported library)
333
+ 2. SDK governance hook intercepts **before** the query → sends `started` evaluation
334
+ 3. If verdict is BLOCK/HALT → query is aborted, `GovernanceBlockedError` raised
335
+ 4. Query executes normally → SDK sends `completed` evaluation with duration and error status
336
+ 5. DB governance is automatic when `instrument_databases=True` (default)
337
+
338
+ **Per-library strategy:**
339
+
340
+ | Library | Hook Method | Can Block? | Reliability |
341
+ |---------|------------|------------|-------------|
342
+ | psycopg2, pymysql, mysql-connector-python | `CursorTracer.traced_execution` patch | Yes | Fully supported |
343
+ | asyncpg | `wrapt` wrapper on Connection methods | Yes | Best-effort |
344
+ | pymongo | CommandListener + `wrapt` Collection wrappers | Yes (wrapt only) | Best-effort |
345
+ | redis | Native OTel `request_hook`/`response_hook` | Yes | Best-effort |
346
+ | sqlalchemy | `before/after_cursor_execute` events | Yes | Best-effort |
347
+
348
+ > **Note:** Libraries marked "Fully supported" use OTel's `CursorTracer`, which guarantees governance hooks run inside the OTel span context. Best-effort libraries use custom hooks that may produce inconsistencies (e.g., missing stages for internal commands). Some C extension types (e.g., `psycopg2.extensions.cursor`) cannot be patched with `wrapt` — in those cases governance hooks are silently skipped, but OTel span capture still works normally.
349
+
350
+ ### Function Tracing
351
+
352
+ Functions decorated with `@traced` are automatically governed when `hook_governance` is configured:
353
+
354
+ | Stage | Trigger | Data Available |
355
+ |-------|---------|----------------|
356
+ | `started` | Before function executes | Function name, module, arguments (if `capture_args=True`) |
357
+ | `completed` | After function returns/raises | All of above + result (if `capture_result=True`) or error info |
358
+
359
+ **How it works (Function Tracing):**
360
+
361
+ 1. `@traced` decorator wrapper starts OTel span
362
+ 2. → SDK sends `started` governance evaluation with function name and module
363
+ 3. If verdict is BLOCK/HALT → `GovernanceBlockedError` raised, function never executes
364
+ 4. Function executes normally
365
+ 5. → SDK sends `completed` governance evaluation with result or error info
366
+ 6. If verdict is BLOCK/HALT → `GovernanceBlockedError` raised after execution
367
+ 7. When `hook_governance` is not configured → zero overhead, no governance calls
368
+
369
+ **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.
370
+
371
+ ---
372
+
260
373
  ## Architecture
261
374
 
262
375
  See [System Architecture](./docs/system-architecture.md) for detailed component design.
@@ -269,8 +382,33 @@ Workflow/Activity → Interceptors → Span Processor → OpenBox Core API
269
382
  Returns Verdict
270
383
 
271
384
  (ALLOW, BLOCK, HALT, etc.)
385
+
386
+ Hook-Level (per HTTP request):
387
+ Activity HTTP Call → OTel Hook → hook_governance → API (started) → Allow/Block
388
+ → Response → hook_governance → API (completed) → Allow/Block
389
+
390
+ Hook-Level (per file operation):
391
+ Activity open() → hook_governance → API (started) → Allow/Block
392
+ Activity read/write() → hook_governance → API (started) → Allow/Block
393
+ → hook_governance → API (completed) → Allow/Block
394
+ Activity close() → hook_governance → API (completed) → Allow/Block
395
+
396
+ Hook-Level (per DB query):
397
+ Activity DB Call → db_governance_hooks → hook_governance → API (started) → Allow/Block
398
+ → Query executes → hook_governance → API (completed)
399
+
400
+ Hook-Level (per @traced function):
401
+ @traced call → hook_governance → API (started) → Allow/Block
402
+ → Function runs → hook_governance → API (completed)
272
403
  ```
273
404
 
405
+ **Module responsibilities:**
406
+ - `otel_setup.py` — OTel instrumentation, hooks, TracedFile wrapper
407
+ - `hook_governance.py` — Shared governance evaluator (payload building, API calls, verdict handling)
408
+ - `db_governance_hooks.py` — Per-library DB governance wrappers (wrapt, OTel hooks, SQLAlchemy events)
409
+ - `span_processor.py` — Span buffering, activity context tracking
410
+ - `activity_interceptor.py` — Activity lifecycle governance events
411
+
274
412
  ---
275
413
 
276
414
  ## Advanced Usage
@@ -287,6 +425,7 @@ from openbox import (
287
425
  from openbox.otel_setup import setup_opentelemetry_for_governance
288
426
  from openbox.activity_interceptor import ActivityGovernanceInterceptor
289
427
  from openbox.activities import send_governance_event
428
+ from openbox.hook_governance import FAIL_OPEN, FAIL_CLOSED # error policy constants
290
429
 
291
430
  # 1. Initialize SDK
292
431
  initialize(api_url="http://localhost:8086", api_key="obx_test_key_1")
@@ -296,9 +435,11 @@ span_processor = WorkflowSpanProcessor(
296
435
  ignored_url_prefixes=["http://localhost:8086"]
297
436
  )
298
437
 
299
- # 3. Setup OTel instrumentation
438
+ # 3. Setup OTel instrumentation (governance always enabled)
300
439
  setup_opentelemetry_for_governance(
301
440
  span_processor,
441
+ api_url="http://localhost:8086",
442
+ api_key="obx_test_key_1",
302
443
  sqlalchemy_engine=engine, # optional: instrument pre-existing engine
303
444
  )
304
445
 
@@ -348,13 +489,13 @@ worker = Worker(
348
489
 
349
490
  ## Testing
350
491
 
351
- The SDK includes comprehensive test coverage with 10 test files:
492
+ The SDK includes comprehensive test coverage with 13 test files:
352
493
 
353
494
  ```bash
354
495
  pytest tests/
355
496
  ```
356
497
 
357
- Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_otel_setup.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
498
+ Test files: `test_activities.py`, `test_activity_interceptor.py`, `test_config.py`, `test_db_governance_hooks.py`, `test_file_governance_hooks.py`, `test_otel_hook_pause.py`, `test_otel_hook_pause_db.py`, `test_otel_setup.py`, `test_span_processor.py`, `test_tracing.py`, `test_types.py`, `test_worker.py`, `test_workflow_interceptor.py`
358
499
 
359
500
  ---
360
501
 
@@ -371,4 +512,4 @@ MIT License - See LICENSE file for details
371
512
 
372
513
  ---
373
514
 
374
- **Version:** 1.0.2 | **Last Updated:** 2026-02-12
515
+ **Version:** 1.1.0 | **Last Updated:** 2026-03-09