Polish Ch 5 TODO 8: pre-wire executor scaffolding in compliance worker#2
Merged
MasonEgger merged 23 commits intomainfrom May 3, 2026
Merged
Polish Ch 5 TODO 8: pre-wire executor scaffolding in compliance worker#2MasonEgger merged 23 commits intomainfrom
MasonEgger merged 23 commits intomainfrom
Conversation
Six-chapter incremental snapshot codebase that backs the Introduction to Temporal Nexus workshop. Each chapter under exercises/ has an exercise/ starting state and a solution/ reference; the solution of one chapter equals the exercise of the next, so attendees can drop in at any chapter without doing prior chapters. Restructures the original edu-nexus-code tutorial so Ch 3 and Ch 4 teach synchronous Nexus operations only, deferring the workflow-backed async path and Workflow Update human-in-the-loop pattern to Ch 5. Ch 6 adds lifecycle scenarios (cancellation, retryable and non-retryable errors, circuit breaker) via failure-injection branches in the compliance handler and a lifecycle_starter.py that exercises each scenario. The polyglot/java-legacy/ directory ships a Java compliance worker that interoperates with Python callers. The Java service contract uses @operation(name = "snake_case_name") and the data classes use @JsonProperty("snake_case_name") so the wire format aligns with Python's defaults; the demo lets a Python caller drive the same workshop scenarios against the Java handler with no code change on either side. Single root pyproject.toml requiring Python 3.14; one shared .venv across all chapter snapshots so attendees run uv sync once at the root and can cd into any chapter to run code from that snapshot.
…-only
Splits the overloaded Ch 5 ("Async Operations and Updates") into two
chapters so attendees can absorb workflow-backed async operations
without simultaneously learning the Workflow Update human-in-the-loop
pattern. Ch 5 is now async-only (the handler becomes
`@nexus.workflow_run_operation`, MEDIUM-risk transactions auto-approve
with an AML monitoring note); Ch 6 layers the `review` Update and the
`submit_review` sync Nexus operation that forwards calls via
`handle.execute_update(...)` on top of Ch 5; the lifecycle chapter
(cancellation, retryable vs non-retryable errors, circuit breaker)
moves from Ch 6 to Ch 7 unchanged in scope.
Flattens the per-domain package layout by collapsing the `temporal/`
subdirectory across every chapter snapshot. `compliance/temporal/`
disappears: `activities.py`, `worker.py`, and `nexus_handler.py` move
up into `compliance/`, with `nexus_handler.py` renamed to
`service_handler.py`. `domain.py` becomes `models.py`,
`shared/nexus_service.py` becomes `shared/service.py`, and the
standalone `compliance_checker.py` / `payment_gateway.py` modules are
folded into their respective `activities.py`. Run commands shorten
correspondingly: `uv run python -m payments.worker` instead of
`uv run python -m payments.temporal.worker`.
Reduces `polyglot/java-legacy/` to a compliance-only worker. All Java
payments code (`PaymentGateway`, `PaymentStarter`,
`PaymentProcessingWorkflow`, `ReviewCallerWorkflow`, supporting
domain/activity classes) is removed; the Java side now exists solely
to serve as a drop-in replacement for the Python compliance handler at
the same Nexus endpoint. `pom.xml` drops the four named exec
executions in favor of a single default mainClass
(`compliance.temporal.ComplianceWorkerApp`), so the run command
collapses from `mvn -q exec:java@compliance-worker` to `mvn -q
exec:java`. The artifact is renamed from `decouple-monolith-solution`
to `polyglot-java-compliance` to match its new role. The Java
compliance handler also gains
`WorkflowIdConflictPolicy.USE_EXISTING`, making retried Nexus start
requests for the same `transaction_id` idempotent, and the durable
sleep is moved into the MEDIUM-risk review branch where it actually
demonstrates worker-restart resumption across an `await`.
Loosens the Python floor from 3.14 to 3.10 and bumps `temporalio` to
`>=1.24.0` so attendees on stock Python distributions can run the
workshop without first installing 3.14.
Ch 1 now ships only a `solution/` directory (no `exercise/`), since
Chapter 1's goal is to run the monolith and observe its behavior, not
to fill in TODOs. The exercise/solution pattern picks up at Ch 2. The
root README is updated to reflect this and to defer Nexus namespace
and endpoint creation (`payments-namespace`, `compliance-namespace`,
`compliance-endpoint`) out of the global setup and into Chapter 2's
Parts D and E, where they are introduced alongside the contract that
uses them.
…handling
Resolves the technical-accuracy nits surfaced by a multi-agent swarm
validation pass against the chapter chain. No runtime behavior changes;
all edits are README, docstring, and one starter except clause.
Three Medium-severity README/docstring corrections:
- Root README pointed at "Parts D and E" of Ch 2's README, but Ch 2
only has Parts A-D and namespace/endpoint creation lives in Parts B
and C. Updated the cross-reference accordingly.
- `compliance/service_handler.py` in Ch 2 (exercise + solution) and
Ch 4 (exercise + solution) said `submit_review` "will be implemented
in Ch 5"; the Ch 2 README, Ch 3 README, and root README all agree
Ch 6 is the chapter that turns it into a real Update sender. Ch 5
only introduces the workflow `submit_review` will Update. Rewrote
the four affected docstrings to "implemented in Ch 6 once
ComplianceWorkflow has a review Update."
- `payments/worker.py` in Ch 5 (exercise + solution) said
`ReviewCallerWorkflow` is "introduced in Ch 5 alongside the
workflow-backed compliance check." `ReviewCallerWorkflow` first
appears in Ch 6's `payments/workflows.py` (TODO 12). Reverted the
forward reference to "introduced in Ch 6", matching the wording
that was already correct in Ch 4's solution.
Lower-severity cleanup:
- Ch 2's stub `service_handler.py` methods returned `None` from
bodies typed `-> ComplianceResult`. Replaced with
`raise NotImplementedError("TODO 2: see Chapter 3 README Part A")`
so the stub matches the workshop's idiom (Ch 3's exercise raises
the same error) and fails loud if anything tried to invoke it.
- Ch 2 and Ch 3 `payments/workflows.py` docstrings introduced an
undefined "Checkpoint 0" term and described the activities as
"stubs" (they are the same fully-implemented Ch 1 activities).
Adopted Ch 1's clean docstring across all four files.
- Ch 5 README Part B now tells students to delete the
`_check_compliance` import once the handler is workflow-backed,
eliminating an exercise-vs-solution paper cut. Part D adds a brief
aside noting that `schedule_to_start_timeout` and
`start_to_close_timeout` on Nexus operations require Temporal
Server v1.31.0 or later.
- Ch 7 `lifecycle_starter.py` Scenario A caught bare `Exception`,
which blunted the typed-error lesson the chapter is teaching.
Switched to catching `temporalio.client.WorkflowFailureError` and
print the cause chain so the underlying `nexusrpc.OperationError`
becomes observable. Other except blocks in the file are
intentionally broad cleanup-on-shutdown patterns and are left
alone.
- Ch 7's exercise and solution had drifted from Ch 6's solution on
four pure-prose docstrings (`compliance/worker.py`,
`compliance/workflows.py`, `payments/worker.py`,
`payments/workflows.py`). Re-synced to Ch 6's wording so the
boundary 6 -> 7 diff is now exactly the intentional TODO 13 add
plus the new `payments/lifecycle_starter.py` file - nothing else.
Co-authored-by: Nikolay Advolodkin <nadvolod@gmail.com>
Co-authored-by: Nikolay Advolodkin <nadvolod@gmail.com>
Co-authored-by: Nikolay Advolodkin <nadvolod@gmail.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
Co-authored-by: Alex Mazzeo <alex.mazzeo@outlook.com>
…nation
Every chapter runs the same three transaction IDs (TXN-A, TXN-B, TXN-C)
through the same `payments-namespace` and `compliance-namespace`, so a
stuck or running workflow from one chapter blocked the same ID in the
next chapter with `WorkflowAlreadyStartedError`. Re-prefixing every
constructed workflow ID with `chNN-` gives each chapter its own ID
space while keeping the IDs stable and business-meaningful (no random
suffixes — the `submit_review` handler still constructs the compliance
workflow ID by string concat from the transaction ID).
Workflow ID rename, applied symmetrically across exercise/ and solution/
for every chapter that constructs an ID:
- `payments/starter.py` (all chapters): `f"payment-{txn.transaction_id}"`
-> `f"payment-chNN-{txn.transaction_id}"`.
- `compliance/service_handler.py` (Ch 5, Ch 6, Ch 7): `start_workflow`
`id=f"compliance-{txn_id}"` -> `id=f"compliance-chNN-{txn_id}"`.
- `compliance/service_handler.py` (Ch 6, Ch 7): `submit_review`'s
`get_workflow_handle_for(... workflow_id=...)` lookup gets the same
`compliance-chNN-` prefix so it still resolves the running handler
workflow.
- `payments/lifecycle_starter.py` (Ch 7 exercise + solution): all four
scenarios' `id=` values, all `temporal workflow describe -w` print
statements, and the inline banner messages updated to `ch07-`. The
six TXN-CIRCUIT-* workflow IDs and the cancellation-side
`compliance-ch07-TXN-CANCEL-1` reference are included.
- `payments/review_starter.py` (Ch 6, Ch 7): docstring reference to
the long-running compliance workflow ID updated to `compliance-chNN-`.
The starter's own `review-TXN-B-{uuid}` ID stays UUID-suffixed
because attendees re-run it multiple times for the same TXN-B.
README updates that quote workflow IDs:
- Root `README.md` (separate concern caught while editing): the
"Running a chapter" section had two stray paragraphs telling
attendees not to run `compliance.worker`. Replaced with the actual
Ch 1 / Ch 2 / Ch 3-7 split: Ch 1 runs only Payments (compliance
in-process), Ch 2 is setup-only, Ch 3-7 run both workers.
- Ch 5 README Part B: example `id=f"compliance-{...}"` snippet ->
`compliance-ch05-{...}`.
- Ch 6 README Parts B, D, E updated all `payment-TXN-B` and
`compliance-TXN-B` references to `payment-ch06-TXN-B` and
`compliance-ch06-TXN-B`. New "A note on Workflow ID design" section
explains why the chapter prefix exists, why no UUID is appended to
the main workflow IDs, and why `review_starter.py`'s
ReviewCallerWorkflow ID is the deliberate exception. New
"Re-running this chapter" caveat warns that stable IDs mean the
starter cannot be re-run while a TXN-B compliance workflow is still
awaiting review.
- Ch 7 README Scenarios A, B, C, D updated all `temporal workflow
describe`/`show` examples and the prose `payment-TXN-*` references
to `payment-ch07-*` / `compliance-ch07-*`. Added a "Re-running
this chapter" note pointing at the dev-server reset workaround.
Two unrelated Ch 6 solution refinements that snuck in with this pass:
- `compliance/workflows.py` adds `assert self._review_result is not
None` after the `wait_condition`, so the `return self._review_result`
on the next line type-checks under strict mypy. The README Part A
code snippet picks up the same assert.
- `payments/workflows.py` bumps the `submit_review` Nexus operation's
`schedule_to_close_timeout` from 10s to 60s. The 10s budget was
fine for the in-process sync handler but was tight against the full
Nexus round-trip (caller -> endpoint -> handler -> Update ->
response) when running on slower laptops.
Audited every exercise/ chapter (Ch 2-7) against its corresponding
assignment.md in the workshop-nexus-intro repo. Goal: every TODO the
assignment references must have a matching marker at the exact insertion
point in the code, with no "uncomment this" patterns and no spoilery
comments that name the answer.
Authoring-standard fixes (see workshop-nexus-intro/tmp/lessons-learned.md
"Exercise file authoring standard"):
- Rule 9 (state the goal, not the answer): rewrote TODO comments that
named the exact decorator, API call, import, or argument literal so
they describe what the learner is producing instead. The assignment
text is where the answer lives. Touched markers in Ch 2 (1a-1c),
Ch 3 (2a-2c, 3), Ch 5 (8), Ch 6 (10b, 10c, 11a, 12a), and Ch 7
(13a-13b).
- Rule 11 (no "Part A"/"Part B" suffixes): dropped "Chapter N, Part X"
suffixes from every TODO. The number itself implies the chapter and
sub-letters disambiguate locations. Cleaned up across all six
chapters' active and breadcrumb markers.
- Rule 3 (no "uncomment this"): removed commented-out solution code
in Ch 2 shared/service.py (the `# @nexusrpc.service` decorator and
`# check_compliance: nexusrpc.Operation[...]` lines). The TODO now
asks the learner to type the line; the breadcrumb is gone.
- Rule 6 (sub-letters per insertion point): split single TODOs that
spanned multiple insertion points. Ch 2 TODO 1 became 1a/1b/1c
(decorator + two operation declarations). Ch 3 TODO 2 became
2a/2b/2c (handler decorator + two method decorators). Ch 6 TODO 10
became 10a/10b/10c (state field + risk branch + Update method).
- Rule 2 (`pass`, not `NotImplementedError`, for exercise stubs):
Ch 2 service_handler stubs flipped from `raise NotImplementedError`
back to `pass`. The runtime stub for `submit_review` between Ch 3
and Ch 6 stays `NotImplementedError` since it ships to attendees as
final state until Ch 6.
- Added the missing `Registered: ComplianceNexusServiceHandler (sync
only)` print line in Ch 3 worker.py that the assignment promises in
its expected output but the exercise had stripped.
Six audit agents ran in parallel (one per chapter); each verified
1:1 mapping between assignment TODOs and code markers. Final state for
all chapters: every active TODO maps to an assignment step, every
breadcrumb TODO is correctly tagged with its target chapter, no
commented-out solution code remains, no spoilery hints inside method
bodies.
… polish Ch 7 lifecycle UX Five changes, all small, propagated across the chapters they touch. 1. Rule 3 explanation now tells the truth about which arm fired (`compliance/activities.py` in Ch 1-7 exercise+solution, plus the Java `ComplianceChecker.java`). The Rule 3 branch fires on `amount > $10K OR international to unusual jurisdiction`, but the result hardcoded `"International transfer above $10K threshold."` That was a lie whenever only the amount arm or only the jurisdiction arm fired (e.g. Ch 7's `TXN-CANCEL-1` is a domestic $12K USD-to-USD transfer — MEDIUM by amount, not by being international). Now branches to three explanations covering "both arms fired", "amount only", "jurisdiction only", and the comment above the rule reads "Amount > $10K or international to unusual jurisdiction" instead of the old "International transfer > $10K". Same fix in the Java polyglot worker so the wire-equivalent handler keeps telling the same story as the Python side. 2. `submit_review` stub language is no longer chapter-specific (Ch 3 `compliance/service_handler.py`, exercise + solution). The Ch 3 stub used to say "Will be implemented in Ch 6" / "Ch 5 introduces the workflow it will Update; Ch 6 fills it in." That leaks chapter numbering into the contract code and dates poorly if chapters get renumbered. Replaced with "gains a real implementation later in the workshop" in the docstring, the TODO 2c hint, the inline comment, and the `NotImplementedError` body. 3. `ComplianceWorkflow.__init__` takes the request via `@workflow.init` (Ch 6 exercise `compliance/workflows.py`). Switched from a no-arg `__init__` plus a `self._request = request` assignment in `run()` to `@workflow.init` receiving the `ComplianceRequest` directly. `_request` is now non-Optional from construction, which simplifies typing in the Update handler the attendee writes for TODO 12. The solution file already used this pattern; this aligns the exercise scaffolding with it. 4. Lifecycle starter pauses between scenarios for inspection (Ch 7 `payments/lifecycle_starter.py`, exercise + solution). Added `pause_for_inspection(scenario)` that prompts the attendee to press Enter between Scenarios A→B, B→C, C→D. Without this, all four scenarios fire back-to-back and the next scenario's Nexus call lands in the Web UI before the attendee can `temporal workflow describe` the one they just ran. Also fixed Scenario C's banner — the old text called the $12K transfer "international", but the request body is US-to-US; it lands in MEDIUM via the amount-above-$10K arm of Rule 3. The new banner says so explicitly, which dovetails with change #1. 5. Java polyglot uses the `ch07` workflow-ID prefix (`compliance/temporal/ComplianceNexusServiceImpl.java`). Both the `checkCompliance` start path and the `submitReview` stub lookup now build `compliance-ch07-{transaction_id}` instead of `compliance-{transaction_id}`. The Java worker ships as a drop-in replacement for the Ch 6/7 Python compliance side, and the Python chapter already prefixes its own workflow IDs with `ch07-` (per the CLAUDE.md note about chapter-prefixed IDs preventing cross-chapter Event History collisions). Without this change, attendees swapping the Python compliance worker for the Java one mid-Ch 7 would land on a different workflow ID namespace and lose the property that re-running an earlier chapter doesn't trip `WorkflowAlreadyStartedError`.
The previous `add interactive game` commit landed `game/index.html` with
no README context, so attendees discovering the file in the repo had no
way to know what it was, how to open it, or how it related to the
chapters. This commit fills that gap.
Two changes to `README.md`:
1. Directory tree (top of the README) gains a `game/` entry alongside
`exercises/` and `polyglot/`, with a one-line description so the
visualizer is visible from the same place attendees look to orient
themselves to repo layout.
2. A new "Topology sandbox (`game/index.html`)" section sits between
the Temporal dev-server setup and the Polyglot section. It covers:
- What the file is: a single-file browser sandbox with no
dependencies, no server, and no connection to a live cluster.
- How to open it (`open` / `xdg-open`).
- What stopping a service demonstrates: workflows go to "blocked",
not "failed", and the `Lost` counter never increments — the point
of the visualization.
- Framing as a Chapter 5+ orientation aid (workflow-backed async
topology, two namespaces, Nexus endpoint as a node) rather than
a debugging tool tied to running code, so readers understand why
the Ch 1 monolith and Ch 3 sync handler are not represented.
- The repo/Instruqt split: this file is the self-paced fiddle
version checked into the code repo; the guided day-of student
experience lives in Instruqt with a talk track authored
separately.
No code or chapter content changed. The visualizer itself is untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mainis two files inexercises/05_async_operations/— the rest ofinit's history was already merged via PR Init #1's merge commit.ThreadPoolExecutorblock, prints, andawait worker.run()are already in place in the exercise. The attendee now just adds three arguments (workflows,activities,activity_executor) to the existingWorker(...)call instead of rewriting the whole block.Test plan
exercises/05_async_operations/exercise/, complete TODO 8 by adding the three lines, then runuv run python -m compliance.workerand confirm it boots.uv run python -m payments.workeranduv run python -m payments.starterand confirm the Nexus call intoComplianceWorkflowcompletes.