How We Used the Temporal Claude Skill to Upgrade a FastAPI Demo Into a Durable Workflow App
A hands-on account of migrating an in-memory FastAPI order dashboard to Temporal-backed durable workflows — using the Temporal Claude Skill as an architectural co-pilot, not just a code generator
|
TL;DR We recently built a team demo to show two things simultaneously: a practical Temporal integration pattern, and how the Temporal Claude Skill accelerates real implementation work. This post is the full technical walkthrough — including the prompt flow our engineer used, the architectural decisions, the skill shaped, the rough edges encountered along the way, and the three lessons that apply to any team introducing Temporal through a small, existing application. |
The Starting Point: Good UI, Weak Orchestration
The demo project was a presentable FastAPI app — an Order Ops dashboard with a clean interface that could create orders, advance order status manually, cancel orders, and display metrics. It looked credible in a demo context.
The problem was behind the UI. The backend lifecycle was in-memory and imperative. State lived in a Python dictionary. Status transitions were direct function calls. There were no retries, no durability guarantees, and no audit trail. If the process restarted mid-operation, everything was lost.
That combination — presentable frontend, fragile orchestration — made it exactly the right kind of project for a Temporal migration demonstration. The goal was to keep all frontend behavior identical while replacing the orchestration layer with Temporal workflows. Engineers watching the demo would see no visible change in the UI, but the backend story would be fundamentally different.
Why We Chose to Use the Temporal Claude Skill
Our engineer could have built this from scratch against the Temporal Python SDK documentation. The reason we used the Temporal Claude Skill was not to generate boilerplate faster — it was to get guardrails throughout the implementation, not just at the start.
The specific value we expected from the skill:
- Python SDK structure recommendations that reflect production patterns, not tutorial patterns
- Guidance on deterministic workflow boundaries — what belongs in a workflow function versus an activity
- Signal and query usage patterns appropriate for a dashboard-style interaction model
- Practical worker setup that would work in a real dev environment, not just a hello-world context
The skill delivered on all four. The framing it consistently reinforced was this:
|
The Rule That Prevented Most Mistakes Put orchestration in workflows. Put non-deterministic side effects in activities. This single architectural boundary, consistently applied, prevented the most common class of Temporal implementation errors before they were written. |
This is the difference between using an AI tool for code generation and using it as an architectural co-pilot. The skill did not just produce code — it kept our engineer on the right side of Temporal’s determinism requirements throughout the implementation.
The Prompt Flow Our Engineer Used
The sequence below is the actual progression of prompts used during the implementation. It is deliberately ordered: architecture first, integration second, polish last. Jumping to integration before the architecture is settled is where most Temporal implementations accumulate technical debt.
| # | Prompt | Intent |
| 1 | Use Temporal Claude skill with Python. Integrate Temporal into this FastAPI order demo. | Establish context: skill + language + project type upfront |
| 2 | Create workflow + activity + worker with clean file separation. | Architecture before integration: get the module structure right first |
| 3 | Use signals for user actions (advance, cancel) and query for dashboard state. | Drive the interaction model from the domain, not from Temporal defaults |
| 4 | Make workflow deterministic and move side effects to activities. | Enforce the architectural boundary before writing business logic |
| 5 | Wire existing API routes so frontend behavior stays the same, backend is Temporal. | Integration last: preserve the external contract, change the internals |
| 6 | Document run commands for dev server, worker, and API. | Operational completeness: the demo must run without tribal knowledge |
What Changed in the Codebase
The migration touched four areas. In each case the change was additive — new modules were introduced, and the existing FastAPI routes were rewired rather than replaced.
1. Dependency
A single addition to requirements.txt:
|
temporalio |
2. Temporal Module Structure
The skill’s guidance on Python project layout resulted in a dedicated Temporal package with clean separation between concerns:
|
app/ temporal/ client.py # Temporal client initialization and connection activities.py # All side-effecting logic lives here workflows.py # Orchestration logic: deterministic, no I/O worker.py # Worker entrypoint: registers workflows and activities |
This separation is not just organizational preference. It is enforced by Temporal’s execution model: workflow code must be deterministic and replay-safe, which means any non-deterministic operation — a database call, an HTTP request, time.time() — must live in an activity, not in workflow code. The file structure makes that boundary visible and auditable.
3. Workflow Design
The OrderWorkflow owns the order lifecycle state and exposes three interaction points:
- request_advance signal: transitions the order to the next status step
- request_cancel signal: moves the order to a CANCELLED terminal state
- get_state query: returns the current workflow state to any caller without mutating it
The workflow loops until one of three terminal states is reached: CONFIRMED, CANCELLED, or FAILED. Each state transition executes a single activity call to handle the side effects of that step.
|
# workflows.py (simplified) @workflow.defn class OrderWorkflow: def __init__(self) -> None: self._state: OrderState = OrderState( status=OrderStatus.PENDING, advance_requested=False, cancel_requested=False, ) @workflow.signal async def request_advance(self) -> None: self._state.advance_requested = True @workflow.signal async def request_cancel(self) -> None: self._state.cancel_requested = True @workflow.query def get_state(self) -> OrderState: return self._state @workflow.run async def run(self, order_id: str) -> str: terminal = {OrderStatus.CONFIRMED, OrderStatus.CANCELLED, OrderStatus.FAILED} while self._state.status not in terminal: # Wait for a signal or check current flags await workflow.wait_condition( lambda: self._state.advance_requested or self._state.cancel_requested ) if self._state.cancel_requested: self._state.status = OrderStatus.CANCELLED break # Delegate side effects to activity self._state.status = await workflow.execute_activity( process_order_step, args=[order_id, self._state.status], start_to_close_timeout=timedelta(seconds=30), ) self._state.advance_requested = False return self._state.status.value |
4. FastAPI Route Integration
The key decision here was to rewire the existing routes rather than redesign them. The frontend contract stayed identical. The backend implementation changed entirely. This is what made the demo effective — engineers could see the UI behaving exactly as before, then inspect the Temporal UI and see durable workflow executions backing each interaction.
|
# Before: in-memory state mutation @router.post(‘/api/orders/{order_id}/advance’) async def advance_order(order_id: str): orders[order_id][‘status’] = next_status(orders[order_id][‘status’]) return orders[order_id] # After: Temporal signal to durable workflow @router.post(‘/api/orders/{order_id}/advance’) async def advance_order(order_id: str, client: Client = Depends(get_temporal_client)): handle = client.get_workflow_handle(order_id) await handle.signal(OrderWorkflow.request_advance) state = await handle.query(OrderWorkflow.get_state) return state |
The complete route mapping after migration:
- POST /api/orders → starts a new OrderWorkflow execution
- POST /api/orders/{id}/advance → sends request_advance signal
- POST /api/orders/{id}/cancel → sends request_cancel signal
- GET /api/orders → queries get_state across all active workflows
- GET /api/metrics → aggregates results from workflow state queries
Three Lessons From the Implementation
These are not theoretical observations. They are the specific points where the implementation hit friction or clicked into place.
Lesson 1: Toolchain Plumbing Matters as Much as Code
Before the application work began, time was lost on skill setup itself. The skill folder existed but linking failed because the ~/.agents directory was owned by root, producing EACCES errors and showing an empty agents array. The fix was a permissions change, not a code change.
This matters for teams introducing the Temporal Claude Skill to new environments: validate the toolchain end-to-end on a simple test prompt before your first real implementation session. The skill is not useful if it cannot be invoked, and debugging permission errors mid-implementation breaks the flow.
Lesson 2: Determinism Is a Mindset Shift, Not Just a Rule
In standard FastAPI code, the natural pattern is to update state directly and call side-effecting logic inline. That pattern breaks inside a Temporal workflow. The question to ask before every line of workflow code is: if this function were replayed from the beginning against the recorded event history, would it produce the same commands in the same order?
The skill enforced this consistently. Every time our engineer wrote something that looked like direct I/O or non-deterministic logic inside a workflow function, the skill redirected it to an activity. That consistent redirection is what made the resulting workflow code correct by default rather than correct by inspection.
Lesson 3: The Signal-Query Model Fits Dashboard Interactions Naturally
The insight that unlocked the architecture: an operational dashboard has exactly two kinds of interactions — commands that change state, and reads that observe state. In Temporal, those map directly to signals and queries respectively.
request_advance and request_cancel are commands: they change the workflow’s internal state by setting flags that the main workflow loop acts on. get_state is a read: it returns the current state without mutating anything and can be called at any time from any caller without affecting execution.
Once the team recognized that the dashboard’s interaction model and Temporal’s signal-query model were isomorphic, the architecture stopped requiring decisions and started requiring implementation.
Running the Demo Locally
The local run sequence requires three concurrent processes. This is a standard Temporal development setup: the Temporal dev server, the application worker, and the FastAPI server.
|
# Environment setup python -m venv .venv source .venv/bin/activate pip install -r requirements.txt |
|
# Terminal 1: Temporal development server # Provides the workflow orchestration backend locally # Temporal UI available at http://127.0.0.1:8233 temporal server start-dev # Terminal 2: Application worker # Registers OrderWorkflow and process_order_step activity # Polls the Temporal server for workflow and activity tasks python worker.py # Terminal 3: FastAPI application server # Serves the Order Ops dashboard UI and API routes uvicorn app.main:app –reload |
With all three running, the Order Ops dashboard is available at http://127.0.0.1:8000 and the Temporal UI at http://127.0.0.1:8233. The Temporal UI is where the demo value becomes visible: every order created in the dashboard appears as a running workflow execution, every advance action appears as a signal in the event history, and the full durability story is observable in real time.
Why This Migration Pattern Works for Internal Introductions
The “small existing app → workflow-backed app” migration pattern is one of the most effective ways to introduce Temporal to an engineering team for two reasons.
First, it grounds the concept in something concrete and familiar. Engineers watching the demo already understand what the Order Ops dashboard does. They do not need to build a mental model of the problem domain at the same time as they are building a mental model of Temporal. The familiar application lets them focus entirely on what Temporal adds.
Second, it demonstrates the migration path, not just the destination. Most teams introducing Temporal are not starting from scratch — they have existing services with existing behavior they cannot break. A demo that shows how to preserve external contracts while replacing internal orchestration is directly applicable to their situation. A greenfield Temporal demo is not.
The Temporal Claude Skill accelerated both the build and the architectural quality of this demo. The prompts were short. The implementation was fast. And the resulting code reflected production patterns rather than tutorial shortcuts — which is what made it worth presenting to an engineering team in the first place.
Closing Thoughts
The Temporal Claude Skill’s highest value in this project was not in the lines of code it produced. It was in the architectural decisions it prevented from being made wrong. The determinism boundary, the signal-query interaction model, the separation between workflow and activity responsibilities — all of these were things our engineer understood conceptually but the skill kept them applied consistently throughout the implementation rather than just at the start.
If your team is evaluating Temporal and looking for a low-risk way to build intuition for how it works in practice, this pattern — take a small application with a familiar interface, identify the lifecycle state it manages imperatively, and replace that with a workflow — is the right starting point. The Temporal Claude Skill is a useful co-pilot for that journey. The three lessons above are what to expect along the way.
Is your team still figuring out where Temporal fits before committing to it?
If the question on the table is “we understand Temporal conceptually but we’re not sure how it maps to our actual application” — that is exactly the gap a structured review can close before you start writing workflow code.
Xgrid offers two entry points depending on where you are:
- Temporal Launch Readiness Review — we review your target workloads, your planned workflow and activity patterns, and your failure-handling approach before you commit to production. Two weeks, fixed scope, concrete go/no-go scorecard.
- Temporal Reliability Partner — for teams that want a named Temporal expert embedded long-term to review new workflow designs as they are built, catch architectural decisions before they reach production, and mentor engineers on the patterns that matter.
Both are fixed-scope. No open-ended retainer required to get started.

