docs(website): add month-end close capstone tutorial - #878
Merged
Conversation
Deploy Outputs
|
Adds "Close the Monthly Books", a capstone tutorial that composes an
orchestration, a workflow, a trigger, and a human approval into one
governed process. The existing graph tutorials each teach a single
mechanism in a toy domain; none of them shows the layers working
together, and none uses a domain where the branch condition has a right
answer.
A financial close does. Every routing decision in the graph is JSON
Logic arithmetic over ledger figures, so the same books always take the
same path, and the one agent node writes a variance memo with no
output_schema and nothing parsing its text. The model gets the last word
on wording, never on control flow.
What it demonstrates:
- Three reconciliations as parallel start nodes converging on an
activation_group join with activation_condition "all"
- A condition node routing on a computed variance vs. tolerance, with
the untaken branch recorded as skipped
- validate-orchestration as a pre-flight check
- A workflow with a backward transition (request_rework), which is the
concrete reason a period is not a DAG
- A transition carrying both gates: a deterministic guard first, then
requires_approval, so a human is only asked about something the
arithmetic already accepted
- The same approvals queue serving a workflow transition rather than an
orchestration node
- Schedule and manual triggers, and the task transition history as an
audit trail
Also documents the input/state namespace rule the validator enforces:
run input reads as {"var": "input.x"}, while a bare {"var": "x"} reads a
state key an upstream node wrote and is never satisfied from run input.
Wires the page into sidebars.ts, adds a capstone routing line to the
tutorials index, and cross-links it from the orchestrations, workflows,
and approvals module pages.
The tutorial is deliberately left out of tests/.tutorialsignore so the
tutorials-test job validates it end to end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjeWpxDqRDtkSv1mCDspf9
The guard-rejection step used a trailing inline `# → expect-fail`. The tutorial runner only matches an annotation at the start of a line, so an inline one reaches the shell as an ordinary comment and is lost — the step would then fail the run on its (expected) non-zero exit. Moved onto its own line before the command, which is the only placement `tests/tutorials-tests.sh` honors and the one every case in tests/harness/tutorialsRetryAnnotation.test.mjs uses. The stale guidance that produced this is corrected in #881. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AjeWpxDqRDtkSv1mCDspf9
The first real CI run of this tutorial (tutorials-test on #878) failed and exposed three wrong assumptions. All three were mine; the annotation fix in the previous commit is what let the run get far enough to reveal them. 1. `start-orchestration-run` does not run the graph synchronously. It enqueues and returns `status: "queued"` with an empty `state`, and a worker drives it. Every `.state.*` read in steps 5 and 6 came back null, so `EXCEPTION_VARIANCE` was the literal string "null" and landed in the task payload as `total_variance: null`. Both steps now capture the run id, poll `get-orchestration-run` with `# → retry N` until the run is terminal, and read results from there. The SDK tabs gain a `waitForRun` helper and the curl tabs an `until` loop. (`orchestrate-a-sonnet` documents a synchronous-looking `succeeded` response for the same call. It passes CI only because it prints that response without asserting on it — worth a separate look.) 2. A `guard` and `requires_approval` on the same transition are not evaluated together. Firing an approval-gated transition parks the approval **first** and re-evaluates the guard when the item resolves. Step 10 claimed the opposite — that the guard refuses before any human is asked — and asserted a failure that does not happen: the CI run returned 200 with `pending_transition: "close_period"`. The step no longer fires `close_period` early, and the prose now describes the real ordering plus what it buys: a sign-off cannot be collected while the books balance and then applied after they stop balancing, because the deterministic check runs last. 3. While an approval is pending, no other transition may fire. The old step 10 fired `close_period` and then `request_rework`, which would have been blocked by the pending gate. The rework move now happens before any sign-off is requested. Also documents both findings in How It Works, since the async-run and gate-ordering rules are the two things a reader is most likely to get wrong when building on this. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AjeWpxDqRDtkSv1mCDspf9
Step 11 was retitled when the guard/approval ordering was corrected, so the anchor in approvals.md no longer resolved and the Docusaurus build failed on it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AjeWpxDqRDtkSv1mCDspf9
arantespp
force-pushed
the
claude/soat-graphed-system-z169oo
branch
from
August 8, 2026 02:03
6395cea to
b2bd195
Compare
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
Adds Close the Monthly Books, a capstone tutorial that composes an orchestration, a workflow, a trigger, and a human approval into one governed process.
The eight existing graph tutorials each teach a single mechanism, almost all of them in the sonnet/poem domain. Two gaps followed from that: nothing showed the layers working together, and no tutorial used a domain where the branch condition has a right answer — so every assertion could only ever be
status == succeeded.A financial close fixes both. Every routing decision in the graph is JSON Logic arithmetic over ledger figures, so the same books always take the same path and the tutorial can assert the business outcome (
bank_variance == 1250) rather than just the plumbing. The single agent node writes a variance memo, declares nooutput_schema, and nothing downstream parses its text — the model gets the last word on wording, never on control flow.What it demonstrates
activation_groupjoin withactivation_condition: "all"conditionnode routing on computed variance vs. tolerance, with the untaken branch recorded asskippedvalidate-orchestrationas a pre-flight check before createrequest_rework) — the concrete reason a close period is not a DAGguardfirst, thenrequires_approval, so a human is only asked about something the arithmetic already acceptedscheduletriggers, and the task transition history as an audit trailIt also documents a contract that was previously only discoverable from the validator source: run input reads as
{"var": "input.x"}, while a bare{"var": "x"}reads a state key an upstream node wrote and is never satisfied from run input.Changes
docs/tutorials/close-the-monthly-books.mdsidebars.tsdocs/tutorials/index.mddocs/modules/orchestrations.mddocs/modules/workflows.mddocs/modules/approvals.mdNo product code changes.
Design decisions
Recorded per the open-questions rule:
Verification
node scripts/docs-lint.mjs— OK, 80 files, no violationspnpm --filter @soat/website build— exit 0, no broken links or anchors (it caught one bad anchor,workflows#tasks, nowworkflows#task)run's— which would have desynced the tutorial runner's line accumulator); all 13<Tabs>groups carry cli/sdk/curl; every step has an inline doc link; no unbacketed braces that MDX would parse as JSXsrc/rest/openapi/v1/*.yamlandPUBLIC_ID_PREFIXES. Three inferences were wrong and were corrected:getTaskHistoryreturns a bare array (not{data}), the history field isprincipal_kind(notprincipal), andupdateTaskisPATCH(notPUT).Not verified locally: the Docker daemon is unavailable in this environment, so the tutorial was never executed against a live server. The tutorial is deliberately not added to
tests/.tutorialsignore, so thetutorials-testjob is the verifier — please treat a red run there as the real signal, and note thatqwen2.5:0.5bonly touches theexceptionbranch in Step 6.Example IDs in expected output use the elided
orch_run_...form rather than invented literals, matchingapproval-gate.md.Follow-ups (not in this PR)
orchestrate-a-sonnet-with-workflowsstays in.tutorialsignore; its asyncon_enterdispatch needs# → retry Non the forward-progress steps. Separate PR whose only real deliverable is the CI signal.SOAT_URLin its curl tabs and has no SDK tab, both against.claude/rules/tutorials.md.@docusaurus/theme-mermaidwould let the graph diagrams in this and the other orchestration tutorials render properly.Related: #872, #873, #877
🤖 Generated with Claude Code
https://claude.ai/code/session_01AjeWpxDqRDtkSv1mCDspf9
Generated by Claude Code