Rework challenge 01, and fix defects found walking the lab - #12
Merged
Conversation
Challenge 01 now runs: run the payments, see them in the Temporal UI, review the architecture, then find the coupling in code. The UI section opens payment-TXN-C and reads its result out of the Results panel. success is false, error is null, and the Workflow is Completed, which is the distinction worth planting before challenge 04 sends that same result across a team boundary: a declined payment is an outcome, a failed Workflow is a defect. The diagram section points at step 5 of 8 (the arrow crossing team zones inside one process) and step 8 of 8 (Compliance owning an empty Namespace). Find the Coupling shows both register lines instead of describing them, so nobody has to guess which two lines put two teams in one process. Diagram fixes: - The step 2 edge label ran over the node text on both sides. It was ~300px of centred text in a 70px gutter. Edge labels now wrap on \n and that one is two short lines, widest ~66px. The boundary wording was already in the step 5 caption, so nothing was lost. - The Task Queue was only in the click-to-open detail panel. It now sits on the dashed process band, which wraps both team zones, so the outline containing both teams' code is the thing labelled with one queue. Event history types are written the way the UI writes them, with spaces. The image is rebuilt and re-pinned because diagrams/ is baked in. Verified the new digest carries both diagram fixes before pinning it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things that cost time on this pass: UI event types are written with spaces on screen, showing a line beats describing it, and an edge label wider than the gutter silently covers node text because they render in different layers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Maintenance mode is owner-only, which stops attendees from launching the track. Turn it off so the workshop can run. The CLI drops `maintenance: false` from track.yml on write, because its serializer omits false values. The off state is therefore an absent key rather than an explicit false. Documented in the README, because an absent key reads like a forgotten setting. Side effect: the create-instruqt-tutorial guardrail hook refuses to run any command whose text contains the word it guards on while a track is out of maintenance mode, so shipping from here needs a human to run the last git step by hand. Also documented. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three separate cases of the same problem: prose in assignment.md restating a TODO comment the learner is about to read anyway. - Challenge 02 lost "One Rule That Bites" entirely. TODO 3 already explains that the runtime validates every method at Worker startup and names the exact "Missing @operation annotation" failure. - Challenge 03 lost the checkCompliance-slow / submitReview-fast explanation, which was TODO 5 and TODO 6 nearly line for line, down to the 10 second cutoff and the duplicate-check-on-retry consequence. Replaced with a pointer to read both TODOs before writing either. - Challenge 03 also lost "A Worker only handles work it has been told about", TODO 7's opening sentence verbatim. Replaced with which of the three registrations fails silently, which is the part worth knowing up front. Challenge 03's body drops from 809 to 507 words, which also helps its 14 minute slot. Kept deliberately: the symptom table in challenge 03, because a lookup table for someone already stuck is a different job from instruction, and the declined-payment-is-not-a-failure line in challenge 01, because it is the payoff for reading TXN-C's result in the UI. Also removed "The annotation lives in io.nexusrpc." from TODO 1. The KDoc above it already points at the Java docs and the Ask AI button, which is the intended route. Added kotlin/.dockerignore. bin/ is git-ignored but was still being copied into the sandbox image, so the image carried a second, stale copy of every .kt file, old TODO comments included. Confirmed present in the image before this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pins 67f4c767, which carries the io.nexusrpc removal and both diagram fixes. Verified inside the running image rather than assumed: the TODO 1 comment no longer names the package, the process band shows the Task Queue, and the step 2 edge label is the wrapped two-line form. Platform is linux/amd64. The .dockerignore added in the previous commit did nothing. A bare `bin/` matches only <context>/bin, not decouple-monolith/exercise/bin, so the image still shipped a stale duplicate of every .kt file. The leading **/ is what makes it recursive. Verified with a local build that is never published: bin/ gone, src/ and gradlew and diagrams/ all intact, context transfer down from 54kB to 8.7kB. 67f4c767 predates that fix, so it still carries bin/. Harmless: code-server opens scoped to src/main/kotlin and nothing points at bin/. It drops out on the next rebuild. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Checksum only. The CLI recomputes it on every publish and writes it into the local file, so committing it keeps the next delta check quiet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A run of `=` in an assignment desynchronises Instruqt's renderer. The Worker's startup banner was in a `bash,nocopy` fence, and the leading `====` line was swallowed into a collapsible divider along with the fence opener. The trailing `====` then formed a setext h1 out of the two lines above it, and every fence after that point was off by one, so the closing fence opened a code block that ate the next several paragraphs and printed the following ```bash,nocopy marker as literal text. The raw markdown was correct and `instruqt track validate` passed, so nothing caught it. Nine other nocopy fences in this track render fine; the `=` runs are the only difference. Replaced it with one realistic output block: the Gradle task line, the stubs line, all three MultiThreadedPoller lines, and the banner text without its `=` separators. That also removes the separate poller fence, which was showing the same Nexus Poller line twice. The three pollers now appear together, so "Workflow and Activity but no Nexus means your handler is not registered" is checkable against what is on screen. Also removed the note about the editor having no autocomplete from TODO 7. It described the tooling rather than the code. Pins 16cb8c55. Verified inside the running image: both comment removals present, diagram fixes present, stale bin/ finally gone now that .dockerignore uses **/, linux/amd64. Context transfer dropped 54kB to 11kB. Documented the `=` trap in INSTRUQT.md with a pre-publish grep. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Checksum only, recomputed by the CLI on publish. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TODOs 10 and 11 became a single TODO 10, and the file dropped from 102 lines to 81. The comment is now operational only: replace one registration with the block given, fill the one blank, delete the other registration and its two imports. The reasoning moved to the assignment, where prose belongs, and appears once. The Endpoint-belongs-on-the-Worker point is the part worth keeping, because it is why the Workflow stays reusable. Exercise TODOs are now 1 to 10. Nothing else referenced TODO 11; the pointer in PaymentProcessingWorkflowImpl already said TODO 10. Fixes a real defect found while checking this. The exercise's startup banner is hardcoded to the monolith text, so a learner who completes TODO 10 correctly still reads "ComplianceActivity (monolith, will decouple)". Challenge 04 told them to look for the solution's banner instead, which they never write. The assignment now says the banner is stale text and to ignore it. Challenge 01 keeps referencing that same banner, correctly, because there the coupling really is written down in it. Both trees compile. Challenge 04 body is down to 440 words. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries the merged TODO 10. Verified inside the running image: one TODO marker in PaymentsWorkerApp.kt, no TODO 11, 81 lines, and the three uncommented registration lines intact so challenge 01 still runs the monolith. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Checksum only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request updates the Kotlin Nexus “decouple-a-monolith” Instruqt track content to reflect a reordered learning flow for challenge 01 (run → observe in UI → review architecture → read code), fixes several track-rendering and packaging defects encountered during lab walkthrough, and refreshes supporting documentation/diagram presentation.
Changes:
- Reworked Instruqt challenge assignments (especially challenge 01) to emphasize observing behavior in Temporal UI and the architecture diagram before diving into source.
- Fixed track correctness/operability issues (maintenance mode behavior, sandbox digest repin,
.dockerignoreto exclude build outputs, UI event naming alignment). - Improved the monolith architecture diagram labeling (task queue emphasis + wrapped edge labels) and consolidated/cleaned exercise TODO guidance.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates Kotlin track structure/docs, tab count, ports, and maintenance-mode guidance. |
| kotlin/instruqt/track.yml | Removes maintenance: true and updates checksum after track changes. |
| kotlin/instruqt/config.yml | Re-pins the sandbox image digest; confirms ports include 8090 for the diagram tab. |
| kotlin/instruqt/01-run-the-monolith/assignment.md | Reorders and expands challenge 01 to include Temporal UI + architecture diagram first. |
| kotlin/instruqt/02-the-shared-contract/assignment.md | Removes duplicated guidance now covered by code TODOs. |
| kotlin/instruqt/03-the-compliance-side/assignment.md | Simplifies narrative, replaces problematic ==== banner output, and emphasizes proof signals. |
| kotlin/instruqt/04-the-payments-side/assignment.md | Updates directions around endpoint mapping/decoupling and corrects banner expectations. |
| kotlin/instruqt/05-durability-and-human-review/assignment.md | Aligns event-history naming with Temporal UI wording. |
| kotlin/diagrams/monolith-architecture.html | Improves process-band labeling (task queue) and wraps long edge labels to avoid overlap. |
| kotlin/decouple-monolith/exercise/src/main/kotlin/shared/nexus/ComplianceNexusService.kt | Refines TODO wording (removes redundant annotation package mention). |
| kotlin/decouple-monolith/exercise/src/main/kotlin/payments/temporal/PaymentsWorkerApp.kt | Consolidates TODO guidance (TODO 10/11 → TODO 10) and clarifies endpoint mapping vs decoupling edits. |
| kotlin/decouple-monolith/exercise/src/main/kotlin/compliance/temporal/ComplianceWorkerApp.kt | Trims non-essential commentary in TODO guidance. |
| kotlin/.dockerignore | Ensures build outputs like **/bin/ don’t get baked into the sandbox image context. |
| INSTRUQT.md | Documents renderer traps, assignment-wording rules, and diagram constraints for future maintenance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+119
to
+122
| [main] INFO io.temporal.serviceclient.WorkflowServiceStubsImpl - Created WorkflowServiceStubs for channel: ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=127.0.0.1:7233}} | ||
| [main] INFO io.temporal.internal.worker.MultiThreadedPoller - start: MultiThreadedPoller{name=Workflow Poller taskQueue="compliance-risk", namespace="compliance-namespace", identity=11418@workshop} | ||
| [main] INFO io.temporal.internal.worker.MultiThreadedPoller - start: MultiThreadedPoller{name=Nexus Poller taskQueue="compliance-risk", namespace="compliance-namespace", identity=11418@workshop} | ||
| [main] INFO io.temporal.internal.worker.MultiThreadedPoller - start: MultiThreadedPoller{name=Activity Poller taskQueue="compliance-risk", namespace="compliance-namespace", identity=11418@workshop} |
| every publish: | ||
|
|
||
| ```bash | ||
| grep -rn '^ *==*$' */assignment.md |
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.
Follow-up to #9, reworking challenge 01 and fixing defects found while walking the lab. Everything here is already published to Instruqt and verified in a running sandbox.
Challenge 01 now goes: run it, see it, understand the shape, then read the code
Old order sent learners from the starter output straight into source. New order:
Run Three Payments→See It in the Temporal UI→Review the Monolith Architecture→Find the Coupling→See the Empty NamespaceThe UI section lands on
payments-namespace, gives a Workflow ID table, and openspayment-TXN-Cto read its result:{ "success": false, "status": "DECLINED_COMPLIANCE", "riskLevel": "HIGH", "error": null }successis false,erroris null, and the Workflow is Completed. That distinction is worth planting in challenge 01, because challenge 04 sends the same result across a team boundary and it should look identical: a declined payment is a business outcome, a failed Workflow is a defect.The diagram section points at step 5 of 8 (the arrow crossing team zones inside one process) and step 8 of 8 (Compliance owning an empty Namespace).
Find the Couplingnow shows both register lines rather than describing them, so nobody has to guess which two lines put two teams in one process.Defects fixed
A run of
=desynchronises Instruqt's renderer. The Compliance Worker's startup banner was in abash,nocopyfence. The opening fence and the leading====line were swallowed into a collapsible divider; the trailing====then formed a setext<h1>from the two lines above it; every fence after that point was off by one, so the closing fence opened a code block that ate several paragraphs of prose and rendered the next```bash,nocopymarker as literal text.The raw markdown was correct and
instruqt track validatepassed, so nothing caught it. Nine othernocopyfences in the same track render fine. Replaced with one realistic output block: the Gradle task line, the stubs line, all threeMultiThreadedPollerlines, and the banner text without its separators. That also removed a duplicate showing the Nexus Poller line twice.Challenge 04 told learners to look for a banner they never write. The exercise's
printlnis hardcoded to the monolith text, so a learner who completes the Endpoint mapping correctly still readsComplianceActivity (monolith, will decouple). The assignment claimed a Nexus line replaced it. The verification step immediately after the hardest edit in the lab would have contradicted itself. Now the assignment says the banner is stale text and the proof comes from the run. Challenge 01 still references that banner, correctly, because there the coupling really is written down in it..dockerignoreshippedbin/anyway. A barebin/matches only<context>/bin, notdecouple-monolith/exercise/bin, so the image carried a stale second copy of every.ktfile including old TODO text. Fixed with**/. Context transfer went 54kB to 11kB.Text the code already said
Per the "let the code guide the TODOs" convention:
One Rule That Bitesentirely. TODO 3 already explains that the runtime validates every method at Worker startup and names the exactMissing @Operation annotationfailure.checkCompliance-slow /submitReview-fast explanation, which was TODO 5 and TODO 6 nearly line for line, andA Worker only handles work it has been told about, which was TODO 7's opening sentence verbatim. Body dropped 809 to 507 words.PaymentsWorkerApp.ktTODOs 10 and 11 became one short TODO 10, file 102 lines to 81. The comment is operational only; the reasoning lives in the assignment once. Exercise TODOs are now 1 to 10.The annotation lives in io.nexusrpc.from TODO 1 and the editor-has-no-autocomplete aside from TODO 7.Diagram
\nand that one is two short lines. The boundary wording was already in the step 5 caption.Other
Event history types are written the way the UI writes them: Activity Task Scheduled, not
ActivityTaskScheduled. A learner told to find the camel-case string is scanning for something that is not on screen.Maintenance mode is off, so attendees can launch the track. Note that
instruqt track pushdropsmaintenance: falsefrom the file because its serializer omits false values, so the off state is an absent key rather than an explicit false.INSTRUQT.mdgained the=trap with a pre-publish grep, the assignment-wording rules, and the note that a long edge label silently covers node text because the two render in different layers.Verification
0896ac82,linux/amd64. Verified inside the running image: one TODO marker inPaymentsWorkerApp.kt, no TODO 11, 81 lines, three uncommented registration lines intact so challenge 01 still runs the monolith, both comment removals present, diagram fixes present,bin/gone.instruqt track validateclean; no=runs anywhere; every tab button verified by label rather than index arithmetic.instruqt track pullreports up to date).Not verified by eye and worth a look after merge: the wrapped step-2 label on the Monolith Architecture tab. That fix is arithmetic against the gutter width, not observation.
This branch never modified
.github/, so the merge keeps main's CI fix from #10.🤖 Generated with Claude Code