Skip to content

docs: restructure to Diátaxis and add versioned deploys - #348

Merged
btravers merged 2 commits into
mainfrom
docs/diataxis-restructure-versioned-deploy
Jul 28, 2026
Merged

docs: restructure to Diátaxis and add versioned deploys#348
btravers merged 2 commits into
mainfrom
docs/diataxis-restructure-versioned-deploy

Conversation

@btravers

Copy link
Copy Markdown
Collaborator

Reworks the documentation into the four Diátaxis modes and adds unthrown-style versioned deploys, so the stable line and the in-flight beta are published side by side.

Documentation

/guide/ (15 pages) is replaced by 31 pages:

Mode Pages
Tutorial 2 — your-first-workflow, adding-signals-and-queries
How-to 18 — install, define-a-contract, implement-activities, model-domain-errors, use-signals-queries-and-updates, index-workflows-with-search-attributes, run-child-workflows, handle-cancellation, continue-as-new, tune-activity-options, add-activity-middleware, intercept-client-calls, schedule-workflows, configure-a-worker, test-workflows, upgrade-to-v8, migrate-from-neverthrow, troubleshoot
Reference 5 — contract-surface, worker-surface, client-surface, errors, glossary
Explanation 6 — why-temporal-contract, the-result-model, validation-boundaries, workflow-determinism, architecture, nexus

Coverage gaps closed. Signals, queries, updates, search attributes, TypedScheduleClient, cancellation scopes, continue-as-new, and time-skipping tests were all implemented in packages/ but appeared in zero guide pages.

Correctness fixes

Each found by writing against the source rather than the old docs:

  • The activity implementation map is nested by workflow. README.md:55 showed a workflow-scoped activity implemented at the root level, which does not type-check (ContractResultActivitiesImplementations).
  • An activity that declares an errors map returns AsyncResult to the workflow, not a plain value — see activities-proxy.ts:30-58. The old docs showed try/catch. This changes the call site, so it is now called out as a deliberate trade in model-domain-errors.md and noted in the tutorial.
  • match({ err }) is errCases; signalWithStart takes signalName, not signal; getHandle returns an AsyncResult, not a bare handle.
  • unthrown documented as ^4.1.0; actual peer is ^5.0.0-beta.6 (README.md, .agents/rules/dependencies.md).
  • Zod 4: z.record(z.string()) needs two args; z.string().email() is deprecated.
  • tag(...) used in ~20 examples with no import shown.
  • The Nexus page claimed "Planned for v0.5.0" while the project is at 8.0.0-beta.1.

Versioned deploys

Ported from btravstack/unthrown. While .changeset/pre.json exists, the site builds twice — the latest stable tag at the root, main under /beta/ — with a dropdown linking them. With no prerelease in progress, main deploys alone to the root exactly as before.

/temporal-contract/       → v7.0.0 (stable)
/temporal-contract/beta/  → v8.0.0-beta.1 (beta)

Two things could not be copied verbatim:

  1. The stable-tag filter. unthrown drops prereleases with grep -v -- '-' over whole tags. Every tag here contains a hyphen inside @temporal-contract/, so that would have discarded all of them and produced an empty git worktree add. The version is now filtered after the package prefix is stripped.
  2. The injection anchor. This repo's nav sits inside withMermaid(defineConfig({...})), so the btravstack line is at eight spaces, not six. The script exits 1 on a mismatch rather than guessing, so this would have hard-failed the build.

New files: .github/scripts/inject-docs-version-nav.ts, docs/.vitepress/theme/version-switch.ts. docs/.vitepress/config.ts gains DOCS_BASE / DOCS_VERSIONS support, and all hardcoded site URLs now derive from the resolved base so the beta site canonicalizes to /beta/.

Verification

