Skip to content

docs(website): add month-end close capstone tutorial - #878

Merged
arantespp merged 4 commits into
mainfrom
claude/soat-graphed-system-z169oo
Aug 8, 2026
Merged

docs(website): add month-end close capstone tutorial#878
arantespp merged 4 commits into
mainfrom
claude/soat-graphed-system-z169oo

Conversation

@arantespp

Copy link
Copy Markdown
Member

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 no output_schema, and nothing downstream parses 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 computed variance vs. tolerance, with the untaken branch recorded as skipped
  • validate-orchestration as a pre-flight check before create
  • A workflow with a backward transition (request_rework) — the concrete reason a close period is not a DAG
  • One 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
  • Manual and schedule triggers, and the task transition history as an audit trail

It 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

File Change
docs/tutorials/close-the-monthly-books.md New tutorial, 12 steps, CLI/SDK/curl tabs throughout
sidebars.ts Added to the Orchestration & Multi-Agent category
docs/tutorials/index.md New capstone routing line
docs/modules/orchestrations.md Related-tutorial deep link (Step 4)
docs/modules/workflows.md Related-tutorial deep link (Step 8)
docs/modules/approvals.md Related-tutorial deep link (Step 11)

No product code changes.

Design decisions

Recorded per the open-questions rule:

Q: New tutorial, or rewrite an existing one?
A: New — resolved by long-term. orchestrate-a-sonnet is a hub with ~12 deep
   step-anchor links from five module pages, so a domain rewrite there breaks
   them. orchestrate-a-sonnet-with-workflows is cheap to rewrite (3 refs, no
   anchors) but is one leg of a deliberate same-domain comparison triple, and
   a four-layer composition doc cannot serve as the minimal workflows entry
   point workflows.md links to. Checked: inbound link graph for both files.

Q: Which domain?
A: Month-end close — resolved by long-term (durability ladder: business rules
   become deterministic JSON Logic assertions rather than prose or LLM
   judgement). Checked: no existing tutorial uses a customer-financial domain,
   and create-an-agent-squad proves role-agent graphs pass CI when no agent
   node declares output_schema.

Q: Include a rendered graph diagram?
A: ASCII, not mermaid — resolved by pareto. Checked: @docusaurus/theme-mermaid
   is not installed and mermaid is unconfigured in docusaurus.config.ts, so a
   ```mermaid fence renders as a plain code block. Adding the dependency is a
   separate change and does not belong in a docs PR.

Verification

  • node scripts/docs-lint.mjs — OK, 80 files, no violations
  • pnpm --filter @soat/website build — exit 0, no broken links or anchors (it caught one bad anchor, workflows#tasks, now workflows#task)
  • Static checks on the extracted CLI blocks: all 8 multi-line JSON payloads parse after shell interpolation; single-quote parity even in every block (this caught an apostrophe in a bash comment — 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 JSX
  • Every command, field name, HTTP verb, and ID prefix cross-checked against src/rest/openapi/v1/*.yaml and PUBLIC_ID_PREFIXES. Three inferences were wrong and were corrected: getTaskHistory returns a bare array (not {data}), the history field is principal_kind (not principal), and updateTask is PATCH (not PUT).

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 the tutorials-test job is the verifier — please treat a red run there as the real signal, and note that qwen2.5:0.5b only touches the exception branch in Step 6.

Example IDs in expected output use the elided orch_run_... form rather than invented literals, matching approval-gate.md.

Follow-ups (not in this PR)

  • orchestrate-a-sonnet-with-workflows stays in .tutorialsignore; its async on_enter dispatch needs # → retry N on the forward-progress steps. Separate PR whose only real deliverable is the CI signal.
  • That file also uses SOAT_URL in its curl tabs and has no SDK tab, both against .claude/rules/tutorials.md.
  • Wiring up @docusaurus/theme-mermaid would 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

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploy Outputs

Package Stack Output Key Output Value
@soat/website SoatWebsite-claude-soat-graphed-system-z169oo BucketWebsiteURL http://soatwebsite-claude-soat-graphed-syste-staticbucket-g5l9mzjqlbtg.s3-website-us-east-1.amazonaws.com

claude added 4 commits August 8, 2026 02:00
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
arantespp force-pushed the claude/soat-graphed-system-z169oo branch from 6395cea to b2bd195 Compare August 8, 2026 02:03
@arantespp
arantespp merged commit 25c590d into main Aug 8, 2026
11 checks passed
@arantespp
arantespp deleted the claude/soat-graphed-system-z169oo branch August 8, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants