From 7bc1136807230108f9ea65cc32572424803fbd87 Mon Sep 17 00:00:00 2001 From: Peter Kraft Date: Tue, 23 Jun 2026 11:31:43 -0700 Subject: [PATCH 1/3] fixes --- pyproject.toml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b2e1c16..5e4d3ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,13 @@ [project] name = "dbosify" dynamic = ["version"] -description = "A drop-in replacement for the Temporal Python SDK, backed by DBOS Transact (Postgres) instead of a Temporal server" +description = "A drop-in replacement for the Temporal Python SDK, backed by DBOS Transact and Postgres instead of a Temporal server" readme = "README.md" requires-python = ">=3.10" authors = [{ name = "DBOS, Inc." }] license = { text = "MIT" } keywords = ["temporal", "dbos", "workflow", "durable-execution"] classifiers = [ - "Development Status :: 2 - Pre-Alpha", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -35,7 +34,7 @@ dev = [ "pytest-asyncio>=1.4.0", "pytest-timeout>=2.4.0", "sqlalchemy>=2.0", - # Dev-only, for signature-parity tests. Never a runtime dependency (see DESIGN.md §2). + # Dev-only, for signature-parity tests. Never a runtime dependency. "temporalio>=1.28,<2", ] @@ -60,9 +59,7 @@ atomic = true strict = true files = ["dbosify", "tests", "publish"] -# dateutil (the pre-3.11 ISO datetime fallback) ships no type stubs; the import -# is also unreachable on 3.11+, so an inline ignore would be flagged unused -# there. Silence it at the module level to stay version-robust. +# dateutil (the pre-3.11 ISO datetime fallback) ships no type stubs [[tool.mypy.overrides]] module = ["dateutil.*"] ignore_missing_imports = true From fff856a27a1d94607a947bff1aca0a61159bea5e Mon Sep 17 00:00:00 2001 From: Peter Kraft Date: Tue, 23 Jun 2026 11:33:16 -0700 Subject: [PATCH 2/3] remove --- tests/conformance/README.md | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 tests/conformance/README.md diff --git a/tests/conformance/README.md b/tests/conformance/README.md deleted file mode 100644 index d765209..0000000 --- a/tests/conformance/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Conformance tests - -Runs [temporalio/samples-python](https://github.com/temporalio/samples-python) -samples against DBOSify. The resulting pass-rate table (mirrored in the -top-level README) is the product's headline number (DESIGN.md §9). - -How it works: - -1. `samples.py` clones samples-python at a **pinned commit** into a cache - under the system temp directory (network needed on first run). -2. The **mechanical migration step** rewrites the import root - (`temporalio` → `dbosify`) into a temp build directory. -3. `runner.py` executes one sample per subprocess with the - **connection-setup adapter** installed — the documented migration delta: - `Client.connect("host:port")` becomes a Client over a `DBOSClient`, and - `Worker(client, ...)` becomes `Worker(DBOSConfig, ...)`. Everything else - in the sample runs as written. -4. `test_hello_samples.py` parametrizes over the corpus with per-sample - expectations; samples blocked on unimplemented features are `xfail` with - the blocker named in the reason. - -Three corpora: - -- `test_hello_samples.py` — the `hello/` directory: single-file samples, - one subprocess each (worker + starter in one process). -- `test_message_passing.py` — the `message_passing/` directory: multi-file - packages run as **two processes** (a long-running worker in module mode, then - a starter driven to completion), with `rewrite_package` preserving their - package-absolute imports. -- `test_schedules.py` — the `schedules/` directory: a long-running worker plus - a sequence of per-operation client scripts - (`start`/`describe`/`list`/`trigger`/`update`/`pause`/`backfill`/`delete`), - run in dependency order; the flat directory is rewritten so the scripts' - sibling imports resolve. - -Run with: `uv run pytest tests/conformance/` (needs Postgres, like all -integration tests). From 45618cd38c73f83b79f7b62b0df2dd14bf9ad375 Mon Sep 17 00:00:00 2001 From: Peter Kraft Date: Tue, 23 Jun 2026 12:06:17 -0700 Subject: [PATCH 3/3] cleanup --- dbosify/__init__.py | 2 - dbosify/_internal/activities.py | 10 +-- dbosify/_internal/activity_interceptor.py | 6 +- dbosify/_internal/activity_workflow.py | 2 +- dbosify/_internal/attributes.py | 2 +- dbosify/_internal/client_interceptor.py | 4 +- dbosify/_internal/conversion.py | 4 +- dbosify/_internal/dispatcher.py | 18 ++--- dbosify/_internal/enqueue.py | 2 +- dbosify/_internal/ids.py | 10 +-- dbosify/_internal/inbox.py | 6 +- dbosify/_internal/interpreter.py | 40 +++++----- dbosify/_internal/namespaces.py | 2 +- dbosify/_internal/payloads.py | 6 +- dbosify/_internal/registry.py | 10 +-- dbosify/_internal/replay.py | 10 +-- dbosify/_internal/schedules.py | 8 +- dbosify/_internal/serializer.py | 2 +- dbosify/_internal/status.py | 6 +- dbosify/_internal/visibility.py | 3 +- dbosify/_internal/workflow_interceptor.py | 6 +- dbosify/_schedule.py | 22 +++--- dbosify/activity.py | 14 ++-- dbosify/client.py | 52 ++++++------- dbosify/common.py | 6 +- dbosify/converter.py | 4 +- dbosify/testing/__init__.py | 2 +- dbosify/worker.py | 16 ++-- dbosify/workflow.py | 34 ++++----- tests/conformance/activity_worker_workers.py | 2 +- tests/conformance/runner.py | 4 +- tests/conformance/sdk_harness.py | 4 +- tests/conformance/test_activity_worker.py | 4 +- tests/conformance/test_feature_samples.py | 58 +++++++------- tests/conformance/test_message_passing.py | 2 +- tests/conformance/test_schedules.py | 2 +- tests/conformance/test_sdk_data_conversion.py | 2 +- tests/conformance/test_sdk_workflow.py | 10 +-- tests/dbconfig.py | 4 +- tests/integration/cross_queue_async_worker.py | 2 +- .../integration/cross_queue_cancel_worker.py | 4 +- .../cross_queue_interceptor_worker.py | 4 +- .../integration/cross_queue_orphan_worker.py | 2 +- .../cross_queue_recovery_worker.py | 4 +- tests/integration/cross_queue_retry_worker.py | 2 +- tests/integration/cross_queue_sts_worker.py | 2 +- .../interpreter_recovery_worker.py | 10 +-- tests/integration/patched_recovery_worker.py | 2 +- tests/integration/test_cancellation.py | 4 +- tests/integration/test_child_workflows.py | 2 +- tests/integration/test_cron.py | 2 +- .../test_cross_queue_activity_async.py | 2 +- .../test_cross_queue_activity_cancel.py | 2 +- .../test_cross_queue_activity_interceptor.py | 2 +- .../test_cross_queue_activity_orphan.py | 2 +- .../test_cross_queue_activity_recovery.py | 2 +- .../test_cross_queue_activity_retry.py | 2 +- ..._cross_queue_activity_schedule_to_start.py | 2 +- tests/integration/test_dbos_semantics.py | 2 +- tests/integration/test_deployment_version.py | 6 +- tests/integration/test_dynamic_handlers.py | 2 +- tests/integration/test_info_root.py | 2 +- tests/integration/test_interceptors.py | 2 +- tests/integration/test_interpreter_basic.py | 6 +- .../integration/test_interpreter_recovery.py | 10 +-- tests/integration/test_list_workflows.py | 2 +- tests/integration/test_namespaces.py | 2 +- tests/integration/test_patched.py | 4 +- tests/integration/test_patched_recovery.py | 2 +- tests/integration/test_retry_interactions.py | 2 +- tests/integration/test_schedules.py | 4 +- tests/integration/test_schedules_recovery.py | 6 +- tests/integration/test_search_attributes.py | 2 +- .../test_version_client_separate_process.py | 2 +- tests/integration/test_version_cross_queue.py | 2 +- tests/integration/test_version_recovery.py | 2 +- .../integration/test_workflow_environment.py | 2 +- tests/integration/test_workflow_retry.py | 2 +- tests/integration/version_client_worker.py | 2 +- .../integration/version_cross_queue_worker.py | 2 +- tests/integration/version_recovery_worker.py | 2 +- tests/unit/_param_audit.py | 2 +- tests/unit/test_activity_environment.py | 2 +- tests/unit/test_client_param_audit.py | 2 +- tests/unit/test_dynamic_handlers.py | 6 +- tests/unit/test_ids.py | 2 +- tests/unit/test_namespaces.py | 2 +- tests/unit/test_schedules.py | 2 +- tests/unit/test_sdk_converter.py | 2 +- tests/unit/test_signature_parity.py | 76 +++++++++---------- tests/unit/test_start_verb_param_audit.py | 4 +- tests/unit/test_versioning_types.py | 2 +- tests/unit/test_visibility.py | 2 +- tests/unit/test_worker_app_version.py | 2 +- tests/unit/test_worker_config_normalize.py | 2 +- tests/unit/test_worker_param_audit.py | 12 +-- 96 files changed, 320 insertions(+), 325 deletions(-) diff --git a/dbosify/__init__.py b/dbosify/__init__.py index faa8e2a..939c66c 100644 --- a/dbosify/__init__.py +++ b/dbosify/__init__.py @@ -4,6 +4,4 @@ Modules mirror ``temporalio``'s layout: ``dbosify.workflow``, ``.activity``, ``.client``, ``.worker``, ``.common``, ``.exceptions``, ``.converter``, ``.testing``. Migration from Temporal is an import-root swap. - -See DESIGN.md for the architecture and compatibility contract. """ diff --git a/dbosify/_internal/activities.py b/dbosify/_internal/activities.py index 0ac7ea8..1394a1f 100644 --- a/dbosify/_internal/activities.py +++ b/dbosify/_internal/activities.py @@ -1,7 +1,7 @@ """Activity execution: one single-attempt DBOS step per activity type. Each registered activity gets its own step function named ``act:{type}`` -(decision §10.1: per-type naming keeps DBOS-native step listings readable +(per-type naming keeps DBOS-native step listings readable and makes replay-mismatch detection precise). The step body resolves the activity from the registry at execution time, so re-registering an activity implementation takes effect without re-decoration. @@ -78,7 +78,7 @@ def retry_decision( elapsed: Optional[float], schedule_to_close: Optional[float], ) -> Tuple[Optional[float], exceptions.RetryState]: - """The activity retry decision (DESIGN §6.1.2), clock-independent so both + """The activity retry decision, clock-independent so both execution paths share it: the local path (interpreter, virtual time) and the queued path (the ``__temporal_activity`` workflow, recorded-timestamp time). @@ -193,7 +193,7 @@ async def attempt( attempt_key = (str(meta.get("workflow_run_id", "")), int(meta.get("seq", -1))) heartbeat_timeout = meta.get("heartbeat_timeout") # On the queued path the workflow can't set our in-process cancel Event; - # it sets a checkpointed cancel event on its run that we poll here (§6.1.2). + # it sets a checkpointed cancel event on its run that we poll here. queued = bool(meta.get("queued")) cancel_target = str(meta.get("workflow_run_id", "")) cancel_key = inbox.activity_cancel_key(str(meta.get("activity_id", ""))) @@ -231,8 +231,8 @@ async def call_user_activity() -> Dict[str, Any]: activity_api._register_attempt(attempt_key, ctx) token = activity_api._current_context.set(ctx) try: - # Build the activity interceptor chain for this attempt (DESIGN - # §6.8): inbound wraps the invocation, outbound wraps info()/heartbeat(). + # Build the activity interceptor chain for this attempt: + # inbound wraps the invocation, outbound wraps info()/heartbeat(). impl: activity_interceptor.ActivityInboundInterceptor = ( _RootActivityInbound(ctx, defn.is_async) ) diff --git a/dbosify/_internal/activity_interceptor.py b/dbosify/_internal/activity_interceptor.py index b9c8786..a3162ce 100644 --- a/dbosify/_internal/activity_interceptor.py +++ b/dbosify/_internal/activity_interceptor.py @@ -11,7 +11,7 @@ A worker ``Interceptor`` advertises an activity interceptor via ``intercept_activity`` and a workflow interceptor via ``workflow_interceptor_class``. Nexus interception is unsupported -(DEVIATIONS no-server) and intentionally absent. +(ARCHITECTURE no-server) and intentionally absent. The activity chain is built per attempt in ``_internal/activities.py`` (mirroring temporalio's ``_activity.py`` chaining): inbound interceptors wrap @@ -66,7 +66,7 @@ def workflow_interceptor_class( ) -> "Optional[Type[WorkflowInboundInterceptor]]": """Class that will be instantiated and used to intercept workflows. - Called once per workflow execution (DESIGN §6.8). The returned class + Called once per workflow execution. The returned class must take the same constructor as :py:meth:`WorkflowInboundInterceptor.__init__` (a single ``next``). Returning ``None`` (the default) means this interceptor does not @@ -74,7 +74,7 @@ def workflow_interceptor_class( Args: input: Carries ``unsafe_extern_functions`` for parity; inert here - (dbosify has no workflow sandbox, DEVIATIONS dbos-native-management). + (dbosify has no workflow sandbox, ARCHITECTURE dbos-native-management). Returns: The class to construct to intercept each workflow, or ``None``. diff --git a/dbosify/_internal/activity_workflow.py b/dbosify/_internal/activity_workflow.py index c7d66b4..d96c3ed 100644 --- a/dbosify/_internal/activity_workflow.py +++ b/dbosify/_internal/activity_workflow.py @@ -1,5 +1,5 @@ """The ``__temporal_activity`` dispatcher: the cross-queue / distributed -activity path (DESIGN.md §6.1.2). +activity path. When ``execute_activity(..., task_queue=)`` names a queue other than the workflow's own, the interpreter does not run the activity as an in-process diff --git a/dbosify/_internal/attributes.py b/dbosify/_internal/attributes.py index d3483fa..2ace396 100644 --- a/dbosify/_internal/attributes.py +++ b/dbosify/_internal/attributes.py @@ -1,5 +1,5 @@ """Memo and search-attribute conversion, backed by DBOS native workflow -attributes (DESIGN §6.2). +attributes. Temporal has two metadata namespaces — ``memo`` (opaque, converter-encoded, codec-capable) and ``search_attributes`` (typed, indexed, queryable). DBOS diff --git a/dbosify/_internal/client_interceptor.py b/dbosify/_internal/client_interceptor.py index 3a5cc4a..93cd3a9 100644 --- a/dbosify/_internal/client_interceptor.py +++ b/dbosify/_internal/client_interceptor.py @@ -6,8 +6,8 @@ ``dbosify.client.Interceptor`` exactly as it would ``temporalio.client.Interceptor``. -The ``*Input`` dataclasses are copied field-for-field from the SDK (DESIGN -§6.8) so signature parity holds; fields dbosify does not act on (e.g. +The ``*Input`` dataclasses are copied field-for-field from the SDK so +signature parity holds; fields dbosify does not act on (e.g. ``callbacks``, ``links``, ``request_id``, ``versioning_override``, ``priority``, ``rpc_metadata``/``rpc_timeout``, ``data_converter_override``) are carried but inert. ``headers`` is *not* inert: a header set on ``start_workflow`` / diff --git a/dbosify/_internal/conversion.py b/dbosify/_internal/conversion.py index 99709a7..85f0612 100644 --- a/dbosify/_internal/conversion.py +++ b/dbosify/_internal/conversion.py @@ -1,4 +1,4 @@ -"""The data-conversion boundary (DESIGN §6.9). +"""The data-conversion boundary. User values are converted to/from small tagged payload dicts at the Temporal boundaries — where function signatures supply the type hints that rebuild the @@ -29,7 +29,7 @@ from ..converter import DataConverter, Payload -# Checkpoints embed a user value as a small dict (DESIGN §6.9 envelope), not a raw +# Checkpoints embed a user value as a small dict (envelope), not a raw # Payload: codec-less json/plain inlines its JSON; binary/codec bytes use base64. _active_converter: DataConverter = DataConverter.default diff --git a/dbosify/_internal/dispatcher.py b/dbosify/_internal/dispatcher.py index 7d3ffce..125629b 100644 --- a/dbosify/_internal/dispatcher.py +++ b/dbosify/_internal/dispatcher.py @@ -1,11 +1,11 @@ """Per-type DBOS workflow dispatchers and the workflow-task retry loop. Each registered Temporal workflow type gets its own thin DBOS workflow named -``wf:{type}`` (resolved decision §10.1) that delegates to the interpreter. +``wf:{type}`` that delegates to the interpreter. Per-type registration keeps DBOS-native listing/filtering by name working and lets clients enqueue by name without importing user code. -Workflow-task failure semantics (§4.2): a non-failure exception from +Workflow-task failure semantics: a non-failure exception from workflow code does NOT fail the workflow. The dispatcher logs loudly, sleeps with capped backoff (a real sleep — this is outside the deterministic boundary), rewinds the DBOS checkpoint cursor (``ctx.function_id``) to where @@ -128,10 +128,10 @@ def register_worker( registry.set_worker_interceptors(interceptors) registry.set_worker_task_queue(task_queue) registry.set_worker_namespace(namespace) - # The generic schedule-fire dispatcher is process-global (§6.7); register + # The generic schedule-fire dispatcher is process-global; register # it so this worker can run schedules whose action targets it. register_schedule_dispatcher() - # The generic queued-activity dispatcher (§6.1.2) is likewise process-global, + # The generic queued-activity dispatcher is likewise process-global, # registered for every worker so any host can run cross-queue activities. register_activity_dispatcher() for cls in workflows: @@ -176,7 +176,7 @@ async def dispatch(payload: Any) -> Any: # status maps to CONTINUED_AS_NEW and awaiters follow new_run_id. raise SerializedContinueAsNew({"new_run_id": can.new_run_id}) from None except WorkflowCancelled as cancelled: - # Cooperative cancellation maps to CANCELED (§6.2), distinct from + # Cooperative cancellation maps to CANCELED, distinct from # FAILED and TERMINATED; it ends the chain (no retry, no cron). raise SerializedWorkflowCancellation( serialize_failure(cancelled.cause) @@ -403,8 +403,8 @@ async def _enqueue_next_run( return new_run_id -# Schedules (§6.7): per-occurrence fire dispatcher — enforces bounds/jitter, applies -# the overlap policy (DEVIATIONS schedules, _apply_overlap_policy), starts the action. +# Schedules: per-occurrence fire dispatcher — enforces bounds/jitter, applies +# the overlap policy (ARCHITECTURE schedules, _apply_overlap_policy), starts the action. SCHEDULE_FIRE_NAME = "__temporal_schedule_fire" _schedule_dispatcher_registered = False @@ -487,7 +487,7 @@ async def _apply_overlap_policy( await DBOS.cancel_workflow_async(prior, cancel_children=True) elif overlap == _OVERLAP_CANCEL_OTHER: # Cooperative cancel (lets the running action's cleanup run); we do not - # wait for it to unwind before starting the next (DEVIATIONS schedules). + # wait for it to unwind before starting the next (ARCHITECTURE schedules). await DBOS.send_async( prior, inbox.cancel_envelope("schedule overlap"), inbox.INBOX_TOPIC ) @@ -507,7 +507,7 @@ async def _running_prior_occurrence( statuses in a single batch; the most recent occurrence that actually left an action row (skipped fires leave none) is the candidate, and it counts as a running prior iff still open. Bounded to the most recent - ``_OVERLAP_LOOKBACK_LIMIT`` fires (DEVIATIONS schedules).""" + ``_OVERLAP_LOOKBACK_LIMIT`` fires (ARCHITECTURE schedules).""" base = action["id"] schedule_name = context["schedule_id"] before_epoch = int(fired_at.timestamp()) diff --git a/dbosify/_internal/enqueue.py b/dbosify/_internal/enqueue.py index fdf0138..04ac4af 100644 --- a/dbosify/_internal/enqueue.py +++ b/dbosify/_internal/enqueue.py @@ -1,4 +1,4 @@ -"""Shared in-workflow run enqueue (§6.4 chain hops). +"""Shared in-workflow run enqueue. Every durable chain hop starts one run under a deterministic id with the same DBOS context plumbing: continue-as-new (``interpreter._begin_continue_as_new``), diff --git a/dbosify/_internal/ids.py b/dbosify/_internal/ids.py index 1c916ad..7776b89 100644 --- a/dbosify/_internal/ids.py +++ b/dbosify/_internal/ids.py @@ -1,8 +1,8 @@ -"""Temporal workflow IDs vs DBOS workflow IDs (DESIGN.md §6.4). +"""Temporal workflow IDs vs DBOS workflow IDs. DBOS allows exactly one execution per DBOS workflow id, ever; Temporal allows reusing a workflow id across *closed* runs, each run having its own -run id. Scheme (resolved decision §10.3): +run id. Scheme: - run n of Temporal id W has DBOS id ``W`` for n=0, else ``W--r{n}`` - the user-visible ``run_id`` IS that DBOS id (stable and unique; @@ -13,7 +13,7 @@ from typing import Any, Awaitable, Callable, Dict, List, Optional, Sequence, Tuple RUN_SEPARATOR = "--r" -# Cross-queue activity workflow ids are ``{run}--a{seq}`` (§6.1.2). Reserving this +# Cross-queue activity workflow ids are ``{run}--a{seq}``. Reserving this # separator keeps an activity workflow id from colliding with any user/child id. ACTIVITY_SEPARATOR = "--a" @@ -54,7 +54,7 @@ def run_dbos_id(workflow_id: str, run_index: int) -> str: def activity_dbos_id(run_id: str, seq: int) -> str: - """The ``__temporal_activity`` workflow id for a cross-queue activity (§6.1.2). + """The ``__temporal_activity`` workflow id for a cross-queue activity. Deterministic in ``seq``, and in the reserved ``--a`` namespace so it cannot collide with any user/child/run id.""" return f"{run_id}{ACTIVITY_SEPARATOR}{seq}" @@ -64,7 +64,7 @@ def replay_scratch_id(run_id: str, mode: str, suffix: str = "") -> str: """The DBOS id for a replay scratch fork of ``run_id``. The mode travels *in the id* so a worker in a different process than the - forker recognizes the replay (DEVIATIONS replay, query-on-closed rehydrate). + forker recognizes the replay (ARCHITECTURE replay, query-on-closed rehydrate). ``suffix`` is a unique token per operation (the request id for a query, a fresh token for a verification), so concurrent replays/queries of the same run each get their own scratch and never collide on one id.""" diff --git a/dbosify/_internal/inbox.py b/dbosify/_internal/inbox.py index 7ad81cc..335295e 100644 --- a/dbosify/_internal/inbox.py +++ b/dbosify/_internal/inbox.py @@ -22,7 +22,7 @@ INBOX_TOPIC = "__dbosify_inbox" # The topic a queued activity workflow parks on for external completion -# (raise_complete_async, §6.1.2): AsyncActivityHandle sends the envelope here. +# (raise_complete_async): AsyncActivityHandle sends the envelope here. ASYNC_COMPLETE_TOPIC = "__dbosify_async_complete" # recv timeout per wait; on (checkpointed, deterministic) timeout the @@ -112,8 +112,8 @@ def async_activity_gone_key(activity_id: str) -> str: def activity_cancel_key(activity_id: str) -> str: - """Event set on the workflow run when a cross-queue activity is cancelled - (§6.1.2): the activity's attempt step on the other worker polls it and, when + """Event set on the workflow run when a cross-queue activity is cancelled: + the activity's attempt step on the other worker polls it and, when set, delivers cancellation into the running activity. (The local path uses an in-process threading.Event instead — same process, no event needed.)""" return f"__dbosify_act_{activity_id}_cancel" diff --git a/dbosify/_internal/interpreter.py b/dbosify/_internal/interpreter.py index acac60f..875aad3 100644 --- a/dbosify/_internal/interpreter.py +++ b/dbosify/_internal/interpreter.py @@ -1,4 +1,4 @@ -"""The deterministic interpreter (DESIGN.md §4) — the load-bearing component. +"""The deterministic interpreter — the load-bearing component. A Temporal workflow is a class: a primary ``run()`` coroutine plus signal/update/query handlers, multiplexed on a *deterministic* event loop @@ -113,7 +113,7 @@ class WorkflowTaskFailure(Exception): """Internal: a non-failure exception escaped workflow code. In Temporal this fails the *workflow task*, not the workflow: the dispatcher logs, backs off, and re-runs the interpreter from checkpoints while the - workflow stays RUNNING (DESIGN.md §4.2). + workflow stays RUNNING. """ def __init__(self, cause: BaseException) -> None: @@ -133,7 +133,7 @@ def __init__(self, new_run_id: str) -> None: class WorkflowCancelled(Exception): """Internal: the workflow ended via cooperative cancellation. The - dispatcher converts this into the cancelled marker (DESIGN §6.5).""" + dispatcher converts this into the cancelled marker.""" def __init__(self, cause: BaseException) -> None: super().__init__(repr(cause)) @@ -312,7 +312,7 @@ async def child_exists_step(child_id: str) -> bool: def _await_activity_result(activity_id: str) -> Any: - """The queued-activity result waiter (the cross-queue path, §6.1.2): our + """The queued-activity result waiter (the cross-queue path): our own step wrapping the non-recording wait on the ``__temporal_activity`` workflow, returning its envelope. Same rationale as ``_await_child_result`` — DBOS's ``get_result`` claims its function_id at completion (a @@ -603,7 +603,7 @@ class _Waiter: @dataclass class _ActivityExec: - """Per-activity retry state machine (DESIGN.md §6.1.2): each attempt is + """Per-activity retry state machine: each attempt is its own DBOS step, each backoff its own durable sleep — both launched only from checkpointed event-delivery points so recovery resumes at the right attempt. @@ -632,7 +632,7 @@ class _ActivityExec: # registered return annotation. result_type: Optional[type] = None # execute_activity(task_queue=...): when set and different from the workflow's - # own queue, the activity runs on another worker via the queued path (§6.1.2). + # own queue, the activity runs on another worker via the queued path. task_queue: Optional[str] = None # The ``__temporal_activity`` workflow id once dispatched on the queued path # (None on the local path); cancellation natively cancels this workflow. @@ -909,7 +909,7 @@ def __init__( self._replay_horizon = 0 # None, or "verify"/"rehydrate" when this run is a replay scratch fork. self._replay_mode: Optional[str] = None - # workflow.patched()/deprecate_patch() state (DESIGN §6.8): recorded + # workflow.patched()/deprecate_patch() state: recorded # marker ids, the per-id decision memo, and markers queued for this turn. self._patches_recorded: Set[str] = set() self._patches_memoized: Dict[str, bool] = {} @@ -928,7 +928,7 @@ def __init__( self._memo: Dict[str, Any] = {} self._typed_sa: TypedSearchAttributes = TypedSearchAttributes.empty # Free-form UI/CLI details set via workflow.set_current_details(): pure - # in-memory state, reconstructed deterministically on recovery (DEVIATIONS current-details). + # in-memory state, reconstructed deterministically on recovery (ARCHITECTURE current-details). self._current_details: str = "" self._random = Random(0) # The deterministic random seed (checkpointed once at run start), exposed @@ -991,7 +991,7 @@ async def execute(self) -> Any: self._replay_horizon = max((step["function_id"] for step in steps), default=0) # Rebuild the set of recorded patch ids by id (set membership), not by - # position, so patched() is stable across checkpoint shifts (DESIGN §6.8). + # position, so patched() is stable across checkpoint shifts. self._patches_recorded = { step["output"] for step in steps @@ -1583,7 +1583,7 @@ async def _process_commands(self) -> bool: # Local / same-queue path: run as an in-process step. self._launch_attempt(exec_state) else: - # Cross-queue path (§6.1.2): enqueue on another worker. + # Cross-queue path: enqueue on another worker. await self._launch_queued_activity(exec_state) elif kind == "child": self._check_replay_horizon() @@ -1763,7 +1763,7 @@ async def _sweep_cancellations(self) -> None: (which records nothing — replays like a crash); ABANDON detaches it; WAIT is handled in ``_request_activity_cancel`` (kept open until confirmed). - Activities (queued path, §6.1.2): the activity runs on another worker, so + Activities (queued path): the activity runs on another worker, so cancellation is delivered cross-process by ``_signal_queued_activity_cancel`` (a cancel event the running attempt polls, plus a marker that wakes an async-parked activity). ABANDON leaves it running. WAIT keeps the exec open until @@ -1887,7 +1887,7 @@ async def _decode_activity_result( return envelope async def _launch_queued_activity(self, exec_state: _ActivityExec) -> None: - """Cross-queue / distributed activity dispatch (§6.1.2): enqueue the + """Cross-queue / distributed activity dispatch: enqueue the ``__temporal_activity`` workflow on the target DBOS queue and await its result envelope, mirroring the child-workflow enqueue (``_start_child``). @@ -2182,7 +2182,7 @@ def _deliver_child_event(self, waiter: _Waiter) -> None: child.result_future.set_exception(error) async def _sweep_children_on_close(self) -> None: - """ParentClosePolicy (§6.5): when the parent reaches a terminal + """ParentClosePolicy: when the parent reaches a terminal outcome, deal with still-running children. TERMINATE (the default) native-cancels them — recursively, applying *their* recorded policies, since a terminated child runs no code of its own; @@ -2440,7 +2440,7 @@ async def _apply_activity_heartbeat(self, envelope: inbox.Envelope) -> None: ) def _apply_cancel(self, envelope: inbox.Envelope) -> None: - """Cooperative cancellation (§6.5): raise CancelledError into the + """Cooperative cancellation: raise CancelledError into the primary task at the next event boundary. Cleanup code runs — and may still execute activities, because the outer loop keeps servicing events until the unwind produces an outcome. @@ -2923,8 +2923,8 @@ def runtime_info(self) -> Info: if self._parent_run_id is not None else None ) - # Root of this run's tree, threaded in via the child-start meta-envelope - # (§6.6); None for a top-level workflow (itself the root). + # Root of this run's tree, threaded in via the child-start meta-envelope; + # None for a top-level workflow (itself the root). root = ( RootInfo( run_id=self._meta.root["run_id"], @@ -3029,7 +3029,7 @@ def runtime_register_random_seed_callback( self, callback: Callable[[int], None] ) -> None: # Accepted for parity but intentionally a no-op: our seed is fixed per - # run, so the callback could never fire (DEVIATIONS random-seed). + # run, so the callback could never fire (ARCHITECTURE random-seed). return None def runtime_instance(self) -> Any: @@ -3115,7 +3115,7 @@ async def _resolve_own_queue(self) -> None: async def _resolve_current_run(self, workflow_id: str) -> str: """Resolve a Temporal workflow id to its current run's DBOS id - (§6.4 run chains) via exact-id probes (ids.resolve_latest_run; no + (run chains) via exact-id probes (ids.resolve_latest_run; no prefix scan). Each batched lookup is a checkpointed management call, and the probe sequence is driven by the recorded results, so the resolution replays deterministically. @@ -3144,7 +3144,7 @@ def runtime_is_read_only(self) -> bool: return self._read_only def _patch(self, id: str) -> bool: - """Shared patched()/deprecate_patch() logic (DESIGN §6.8). + """Shared patched()/deprecate_patch() logic. Returns whether the *newer* code path should run, mirroring temporalio: true on first (non-replaying) execution or when this patch's marker is @@ -3188,7 +3188,7 @@ def runtime_get_current_deployment_version( self, ) -> Optional[WorkerDeploymentVersion]: # Deployment name is a process-global set by the Worker; build_id is read - # live from the worker's DBOS application_version (DEVIATIONS worker-versioning). + # live from the worker's DBOS application_version (ARCHITECTURE worker-versioning). from . import registry name = registry.worker_deployment_name diff --git a/dbosify/_internal/namespaces.py b/dbosify/_internal/namespaces.py index fce828d..4c5d0bb 100644 --- a/dbosify/_internal/namespaces.py +++ b/dbosify/_internal/namespaces.py @@ -1,4 +1,4 @@ -"""Temporal namespaces backed by DBOS system schemas (DEVIATIONS no-server). +"""Temporal namespaces backed by DBOS system schemas (ARCHITECTURE no-server). Each Temporal namespace maps to its own Postgres schema holding the DBOS system tables (``dbos_system_schema``), so workflows in different namespaces diff --git a/dbosify/_internal/payloads.py b/dbosify/_internal/payloads.py index 6e2498d..a3837eb 100644 --- a/dbosify/_internal/payloads.py +++ b/dbosify/_internal/payloads.py @@ -1,7 +1,7 @@ """Serialized envelope formats stored in DBOS checkpoints. The failure envelope is the stable, bidirectional serialization of the -Temporal exception tree (DESIGN.md §6.3): exception -> plain dict -> equal +Temporal exception tree: exception -> plain dict -> equal exception. It is used for activity results, workflow results, and child-workflow errors, so reconstruction is exact across processes — pickle of exception objects loses ``__cause__`` chains, envelopes don't. @@ -58,7 +58,7 @@ class RunMeta: # ExecuteWorkflowInput.headers and carried across cron/retry hops. headers: Optional[Dict[str, Any]] = None # The root workflow of this run's tree ({"workflow_id", "run_id"}); None for a - # top-level workflow (workflow.info().root, §6.6). Carries across chain hops. + # top-level workflow (workflow.info().root). Carries across chain hops. root: Optional[Dict[str, str]] = None def is_empty(self) -> bool: @@ -205,7 +205,7 @@ def __str__(self) -> str: class SerializedWorkflowCancellation(SerializedWorkflowFailure): - """The ``_TemporalCancelledMarker`` of DESIGN §6.2: a workflow that ended + """The ``_TemporalCancelledMarker``: a workflow that ended via *cooperative cancellation* records this subclass, so status mapping can distinguish CANCELED (this, recorded by the dispatcher) from FAILED (plain SerializedWorkflowFailure) and TERMINATED (native DBOS cancel, diff --git a/dbosify/_internal/registry.py b/dbosify/_internal/registry.py index e1e4b75..a29eed9 100644 --- a/dbosify/_internal/registry.py +++ b/dbosify/_internal/registry.py @@ -95,7 +95,7 @@ class WorkflowDefinition: arg_types: Optional[List[type]] = None ret_type: Optional[type] = None # @workflow.defn(versioning_behavior=...): stored for parity. PINNED is what - # DBOS enforces; AUTO_UPGRADE has no analog (DEVIATIONS worker-versioning). + # DBOS enforces; AUTO_UPGRADE has no analog (ARCHITECTURE worker-versioning). versioning_behavior: Optional[int] = None @@ -109,7 +109,7 @@ class ActivityDefinition: arg_types: Optional[List[type]] = None ret_type: Optional[type] = None # A *dynamic* activity (catch-all): invoked as ``fn(Sequence[RawValue])`` for - # any activity type with no exact registration (§6.1.2). ``name`` is not routed. + # any activity type with no exact registration. ``name`` is not routed. dynamic: bool = False @@ -138,7 +138,7 @@ def dbos_workflow_for(name: str) -> Callable[..., Any]: # The process-global ``__temporal_activity`` dispatcher (the cross-queue activity -# path, §6.1.2). Lives here so the interpreter can resolve it without a cycle. +# path). Lives here so the interpreter can resolve it without a cycle. _activity_dispatcher: Optional[Callable[..., Any]] = None @@ -170,7 +170,7 @@ def add_worker_failure_exception_types( # Worker-level activity interceptors (Worker(interceptors=...)); the activity -# attempt step folds these around each attempt (DESIGN §6.8). One Worker owns them. +# attempt step folds these around each attempt. One Worker owns them. worker_interceptors: Tuple[Any, ...] = () @@ -200,7 +200,7 @@ def set_worker_namespace(namespace: Optional[str]) -> None: # This process's worker deployment NAME (None when no Worker is active). The build_id -# half is read live from the DBOS application_version (DEVIATIONS worker-versioning). +# half is read live from the DBOS application_version (ARCHITECTURE worker-versioning). worker_deployment_name: Optional[str] = None diff --git a/dbosify/_internal/replay.py b/dbosify/_internal/replay.py index 507b738..b11081d 100644 --- a/dbosify/_internal/replay.py +++ b/dbosify/_internal/replay.py @@ -2,7 +2,7 @@ currently-registered code to detect non-determinism, mirroring ``temporalio.worker.Replayer``. -Mechanism (DESIGN.md §9, "Replayer over DBOS step checkpoints / fork_workflow"): +Mechanism ("Replayer over DBOS step checkpoints / fork_workflow"): re-execution from checkpoints is exactly the path crash-recovery already takes, and DBOS itself raises ``DBOSUnexpectedStepError`` when a re-run claims a different step at a recorded ``function_id``. So a replay is a *fork* of the @@ -32,7 +32,7 @@ steps, so nothing about the replay has to be passed out of band. The same fork machinery (``start_replay_fork`` with ``mode``) backs -**query-on-closed-workflow rehydrate** (DEVIATIONS replay): the client forks a +**query-on-closed-workflow rehydrate** (ARCHITECTURE replay): the client forks a closed run in ``mode="rehydrate"``, the forked run replays to its final state and then *keeps serving* one query against the reconstructed instance (it is not deleted-after-verify but stops on a client signal or the @@ -165,7 +165,7 @@ async def replay_one(history: "WorkflowHistory") -> Optional[Exception]: ) from .status import WorkflowExecutionStatus - # States that cannot be faithfully reconstructed by replay (DEVIATIONS + # States that cannot be faithfully reconstructed by replay (ARCHITECTURE # replay): partial-history (TERMINATED/TIMED_OUT) or CONTINUED_AS_NEW. if history.status in ( WorkflowExecutionStatus.TERMINATED, @@ -174,7 +174,7 @@ async def replay_one(history: "WorkflowHistory") -> Optional[Exception]: ): return ValueError( f"cannot replay a {history.status.name} workflow: it cannot be " - "faithfully reconstructed from its checkpoints (DEVIATIONS replay)" + "faithfully reconstructed from its checkpoints (ARCHITECTURE replay)" ) handle = await start_replay_fork( @@ -241,7 +241,7 @@ class Replayer: mutate it: ``data_converter``, ``interceptors``, and ``workflow_failure_exception_types`` are accepted for API parity but the running Worker's values are authoritative (overriding them here would - clobber the live Worker, since one Worker owns the process; DEVIATIONS replay). + clobber the live Worker, since one Worker owns the process; ARCHITECTURE replay). Parameters with no dbosify analog (``namespace``, ``build_id``, ``identity``, ``workflow_runner``/``unsandboxed_workflow_runner``, ``debug_mode``, ``runtime``, ``plugins``, ``workflow_task_executor``, ...) diff --git a/dbosify/_internal/schedules.py b/dbosify/_internal/schedules.py index 536e6b5..c50cdfa 100644 --- a/dbosify/_internal/schedules.py +++ b/dbosify/_internal/schedules.py @@ -1,4 +1,4 @@ -"""Cron-expression helpers (DESIGN §6.4 cron; §6.7 schedules build on this). +"""Cron-expression helpers. DBOS vendors a croniter; we reuse it so client-side fire computations and the DBOS worker-side scheduler agree on semantics. Temporal cron strings are @@ -7,11 +7,11 @@ (trailing year) expressions are accepted as an extension — Temporal rejects them, but they are invaluable for fast tests and cost nothing to support. -§6.7 schedules compile a ``ScheduleSpec`` (intervals / calendars / cron +Schedules compile a ``ScheduleSpec`` (intervals / calendars / cron expressions) down to a single cron string + timezone for ``DBOS.create_schedule``. Interval periods that don't divide a cron boundary evenly, calendar fields beyond cron's expressiveness, and interval offsets are approximated with a -logged deviation (DEVIATIONS schedules). +logged deviation (ARCHITECTURE schedules). """ import logging @@ -137,7 +137,7 @@ def calendar_to_cron( Emits a 6-field (seconds-first) expression when any second range is non-default, else 5-field. The ``year`` field has no cron equivalent and - is dropped with a deviation when constraining (DEVIATIONS schedules). + is dropped with a deviation when constraining (ARCHITECTURE schedules). """ if year: logger.debug( diff --git a/dbosify/_internal/serializer.py b/dbosify/_internal/serializer.py index 17f8a01..c4dc684 100644 --- a/dbosify/_internal/serializer.py +++ b/dbosify/_internal/serializer.py @@ -1,5 +1,5 @@ """The DBOS ``Serializer`` adapter: JSON transport for dbosify -checkpoints (DESIGN §6.9), replacing DBOS's default pickle. +checkpoints, replacing DBOS's default pickle. By the time data reaches this serializer it is already JSON-safe: every user value has been converted to a payload dict at the Temporal boundaries diff --git a/dbosify/_internal/status.py b/dbosify/_internal/status.py index 490850c..06bba55 100644 --- a/dbosify/_internal/status.py +++ b/dbosify/_internal/status.py @@ -1,4 +1,4 @@ -"""DBOS workflow status -> Temporal ``WorkflowExecutionStatus`` (DESIGN §6.2). +"""DBOS workflow status -> Temporal ``WorkflowExecutionStatus``. The enum lives here (re-exported by ``dbosify.client``) so internal modules can map statuses without importing the client facade. @@ -28,7 +28,7 @@ class WorkflowExecutionStatus(IntEnum): def to_execution_status( dbos_status: Optional[str], *, error: Optional[BaseException] = None ) -> WorkflowExecutionStatus: - """Map a DBOS status string per the DESIGN §6.2 table. For ERROR, the + """Map a DBOS status string to a Temporal status. For ERROR, the recorded error distinguishes cooperative cancellation (CANCELED) from failure; pass it when available. @@ -48,7 +48,7 @@ def to_execution_status( return WorkflowExecutionStatus.CANCELED return WorkflowExecutionStatus.FAILED if dbos_status == "CANCELLED": - # Native DBOS cancel is reserved for terminate (decision §10.4). + # Native DBOS cancel is reserved for terminate. return WorkflowExecutionStatus.TERMINATED if dbos_status == "MAX_RECOVERY_ATTEMPTS_EXCEEDED": return WorkflowExecutionStatus.RUNNING diff --git a/dbosify/_internal/visibility.py b/dbosify/_internal/visibility.py index 3f899b9..5336e9e 100644 --- a/dbosify/_internal/visibility.py +++ b/dbosify/_internal/visibility.py @@ -1,5 +1,4 @@ -"""Visibility-query parsing for ``client.list_workflows`` / ``count_workflows`` -(DESIGN §6.2). +"""Visibility-query parsing for ``client.list_workflows`` / ``count_workflows``. Temporal sends the visibility filter *string* to the server, which parses it; the ``temporalio`` SDK ships no parser (``list_workflows`` passes the string diff --git a/dbosify/_internal/workflow_interceptor.py b/dbosify/_internal/workflow_interceptor.py index dce007d..4a43c0e 100644 --- a/dbosify/_internal/workflow_interceptor.py +++ b/dbosify/_internal/workflow_interceptor.py @@ -16,8 +16,8 @@ outbound so ``start_activity`` / ``start_child_workflow`` / signals / ``continue_as_new`` route through it. -The ``*Input`` dataclasses are copied field-for-field from the SDK (DESIGN -§6.8) so signature parity holds; fields dbosify does not act on (e.g. +The ``*Input`` dataclasses are copied field-for-field from the SDK so +signature parity holds; fields dbosify does not act on (e.g. ``versioning_intent``, ``initial_versioning_behavior``, ``priority``, ``disable_eager_execution``, ``arg_types``/``ret_type``) are carried but inert. Annotations use our own types or ``Any`` (the parity test checks @@ -78,7 +78,7 @@ class WorkflowInterceptorClassInput: """Input for :py:meth:`dbosify.worker.Interceptor.workflow_interceptor_class`. ``unsafe_extern_functions`` is carried for parity; dbosify has no - workflow sandbox (DEVIATIONS dbos-native-management), so there is nothing to expose extern + workflow sandbox (ARCHITECTURE dbos-native-management), so there is nothing to expose extern functions *into* — the mapping is inert. """ diff --git a/dbosify/_schedule.py b/dbosify/_schedule.py index 5370686..d8d4154 100644 --- a/dbosify/_schedule.py +++ b/dbosify/_schedule.py @@ -1,4 +1,4 @@ -"""Schedules (DESIGN §6.7): the temporalio-shaped ``Schedule`` type surface +"""Schedules: the temporalio-shaped ``Schedule`` type surface and ``ScheduleHandle``, backed by DBOS schedule primitives. Mirrors ``temporalio.client``'s schedule types (re-exported from @@ -10,7 +10,7 @@ the schedule's ``context`` so ``describe``/``list``/``update`` can reconstruct it; ``ScheduleSpec`` itself compiles down to a cron string + timezone for DBOS. -Deviations (DEVIATIONS schedules): interval periods that don't divide a cron +Deviations (ARCHITECTURE schedules): interval periods that don't divide a cron boundary, calendar ``year`` constraints, and interval offsets are approximated; overlap policy honors SKIP / CANCEL_OTHER / TERMINATE_OTHER / ALLOW_ALL (via a bounded backward walk of prior occurrences at fire time) but rejects @@ -173,7 +173,7 @@ class SchedulePolicy: ``overlap`` defaults to ``SKIP`` (matching Temporal). SKIP, CANCEL_OTHER, TERMINATE_OTHER, and ALLOW_ALL are honored; BUFFER_ONE/BUFFER_ALL are - rejected at ``create_schedule`` time (DEVIATIONS schedules).""" + rejected at ``create_schedule`` time (ARCHITECTURE schedules).""" overlap: ScheduleOverlapPolicy = field( default_factory=lambda: ScheduleOverlapPolicy.SKIP @@ -464,7 +464,7 @@ async def update( ) -> None: """Update this schedule. The ``updater`` (sync or async) receives the current description and returns the new ``ScheduleUpdate`` (or ``None`` - to skip). Implemented as delete-then-recreate (DEVIATIONS schedules).""" + to skip). Implemented as delete-then-recreate (ARCHITECTURE schedules).""" await self._client._impl.update_schedule( UpdateScheduleInput( id=self.id, @@ -496,7 +496,7 @@ async def pause( rpc_timeout: Optional[timedelta] = None, ) -> None: """Pause this schedule (DBOS ``pause_schedule``). ``note`` is accepted - but not persisted (DEVIATIONS schedules).""" + but not persisted (ARCHITECTURE schedules).""" await self._client._impl.pause_schedule( PauseScheduleInput( id=self.id, @@ -517,7 +517,7 @@ async def unpause( rpc_timeout: Optional[timedelta] = None, ) -> None: """Unpause this schedule (DBOS ``resume_schedule``). ``note`` is accepted - but not persisted (DEVIATIONS schedules).""" + but not persisted (ARCHITECTURE schedules).""" await self._client._impl.unpause_schedule( UnpauseScheduleInput( id=self.id, @@ -539,7 +539,7 @@ async def trigger( ) -> None: """Trigger an immediate action on this schedule. The action runs under the schedule's configured overlap policy; a per-call ``overlap`` override - is accepted only as ``ALLOW_ALL`` (others raise — DEVIATIONS schedules).""" + is accepted only as ``ALLOW_ALL`` (others raise — ARCHITECTURE schedules).""" await self._client._impl.trigger_schedule( TriggerScheduleInput( id=self.id, @@ -562,7 +562,7 @@ async def backfill( """Backfill this schedule over the given time periods. Backfilled actions run under the schedule's configured overlap policy; a per-backfill ``overlap`` override is accepted only as ``ALLOW_ALL`` (others raise — - DEVIATIONS schedules).""" + ARCHITECTURE schedules).""" await self._client._impl.backfill_schedule( BackfillScheduleInput( id=self.id, @@ -837,7 +837,7 @@ def compile_spec(spec: ScheduleSpec) -> "tuple[str, Optional[str]]": """Compile a ``ScheduleSpec`` to a (cron, timezone_name) pair for DBOS. cron_expressions win, then intervals, then calendars. Extra entries beyond - the first are dropped with a deviation (DEVIATIONS schedules).""" + the first are dropped with a deviation (ARCHITECTURE schedules).""" tz_name = spec.time_zone_name if spec.cron_expressions: if len(spec.cron_expressions) > 1: @@ -928,7 +928,7 @@ def _list_description_from_row(row: Mapping[str, Any]) -> ScheduleListDescriptio def require_supported_overlap(overlap: Optional[ScheduleOverlapPolicy]) -> None: - """Reject overlap policies dbosify does not implement (DEVIATIONS schedules). + """Reject overlap policies dbosify does not implement (ARCHITECTURE schedules). SKIP, CANCEL_OTHER, TERMINATE_OTHER, and ALLOW_ALL are honored; BUFFER_ONE/BUFFER_ALL need durable start-after-completion queueing we don't @@ -949,7 +949,7 @@ def require_overlap_override_supported( overlap: Optional[ScheduleOverlapPolicy], ) -> None: """Reject a per-call (trigger/backfill) overlap override we don't honor - (DEVIATIONS schedules). A per-call override can't be threaded through DBOS's + (ARCHITECTURE schedules). A per-call override can't be threaded through DBOS's trigger/backfill, so only ``None`` (use the schedule's configured policy) and ``ALLOW_ALL`` (the least-restrictive, no-op case) are accepted; any other value raises rather than being silently ignored.""" diff --git a/dbosify/activity.py b/dbosify/activity.py index 76b02a5..43725ba 100644 --- a/dbosify/activity.py +++ b/dbosify/activity.py @@ -8,7 +8,7 @@ ``heartbeat`` raises CancelledError when cancellation of the activity has been requested (how sync activities observe cancellation, as in Temporal) and records details for the next retry attempt — in worker memory, not -durably (DEVIATIONS failover). ``raise_complete_async`` parks the activity for +durably (ARCHITECTURE failover). ``raise_complete_async`` parks the activity for external completion via ``client.get_async_activity_handle``. """ @@ -115,14 +115,14 @@ def defn( ``dynamic=True`` makes this the catch-all activity, invoked for any activity type with no exact registration; it must accept a single - ``Sequence[RawValue]`` and cannot also set ``name`` (§6.1.2). + ``Sequence[RawValue]`` and cannot also set ``name``. ``no_thread_cancel_exception`` defaults to ``True`` (temporalio's default is ``False``): dbosify delivers cancellation to sync activities *cooperatively* and never raises into their worker thread, so it always behaves as ``True``. Setting it ``False`` — asking for Temporal's raise-into-the-thread behavior — raises ``NotImplementedError`` rather than - silently doing something else (DEVIATIONS sync-activity-cancel). + silently doing something else (ARCHITECTURE sync-activity-cancel). """ if name is not None and dynamic: raise RuntimeError("Cannot provide name and dynamic boolean") @@ -215,7 +215,7 @@ class Info: class ActivityCancellationDetails: """The reasons for an activity's cancellation, mirroring ``temporalio.activity.ActivityCancellationDetails``. Accepted for parity; - dbosify never populates it (DEVIATIONS activity-cancel-details), so + dbosify never populates it (ARCHITECTURE activity-cancel-details), so :py:func:`cancellation_details` always returns ``None``.""" not_found: bool = False @@ -239,7 +239,7 @@ class _Context: # (workflow_run_id, seq) for real runs; None in ActivityEnvironment. attempt_key: Optional[Tuple[str, int]] = None # Head of the activity *outbound* interceptor chain, installed per attempt - # (DESIGN §6.8); ``info()``/``heartbeat()`` route through it. None in tests. + # ``info()``/``heartbeat()`` route through it. None in tests. outbound: Optional[Any] = None # A Temporal client set explicitly by ``ActivityEnvironment(client=...)``; # None on real worker runs, where ``client()`` uses ``worker_state`` below. @@ -590,7 +590,7 @@ def shield_thread_cancel_exception() -> Iterator[None]: In dbosify this is always a no-op: cancellation is delivered cooperatively (via ``is_cancelled()``/``heartbeat()``) and never raised into - a sync activity's worker thread (DEVIATIONS sync-activity-cancel), so there is nothing to + a sync activity's worker thread (ARCHITECTURE sync-activity-cancel), so there is nothing to shield against — matching temporalio's own no-op behavior for async and multiprocess activities. """ @@ -636,7 +636,7 @@ def _make_info(meta: dict[str, Any]) -> Info: if seq is not None: heartbeat_details = tuple(_heartbeat_store.get((run_id, int(seq)), ())) # An opaque structured token (no string separator is safe). On the queued - # path it carries the activity workflow id ("qwf") so completion routes there (§6.1.2). + # path it carries the activity workflow id ("qwf") so completion routes there. token: dict[str, Any] = { "run": run_id, "aid": str(meta.get("activity_id", "")), diff --git a/dbosify/client.py b/dbosify/client.py index a8a5e76..fd13458 100644 --- a/dbosify/client.py +++ b/dbosify/client.py @@ -5,7 +5,7 @@ Surface: ``start_workflow`` / ``execute_workflow`` / ``get_workflow_handle``, and ``WorkflowHandle`` with ``result``/``signal``/``query``/``execute_update``/ -``describe``/``cancel`` (cooperative, §6.5) / ``terminate`` (forceful). +``describe``/``cancel`` (cooperative) / ``terminate`` (forceful). Parameters not yet honored are accepted and ignored with a debug log. """ @@ -84,7 +84,7 @@ from ._internal.serializer import TEMPORAL_SERIALIZER from ._internal.status import WorkflowExecutionStatus -# Schedule types (DESIGN §6.7) live in _schedule.py and are re-exported here +# Schedule types live in _schedule.py and are re-exported here # to mirror temporalio.client's namespace. from ._schedule import ( # noqa: E402 Schedule, @@ -361,14 +361,14 @@ def __init__( ) -> None: self._client = client # The original addressing argument, re-used to rebuild this handle at - # the root of the outbound chain (DESIGN §6.8). + # the root of the outbound chain. self._id_or_token = id_or_token # Per-handle converter override for complete/fail/heartbeat encoding; it # rides on each *Input so it survives the chain-root handle rebuild. self._converter = data_converter_override self._workflow_id: Optional[str] = None self._run_id: Optional[str] = None - # On the queued path (§6.1.2) the completion goes to the activity + # On the queued path the completion goes to the activity # workflow's recv topic, not the parent run's inbox. self._queued_wf: Optional[str] = None if isinstance(id_or_token, bytes): @@ -880,7 +880,7 @@ def _execution_from_status( """Synthesize a :class:`WorkflowExecution` (or a subclass — ``describe()`` passes :class:`WorkflowExecutionDescription`) from a DBOS ``WorkflowStatus``. - The DBOS workflow id is the run id (decision §10.3); the Temporal workflow + The DBOS workflow id is the run id; the Temporal workflow id is its run-chain base. The DBOS workflow name is ``wf:{type}``. """ workflow_type = status.name or "" @@ -925,7 +925,7 @@ def _ignore_rpc_options( where: str, rpc_metadata: Mapping[str, Any], rpc_timeout: Optional[timedelta] ) -> None: """RPC transport options have no dbosify equivalent; accept and - debug-log them (DESIGN convention for tuning parameters).""" + debug-log them.""" if rpc_metadata: logger.debug("%s: ignoring rpc_metadata", where) if rpc_timeout is not None: @@ -1079,7 +1079,7 @@ class Client: Use :py:meth:`connect` — ``Client.connect(system_database_url, namespace=...)`` builds the underlying ``dbos.DBOSClient`` pointed at the - namespace's schema (DEVIATIONS no-server), so you state the namespace once and + namespace's schema (ARCHITECTURE no-server), so you state the namespace once and never touch ``dbos_system_schema``. For full control of the DBOSClient (custom engine/pool), build it yourself and use the constructor, where the DBOSClient's schema *is* the namespace. @@ -1094,7 +1094,7 @@ def __init__( default_workflow_query_reject_condition: Optional[QueryRejectCondition] = None, ) -> None: """Low-level constructor over a caller-built ``dbos.DBOSClient``. The - client's **namespace is its DBOSClient's schema** (DEVIATIONS no-server) — the + client's **namespace is its DBOSClient's schema** (ARCHITECTURE no-server) — the single source of truth — so build the DBOSClient with ``dbos_system_schema=namespace_schema()``, or just use :py:meth:`connect`, which takes a namespace and builds the DBOSClient @@ -1110,7 +1110,7 @@ def __init__( self._data_converter = data_converter self._default_query_reject_condition = default_workflow_query_reject_condition # Build the outbound interceptor chain: user interceptors fold (in - # reverse) over the root that performs the DBOS operations (DESIGN §6.8). + # reverse) over the root that performs the DBOS operations. impl: OutboundInterceptor = _ClientOutbound(self) for interceptor in reversed(interceptors): impl = interceptor.intercept_client(impl) @@ -1140,7 +1140,7 @@ async def connect( interceptors: Sequence[Interceptor] = [], default_workflow_query_reject_condition: Optional[QueryRejectCondition] = None, ) -> "Client": - """Connect to ``system_database_url`` in ``namespace`` (DEVIATIONS no-server). + """Connect to ``system_database_url`` in ``namespace`` (ARCHITECTURE no-server). Builds the underlying ``dbos.DBOSClient`` for you — pointed at the namespace's schema, with the JSON serializer — so the namespace is @@ -1232,7 +1232,7 @@ async def start_workflow( "priority": priority, "request_id": request_id, # PinnedVersioningOverride matches the enforced default; the - # auto-upgrade override has no DBOS analog (DEVIATIONS worker-versioning). + # auto-upgrade override has no DBOS analog (ARCHITECTURE worker-versioning). "versioning_override": versioning_override, **unsupported, }.items(): @@ -1273,7 +1273,7 @@ async def start_workflow( return await self._impl.start_workflow(input) async def _start_workflow_impl(self, input: StartWorkflowInput) -> "WorkflowHandle": - """Root of the ``start_workflow`` outbound chain (DESIGN §6.8): the + """Root of the ``start_workflow`` outbound chain: the actual enqueue, reading the (possibly interceptor-modified) input.""" workflow_args = input.args type_name = input.workflow @@ -1313,7 +1313,7 @@ async def _start_workflow_impl(self, input: StartWorkflowInput) -> "WorkflowHand # created now but fires at the next occurrence (first-task backoff). _schedules.validate_cron(cron_schedule) if start_delay is not None: - # DEVIATION (DEVIATIONS cron-chains): cron uses the enqueue delay + # DEVIATION (ARCHITECTURE cron-chains): cron uses the enqueue delay # to back off run 0, so a user start_delay can't ride alongside. raise ValueError( "start_delay cannot be used together with cron_schedule" @@ -1333,7 +1333,7 @@ async def _start_workflow_impl(self, input: StartWorkflowInput) -> "WorkflowHand # into the run as ExecuteWorkflowInput.headers. meta.headers = (await conversion.encode_headers(input.headers)) or None - # Messages to deliver with the start (DEVIATIONS start-policies): start + # Messages to deliver with the start (ARCHITECTURE start-policies): start # signal and/or update, built before conflict resolution to ride either path. with_start_msgs: List[Tuple[Any, str, Optional[str]]] = [] if start_signal is not None: @@ -1357,7 +1357,7 @@ async def _start_workflow_impl(self, input: StartWorkflowInput) -> "WorkflowHand current_index, current_status = current if _status.is_open(current_status.status): # Conflict policies (vs a RUNNING run). There is an inherent - # TOCTOU window here, accepted for v1 (DESIGN §6.4). + # TOCTOU window here, accepted for v1. if id_conflict_policy == WorkflowIDConflictPolicy.USE_EXISTING: # Attaching to the running run: deliver the with-start # messages to it (there is no enqueue to bundle with). @@ -1536,7 +1536,7 @@ def get_workflow_handle_for( ) # ------------------------------------------------------------------ - # Visibility (DESIGN §6.2) + # Visibility # ------------------------------------------------------------------ def list_workflows( @@ -1555,7 +1555,7 @@ def list_workflows( until the first iteration, so a bad query raises on first ``__anext__``. Each run-chain link (continue-as-new / workflow-retry / cron hop) is a - separate row, keyed by its run id (decision §10.3). + separate row, keyed by its run id. """ _ignore_rpc_options("list_workflows", rpc_metadata, rpc_timeout) return WorkflowExecutionAsyncIterator( @@ -1655,7 +1655,7 @@ async def start_update_with_start_workflow( typically USE_EXISTING) and send it an update, waiting for ``wait_for_stage``. The update rides the start: on a *fresh* run the start enqueue and the update request commit in one system-database - transaction (Temporal's atomic update-with-start, DEVIATIONS.md start-policies); on + transaction (Temporal's atomic update-with-start, ARCHITECTURE.md start-policies); on a USE_EXISTING attach it is sent to the already-running run as part of the start. The request is routed through the update outbound interceptors first, so their modifications apply on both paths. @@ -1729,7 +1729,7 @@ def get_async_activity_handle( return AsyncActivityHandle(self, (workflow_id, run_id, activity_id)) # ------------------------------------------------------------------ - # Schedules (DESIGN §6.7) + # Schedules # ------------------------------------------------------------------ async def create_schedule( @@ -1744,7 +1744,7 @@ async def create_schedule( rpc_metadata: Mapping[str, Any] = {}, rpc_timeout: Optional[timedelta] = None, ) -> ScheduleHandle: - """Create a schedule and return its handle (DESIGN §6.7). + """Create a schedule and return its handle. The schedule's ``ScheduleSpec`` is compiled to a cron expression and backed by a DBOS schedule that fires a generic dispatcher; ``memo`` and @@ -2011,7 +2011,7 @@ async def result( cause = deserialize_failure(failure.envelope) raise WorkflowFailureError(cause=cause) from cause except dbos_error.DBOSAwaitedWorkflowCancelledError: - # Native DBOS cancel == terminate in our scheme (§6.5). + # Native DBOS cancel == terminate in our scheme. terminated = exceptions.TerminatedError("Workflow terminated") raise WorkflowFailureError(cause=terminated) from terminated except Exception as err: @@ -2093,7 +2093,7 @@ async def _query_impl(self, input: QueryWorkflowInput) -> Any: input.reject_condition or self._client._default_query_reject_condition ) # Read status directly (not describe(), to skip its interceptor): gates - # reject_condition, picks the rehydrate path. DEVIATIONS start-policies. + # reject_condition, picks the rehydrate path. ARCHITECTURE start-policies. try: target = await self._target() raw = await self._client._status_of(target) @@ -2131,7 +2131,7 @@ async def _query_impl(self, input: QueryWorkflowInput) -> Any: reply = await self._rehydrate_query(target, envelope, request_id, timeout) else: # TERMINATED/TIMED_OUT/CONTINUED_AS_NEW can't be faithfully replayed - # to a queryable final state — fail clearly (DEVIATIONS replay). + # to a queryable final state — fail clearly (ARCHITECTURE replay). raise WorkflowQueryFailedError( f"cannot query a workflow in state {status.name}: rehydrate-by-" "replay supports COMPLETED/FAILED/CANCELED runs only" @@ -2491,7 +2491,7 @@ async def cancel( rpc_metadata: Mapping[str, Any] = {}, rpc_timeout: Optional[timedelta] = None, ) -> None: - """Request cooperative cancellation (§6.5): the workflow's primary + """Request cooperative cancellation: the workflow's primary coroutine gets CancelledError at its next event boundary; cleanup code runs and may still execute activities. The workflow may also swallow the cancel and complete normally. Raises if the targeted @@ -2529,7 +2529,7 @@ async def terminate( rpc_metadata: Mapping[str, Any] = {}, rpc_timeout: Optional[timedelta] = None, ) -> None: - """Forcefully terminate (§6.5): native DBOS cancellation. No + """Forcefully terminate: native DBOS cancellation. No workflow code runs; status becomes TERMINATED. ``reason``/details are accepted but not stored (DBOS cancellation has no reason field). """ @@ -2588,7 +2588,7 @@ async def _terminate_impl(self, input: TerminateWorkflowInput) -> None: class _ClientOutbound(OutboundInterceptor): - """Root of the client outbound interceptor chain (DESIGN §6.8): performs + """Root of the client outbound interceptor chain: performs the actual DBOS operations, reading the (possibly interceptor-modified) ``*Input``. Verbs whose work lives on a handle reconstruct the handle from the input's identity and call its ``__impl``; the rest delegate to diff --git a/dbosify/common.py b/dbosify/common.py index f48f24b..976ff20 100644 --- a/dbosify/common.py +++ b/dbosify/common.py @@ -542,7 +542,7 @@ def _warn_on_deprecated_search_attributes( # Worker versioning / deployments: a "deployment version" maps onto DBOS versioning -# (enforces PINNED; AUTO_UPGRADE has none — see DEVIATIONS worker-versioning). +# (enforces PINNED; AUTO_UPGRADE has none — see ARCHITECTURE worker-versioning). class VersioningBehavior(IntEnum): @@ -551,7 +551,7 @@ class VersioningBehavior(IntEnum): ``PINNED`` is dbosify's enforced behavior (DBOS pins recovery/dequeue to ``application_version`` = the build ID). ``AUTO_UPGRADE`` has no DBOS - analog and degrades to pinned. See DEVIATIONS worker-versioning. + analog and degrades to pinned. See ARCHITECTURE worker-versioning. """ UNSPECIFIED = 0 @@ -599,7 +599,7 @@ class VersioningOverride(ABC): execution, mirroring ``temporalio.common.VersioningOverride``. ``PinnedVersioningOverride`` matches dbosify's enforced default; - ``AutoUpgradeVersioningOverride`` has no DBOS analog (DEVIATIONS worker-versioning). + ``AutoUpgradeVersioningOverride`` has no DBOS analog (ARCHITECTURE worker-versioning). """ diff --git a/dbosify/converter.py b/dbosify/converter.py index e20bbde..8de583a 100644 --- a/dbosify/converter.py +++ b/dbosify/converter.py @@ -1,4 +1,4 @@ -"""Data conversion, mirroring ``temporalio.converter`` (DESIGN §6.9). +"""Data conversion, mirroring ``temporalio.converter``. This converts Python values to/from encoding-tagged :py:class:`Payload` records and (optionally) runs a :py:class:`PayloadCodec` over the payload @@ -592,7 +592,7 @@ def value_to_type( return hint(**field_values) # Pydantic v1 model (``parse_obj``). Pydantic v2 models need a custom - # ``DataConverter`` (the default converter doesn't support them; DESIGN §6.9). + # ``DataConverter`` (the default converter doesn't support them). parse_obj_attr = inspect.getattr_static(hint, "parse_obj", None) if isinstance(parse_obj_attr, (classmethod, staticmethod)): if not isinstance(value, dict): diff --git a/dbosify/testing/__init__.py b/dbosify/testing/__init__.py index 2810cbc..def6323 100644 --- a/dbosify/testing/__init__.py +++ b/dbosify/testing/__init__.py @@ -4,7 +4,7 @@ :py:class:`WorkflowEnvironment.start_local` provisions an isolated, throwaway database on an externally provided Postgres server (there is no dev server to download — Postgres *is* the server). Time-skipping is out of -scope (see DESIGN §6.10). +scope. """ import asyncio diff --git a/dbosify/worker.py b/dbosify/worker.py index 1900b85..fbdc9e9 100644 --- a/dbosify/worker.py +++ b/dbosify/worker.py @@ -102,7 +102,7 @@ logger = logging.getLogger("dbosify.worker") # A stable default DBOS application version. Pinning a constant (vs DBOS's code-hash -# auto-version) lets workers agree and deploys preserve in-flight work (DESIGN §6.8). +# auto-version) lets workers agree and deploys preserve in-flight work. DEFAULT_APP_VERSION = "0.1" # Default DBOS application name when the Worker is given only a Postgres URL. @@ -111,7 +111,7 @@ # Behavior-changing AND unsupported options: passing a non-default value raises # rather than silently no-ops. arg name -> hint; arrive via ``**unsupported``. _REJECTED_OPTIONS = { - "nexus_service_handlers": "Nexus is not supported (DESIGN §1)", + "nexus_service_handlers": "Nexus is not supported", "tuner": "resource-based slot tuning has no DBOS analog; use " "max_concurrent_workflow_tasks / max_concurrent_activities", "plugins": "Worker plugins are not supported; use interceptors=", @@ -151,7 +151,7 @@ def _normalize_config(config: str | DBOSConfig) -> DBOSConfig: A bare string is a Postgres URL: expand it into a minimal config under the default application name, pointed at that system database. A ``DBOSConfig`` is taken as given. Either way the DBOS admin server is forced off — DBOSify - exposes DBOS's management APIs, not the admin HTTP port (DESIGN §1), and an + exposes DBOS's management APIs, not the admin HTTP port, and an always-on port collides when workers share a host. """ if isinstance(config, str): @@ -190,7 +190,7 @@ class WorkerDeploymentConfig: The ``version.build_id`` becomes the DBOS ``application_version``, which DBOS uses to pin workflow recovery/dequeue — i.e. Temporal's PINNED behavior, enforced. ``default_versioning_behavior`` / ``use_worker_versioning`` are - accepted for parity; AUTO_UPGRADE has no DBOS analog (DEVIATIONS worker-versioning). + accepted for parity; AUTO_UPGRADE has no DBOS analog (ARCHITECTURE worker-versioning). """ version: WorkerDeploymentVersion @@ -237,7 +237,7 @@ def __init__( system database) or a full ``dbos.DBOSConfig``. A URL is expanded into a minimal config under a default application name; pass a ``DBOSConfig`` for full control (custom engine, executor, telemetry, ...). The DBOS - admin server is disabled regardless (DESIGN §1). + admin server is disabled regardless. ``build_id`` / ``deployment_config`` set the worker's deployment version (surfaced via ``workflow.Info.get_current_deployment_version()``). The @@ -246,7 +246,7 @@ def __init__( and continued only on workers of its build ID. That pinning *is* Temporal's PINNED versioning behavior, enforced. What DBOS has no analog for is AUTO_UPGRADE (moving a running workflow to a newer version) and - the cluster routing-fleet / ramping concepts; see DEVIATIONS worker-versioning. + the cluster routing-fleet / ramping concepts; see ARCHITECTURE worker-versioning. ``use_worker_versioning`` is accepted for parity. When neither build_id nor deployment_config is given, the deployment version is derived from the DBOS application name + application_version. @@ -315,7 +315,7 @@ def __init__( # The interpreter decodes run args / encodes results with this # converter; configure the Client the same. conversion.set_converter(data_converter) - # The namespace owns the DBOS system schema (DEVIATIONS no-server); a + # The namespace owns the DBOS system schema (ARCHITECTURE no-server); a # conflicting explicit dbos_system_schema is an error. schema = namespace_schema(namespace) configured_schema = config.get("dbos_system_schema") @@ -337,7 +337,7 @@ def __init__( if identity is not None: config = {**config, "executor_id": identity} # An explicit build_id / deployment_config IS the DBOS application_version - # (DEVIATIONS worker-versioning); without one, pin DEFAULT_APP_VERSION. + # (ARCHITECTURE worker-versioning); without one, pin DEFAULT_APP_VERSION. explicit_build = ( deployment_config.version.build_id if deployment_config is not None diff --git a/dbosify/workflow.py b/dbosify/workflow.py index c4aa36b..9f32515 100644 --- a/dbosify/workflow.py +++ b/dbosify/workflow.py @@ -254,7 +254,7 @@ class ContinueAsNewVersioningBehavior(IntEnum): A continue-as-new run is a fresh DBOS workflow enqueued by the current worker, so it takes that worker's build ID (pinned). ``AUTO_UPGRADE`` / - ``USE_RAMPING_VERSION`` have no DBOS analog (DEVIATIONS worker-versioning). + ``USE_RAMPING_VERSION`` have no DBOS analog (ARCHITECTURE worker-versioning). """ UNSPECIFIED = 0 @@ -297,11 +297,11 @@ def defn( ``versioning_behavior`` is accepted and stored. ``PINNED`` is what dbosify enforces anyway (DBOS pins recovery/dequeue to the build ID = ``application_version``); ``AUTO_UPGRADE`` has no DBOS analog and degrades to - pinned (DEVIATIONS worker-versioning). + pinned (ARCHITECTURE worker-versioning). ``dynamic`` is **not supported**: a catch-all workflow has no ``wf:{type}`` registration to dispatch to, which conflicts with the - one-DBOS-workflow-per-type model (DESIGN §10.1, DEVIATIONS dynamic-handlers). Passing + one-DBOS-workflow-per-type model (ARCHITECTURE dynamic-handlers). Passing ``dynamic=True`` raises ``NotImplementedError``. (Dynamic *signal/query/ update* handlers and dynamic *activities* are supported.) """ @@ -649,10 +649,10 @@ class Info: continued_run_id: Optional[str] = None cron_schedule: Optional[str] = None # Whole-execution (run-chain) timeout: accepted but not enforced - # (DEVIATIONS start-params). Surfaced for parity; always None. + # (ARCHITECTURE start-params). Surfaced for parity; always None. execution_timeout: Optional[timedelta] = None # The run id of the first execution in this run chain (run 0's DBOS id = - # the Temporal workflow id). Derived from our run-chain id scheme (§6.4). + # the Temporal workflow id). Derived from our run-chain id scheme. first_execution_run_id: str = "" # The run's interceptor headers, decoded to Payloads (the same mapping # surfaced to workflow interceptors as ExecuteWorkflowInput.headers). @@ -661,7 +661,7 @@ class Info: # The parent workflow, when started cross-chain as a child; None otherwise. parent: Optional[ParentInfo] = None # The root workflow of this run's tree; None for a top-level workflow (which - # is itself the root). Threaded through child starts (§6.6). + # is itself the root). Threaded through child starts. root: Optional[RootInfo] = None # Priority is accepted-and-inert (DBOS queues are FIFO); always the default # instance, as temporalio returns for an unset priority. @@ -706,7 +706,7 @@ def is_continue_as_new_suggested(self) -> bool: def get_current_build_id(self) -> str: """The build id of the worker executing this run — the DBOS - ``application_version`` DBOS pins recovery/dequeue to (DEVIATIONS worker-versioning). + ``application_version`` DBOS pins recovery/dequeue to (ARCHITECTURE worker-versioning). Empty string when no worker deployment version is set. .. warning:: @@ -726,7 +726,7 @@ def get_current_deployment_version(self) -> Optional[WorkerDeploymentVersion]: name = DBOS application/deployment name, build id = the DBOS ``application_version`` DBOS pins recovery/dequeue to). None when no worker deployment version is set (e.g. the in-process dispatcher - harness). DEVIATIONS worker-versioning. + harness). ARCHITECTURE worker-versioning. .. warning:: Read *live* from the executing worker, so it is **not replay-stable** @@ -738,7 +738,7 @@ def get_current_deployment_version(self) -> Optional[WorkerDeploymentVersion]: def is_target_worker_deployment_version_changed(self) -> bool: """Whether the target worker deployment version has changed (upgrade-on-continue-as-new). Always False in dbosify: workflows - are pinned to their build id and never auto-upgrade (DEVIATIONS worker-versioning). + are pinned to their build id and never auto-upgrade (ARCHITECTURE worker-versioning). """ return False @@ -1137,7 +1137,7 @@ def get_current_details() -> str: the life of the workflow via :py:func:`set_current_details`. It is in-memory workflow state — reconstructed deterministically on recovery by replaying the same :py:func:`set_current_details` calls — and is not surfaced to - ``describe()``/``list_workflows`` in v1 (DEVIATIONS current-details). Empty string if + ``describe()``/``list_workflows`` in v1 (ARCHITECTURE current-details). Empty string if never set. """ return _runtime().runtime_get_current_details() @@ -1250,7 +1250,7 @@ def register_random_seed_callback(callback: Callable[[int], None]) -> None: """Register a callback invoked when the workflow's random seed changes, mirroring ``temporalio.workflow.register_random_seed_callback``. In dbosify the seed is fixed for a run's lifetime (it never changes - mid-run), so the callback is stored but never invoked (DEVIATIONS random-seed).""" + mid-run), so the callback is stored but never invoked (ARCHITECTURE random-seed).""" _runtime().runtime_register_random_seed_callback(callback) @@ -1289,7 +1289,7 @@ def patched(id: str) -> bool: which means this is either not replaying or is replaying and has seen this patch before. - Backed by a durable checkpoint marker (DESIGN §6.8): the first non-replaying + Backed by a durable checkpoint marker: the first non-replaying execution records the marker and takes the newer path; a run replaying history that predates the patch finds no marker and takes the older path. @@ -1377,7 +1377,7 @@ def start_activity( ``TimeoutType.HEARTBEAT`` and retries), ``retry_policy``, ``cancellation_type``, ``activity_id``, ``task_queue`` (when it differs from the workflow's own queue the activity runs on another worker via the - cross-queue path, §6.1.2), and ``schedule_to_start_timeout`` (bounds the + cross-queue path), and ``schedule_to_start_timeout`` (bounds the queue dwell on the cross-queue path; a no-op on the local path, which has no queue wait); the remaining parameters are accepted and ignored (debug-logged). ``result_type``, when given, is the type hint used to reconstruct the @@ -1647,7 +1647,7 @@ def continue_as_new( ``versioning_intent``/``initial_versioning_behavior`` are accepted for parity; the new run is pinned to the enqueuing worker's build ID, and the - auto-upgrade/ramping variants have no DBOS analog (DEVIATIONS worker-versioning). + auto-upgrade/ramping variants have no DBOS analog (ARCHITECTURE worker-versioning). """ if _runtime().runtime_is_read_only(): raise ReadOnlyContextError("Cannot continue-as-new in a read-only context") @@ -1727,7 +1727,7 @@ async def start_child_workflow( parent_close_policy, cancellation_type, and (matching top-level starts) ``run_timeout`` and ``retry_policy``. ``cron_schedule`` and ``id_reuse_policy`` are accepted-but-pending for children - (DEVIATIONS start-params); the remaining parameters are accepted and ignored + (ARCHITECTURE start-params); the remaining parameters are accepted and ignored (debug-logged). """ for key, value in { @@ -2006,7 +2006,7 @@ def start_local_activity( summary: Optional[str] = None, ) -> ActivityHandle: """Start a local activity. In dbosify, in-process step execution - *is* the local path (DESIGN §6.1.2), so this shares machinery with + *is* the local path, so this shares machinery with ``start_activity``. """ if not start_to_close_timeout and not schedule_to_close_timeout: @@ -2211,7 +2211,7 @@ def is_replaying() -> bool: @staticmethod def imports_passed_through() -> "AbstractContextManager[None]": """No-op context manager: there is no sandbox to pass imports - through (DEVIATIONS.md no-sandbox).""" + through (ARCHITECTURE.md no-sandbox).""" return nullcontext() @staticmethod diff --git a/tests/conformance/activity_worker_workers.py b/tests/conformance/activity_worker_workers.py index 3262062..f2c565f 100644 --- a/tests/conformance/activity_worker_workers.py +++ b/tests/conformance/activity_worker_workers.py @@ -6,7 +6,7 @@ ``execute_workflow("say-hello-workflow", ...)`` targets a workflow registered only in Go). This helper proves the capability the directory demonstrates — an **activity-only** Python worker reachable from a workflow on a **different task -queue** (the cross-queue / distributed activity path, DESIGN §6.1.2). +queue** (the cross-queue / distributed activity path). The Go ``say-hello-workflow`` is substituted by ``SayHelloWorkflow`` (the documented migration delta); the activity is the sample's ``say_hello_activity`` diff --git a/tests/conformance/runner.py b/tests/conformance/runner.py index e43c555..863f15f 100644 --- a/tests/conformance/runner.py +++ b/tests/conformance/runner.py @@ -62,7 +62,7 @@ def install_shim() -> None: from dbosify.client import Client from dbosify.worker import Worker - # The default namespace maps to its own DBOS schema (DEVIATIONS no-server); the + # The default namespace maps to its own DBOS schema (ARCHITECTURE no-server); the # client must use it, and the Worker derives the same from namespace="default". schema = namespace_schema(DEFAULT_NAMESPACE) @@ -105,7 +105,7 @@ async def patched_connect(cls: Any, *args: Any, **kwargs: Any) -> Any: Client.connect = classmethod(patched_connect) # type: ignore[assignment, method-assign] # -- dbosify.api.common.v1 stand-in: register the module chain so samples' - # annotation-only Payload imports resolve. Protobuf API is a non-goal (DEVIATIONS no-server). + # annotation-only Payload imports resolve. Protobuf API is a non-goal (ARCHITECTURE no-server). import dbosify as _dbosify_pkg from dbosify import converter as _dbosify_converter diff --git a/tests/conformance/sdk_harness.py b/tests/conformance/sdk_harness.py index 1153590..04d5148 100644 --- a/tests/conformance/sdk_harness.py +++ b/tests/conformance/sdk_harness.py @@ -39,7 +39,7 @@ async def new_worker( ``client`` already targets the same database. ``data_converter``/``interceptors`` are forwarded to the Worker. The - converter is process-global (DESIGN §6.9), so configuring it on the Worker + converter is process-global, so configuring it on the Worker also applies to the ``client`` for the duration of the test.""" worker = Worker( default_config(), @@ -96,7 +96,7 @@ async def run(self) -> None: async def warm_schema(client: Client) -> None: """Migrate the namespace schema before a client-before-worker test issues - start/describe/update calls. Our Worker owns schema creation (DESIGN §5), so + start/describe/update calls. Our Worker owns schema creation, so on a freshly-dropped test database the schema does not exist until a Worker launches; production always has it pre-migrated. Launching and immediately stopping a throwaway worker creates the schema (it persists), faithfully diff --git a/tests/conformance/test_activity_worker.py b/tests/conformance/test_activity_worker.py index d563e74..66c690c 100644 --- a/tests/conformance/test_activity_worker.py +++ b/tests/conformance/test_activity_worker.py @@ -1,10 +1,10 @@ -"""Conformance: the samples-python ``activity_worker/`` corpus (DESIGN §9). +"""Conformance: the samples-python ``activity_worker/`` corpus. That corpus is a single **cross-language** sample (a Go workflow calling a Python activity over a Temporal server), so it cannot run as written against a serverless, in-process model. We prove the capability it demonstrates: an **activities-only** worker reachable from a workflow on a **different task -queue** — the cross-queue / distributed activity path (DESIGN §6.1.2). The Go +queue** — the cross-queue / distributed activity path. The Go workflow is substituted by a Python ``SayHelloWorkflow`` (the documented migration delta); the activity body is the sample's ``say_hello_activity`` verbatim. See ``activity_worker_workers.py``. diff --git a/tests/conformance/test_feature_samples.py b/tests/conformance/test_feature_samples.py index d0362b6..6e7b396 100644 --- a/tests/conformance/test_feature_samples.py +++ b/tests/conformance/test_feature_samples.py @@ -101,24 +101,23 @@ def starter(self) -> str: package="custom_converter", skip="sample's PayloadConverter is built on protobuf " "temporalio.api.common.v1.Payload; our Payload is a lightweight dict and " - "protobuf payloads are a non-goal (DEVIATIONS no-server)", + "protobuf payloads are a non-goal (ARCHITECTURE no-server)", ), "encryption": Sample( package="encryption", skip="EncryptionCodec serializes protobuf Payloads (.SerializeToString); " "our PayloadCodec operates on a lightweight Payload, and protobuf payloads " - "are a non-goal (DEVIATIONS no-server)", + "are a non-goal (ARCHITECTURE no-server)", ), "worker_specific_task_queues": Sample( package="worker_specific_task_queues", - skip="runs two Workers in one process; dbosify is one Worker per " - "process (DESIGN §5)", + skip="runs two Workers in one process; dbosify is one Worker per " "process", ), # ---- skip: not runnable in this harness --------------------------------- "sleep_for_days": Sample( package="sleep_for_days", skip="workflow sleeps timedelta(days=30) with no auto-complete; needs a " - "time-skipping WorkflowEnvironment (out of scope — DESIGN §9)", + "time-skipping WorkflowEnvironment (out of scope)", ), "polling_infrequent": Sample( package="polling", @@ -136,68 +135,67 @@ def starter(self) -> str: "harness rewrites imports but does not install per-sample deps", ), # ---- skip: sample depends on a non-goal/unsupported Temporal feature (coverage gaps) ---- - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "hello_nexus": Sample( - package="hello_nexus", skip="Nexus is a non-goal (DEVIATIONS no-server)" + package="hello_nexus", skip="Nexus is a non-goal (ARCHITECTURE no-server)" ), "nexus_cancel": Sample( - package="nexus_cancel", skip="Nexus is a non-goal (DEVIATIONS no-server)" + package="nexus_cancel", skip="Nexus is a non-goal (ARCHITECTURE no-server)" ), "nexus_messaging": Sample( - package="nexus_messaging", skip="Nexus is a non-goal (DEVIATIONS no-server)" + package="nexus_messaging", skip="Nexus is a non-goal (ARCHITECTURE no-server)" ), "nexus_multiple_args": Sample( - package="nexus_multiple_args", skip="Nexus is a non-goal (DEVIATIONS no-server)" + package="nexus_multiple_args", + skip="Nexus is a non-goal (ARCHITECTURE no-server)", ), - # Metrics + telemetry runtime — not implemented (DEVIATIONS no-metrics). + # Metrics + telemetry runtime — not implemented (ARCHITECTURE no-metrics). "custom_metric": Sample( package="custom_metric", - skip="metrics are not implemented (DEVIATIONS no-metrics)", + skip="metrics are not implemented (ARCHITECTURE no-metrics)", ), "prometheus": Sample( package="prometheus", - skip="metrics / Prometheus telemetry not implemented (DEVIATIONS no-metrics)", + skip="metrics / Prometheus telemetry not implemented (ARCHITECTURE no-metrics)", ), "open_telemetry": Sample( package="open_telemetry", - skip="OpenTelemetry metrics/tracing runtime not implemented (DEVIATIONS no-metrics)", + skip="OpenTelemetry metrics/tracing runtime not implemented (ARCHITECTURE no-metrics)", ), - # Client-initiated (standalone) activities — unsupported (DEVIATIONS no-client-activities). + # Client-initiated (standalone) activities — unsupported (ARCHITECTURE no-client-activities). "hello_standalone_activity": Sample( package="hello_standalone_activity", - skip="client-initiated standalone activities are unsupported (DEVIATIONS no-client-activities)", + skip="client-initiated standalone activities are unsupported (ARCHITECTURE no-client-activities)", ), - # Pydantic converter — not provided; configure a custom DataConverter (§6.9). + # Pydantic converter — not provided; configure a custom DataConverter. "pydantic_converter": Sample( package="pydantic_converter", - skip="contrib.pydantic is not provided; configure a custom DataConverter " - "(DESIGN §6.9)", + skip="contrib.pydantic is not provided; configure a custom DataConverter", ), "pydantic_converter_v1": Sample( package="pydantic_converter_v1", - skip="contrib.pydantic is not provided; configure a custom DataConverter " - "(DESIGN §6.9)", + skip="contrib.pydantic is not provided; configure a custom DataConverter", ), - # External payload storage — not implemented (DESIGN §6.9). + # External payload storage — not implemented. "external_storage": Sample( package="external_storage", - skip="external payload storage is not implemented (DESIGN §6.9)", + skip="external payload storage is not implemented", ), "external_storage_redis": Sample( package="external_storage_redis", - skip="external payload storage (redis) is not implemented (DESIGN §6.9)", + skip="external payload storage (redis) is not implemented", ), - # Multiprocess activity worker — one Worker per process (DESIGN §5). + # Multiprocess activity worker — one Worker per process. "worker_multiprocessing": Sample( package="worker_multiprocessing", - skip="multiprocess activity executors are unsupported (DESIGN §5)", + skip="multiprocess activity executors are unsupported", ), # Worker deployment versioning walkthrough — protobuf + 3 version-workers + - # AUTO_UPGRADE; we enforce PINNED only (DEVIATIONS worker-versioning). + # AUTO_UPGRADE; we enforce PINNED only (ARCHITECTURE worker-versioning). "worker_versioning": Sample( package="worker_versioning", skip="multi-version-worker walkthrough using protobuf + AUTO_UPGRADE; we " - "support PINNED only (DEVIATIONS worker-versioning)", + "support PINNED only (ARCHITECTURE worker-versioning)", ), # ---- skip: sample structure / non-Temporal dependency makes it un-runnable here ---- # Patching across worker code versions (like hello_patch) — not a single run. @@ -212,9 +210,9 @@ def starter(self) -> str: "replay": Sample( package="replay", # Our Replayer re-executes checkpoints through a running Worker's DBOS runtime, - # so it needs a Worker for the types (DEVIATIONS replay), unlike temporalio's. + # so it needs a Worker for the types (ARCHITECTURE replay), unlike temporalio's. skip="our Replayer needs a Worker for the types in the process; the " - "sample's replayer.py is client-only (DEVIATIONS replay)", + "sample's replayer.py is client-only (ARCHITECTURE replay)", ), # Eager workflow start — a server-side optimization (inert, no server); the # sample also reads the temporalio-internal __temporal_eagerly_started flag. diff --git a/tests/conformance/test_message_passing.py b/tests/conformance/test_message_passing.py index b26ef89..7ce339b 100644 --- a/tests/conformance/test_message_passing.py +++ b/tests/conformance/test_message_passing.py @@ -1,4 +1,4 @@ -"""Conformance: the samples-python ``message_passing/`` corpus (DESIGN §9). +"""Conformance: the samples-python ``message_passing/`` corpus. Unlike ``hello/``, these are multi-file packages run as two processes: a worker (runs until interrupted) and a starter (drives the workflow and diff --git a/tests/conformance/test_schedules.py b/tests/conformance/test_schedules.py index 78c1c19..1b5edf2 100644 --- a/tests/conformance/test_schedules.py +++ b/tests/conformance/test_schedules.py @@ -1,4 +1,4 @@ -"""Conformance: the samples-python ``schedules/`` corpus (DESIGN §9). +"""Conformance: the samples-python ``schedules/`` corpus. Unlike ``hello/`` and ``message_passing/``, this corpus is a long-running worker (``run_worker.py``) plus a series of independent operation scripts diff --git a/tests/conformance/test_sdk_data_conversion.py b/tests/conformance/test_sdk_data_conversion.py index 5e46c50..012608b 100644 --- a/tests/conformance/test_sdk_data_conversion.py +++ b/tests/conformance/test_sdk_data_conversion.py @@ -3,7 +3,7 @@ temporalio rebuilds the client via ``client.config()`` to install a codec; we forward the ``DataConverter`` to ``new_worker`` instead — the converter is -process-global (DESIGN §6.9), so it applies to the client too. Server-only log +process-global, so it applies to the client too. Server-only log assertions (e.g. "Completing activity as failed") are dropped; the behavioral core (codec round-trips, error category survives, an interceptor may register a signal handler in ``init()``) is kept. diff --git a/tests/conformance/test_sdk_workflow.py b/tests/conformance/test_sdk_workflow.py index 5559415..455e47c 100644 --- a/tests/conformance/test_sdk_workflow.py +++ b/tests/conformance/test_sdk_workflow.py @@ -585,7 +585,7 @@ async def child(id: str) -> None: @pytest.mark.skip( - reason="DEVIATIONS sync-activity-cancel/activity-cancel-details (cooperative cancellation): a cancelled `wait_cancel` " + reason="ARCHITECTURE sync-activity-cancel/activity-cancel-details (cooperative cancellation): a cancelled `wait_cancel` " "activity catches asyncio.CancelledError and *returns a value*, so our model " "records it as a successful completion rather than ActivityError(CancelledError). " "Temporal's hard cancellation discards the late result. The 4-way " @@ -648,7 +648,7 @@ async def run(self) -> str: async def test_workflow_cancel_before_run(client: Client) -> None: # Start the workflow and cancel it before the worker exists; warm_schema - # pre-migrates the namespace schema (our Worker owns schema creation, DESIGN §5). + # pre-migrates the namespace schema (our Worker owns schema creation). await warm_schema(client) task_queue = str(uuid.uuid4()) handle = await client.start_workflow( @@ -942,7 +942,7 @@ async def test_workflow_signal_and_query_errors(client: Client) -> None: @pytest.mark.skip( - reason="DEVIATIONS dynamic-handler-signature: the legacy `(name, *args)` dynamic-handler signature " + reason="ARCHITECTURE dynamic-handler-signature: the legacy `(name, *args)` dynamic-handler signature " "is not supported; we require `(name, args: Sequence[RawValue])`. The " "new-style equivalent is covered by test_workflow_signal_and_query. The " "workflow can't even be defined (registration rejects the signature), so " @@ -1092,7 +1092,7 @@ async def run(self) -> None: @pytest.mark.skip( - reason="DEVIATIONS replay: this test queries a *completed* workflow after the " + reason="ARCHITECTURE replay: this test queries a *completed* workflow after the " "worker's registered code for that type name has been swapped (PrePatch -> " "Patch). Queries on closed runs rehydrate-by-replay under the currently-" "registered code; when that code differs from what the run executed, our " @@ -1892,7 +1892,7 @@ def query_sync(self, param: MyDataClass) -> MyDataClass: return param # temporalio declares this async (a deprecated form); we require sync query - # handlers (DEVIATIONS sync-queries), so it is a normal def here. + # handlers (ARCHITECTURE sync-queries), so it is a normal def here. @workflow.query def query_async(self, param: MyDataClass) -> MyDataClass: return param diff --git a/tests/dbconfig.py b/tests/dbconfig.py index b76ef37..ea01cd8 100644 --- a/tests/dbconfig.py +++ b/tests/dbconfig.py @@ -27,7 +27,7 @@ TEST_SYSTEM_DB_NAME = "dbosify_test_dbos_sys" -# Every namespace maps to its own DBOS system schema (DEVIATIONS no-server). +# Every namespace maps to its own DBOS system schema (ARCHITECTURE no-server). # Tests run in the default namespace; all components must agree on its schema. TEST_SCHEMA = namespace_schema(DEFAULT_NAMESPACE) @@ -56,7 +56,7 @@ def default_config() -> DBOSConfig: # JSON transport (matches the Worker/Client and the raw-DBOS test # workers that build on default_config()). "serializer": TEMPORAL_SERIALIZER, - # Default-namespace schema (DEVIATIONS no-server); raw-DBOS test drivers + # Default-namespace schema (ARCHITECTURE no-server); raw-DBOS test drivers # that launch on this config land in the same schema as the product. "dbos_system_schema": TEST_SCHEMA, } diff --git a/tests/integration/cross_queue_async_worker.py b/tests/integration/cross_queue_async_worker.py index f45e978..a08b966 100644 --- a/tests/integration/cross_queue_async_worker.py +++ b/tests/integration/cross_queue_async_worker.py @@ -1,5 +1,5 @@ """Subprocess worker for cross-queue async-completion tests -(raise_complete_async on the queued path, §6.1.2). +(raise_complete_async on the queued path). The activity records its ``info().task_token`` (which carries the activity workflow id) and parks via ``raise_complete_async()``; the ``__temporal_activity`` diff --git a/tests/integration/cross_queue_cancel_worker.py b/tests/integration/cross_queue_cancel_worker.py index c92751c..2922989 100644 --- a/tests/integration/cross_queue_cancel_worker.py +++ b/tests/integration/cross_queue_cancel_worker.py @@ -1,5 +1,5 @@ -"""Subprocess worker for cross-queue activity *cancellation* tests -(§6.1.2). The activity runs on a different worker than the workflow, so +"""Subprocess worker for cross-queue activity *cancellation* tests. +The activity runs on a different worker than the workflow, so cancellation must reach it cross-process: the interpreter sets a checkpointed cancel event, the activity's attempt step polls it on the other worker and delivers an ``asyncio.CancelledError`` into the (async) activity. diff --git a/tests/integration/cross_queue_interceptor_worker.py b/tests/integration/cross_queue_interceptor_worker.py index d688ddf..229ac65 100644 --- a/tests/integration/cross_queue_interceptor_worker.py +++ b/tests/integration/cross_queue_interceptor_worker.py @@ -1,5 +1,5 @@ -"""Subprocess workers for the cross-queue activity *interceptor* test -(DESIGN §6.1.2 + §6.8). The activity runs on a different worker process than the +"""Subprocess workers for the cross-queue activity *interceptor* test. +The activity runs on a different worker process than the workflow that calls it, and that activity worker carries its own ``Worker(interceptors=[...])`` — so this proves activity interceptors fire on the queued path, applied by whichever worker actually runs the activity. diff --git a/tests/integration/cross_queue_orphan_worker.py b/tests/integration/cross_queue_orphan_worker.py index d8b6ebf..e7c1e5a 100644 --- a/tests/integration/cross_queue_orphan_worker.py +++ b/tests/integration/cross_queue_orphan_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for the cross-queue orphan test (§6.1.2): a workflow that +"""Subprocess worker for the cross-queue orphan test: a workflow that continues-as-new with a fire-and-forget cross-queue activity still in flight. The close path must cancel that activity (cross-process) rather than leave its ``__temporal_activity`` workflow running to completion on its worker. diff --git a/tests/integration/cross_queue_recovery_worker.py b/tests/integration/cross_queue_recovery_worker.py index a0d57fd..4ea631a 100644 --- a/tests/integration/cross_queue_recovery_worker.py +++ b/tests/integration/cross_queue_recovery_worker.py @@ -1,5 +1,5 @@ -"""Subprocess worker for cross-queue (distributed) activity recovery tests -(DESIGN §6.1.2). Two roles run as separate processes so an activity genuinely +"""Subprocess worker for cross-queue (distributed) activity recovery tests. +Two roles run as separate processes so an activity genuinely executes on a different worker than the workflow that calls it: activity an activities-only Worker on the activity queue, diff --git a/tests/integration/cross_queue_retry_worker.py b/tests/integration/cross_queue_retry_worker.py index 0553ec6..f0b48e1 100644 --- a/tests/integration/cross_queue_retry_worker.py +++ b/tests/integration/cross_queue_retry_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for cross-queue activity *retry* tests (§6.1.2). +"""Subprocess worker for cross-queue activity *retry* tests. The retry loop lives on the activity worker (Design A): a queued activity that fails retries on its own worker, with durable backoff, until success or the diff --git a/tests/integration/cross_queue_sts_worker.py b/tests/integration/cross_queue_sts_worker.py index 22be55b..200e4f4 100644 --- a/tests/integration/cross_queue_sts_worker.py +++ b/tests/integration/cross_queue_sts_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for the cross-queue schedule_to_start test (§6.1.2). +"""Subprocess worker for the cross-queue schedule_to_start test. The test starts the workflow worker first (enqueueing the activity) and delays the activity worker, so the activity sits in the queue past ``schedule_to_start_timeout`` and the activity workflow fails it before running. diff --git a/tests/integration/interpreter_recovery_worker.py b/tests/integration/interpreter_recovery_worker.py index ff77b15..39a7499 100644 --- a/tests/integration/interpreter_recovery_worker.py +++ b/tests/integration/interpreter_recovery_worker.py @@ -1,16 +1,16 @@ -"""Subprocess worker hosting the §4.3 exit-criteria workflows. +"""Subprocess worker hosting the exit-criteria workflows. Run as: python interpreter_recovery_worker.py [extra] Modes (X-start launches and starts the workflow; X-resume launches and lets DBOS recovery re-execute it): - approval §4.3 test 1 — wait_condition + signal, kill between signal and + approval test 1 — wait_condition + signal, kill between signal and completion - race §4.3 test 2 — two activities + a timer racing; completion order + race test 2 — two activities + a timer racing; completion order must replay identically - counter §4.3 test 4 — update validator; rejected updates leave no trace + counter test 4 — update validator; rejected updates leave no trace across recovery - perf §4.3 test 5 — N iterations of (sleep(0) + tiny activity); + perf test 5 — N iterations of (sleep(0) + tiny activity); recovery replay time is the perf baseline """ diff --git a/tests/integration/patched_recovery_worker.py b/tests/integration/patched_recovery_worker.py index 7b97350..e790a6a 100644 --- a/tests/integration/patched_recovery_worker.py +++ b/tests/integration/patched_recovery_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for workflow.patched() recovery (DESIGN §6.8). +"""Subprocess worker for workflow.patched() recovery. Run as: python patched_recovery_worker.py with env PATCH_VERSION in {"v1", "v2"} selecting the deployed code. diff --git a/tests/integration/test_cancellation.py b/tests/integration/test_cancellation.py index 91d08ea..0852582 100644 --- a/tests/integration/test_cancellation.py +++ b/tests/integration/test_cancellation.py @@ -1,4 +1,4 @@ -"""The §6.5 cancellation matrix, in-process: cooperative cancel (parked, +"""The cancellation matrix, in-process: cooperative cancel (parked, swallowed, cleanup-during-unwind, mid-activity), forceful terminate, and the TERMINATE_EXISTING conflict policy. The SIGKILL-during-unwind recovery test lives in test_cancellation_recovery.py. @@ -99,7 +99,7 @@ async def run(self, path: str) -> str: try: await workflow.wait_condition(lambda: False) finally: - # The load-bearing §6.5 row: cleanup during cancellation unwind + # The load-bearing row: cleanup during cancellation unwind # may still execute activities. await workflow.execute_activity( record, diff --git a/tests/integration/test_child_workflows.py b/tests/integration/test_child_workflows.py index 1a01a46..de56bd2 100644 --- a/tests/integration/test_child_workflows.py +++ b/tests/integration/test_child_workflows.py @@ -1,4 +1,4 @@ -"""Child workflows (§6.6), in-process: parent/child results, default ids, +"""Child workflows, in-process: parent/child results, default ids, failure cause fidelity, signaling children, and ParentClosePolicy. The SIGKILL re-attach proof lives in test_child_workflows_recovery.py. """ diff --git a/tests/integration/test_cron.py b/tests/integration/test_cron.py index c7e101e..3bfd029 100644 --- a/tests/integration/test_cron.py +++ b/tests/integration/test_cron.py @@ -1,4 +1,4 @@ -"""Legacy cron workflows (DESIGN §6.4): ``start_workflow( +"""Legacy cron workflows: ``start_workflow( cron_schedule=...)`` creates the first run immediately (delayed to the next cron occurrence — Temporal's first-task backoff), and each close enqueues run n+1 of the chain at the next occurrence. Tests use the 6-field diff --git a/tests/integration/test_cross_queue_activity_async.py b/tests/integration/test_cross_queue_activity_async.py index 80cae0e..c562320 100644 --- a/tests/integration/test_cross_queue_activity_async.py +++ b/tests/integration/test_cross_queue_activity_async.py @@ -1,4 +1,4 @@ -"""External (async) completion of a cross-queue activity (§6.1.2). +"""External (async) completion of a cross-queue activity. ``raise_complete_async()`` on the queued path parks the ``__temporal_activity`` workflow on its completion topic (rather than the parent run's inbox), addressed diff --git a/tests/integration/test_cross_queue_activity_cancel.py b/tests/integration/test_cross_queue_activity_cancel.py index 631443d..2d470fa 100644 --- a/tests/integration/test_cross_queue_activity_cancel.py +++ b/tests/integration/test_cross_queue_activity_cancel.py @@ -1,4 +1,4 @@ -"""Cooperative cancellation of a cross-queue activity (§6.1.2). +"""Cooperative cancellation of a cross-queue activity. The activity runs on a different worker than the workflow, so cancellation must cross the process boundary *cooperatively*: the interpreter sets a checkpointed diff --git a/tests/integration/test_cross_queue_activity_interceptor.py b/tests/integration/test_cross_queue_activity_interceptor.py index a55fe90..41f1fc2 100644 --- a/tests/integration/test_cross_queue_activity_interceptor.py +++ b/tests/integration/test_cross_queue_activity_interceptor.py @@ -1,4 +1,4 @@ -"""Activity interceptors fire on the cross-queue path (DESIGN §6.1.2 + §6.8). +"""Activity interceptors fire on the cross-queue path. The activity runs on a different worker process than the calling workflow, and that activity worker carries its own ``Worker(interceptors=[...])``. The diff --git a/tests/integration/test_cross_queue_activity_orphan.py b/tests/integration/test_cross_queue_activity_orphan.py index 6f2b313..75534a0 100644 --- a/tests/integration/test_cross_queue_activity_orphan.py +++ b/tests/integration/test_cross_queue_activity_orphan.py @@ -1,4 +1,4 @@ -"""A cross-queue activity must not outlive its workflow (§6.1.2). +"""A cross-queue activity must not outlive its workflow. When a workflow continues-as-new (or completes / is cancelled) with a fire-and-forget cross-queue activity still in flight, the close path must diff --git a/tests/integration/test_cross_queue_activity_recovery.py b/tests/integration/test_cross_queue_activity_recovery.py index 129b662..ee10e1e 100644 --- a/tests/integration/test_cross_queue_activity_recovery.py +++ b/tests/integration/test_cross_queue_activity_recovery.py @@ -1,4 +1,4 @@ -"""SIGKILL recovery for the cross-queue / distributed activity path (§6.1.2). +"""SIGKILL recovery for the cross-queue / distributed activity path. The activity runs on a *different* worker process than the workflow that calls it (via ``execute_activity(..., task_queue=)``), so two failure modes matter and diff --git a/tests/integration/test_cross_queue_activity_retry.py b/tests/integration/test_cross_queue_activity_retry.py index f732576..9d075d3 100644 --- a/tests/integration/test_cross_queue_activity_retry.py +++ b/tests/integration/test_cross_queue_activity_retry.py @@ -1,4 +1,4 @@ -"""Retry policy on the cross-queue / distributed activity path (§6.1.2). +"""Retry policy on the cross-queue / distributed activity path. The ``__temporal_activity`` workflow owns the retry loop on the activity worker (Design A): attempts, durable backoff, ``maximum_attempts`` / non-retryable, and diff --git a/tests/integration/test_cross_queue_activity_schedule_to_start.py b/tests/integration/test_cross_queue_activity_schedule_to_start.py index 29d3fe9..6a73b38 100644 --- a/tests/integration/test_cross_queue_activity_schedule_to_start.py +++ b/tests/integration/test_cross_queue_activity_schedule_to_start.py @@ -1,4 +1,4 @@ -"""schedule_to_start on the cross-queue activity path (§6.1.2). +"""schedule_to_start on the cross-queue activity path. schedule_to_start bounds how long a queued activity may sit before a worker starts it. The test enqueues the activity (workflow worker up) but delays the diff --git a/tests/integration/test_dbos_semantics.py b/tests/integration/test_dbos_semantics.py index a9d8ac0..7fde05c 100644 --- a/tests/integration/test_dbos_semantics.py +++ b/tests/integration/test_dbos_semantics.py @@ -1,4 +1,4 @@ -"""Proofs of the DBOS semantics DESIGN.md §7 declares foundational, in +"""Proofs of the foundational DBOS semantics, in isolation from the interpreter (debugging them inside it would be far harder): 1. function_id assignment stays deterministic when many async steps execute diff --git a/tests/integration/test_deployment_version.py b/tests/integration/test_deployment_version.py index a7ef632..c164645 100644 --- a/tests/integration/test_deployment_version.py +++ b/tests/integration/test_deployment_version.py @@ -1,5 +1,5 @@ """Worker deployment versioning (audit item 2), built on DBOS versioning -(DEVIATIONS worker-versioning): a build ID *is* the DBOS ``application_version`` (set via +(ARCHITECTURE worker-versioning): a build ID *is* the DBOS ``application_version`` (set via ``build_id`` / ``deployment_config``, else derived from the app name + ``application_version``), surfaced via ``workflow.Info.get_current_deployment_version()``. Because DBOS scopes recovery @@ -109,7 +109,7 @@ async def test_explicit_build_id() -> None: assert result["build_id_method"] == "bld-xyz" assert result["target_changed"] is False # The build_id IS the DBOS application_version scoping recovery/dequeue, so - # the reported version is the actual pinned routing one (DEVIATIONS worker-versioning). + # the reported version is the actual pinned routing one (ARCHITECTURE worker-versioning). probe = make_dbos_client() try: status = probe.retrieve_workflow("dv-build-id").get_status() @@ -173,7 +173,7 @@ async def test_continue_as_new_successor_inherits_build_id() -> None: assert result == "done-1" probe = make_dbos_client() try: - # Run-chain id scheme (§6.4): successor run n=1 is "--r1". + # Run-chain id scheme: successor run n=1 is "--r1". successor = probe.retrieve_workflow("dv-can--r1").get_status() finally: probe.destroy() diff --git a/tests/integration/test_dynamic_handlers.py b/tests/integration/test_dynamic_handlers.py index d76cb84..55b360b 100644 --- a/tests/integration/test_dynamic_handlers.py +++ b/tests/integration/test_dynamic_handlers.py @@ -1,5 +1,5 @@ """End-to-end dispatch of dynamic signal/query/update handlers and dynamic -activities (DESIGN §6.1/§6.8), driven through a real Worker + Client. Exact +activities, driven through a real Worker + Client. Exact matches always win over the catch-all; unmatched names/types fall back to it, and the handler decodes its RawValue args via payload_converter().""" diff --git a/tests/integration/test_info_root.py b/tests/integration/test_info_root.py index 49d88ce..2750029 100644 --- a/tests/integration/test_info_root.py +++ b/tests/integration/test_info_root.py @@ -1,5 +1,5 @@ """workflow.info().root: the root workflow of a run's tree, threaded to -children (§6.6). None for a top-level workflow (itself the root).""" +children. None for a top-level workflow (itself the root).""" from contextlib import asynccontextmanager from typing import Any, AsyncIterator, Dict, Optional diff --git a/tests/integration/test_interceptors.py b/tests/integration/test_interceptors.py index 4edccf9..e1dbefc 100644 --- a/tests/integration/test_interceptors.py +++ b/tests/integration/test_interceptors.py @@ -1,4 +1,4 @@ -"""Interceptor tests (DESIGN §6.8): client outbound + activity inbound/outbound. +"""Interceptor tests: client outbound + activity inbound/outbound. Both run against an in-process Worker and a Client over the same database, so the recording interceptor instances (and their event lists) live in the test diff --git a/tests/integration/test_interpreter_basic.py b/tests/integration/test_interpreter_basic.py index d46b33c..3cab1a7 100644 --- a/tests/integration/test_interpreter_basic.py +++ b/tests/integration/test_interpreter_basic.py @@ -1,4 +1,4 @@ -"""In-process interpreter tests: happy paths plus the §4.3 cases that don't +"""In-process interpreter tests: happy paths plus the cases that don't need a SIGKILL (3: gather + retry exhaustion; 4: update validator, accepted half; 6: non-failure exception keeps the workflow running until the implementation is swapped). The SIGKILL-recovery suite lives in @@ -322,7 +322,7 @@ def test_updates_queries_and_validator() -> None: @pytest.mark.usefixtures("dbosify") def test_gather_with_retry_exhaustion() -> None: - """§4.3 test 3: gather of three activities; one fails through its retry + """Test 3: gather of three activities; one fails through its retry policy and surfaces as ActivityError(cause=ApplicationError).""" attempt_counts.clear() dispatcher.register_worker( @@ -343,7 +343,7 @@ def test_gather_with_retry_exhaustion() -> None: def test_buggy_workflow_stays_running_until_fixed( monkeypatch: pytest.MonkeyPatch, ) -> None: - """§4.3 test 6: a non-failure exception fails the workflow task, not the + """Test 6: a non-failure exception fails the workflow task, not the workflow; swapping in a fixed implementation lets it complete.""" monkeypatch.setenv(dispatcher.TASK_RETRY_INITIAL_ENV, "0.2") diff --git a/tests/integration/test_interpreter_recovery.py b/tests/integration/test_interpreter_recovery.py index da51372..d53db19 100644 --- a/tests/integration/test_interpreter_recovery.py +++ b/tests/integration/test_interpreter_recovery.py @@ -1,4 +1,4 @@ -"""The §4.3 SIGKILL-recovery suite (criteria 1, 2, 4, and 5), driven through +"""The SIGKILL-recovery suite (criteria 1, 2, 4, and 5), driven through real worker subprocesses against real Postgres. The non-kill criteria (3 and 6) live in test_interpreter_basic.py. """ @@ -90,7 +90,7 @@ def _spawn(mode: str, workflow_id: str, *extra: str) -> PythonProcess: @pytest.mark.usefixtures("cleanup_test_databases") def test_signal_recovery_not_applied_twice(driver: Driver) -> None: - """§4.3 test 1: kill after the signal is recorded but before completion; + """Test 1: kill after the signal is recorded but before completion; after recovery the result is the same and the handler was not re-applied twice to state. """ @@ -122,7 +122,7 @@ def test_signal_recovery_not_applied_twice(driver: Driver) -> None: @pytest.mark.usefixtures("cleanup_test_databases") def test_race_order_identical_across_recovery(driver: Driver) -> None: - """§4.3 test 2: two concurrent activities + a timer racing; the + """Test 2: two concurrent activities + a timer racing; the completion order observed by user code is identical across a forced recovery replay. """ @@ -158,7 +158,7 @@ def test_race_order_identical_across_recovery(driver: Driver) -> None: @pytest.mark.usefixtures("cleanup_test_databases") def test_rejected_update_leaves_no_trace_across_recovery(driver: Driver) -> None: - """§4.3 test 4: a rejected update leaves no trace in workflow state + """Test 4: a rejected update leaves no trace in workflow state across recovery; an accepted update returns its value. """ first = _spawn("counter-start", "counter-wf") @@ -193,7 +193,7 @@ def test_rejected_update_leaves_no_trace_across_recovery(driver: Driver) -> None @pytest.mark.timeout(600) @pytest.mark.usefixtures("cleanup_test_databases") def test_perf_baseline_1000_iterations(driver: Driver) -> None: - """§4.3 test 5: 1,000 iterations of (sleep(0) + tiny activity); measure + """Test 5: 1,000 iterations of (sleep(0) + tiny activity); measure first-execution and recovery-replay times. Numbers go in docs/perf.md; the only hard assertion is correctness. """ diff --git a/tests/integration/test_list_workflows.py b/tests/integration/test_list_workflows.py index 4fca6bb..666a6a8 100644 --- a/tests/integration/test_list_workflows.py +++ b/tests/integration/test_list_workflows.py @@ -1,4 +1,4 @@ -"""Visibility: ``Client.list_workflows`` / ``count_workflows`` (DESIGN §6.2). +"""Visibility: ``Client.list_workflows`` / ``count_workflows``. Exercises the query subset end-to-end against Postgres: filtering by workflow type, ExecutionStatus (clean + the ERROR-family post-filter), WorkflowId diff --git a/tests/integration/test_namespaces.py b/tests/integration/test_namespaces.py index fea750b..0f4c0d2 100644 --- a/tests/integration/test_namespaces.py +++ b/tests/integration/test_namespaces.py @@ -1,4 +1,4 @@ -"""Namespaces backed by DBOS schemas (DEVIATIONS no-server): a Worker/Client pair runs +"""Namespaces backed by DBOS schemas (ARCHITECTURE no-server): a Worker/Client pair runs in a namespace's own schema, and clients for different namespaces coexist in one process (per-engine schema isolation). The low-level constructor's rejection of a non-namespace schema is unit-tested in tests/unit/test_namespaces.py.""" diff --git a/tests/integration/test_patched.py b/tests/integration/test_patched.py index 3e35fb7..40da9a0 100644 --- a/tests/integration/test_patched.py +++ b/tests/integration/test_patched.py @@ -1,5 +1,5 @@ -"""In-process behavior tests for ``workflow.patched`` / ``deprecate_patch`` -(DESIGN §6.8). These cover the first-execution semantics, per-id memoization, +"""In-process behavior tests for ``workflow.patched`` / ``deprecate_patch``. +These cover the first-execution semantics, per-id memoization, the durable marker, and the read-only rejection. The replay semantics — old in-flight runs taking the OLD path after a redeploy, and new runs replaying the NEW path across a crash — need real recovery and live in diff --git a/tests/integration/test_patched_recovery.py b/tests/integration/test_patched_recovery.py index 6f68c6b..0116242 100644 --- a/tests/integration/test_patched_recovery.py +++ b/tests/integration/test_patched_recovery.py @@ -1,4 +1,4 @@ -"""SIGKILL-recovery tests for workflow.patched() (DESIGN §6.8) — the replay +"""SIGKILL-recovery tests for workflow.patched() — the replay semantics that the in-process suite (test_patched.py) can't reach. Two scenarios, both crashing a parked run mid-flight and resuming a fresh diff --git a/tests/integration/test_retry_interactions.py b/tests/integration/test_retry_interactions.py index ca440fe..c87ea69 100644 --- a/tests/integration/test_retry_interactions.py +++ b/tests/integration/test_retry_interactions.py @@ -1,5 +1,5 @@ """How workflow retry policies compose with continue-as-new, cancellation, -and message carryover (DESIGN §6.4). These are the cross-feature interaction +and message carryover. These are the cross-feature interaction paths — independently each works; the question is the seams. """ diff --git a/tests/integration/test_schedules.py b/tests/integration/test_schedules.py index 4e9aa63..0b098fa 100644 --- a/tests/integration/test_schedules.py +++ b/tests/integration/test_schedules.py @@ -1,4 +1,4 @@ -"""Schedules (DESIGN §6.7): create/describe/list/update/pause/trigger/backfill/ +"""Schedules: create/describe/list/update/pause/trigger/backfill/ delete against real Postgres, plus an automatic cron fire and persistence across a worker restart. """ @@ -451,7 +451,7 @@ async def test_schedule_persists_across_worker_restart() -> None: await handle.delete() -# --- overlap policies (DEVIATIONS schedules) -------------------------------------- +# --- overlap policies (ARCHITECTURE schedules) -------------------------------------- async def test_overlap_skip_suppresses_runs() -> None: diff --git a/tests/integration/test_schedules_recovery.py b/tests/integration/test_schedules_recovery.py index e364819..e1aaca5 100644 --- a/tests/integration/test_schedules_recovery.py +++ b/tests/integration/test_schedules_recovery.py @@ -1,4 +1,4 @@ -"""SIGKILL mid scheduled-action (DESIGN §6.7 + recovery is the product). +"""SIGKILL mid scheduled-action (recovery is the product). The kill lands while scheduled actions are in flight (every-second fires with ~2s actions overlap, so several run at once). Recovery must: @@ -6,7 +6,7 @@ * keep the persisted schedule firing after restart (more occurrences), with each occurrence's deterministic id ensuring no *twin* execution. -Note on at-least-once (DEVIATIONS failover): an action whose ``record_occurrence`` +Note on at-least-once (ARCHITECTURE failover): an action whose ``record_occurrence`` activity wrote its side-effect but had not yet checkpointed that step when the worker was killed re-executes that activity on recovery — so an occurrence may legitimately be recorded twice. The deterministic per-occurrence id still bars @@ -55,5 +55,5 @@ def test_sigkill_mid_scheduled_action(tmp_path: Path) -> None: # the one in flight at kill time). assert len(counts) >= 2, occurrences # The deterministic per-occurrence id bars a twin / replay storm: no occurrence - # is recorded more than twice (original write + one at-least-once re-run, DEVIATIONS failover). + # is recorded more than twice (original write + one at-least-once re-run, ARCHITECTURE failover). assert max(counts.values()) <= 2, occurrences diff --git a/tests/integration/test_search_attributes.py b/tests/integration/test_search_attributes.py index cc246f3..d8c7d43 100644 --- a/tests/integration/test_search_attributes.py +++ b/tests/integration/test_search_attributes.py @@ -1,4 +1,4 @@ -"""Memo + search-attribute storage (DESIGN §6.2), backed by DBOS native +"""Memo + search-attribute storage, backed by DBOS native workflow attributes. Covers the full surface: setting at start, reading back via ``describe()`` and diff --git a/tests/integration/test_version_client_separate_process.py b/tests/integration/test_version_client_separate_process.py index 0c92750..6bd0d1d 100644 --- a/tests/integration/test_version_client_separate_process.py +++ b/tests/integration/test_version_client_separate_process.py @@ -1,4 +1,4 @@ -"""A separate-process client against a versioned worker (DEVIATIONS worker-versioning). +"""A separate-process client against a versioned worker (ARCHITECTURE worker-versioning). A bare client (the test process — it never set this build id) enqueues a workflow with no version (NULL); the versioned worker dequeues it (NULL matches diff --git a/tests/integration/test_version_cross_queue.py b/tests/integration/test_version_cross_queue.py index e9f789c..ae63a38 100644 --- a/tests/integration/test_version_cross_queue.py +++ b/tests/integration/test_version_cross_queue.py @@ -1,4 +1,4 @@ -"""Cross-queue (queued) activities under a build id (DEVIATIONS worker-versioning). A +"""Cross-queue (queued) activities under a build id (ARCHITECTURE worker-versioning). A cross-queue activity is enqueued in-workflow as a ``__temporal_activity`` workflow, stamped with the workflow worker's build id; only an activity worker on the same build id dequeues it. With both workers on ``cq-build`` the activity diff --git a/tests/integration/test_version_recovery.py b/tests/integration/test_version_recovery.py index cbe5ad0..b791afc 100644 --- a/tests/integration/test_version_recovery.py +++ b/tests/integration/test_version_recovery.py @@ -1,4 +1,4 @@ -"""Cross-version PINNED recovery (DEVIATIONS worker-versioning): the behavioral proof that a +"""Cross-version PINNED recovery (ARCHITECTURE worker-versioning): the behavioral proof that a build id (= DBOS application_version) actually pins a workflow to its version. A workflow stamped with build id ``v1`` is crashed mid-flight. A ``v2`` worker diff --git a/tests/integration/test_workflow_environment.py b/tests/integration/test_workflow_environment.py index 4a9dfc0..78446de 100644 --- a/tests/integration/test_workflow_environment.py +++ b/tests/integration/test_workflow_environment.py @@ -1,6 +1,6 @@ """WorkflowEnvironment.start_local: an isolated throwaway database per environment on the externally provided Postgres server, torn down on -shutdown (DESIGN §6.10). +shutdown. """ from datetime import timedelta diff --git a/tests/integration/test_workflow_retry.py b/tests/integration/test_workflow_retry.py index 90c4e49..8db1cce 100644 --- a/tests/integration/test_workflow_retry.py +++ b/tests/integration/test_workflow_retry.py @@ -1,4 +1,4 @@ -"""Workflow retry policies (DESIGN §6.4): a failed run starts run n+1 of the +"""Workflow retry policies: a failed run starts run n+1 of the chain with attempt+1 and backoff, `workflow.info().attempt` is real, attempts see the previous failure via `workflow.get_last_failure()`, and `result(follow_runs=True)` follows a failed run to its retry successor. diff --git a/tests/integration/version_client_worker.py b/tests/integration/version_client_worker.py index 2d57c83..f4e27a7 100644 --- a/tests/integration/version_client_worker.py +++ b/tests/integration/version_client_worker.py @@ -1,5 +1,5 @@ """Subprocess worker for the separate-process-client versioning test -(DEVIATIONS worker-versioning). Runs a Worker on ``$DBOSIFY_BUILD_ID`` hosting a trivial echo +(ARCHITECTURE worker-versioning). Runs a Worker on ``$DBOSIFY_BUILD_ID`` hosting a trivial echo workflow, prints ``READY``, and runs until killed. The *test* process acts as a bare client (it never sets this build id) and starts the workflow. """ diff --git a/tests/integration/version_cross_queue_worker.py b/tests/integration/version_cross_queue_worker.py index c8ce414..1f09500 100644 --- a/tests/integration/version_cross_queue_worker.py +++ b/tests/integration/version_cross_queue_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for cross-queue (queued) activity versioning (DEVIATIONS +"""Subprocess worker for cross-queue (queued) activity versioning (ARCHITECTURE worker-versioning). Two roles run as separate processes, both on the same build id: activity an activities-only Worker on the activity queue. diff --git a/tests/integration/version_recovery_worker.py b/tests/integration/version_recovery_worker.py index 11cc729..b6cbb35 100644 --- a/tests/integration/version_recovery_worker.py +++ b/tests/integration/version_recovery_worker.py @@ -1,4 +1,4 @@ -"""Subprocess worker for cross-version PINNED-recovery (DEVIATIONS worker-versioning). +"""Subprocess worker for cross-version PINNED-recovery (ARCHITECTURE worker-versioning). Run as: python version_recovery_worker.py with env DBOSIFY_BUILD_ID selecting the worker's build id (= DBOS application_version). diff --git a/tests/unit/_param_audit.py b/tests/unit/_param_audit.py index 54c75e6..e815827 100644 --- a/tests/unit/_param_audit.py +++ b/tests/unit/_param_audit.py @@ -1,4 +1,4 @@ -"""Shared harness for the accepted-parameter audits (DESIGN §9). +"""Shared harness for the accepted-parameter audits. The Worker, Client, and start-verb audits each classify every temporalio parameter of one or more callables into named buckets — some plain sets diff --git a/tests/unit/test_activity_environment.py b/tests/unit/test_activity_environment.py index 21ff17b..ed40625 100644 --- a/tests/unit/test_activity_environment.py +++ b/tests/unit/test_activity_environment.py @@ -31,7 +31,7 @@ def cancellable_activity() -> str: @activity.defn def worker_lifecycle_activity() -> str: # shield_thread_cancel_exception is a no-op here (cooperative cancellation, - # DEVIATIONS sync-activity-cancel) — the body still runs. + # ARCHITECTURE sync-activity-cancel) — the body still runs. with activity.shield_thread_cancel_exception(): shutdown = activity.is_worker_shutdown() activity.wait_for_worker_shutdown_sync(timeout=0.01) diff --git a/tests/unit/test_client_param_audit.py b/tests/unit/test_client_param_audit.py index a18b523..96b8a16 100644 --- a/tests/unit/test_client_param_audit.py +++ b/tests/unit/test_client_param_audit.py @@ -1,4 +1,4 @@ -"""Accepted-parameter audit for ``Client`` (DESIGN §9), mirroring the Worker +"""Accepted-parameter audit for ``Client``, mirroring the Worker audit. Every parameter of ``temporalio.client.Client.connect`` / ``__init__`` is classified — honored, subsumed by the ``DBOSClient`` we wrap (connection-surface), or a fundamental deviation. Unlike the Worker, our ``Client`` has no ``**kwargs`` diff --git a/tests/unit/test_dynamic_handlers.py b/tests/unit/test_dynamic_handlers.py index 6a4f26a..59e2194 100644 --- a/tests/unit/test_dynamic_handlers.py +++ b/tests/unit/test_dynamic_handlers.py @@ -1,7 +1,7 @@ """Decoration-time behavior of dynamic handlers/activities and handler -descriptions (DESIGN §6.1/§6.8). End-to-end dispatch lives in +descriptions. End-to-end dispatch lives in tests/integration/test_dynamic_handlers.py; dynamic *workflows* are rejected -(DEVIATIONS dynamic-handlers).""" +(ARCHITECTURE dynamic-handlers).""" import collections.abc import typing @@ -248,7 +248,7 @@ def b() -> None: ... def test_no_thread_cancel_exception_false_rejected() -> None: # Asking for Temporal's raise-into-the-thread behavior fails loudly at - # decoration time rather than silently degrading (DEVIATIONS sync-activity-cancel). + # decoration time rather than silently degrading (ARCHITECTURE sync-activity-cancel). with pytest.raises(NotImplementedError, match="no_thread_cancel_exception"): @activity.defn(no_thread_cancel_exception=False) diff --git a/tests/unit/test_ids.py b/tests/unit/test_ids.py index 028cefb..0eecd29 100644 --- a/tests/unit/test_ids.py +++ b/tests/unit/test_ids.py @@ -1,5 +1,5 @@ """Workflow-id namespacing (ids.py): the ``--r`` (run-chain) and ``--a`` -(cross-queue activity, §6.1.2) separators are reserved so internal ids can never +(cross-queue activity) separators are reserved so internal ids can never collide with a user/child id — a collision would make the idempotent ``SetWorkflowID`` enqueue silently re-attach to an unrelated workflow. """ diff --git a/tests/unit/test_namespaces.py b/tests/unit/test_namespaces.py index 71c51ff..f45044b 100644 --- a/tests/unit/test_namespaces.py +++ b/tests/unit/test_namespaces.py @@ -1,4 +1,4 @@ -"""Namespace -> DBOS schema mapping (DEVIATIONS no-server).""" +"""Namespace -> DBOS schema mapping (ARCHITECTURE no-server).""" import pytest diff --git a/tests/unit/test_schedules.py b/tests/unit/test_schedules.py index 3b12e75..36b0905 100644 --- a/tests/unit/test_schedules.py +++ b/tests/unit/test_schedules.py @@ -1,4 +1,4 @@ -"""Unit coverage (no database) for the §6.7 schedule building blocks: +"""Unit coverage (no database) for the schedule building blocks: ``ScheduleSpec`` cron compilation and the schedule ``context`` round-trip. """ diff --git a/tests/unit/test_sdk_converter.py b/tests/unit/test_sdk_converter.py index 81a5433..a4d1bbe 100644 --- a/tests/unit/test_sdk_converter.py +++ b/tests/unit/test_sdk_converter.py @@ -124,7 +124,7 @@ async def assert_payload( await assert_payload(None, "binary/null", "") await assert_payload(b"some binary", "binary/plain", "some binary") # Adapted: dropped the json/protobuf WorkflowExecution case — our converter - # has no protobuf payload converter (DESIGN: lightweight Payload, no proto). + # has no protobuf payload converter. await assert_payload( {"foo": "bar", "baz": "qux"}, "json/plain", '{"baz":"qux","foo":"bar"}' ) diff --git a/tests/unit/test_signature_parity.py b/tests/unit/test_signature_parity.py index b0a8237..c8febd4 100644 --- a/tests/unit/test_signature_parity.py +++ b/tests/unit/test_signature_parity.py @@ -1,6 +1,6 @@ """Signature parity against the real temporalio SDK (a dev-dependency). -This is the API-drift alarm (DESIGN §9): every public name dbosify +This is the API-drift alarm: every public name dbosify exposes is diffed against its temporalio counterpart. Rules enforced per callable: @@ -51,7 +51,7 @@ # Names/methods whose shape deliberately differs. qualname -> reason. Whole- # callable exemptions' honored params are guarded by EXPLICITLY_ACCEPTED_PARAMS. DELIBERATE_DEVIATIONS: Dict[str, str] = { - "client.Client.__init__": "wraps a dbos.DBOSClient (DESIGN §5, revised)", + "client.Client.__init__": "wraps a dbos.DBOSClient", "client.Client.connect": "takes system_database_url + namespace and builds the DBOSClient", "client.Client.close": ( "DBOS extension: connect() builds a DBOSClient (a DB connection pool) " @@ -60,7 +60,7 @@ "worker.Worker.__init__": "takes dbos.DBOSConfig; one worker per process", "worker.Worker.namespace": ( "DBOS extension: our Worker takes a namespace (mapped to its own DBOS " - "system schema, DEVIATIONS no-server) rather than a namespaced client, so it " + "system schema, ARCHITECTURE no-server) rather than a namespaced client, so it " "surfaces the namespace it serves; temporalio's Worker has no such " "property" ), @@ -81,15 +81,15 @@ "(corollary of no-server, no non-Python clients)" ), "client.ScheduleAsyncIterator.__init__": ( - "wraps a pre-fetched page of DBOS schedule rows, not a gRPC paginator " - "(DESIGN §6.7); the async-iteration contract is identical" + "wraps a pre-fetched page of DBOS schedule rows, not a gRPC paginator; " + "the async-iteration contract is identical" ), "client.WorkflowExecutionAsyncIterator.__init__": ( - "pages via DBOS limit/offset (DESIGN §6.2) instead of a gRPC cursor + " + "pages via DBOS limit/offset instead of a gRPC cursor + " "ListWorkflowsInput; the async-iteration contract is identical" ), "worker.Replayer.__init__": ( - "re-executes DBOS step checkpoints in this process's runtime (DEVIATIONS " + "re-executes DBOS step checkpoints in this process's runtime (ARCHITECTURE " "replay); server/sandbox params (namespace, build_id, identity, " "workflow_runner, debug_mode, runtime, plugins, ...) have no analog and " "are accepted-and-ignored — honored params guarded by " @@ -98,7 +98,7 @@ "client.WorkflowHistory.__init__": ( "DB-bound: carries a run's DBOS step checkpoints (run_id, workflow_type, " "recorded_steps, attributes, app_version), not a Temporal event log " - "(DEVIATIONS replay)" + "(ARCHITECTURE replay)" ), "client.WorkflowHistory.replay_horizon": ( "DBOS-native helper: the recorded checkpoint horizon (max function_id)" @@ -108,20 +108,20 @@ ), "client.WorkflowHandle.fetch_history_events": ( "no Temporal event history; raises NotImplementedError pointing at " - "fetch_history (DEVIATIONS replay)" + "fetch_history (ARCHITECTURE replay)" ), "client.Client.start_workflow._with_start_update": ( "internal: carries a pre-built update request delivered atomically with " - "the start for update-with-start (DEVIATIONS start-policies); not a temporalio param" + "the start for update-with-start (ARCHITECTURE start-policies); not a temporalio param" ), "client.StartWorkflowInput.__init__.with_start_update": ( "internal field threading the atomic update-with-start request to " - "_start_workflow_impl (DEVIATIONS start-policies); not part of temporalio's input" + "_start_workflow_impl (ARCHITECTURE start-policies); not part of temporalio's input" ), "client.StartWorkflowUpdateInput.__init__.with_start_op": ( "internal field: the WithStartWorkflowOperation for update-with-start, " "so the terminal performs the start that delivers the update atomically " - "(DEVIATIONS start-policies); not part of temporalio's input" + "(ARCHITECTURE start-policies); not part of temporalio's input" ), } @@ -156,14 +156,14 @@ # as fetch_history (the per-execution history fetch it delegates to) "client.WorkflowExecutionAsyncIterator.map_histories": {"skip_archival"}, # gRPC-era callbacks/links/stack_level (versioning_override is accepted - # and inert — DEVIATIONS worker-versioning) + # and inert — ARCHITECTURE worker-versioning) "client.Client.start_workflow": { "callbacks", "links", "stack_level", }, # gRPC-era stack_level (versioning_override is accepted and inert, like - # start_workflow — DEVIATIONS worker-versioning) + # start_workflow — ARCHITECTURE worker-versioning) "client.WithStartWorkflowOperation.__init__": { "stack_level", }, @@ -175,7 +175,7 @@ "converter.DefaultFailureConverter.to_failure": {"failure"}, # no protobuf Failure to fill in place; we return the failure envelope instead "converter.DataConverter.encode_failure": {"failure"}, - # external storage + payload-size limits not implemented (DESIGN §6.9) + # external storage + payload-size limits not implemented "converter.DataConverter.__init__": {"external_storage", "payload_limits"}, # interceptor headers on scheduled starts not propagated; protobuf raw_info "client.ScheduleActionStartWorkflow.__init__": { @@ -506,20 +506,20 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: # module key -> temporalio public top-level names we deliberately do not expose. KNOWN_MISSING_NAMES: Dict[str, Set[str]] = { "workflow": { - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "NexusClient", "NexusOperationCancellationType", "NexusOperationHandle", "create_nexus_client", - # Metrics — not implemented in v1 (DEVIATIONS no-metrics). + # Metrics — not implemented in v1 (ARCHITECTURE no-metrics). "metric_meter", # No workflow sandbox: import-policy + extern-fn plumbing have no analog. "SandboxImportNotificationPolicy", "extern_functions", # Per-call version intent has no DBOS analog; build-id is the app version - # (DEVIATIONS worker-versioning). + # (ARCHITECTURE worker-versioning). "VersioningIntent", - # Dynamic *workflows* are unsupported (DEVIATIONS dynamic-handlers / one-wf-per-type). + # Dynamic *workflows* are unsupported (ARCHITECTURE dynamic-handlers / one-wf-per-type). "DynamicWorkflowConfig", "dynamic_config", # Typed config dicts + multi-param update typing helper: we take the @@ -533,15 +533,15 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: }, "activity": { "LoggerAdapter", # logging adapter class — not exposed (minor) - "metric_meter", # metrics not implemented (DEVIATIONS no-metrics) + "metric_meter", # metrics not implemented (ARCHITECTURE no-metrics) }, "common": { - # Client-initiated (standalone) activities — unsupported (DEVIATIONS no-client-activities). + # Client-initiated (standalone) activities — unsupported (ARCHITECTURE no-client-activities). "ActivityIDConflictPolicy", "ActivityIDReusePolicy", # Codec-on-headers control; our codec runs at the async boundaries. "HeaderCodecBehavior", - # Metrics — not implemented in v1 (DEVIATIONS no-metrics). + # Metrics — not implemented in v1 (ARCHITECTURE no-metrics). "MetricCommon", "MetricCounter", "MetricGauge", @@ -550,7 +550,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "MetricHistogramFloat", "MetricHistogramTimedelta", "MetricMeter", - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "NexusOperationCancellationState", "NexusOperationExecutionStatus", "NexusOperationIDConflictPolicy", @@ -558,7 +558,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "PendingNexusOperationExecutionState", }, "client": { - # Client-initiated (standalone) activities — unsupported (DEVIATIONS no-client-activities). + # Client-initiated (standalone) activities — unsupported (ARCHITECTURE no-client-activities). "ActivityExecution", "ActivityExecutionAsyncIterator", "ActivityExecutionCount", @@ -575,9 +575,9 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "StartActivityInput", "TerminateActivityInput", # Async activity completion by id-reference — we complete via task_token - # (DESIGN §6.1.2); the id-reference form isn't exposed. + # The id-reference form isn't exposed. "AsyncActivityIDReference", - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "CancelNexusOperationInput", "CountNexusOperationsInput", "DescribeNexusOperationInput", @@ -594,7 +594,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "NexusOperationHandle", "StartNexusOperationInput", "TerminateNexusOperationInput", - # Worker build-id compatibility sets (DEVIATIONS worker-versioning); + # Worker build-id compatibility sets (ARCHITECTURE worker-versioning); # no DBOS analog. "BuildIdOp", "BuildIdOpAddNewCompatible", @@ -618,7 +618,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "RPCTimeoutOrCancelledError", "WorkflowUpdateRPCTimeoutOrCancelledError", # Event-log history + visibility input objects — no event history - # (DEVIATIONS replay); list/count use DBOS filters, not these inputs. + # (ARCHITECTURE replay); list/count use DBOS filters, not these inputs. "CountWorkflowsInput", "ListWorkflowsInput", "FetchWorkflowHistoryEventsInput", @@ -629,7 +629,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "StartWorkflowUpdateWithStartInput", "UpdateWithStartStartWorkflowInput", "UpdateWithStartUpdateWorkflowInput", - # Schedule overlap error not raised in our model (DEVIATIONS schedules). + # Schedule overlap error not raised in our model (ARCHITECTURE schedules). "ScheduleAlreadyRunningError", }, "worker": { @@ -653,7 +653,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "SlotReserveContext", "WorkerTuner", "WorkflowSlotInfo", - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "ExecuteNexusOperationCancelInput", "ExecuteNexusOperationStartInput", "NexusOperationInboundInterceptor", @@ -664,7 +664,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "WorkflowRunner", "WorkflowInstance", "WorkflowInstanceDetails", - # Typed config dicts — Worker/Replayer take a DBOSConfig (DESIGN §5). + # Typed config dicts — Worker/Replayer take a DBOSConfig. "WorkerConfig", "ReplayerConfig", # gRPC plugin surface — no Temporal server (no-server). @@ -675,7 +675,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: # clients). "BinaryProtoPayloadConverter", "JSONProtoPayloadConverter", - # External payload storage + size limits — not implemented (DESIGN §6.9). + # External payload storage + size limits — not implemented. "ExternalStorage", "PayloadLimitsConfig", "PayloadSizeWarning", @@ -695,7 +695,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "encode_typed_search_attribute_value", }, "exceptions": { - # Nexus — non-goal (DESIGN §1, DEVIATIONS no-server). + # Nexus — non-goal (ARCHITECTURE no-server). "NexusOperationAlreadyStartedError", "NexusOperationError", }, @@ -720,7 +720,7 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: # Context-propagation variant of the async-activity handle — not exposed. "client.AsyncActivityHandle": {"with_context"}, # Standalone activities (no-client-activities) + nexus (no-server) + legacy build-id versioning (worker-versioning) - # + gRPC connection surface (no-server). We wrap a dbos.DBOSClient (DESIGN §5). + # + gRPC connection surface (no-server). We wrap a dbos.DBOSClient. "client.Client": { "start_activity", "execute_activity", @@ -771,23 +771,23 @@ def test_supported_params_explicitly_accepted(qualname: str) -> None: "fetch_workflow_history_events", "start_update_with_start_workflow", }, - # Schedule memo not tracked (DEVIATIONS schedules). + # Schedule memo not tracked (ARCHITECTURE schedules). "client.ScheduleDescription": {"memo", "memo_value"}, "client.ScheduleListDescription": {"memo", "memo_value"}, # The execution object doesn't carry a data-converter handle. "client.WorkflowExecution": {"data_converter"}, # Static UI metadata not surfaced to describe() (start-verb inert params). "client.WorkflowExecutionDescription": {"static_details", "static_summary"}, - # DB-bound history; no offline JSON history (DEVIATIONS replay). + # DB-bound history; no offline JSON history (ARCHITECTURE replay). "client.WorkflowHistory": {"from_json", "to_json", "to_json_dict"}, # message property not exposed on the query-failed error. "client.WorkflowQueryFailedError": {"message"}, # Nexus interception unsupported. "worker.Interceptor": {"intercept_nexus_operation"}, "worker.WorkflowOutboundInterceptor": {"start_nexus_operation"}, - # Replayer config object not exposed (DB-bound replay, DEVIATIONS replay). + # Replayer config object not exposed (DB-bound replay, ARCHITECTURE replay). "worker.Replayer": {"config"}, - # Worker wraps a DBOSConfig, not a temporalio client/config (DESIGN §5). + # Worker wraps a DBOSConfig, not a temporalio client/config. "worker.Worker": {"client", "config"}, # Protobuf round-trip; no protobuf (no-server). "common.RetryPolicy": {"apply_to_proto", "from_proto"}, diff --git a/tests/unit/test_start_verb_param_audit.py b/tests/unit/test_start_verb_param_audit.py index b15846e..6292dc7 100644 --- a/tests/unit/test_start_verb_param_audit.py +++ b/tests/unit/test_start_verb_param_audit.py @@ -1,4 +1,4 @@ -"""Accepted-parameter audit for the *start verbs* (DESIGN §9), the third audit +"""Accepted-parameter audit for the *start verbs*, the third audit alongside the Worker and Client constructor audits. The start verbs (``Client.start_workflow``, ``workflow.start_child_workflow``, ``workflow.start_activity``, ``workflow.continue_as_new``) accept every @@ -11,7 +11,7 @@ silently swallowed unnoticed. The pending set is the point of the audit: it is the explicit, reviewed list of -behavior-changing options we accept but do not yet honor (see DEVIATIONS start-params). +behavior-changing options we accept but do not yet honor (see ARCHITECTURE start-params). ``execute_*`` variants share the start verbs' parameter surface and impl path; their signatures are pinned separately by ``test_signature_parity``. """ diff --git a/tests/unit/test_versioning_types.py b/tests/unit/test_versioning_types.py index f254e98..505750b 100644 --- a/tests/unit/test_versioning_types.py +++ b/tests/unit/test_versioning_types.py @@ -1,6 +1,6 @@ """Worker-versioning value types (no database). These mirror ``temporalio.common`` for parity; their behavior is inert in dbosify -(DEVIATIONS worker-versioning).""" +(ARCHITECTURE worker-versioning).""" import pytest diff --git a/tests/unit/test_visibility.py b/tests/unit/test_visibility.py index cad33dd..6d38bb9 100644 --- a/tests/unit/test_visibility.py +++ b/tests/unit/test_visibility.py @@ -1,4 +1,4 @@ -"""Unit tests for the visibility-query parser (no Postgres) — the §6.2 subset: +"""Unit tests for the visibility-query parser (no Postgres): field/operator coverage, AND conjunction, the SA-equality containment shape, the ExecutionStatus mapping (incl. the ERROR-family post-filter), and rejection of everything outside the supported grammar.""" diff --git a/tests/unit/test_worker_app_version.py b/tests/unit/test_worker_app_version.py index dbef0f6..7e7adaa 100644 --- a/tests/unit/test_worker_app_version.py +++ b/tests/unit/test_worker_app_version.py @@ -1,6 +1,6 @@ """The Worker pins a stable default DBOS ``application_version`` so redeploys don't strand in-flight workflows, cooperating workers agree on a version, and -``workflow.patched()`` reaches pre-patch runs (DESIGN §6.8). +``workflow.patched()`` reaches pre-patch runs. These cover the resolution of ``_with_default_app_version`` without launching DBOS. The override is config-only — there is no ``DBOS__APPVERSION`` special case. diff --git a/tests/unit/test_worker_config_normalize.py b/tests/unit/test_worker_config_normalize.py index 451cf16..e0a4fc7 100644 --- a/tests/unit/test_worker_config_normalize.py +++ b/tests/unit/test_worker_config_normalize.py @@ -1,6 +1,6 @@ """The Worker accepts either a Postgres URL or a full ``DBOSConfig`` as its first argument, and forces the DBOS admin server off either way (DBOSify exposes -DBOS's management APIs, not the admin HTTP port — DESIGN §1). These cover +DBOS's management APIs, not the admin HTTP port). These cover ``_normalize_config`` without launching DBOS. """ diff --git a/tests/unit/test_worker_param_audit.py b/tests/unit/test_worker_param_audit.py index 6ec452b..5603d89 100644 --- a/tests/unit/test_worker_param_audit.py +++ b/tests/unit/test_worker_param_audit.py @@ -1,4 +1,4 @@ -"""Accepted-parameter audit for ``Worker`` (DESIGN §9): every parameter of +"""Accepted-parameter audit for ``Worker``: every parameter of ``temporalio.worker.Worker.__init__`` is classified — honored, inert (accepted but a defensible no-op), rejected (behavior-changing + unsupported → raises), or a fundamental deviation. The completeness check is machine-enforced: a new @@ -55,11 +55,11 @@ # Accepted but genuinely a no-op in our model — each with a defensible reason. INERT: Dict[str, str] = { "workflow_task_executor": "interpreter runs on the event loop; no separate workflow-task thread pool", - "nexus_task_executor": "Nexus not supported (DESIGN §1)", + "nexus_task_executor": "Nexus not supported", "workflow_runner": "no workflow sandbox (no-sandbox)", "unsandboxed_workflow_runner": "no workflow sandbox (no-sandbox)", "max_cached_workflows": "no sticky cache; workflows replay from DBOS checkpoints", - "max_concurrent_nexus_tasks": "Nexus not supported (DESIGN §1)", + "max_concurrent_nexus_tasks": "Nexus not supported", "max_concurrent_workflow_task_polls": "DBOS queue listener, not Temporal long-polling", "nonsticky_to_sticky_poll_ratio": "no sticky cache", "max_concurrent_activity_task_polls": "DBOS queue listener, not Temporal long-polling", @@ -73,9 +73,9 @@ "disable_safe_workflow_eviction": "no sticky-cache eviction", "workflow_task_poller_behavior": "DBOS queue listener, not Temporal pollers", "activity_task_poller_behavior": "DBOS queue listener, not Temporal pollers", - "nexus_task_poller_behavior": "Nexus not supported (DESIGN §1)", - "disable_payload_error_limit": "Temporal payload-size caps not enforced (blocking-stalls-worker/§6.9)", - "max_workflow_task_external_storage_concurrency": "no external payload storage (§6.9)", + "nexus_task_poller_behavior": "Nexus not supported", + "disable_payload_error_limit": "Temporal payload-size caps not enforced (blocking-stalls-worker)", + "max_workflow_task_external_storage_concurrency": "no external payload storage", }