Not just inspected — run locally:

  • pnpm lint, pnpm typecheck, pnpm knip clean (knip now ignores .github/scripts/**, which is CI-invoked only).
  • pnpm --filter ./docs exec turbo build clean, 34 pages, no dead links.
  • Beta path: built with DOCS_BASE=/temporal-contract/beta/ — assets and canonical URLs correctly under /beta/, dropdown rendered with both entries.
  • Stable path: created a real worktree at @temporal-contract/contract@7.0.0, ran the injection script, pnpm install --frozen-lockfile, and built it — root base, dropdown injected, its own /guide/* pages intact.

Notes for review

  • Old /guide/* URLs will 404. No redirect shims, per the agreed scope. All in-repo links are updated (package READMEs, examples, docs/api/index.md); the four package CHANGELOG.md files still contain /guide/ links and were deliberately left alone as historical records.
  • No changeset. Nothing published changed. Happy to add one if you want it in the release notes.
  • Unrelated, not fixed here: packages/client/src/interceptors.ts:12,16 documents tapErr and flatMapErr in prose, but unthrown 5 only has the *ErrCases forms.

🤖 Generated with Claude Code

Replace the flat /guide/ layout with the four Diátaxis modes (tutorial,
how-to, reference, explanation) and close the coverage gaps: signals,
queries, updates, search attributes, schedules, cancellation scopes,
continue-as-new, and time-skipping tests were all implemented but
documented nowhere.

Correctness fixes found while writing against the source:

- The activity implementation map is nested by workflow; the README
  showed a workflow-scoped activity at the root level, which does not
  type-check.
- An activity that declares an `errors` map returns AsyncResult to the
  workflow, not a plain value (activities-proxy.ts) — the old docs
  showed try/catch.
- `match({ err })` is `errCases`; `signalWithStart` takes `signalName`;
  `getHandle` returns an AsyncResult.
- unthrown peer range was documented as ^4.1.0, actual ^5.0.0-beta.6.
- Zod 4: `z.record` needs two args, `.email()` is deprecated.
- `tag(...)` was used in ~20 examples with no import shown.
- The Nexus page claimed "planned for v0.5.0" at 8.0.0-beta.1.

Versioned deploys are ported from btravstack/unthrown: while a
prerelease is in progress, the latest stable tag builds at the root and
main builds under /beta/, with a version dropdown linking the two. Two
things could not be copied verbatim:

- unthrown drops prereleases with `grep -v -- '-'` over whole tags.
  Every tag here contains a hyphen inside "@temporal-contract/", so that
  would discard all of them; the version is now filtered after the
  package prefix is stripped.
- This repo's nav is nested one level deeper, so the injection script's
  anchor needs eight spaces of indentation, not six.

Verified end to end locally: main builds at /beta/ with correct asset
paths and canonicals, and a real worktree at
@temporal-contract/contract@7.0.0 builds at the root with the dropdown
injected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 11:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR overhauls the project documentation to follow the Diátaxis framework (tutorial / how-to / reference / explanation), and updates the docs deployment workflow to support versioned docs (latest stable tag at /temporal-contract/, main prerelease docs at /temporal-contract/beta/) with a version switcher that preserves the current page when swapping versions.

Changes:

  • Restructure docs content into Diátaxis sections and replace/remove legacy /guide/* content.
  • Add versioned GitHub Pages deploy logic (stable tag + beta main) including a legacy-theme injection script and a client-side “same-page” version switch enhancement.
  • Update in-repo links (package READMEs/examples/docs) and dependency guidance (unthrown peer dep range) to align with current APIs.

Reviewed changes

Copilot reviewed 63 out of 63 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/worker/README.md Update docs links to new reference structure.
packages/contract/README.md Point “getting started” links to new tutorial/explanation pages.
packages/client/README.md Update docs link to new tutorial entry point.
examples/README.md Update docs link to new tutorial entry point.
examples/order-processing-worker/README.md Update docs link to new tutorial entry point.
knip.json Ignore .github/scripts/** for knip (CI-invoked scripts).
docs/index.md Rewrite landing page content/examples and add “Where to start” navigation aligned with Diátaxis.
docs/api/index.md Refresh API landing page links to new hand-written reference/explanation pages.
docs/reference/contract-surface.md New grouped reference for @temporal-contract/contract exports and option semantics.
docs/reference/client-surface.md New grouped reference for @temporal-contract/client exports and typed handle/schedule APIs.
docs/reference/glossary.md New glossary defining contract/result/Temporal terminology used across the docs.
docs/how-to/install.md New install guide including peer deps and ESM/import rules.
docs/how-to/define-a-contract.md New how-to guide for contract authoring patterns and validation behavior.
docs/how-to/implement-activities.md New how-to guide for activity handler shape, qualify, and activity error behavior.
docs/how-to/model-domain-errors.md New how-to guide explaining declared errors and how they change call sites.
docs/how-to/use-signals-queries-and-updates.md New how-to guide for declaring/handling/calling signals/queries/updates.
docs/how-to/tune-activity-options.md New how-to guide for activity option merge order and timeout/retry guidance.
docs/how-to/run-child-workflows.md New how-to guide for child workflow execution patterns and error handling.
docs/how-to/continue-as-new.md New how-to guide for continue-as-new patterns and determinism-safe triggers.
docs/how-to/handle-cancellation.md New how-to guide for client + workflow + activity cancellation patterns.
docs/how-to/schedule-workflows.md New how-to guide for typed schedules creation and lifecycle operations.
docs/how-to/intercept-client-calls.md New how-to guide for typed client interceptors, patching, retries, and ordering.
docs/how-to/configure-a-worker.md New how-to guide for worker creation, workflows path resolution, and runtime configuration.
docs/how-to/upgrade-to-v8.md New upgrade guide documenting unthrown 5 + defect-channel changes for v8.
docs/how-to/migrate-from-neverthrow.md New migration guide mapping neverthrow → unthrown APIs and patterns.
docs/how-to/add-activity-middleware.md New how-to guide for activity middleware positioning, composition, and context injection.
docs/how-to/index-workflows-with-search-attributes.md New how-to guide for declaring/registering/using typed search attributes.
docs/explanation/why-temporal-contract.md New explanation of the contract boundary value proposition.
docs/explanation/the-result-model.md New explanation of ok/err/defect semantics across Temporal boundaries.
docs/explanation/validation-boundaries.md New explanation of where schemas run and why dual validation is intentional.
docs/explanation/workflow-determinism.md New explanation of replay determinism constraints and safe alternatives.
docs/explanation/architecture.md New explanation of package split and workflow-bundling constraints.
docs/explanation/nexus.md Replace legacy Nexus page with an explicit “not implemented” explanation and guidance.
docs/.vitepress/theme/version-switch.ts Add click-intercept logic to keep the same page when switching docs versions.
docs/.vitepress/theme/index.ts Hook version switch setup into the theme entry.
.github/workflows/deploy-docs.yml Build/deploy versioned docs (stable tag root + beta main under /beta/) and assemble into one Pages artifact.
.github/scripts/inject-docs-version-nav.ts Inject version dropdown + version-switch enhancement into legacy stable-tag VitePress theme/config when needed.
.agents/rules/dependencies.md Update documented peer-dependency ranges to unthrown ^5.
docs/guide/why-temporal-contract.md Remove legacy guide page (replaced by Diátaxis explanation content).
docs/guide/testing.md Remove legacy guide page (replaced by Diátaxis how-to/testing content).
docs/guide/nexus-integration.md Remove legacy guide page (replaced by Diátaxis explanation/nexus content).
docs/guide/installation.md Remove legacy guide page (replaced by Diátaxis install how-to).
docs/guide/getting-started.md Remove legacy guide page (replaced by Diátaxis tutorial/how-to).
docs/guide/defining-contracts.md Remove legacy guide page (replaced by Diátaxis how-to/reference split).
docs/guide/core-concepts.md Remove legacy guide page (replaced by Diátaxis explanation/reference content).
docs/examples/basic-order-processing.md Remove legacy example page (examples covered via new docs + examples section).

Comment thread docs/how-to/schedule-workflows.md Outdated
Comment thread docs/how-to/upgrade-to-v8.md Outdated
Comment thread docs/how-to/use-signals-queries-and-updates.md Outdated
Comment thread docs/how-to/intercept-client-calls.md
Addresses PR review. `AsyncResult` is a success-only thenable — its
internal promise never rejects (core.ts:616) — so `await` collapses it
to a Result and a bare `await handle.signals.x(...)` or
`await schedule.pause(...)` discards the failure entirely.

Copilot flagged four sites; the same bug was present at ~15. All are
now unwrapped with `.get()` / `.getOrThrow()` or an explicit branch, and
the trap is documented in the-result-model.md ("`await` is not an
extractor") plus inline warnings on the schedule, signals, and upgrade
pages.

Also fixes the interceptor short-circuit, which returned
`Err(new RuntimeClientError(...))`. That is both a type error —
`ClientCallError` excludes it since 8.0 — and the wrong channel. It now
defects via `fromSafePromise`, and the note points at the `defect`
helper passed as the second argument to every `*ErrCases` callback.

Auditing that turned up a further class of broken example: all the
`*ErrCases` combinators require an `ExhaustiveMatch`, so the single-arm
`.with(tag(...))` calls in the interceptor and middleware pages would
not compile against their wide unions. Those now close with `P._`, and
the abbreviated before/after snippets in the migration guides are
labelled as such.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@btravers

Copy link
Copy Markdown
Collaborator Author

Review fixes — 4deee0b

All four comments were valid. Each led to a wider fix than the single line flagged.

1. Discarded AsyncResults (3 comments)

Root cause confirmed in the source: AsyncResult is an intentional success-only thenable whose internal promise never rejects (unthrown/packages/core/src/core.ts:616). So await collapses it to a Result and never throws — a bare await handle.signals.x(...) or await schedule.pause(...) drops the failure on the floor.

Copilot flagged 3 sites. Grepping for the pattern found ~15, all now fixed:

File Sites
how-to/schedule-workflows.md 4 × schedule.create, plus pause/unpause/trigger/delete/describe
how-to/use-signals-queries-and-updates.md 2 × signals.cancelRequested
how-to/handle-cancellation.md cancel, terminate, nonCancellableScope
tutorial/adding-signals-and-queries.md signals.approve
how-to/index-workflows-with-search-attributes.md schedule.create
how-to/upgrade-to-v8.md schedule.pause
explanation/workflow-determinism.md nonCancellableScope

Two left deliberately: temporalClient.schedule.getHandle(...).pause() and the raw-SDK client.workflow.execute example both return real promises.

Documented the rule once conceptually in explanation/the-result-model.md → "await is not an extractor", with inline callouts on the pages where it bites.

2. RuntimeClientError on the wrong channel

Right, and it contradicted reference/client-surface.md two pages over, which documents that ClientCallError excludes it. Now defects via fromSafePromise. Also documented the defect helper passed as the second argument to *ErrCases callbacks and fromPromise qualifiers, which was previously undocumented.

3. Non-exhaustive matchers (found while auditing #2)

All *ErrCases combinators constrain M extends ExhaustiveMatch<...>, so single-arm .with(tag(...)) does not compile against a wide union. Two examples were broken (fallback in the interceptor page, retryOnce in the middleware page) — both now close with P._. The before/after snippets in the migration guides are labelled abbreviated rather than padded out, since their point is the rename.


pnpm lint, pnpm typecheck, pnpm knip, and the docs build are all clean.

One caveat worth stating plainly: these are prose examples, not compiled fixtures, so "clean build" means VitePress rendered them and links resolve — it does not mean the snippets typecheck. I verified each change against the source signatures by hand. A doc-snippet typecheck harness would close that gap properly, but it is out of scope here.

@btravers
btravers merged commit 73ae867 into main Jul 28, 2026
11 checks passed
@btravers
btravers deleted the docs/diataxis-restructure-versioned-deploy branch July 28, 2026 11:58
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