# Annalward > Audit Temporal Python (temporalio) workflows before they ship - will the workflow code replay deterministically, will the SDK accept every activity call, are retries bounded and side effects idempotent, do saga compensations run first-registered-last, are failures and cancellations re-raised, does the history stay bounded - then fix them. The free check runs in the browser and uploads nothing: it lexes pasted Python (one or more files, plus pyproject.toml for the temporalio version), rebuilds its blocks from indentation, inventories every @workflow.defn class and its run, signal, query and update handlers, every @activity.defn function and whether it heartbeats, every activity call with its timeouts and retry policy, every compensation list and the order it is replayed in, and every Worker and what it registers, and runs 21 deterministic checks drawn from durable-execution practice. The paid audit rates determinism, activities, compensation and operations, confirms or dismisses every flag and gives exact before/after edits per finding; the paid fix lane writes the whole fixed file(s) and a changes list tied to the flags. The page checks every quote and line against the paste, applies every edit and file back into the paste and re-runs its checks, and calls out a reply that repeats a credential from the paste. App: https://annalward.skillsafe.ai/ API tutorial: https://annalward.skillsafe.ai/api.html ## Input - The code pasted into one box, each file introduced by a `# file: ` line; dropped files or a dropped package folder are joined that way automatically. A paste with no `# file:` line is read as one file (workflows.py). pyproject.toml, when pasted, gives the temporalio and Python versions. - Nothing is imported or run and no Temporal server is contacted: every flag is a reading of the text, with the file and line it points at. Strings and comments are lexed out first, so a string that mentions datetime.now( is never a finding. - Workflow code is the methods of @workflow.defn classes. Helpers imported from other modules, whether an external API is idempotent, and how a change replays against executions already running are outside the check (TW-22 says so); replaying a recorded history with temporalio.worker.Replayer stays the ground truth for non-determinism. ## Flags the free check raises - TW-01 (high): the system clock in workflow code - datetime.now/utcnow/today, date.today, time.time, time.monotonic, time.perf_counter. Use workflow.now() or workflow.time(). - TW-02 (high): randomness in workflow code - random.*, uuid.uuid1/uuid4, secrets.*, os.urandom, numpy.random. Use workflow.random() or workflow.uuid4(). - TW-03 (high): network, file, database or subprocess I/O in workflow code - requests, httpx, urllib, socket, boto3, an async client session, sqlite3/psycopg/redis/pymongo, subprocess, open(), Path.read_text. It belongs in an activity. - TW-04 (high): time.sleep in workflow code. Use await asyncio.sleep(...), a durable timer. - TW-05 (high): threads, executors, asyncio.to_thread, run_in_executor or threading locks in workflow code. - TW-06 (medium): workflow code that changes a module-level list, dict or set (a mutating method, a subscript assignment, or a global statement). - TW-07 (low): the stdlib logger (logging or a logging.getLogger variable) or print in workflow code, one flag per workflow. Use workflow.logger. - TW-08 (high): workflow.execute_activity / start_activity (and the _method and local variants) with neither start_to_close_timeout nor schedule_to_close_timeout. - TW-09 (high): an activity timeout given as a number instead of a timedelta. - TW-10 (high): an activity call with more than one positional argument after the activity (use args=[...]). - TW-11 (low): activity calls that can retry forever - no retry policy with maximum_attempts and no schedule_to_close_timeout; one flag per workflow with every line. - TW-12 (medium): heartbeat_timeout set at the call site of a pasted activity that never calls activity.heartbeat(). - TW-13 (medium): a blocking call (time.sleep, requests, sync database, subprocess, thread queue) inside an async def activity; one flag per activity. - TW-14 (low): an activity that loops over slow work (awaits, network or database calls) and never heartbeats, when no call site sets heartbeat_timeout. - TW-15 (medium): a compensation list (a list in workflow code named compensations/undo/rollback/saga) replayed in the order it was filled. - TW-16 (medium): a compensation appended right after the awaited step it undoes, built from that step's result - so a step that did its side effect and then failed is never compensated. - TW-17 (medium): an except in workflow code that catches the workflow's cancellation (CancelledError, BaseException, bare) and does not re-raise it, or that catches an activity's failure and neither re-raises, returns nor compensates. - TW-18 (medium): a while True / while flag loop in a workflow's run method with no continue_as_new anywhere in the workflow. - TW-19 (medium): a @workflow.query handler that assigns or mutates self state. - TW-20 (medium): a workflow class or a called activity missing from every pasted Worker's workflows=[...] / activities=[...] (literal lists only). - TW-21 (high): a Worker that registers sync (def) activities with no activity_executor. - TW-22 (info): context - counts, the temporalio version, what is outside the check. ## Free extras - Every workflow with its run, signal, query and update handlers; every activity call with its start_to_close, schedule_to_close and heartbeat timeouts and its retry bound; every activity and whether it heartbeats; every compensation list and the order it is replayed in; every Worker with its task queue, registrations and executor. - Put any audit finding (or all of them, or the fixed files) back into the paste and re-run the check; undo puts the original back. - Exports: the check as Markdown, the flags as CSV, the edited or fixed code, the findings or changes as CSV, the whole result as JSON. ## Paid lanes (the `task` field) - `audit`: `{task, verdict: blocked|needs_work|ready, headline, system, areas: [{area: determinism|activities|compensation|operations, status: sound|fragile|broken, note}], findings: [{id, flags, file, line, severity, area, title, evidence, why, edits: [{before, after}]}], flag_answers: [{flag, status: confirmed|dismissed, reason}], plan, questions, summary}`. `before` is an exact slice of the file, applied in order. - `fix`: `{task, verdict: fixed|partial, headline, files: [{path, content}], changes: [{file, function, flags, change, why}], verify, open: [{item, flags, reason}], questions, summary}`. `content` is the whole file; every function keeps its name, parameters, def or async def and Temporal role. - Run input: `{task, code, facts, note?, audit_handoff?, retry_note?}` - all strings; `facts` is the page's analysis as a JSON string. The audit hands its findings to the fix lane with every edit applied to `code`. - Model: gpt-terra, 10% publisher markup. A run needs a signed-in account; the reservation shown before a run is a hold, and only what the run uses is charged. ## Sources - workflow-orchestration-patterns by Seth Hobson, wshobson/agents (github.com/wshobson/agents, plugins/backend-development/skills/workflow-orchestration-patterns), MIT - https://skillsafe.ai/skill/@wshobson/workflow-orchestration-patterns - The checks and the reconciliation are original to this app. Temporal is a trademark of Temporal Technologies Inc. and Python of the Python Software Foundation; this app is not affiliated with or endorsed by either.