Skip to content
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
8a19729
WIP DE docs
mnafees Feb 14, 2026
92f8568
WIP DE docs
mnafees Feb 14, 2026
e3103eb
Merge branch 'main' into nafees/de-docs
mnafees Feb 16, 2026
226e0a0
fanout docs initial
mnafees Feb 16, 2026
fa01035
pipelines doc
mnafees Feb 16, 2026
c26dfe4
cycles WIP
mnafees Feb 16, 2026
25c4160
long waits
mnafees Feb 16, 2026
d7037fc
hatchet building blocks
mnafees Feb 16, 2026
fd4f374
lint
mnafees Feb 16, 2026
1142be2
comparison
mnafees Feb 16, 2026
e581a8e
Merge branch 'main' into nafees/de-docs
mnafees Feb 16, 2026
9cba7a1
use cases and workflow type
mnafees Feb 16, 2026
9b6f0d5
no background jpbs migration for now
mnafees Feb 16, 2026
5684487
changes
mnafees Feb 17, 2026
af69110
fanout fix
mnafees Feb 17, 2026
3a27b27
some improvements to visuals
mnafees Feb 17, 2026
4e4f1b7
fix CI
mnafees Feb 17, 2026
ffa34f4
fix lint
mnafees Feb 17, 2026
955e3be
Merge branch 'main' into nafees/de-docs
mnafees Feb 17, 2026
a49fb1c
Merge branch 'main' into nafees/de-docs
mnafees Feb 17, 2026
6c43b4f
Gr/docs-overhaul (#3074)
grutt Feb 23, 2026
a83d4a9
refactor: last big shuffle
grutt Mar 1, 2026
6da9e6c
fix: redirects
grutt Mar 1, 2026
09ce4a5
feat: doc and human in the loop pages
grutt Mar 1, 2026
91f091f
fix: diagrams
grutt Mar 1, 2026
78de27f
refactor: merge patterns into concepts
grutt Mar 1, 2026
69cacd0
fix: no extra pages
grutt Mar 1, 2026
8d4b792
fix: cards
grutt Mar 1, 2026
0bc6e3c
wip: guides
grutt Mar 1, 2026
c04d5ef
chore: gen
grutt Mar 1, 2026
10e4fc9
feat: fewer tabs
grutt Mar 1, 2026
29df31d
feat: global lang selector
grutt Mar 1, 2026
c02b8a1
fix: no callout on hidden
grutt Mar 1, 2026
c6f4375
wip: lots of iterations on guides
grutt Mar 1, 2026
367a31a
feat: branded 404 page
grutt Mar 1, 2026
d710e18
chore: organization
grutt Mar 1, 2026
09e5c12
fix: search
grutt Mar 1, 2026
6a3ce15
chore: lint
grutt Mar 1, 2026
1b205aa
chore: update link
grutt Mar 1, 2026
acd1e5f
fix: guides overview
grutt Mar 1, 2026
37bdece
chore: generate
grutt Mar 1, 2026
ddffcce
fix: section title
grutt Mar 1, 2026
9b9a1a8
refactor: get started + concepts
grutt Mar 3, 2026
8bcb352
feat: combined durable workflows
grutt Mar 3, 2026
38cc864
fix: dedupe headers
grutt Mar 3, 2026
0b38b8b
feat: drop redundant manual setup page
grutt Mar 3, 2026
3f1f82e
feat: theming
grutt Mar 3, 2026
d6c5548
feat: last big change...
grutt Mar 3, 2026
94105b2
chore: lint and move
grutt Mar 3, 2026
efc269a
fix: build
grutt Mar 3, 2026
132d262
chore: lint
grutt Mar 3, 2026
de13522
Merge branch 'main' into gr/docs-final-push
grutt Mar 4, 2026
ada66bb
fix: redirects
grutt Mar 4, 2026
c177f83
chore: cleanup worker nav
grutt Mar 4, 2026
bb6157d
nit: order
grutt Mar 4, 2026
029a4a9
nit: name
grutt Mar 4, 2026
e4ea55a
fix: table of contents
grutt Mar 4, 2026
9d2b441
feat: last mile
grutt Mar 4, 2026
597ca71
chore: lint
grutt Mar 4, 2026
b4dd930
fix: build
grutt Mar 4, 2026
631b841
chore: lint
grutt Mar 4, 2026
521ee3f
fix: cli
grutt Mar 4, 2026
474101b
fix: lockfile
grutt Mar 4, 2026
54528e3
chore: generate
grutt Mar 4, 2026
e8a1715
fix: minimal migraitons
grutt Mar 4, 2026
b8cfd1a
fix: cookie link
grutt Mar 4, 2026
7a358b2
chore: generate
grutt Mar 4, 2026
d1c2cb4
chore: revert changes
grutt Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 33 additions & 0 deletions .cursor/rules/docs-snippet-workflow.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
description: How to edit guide code examples and regenerate snippets
globs: sdks/guides/**/*,frontend/docs/pages/guides/**/*.mdx,examples/*/guides/**/*
alwaysApply: false
---

# Guide Snippet Workflow

## Source of truth

All guide code lives in `sdks/guides/{lang}/`. The files under `examples/{lang}/guides/` are **generated** mirrors. Never edit the `examples/` copies directly.

## Snippet markers

Use `# > Step Title` (or `// >` for TS/Go) to open a snippet and `# !!` (or `// !!`) to close it. The generator converts the title to snake_case for the snippet key (e.g. `# > Step 04 Rate Limited Scrape` → `step_04_rate_limited_scrape`).

## Referencing snippets in MDX

```
snippets.{lang}.guides.{guide_dir}.{file_stem}.{snippet_key}
```

Example: `snippets.python.guides.web_scraping.worker.step_01_define_scrape_task`

## Regenerating

After editing any file in `sdks/guides/`, run:

```
cd frontend/snippets && python3 generate.py
```

This regenerates both `examples/*/guides/` mirror files and `frontend/docs/lib/generated/snippets/index.ts`.
22 changes: 22 additions & 0 deletions .cursor/rules/docs-writing-style.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Writing style conventions for Hatchet documentation
globs: frontend/docs/**/*.mdx
alwaysApply: false
---

# Docs Writing Style

## Punctuation

- Do NOT use em dashes (—). Use commas, parentheses, or separate sentences instead.
- Prefer short, direct sentences over long compound ones.

## Phrases to Avoid

- Do not use "under the hood." Just state the fact directly.

## Terminology

- Do not use the word "cycle" to describe agent loops. Use "child spawning" or "self-spawning child task" instead.
- Use "durable task" (not "durable function" or "durable workflow step") for the core execution primitive.
- Link concept terms to their concept pages on first use (e.g. `[durable task](/concepts/durable-workflows/durable-task-execution)`).
28 changes: 24 additions & 4 deletions .github/workflows/cli-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@ name: cli-e2e-tests
on:
pull_request:
paths:
- ".github/workflows/cli-e2e-tests.yml"
- "cmd/hatchet-cli/cli/templates/**"
- "cmd/hatchet-cli/cli/quickstart.go"
- "cmd/hatchet-cli/cli/internal/templater/**"
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"
- ".github/workflows/test-templates.yml"
- "cmd/hatchet-cli/cli/*.go"
- "cmd/hatchet-cli/cli/*_e2e_test.go"
- "cmd/hatchet-cli/cli/tui/**"
- "cmd/hatchet-cli/cli/testharness/**"
push:
branches:
- main
paths:
- ".github/workflows/cli-e2e-tests.yml"
- "cmd/hatchet-cli/cli/templates/**"
- "cmd/hatchet-cli/cli/quickstart.go"
- "cmd/hatchet-cli/cli/internal/templater/**"
- "cmd/hatchet-cli/cli/quickstart_e2e_test.go"
- "cmd/hatchet-cli/cli/*.go"
- "cmd/hatchet-cli/cli/*_e2e_test.go"
- "cmd/hatchet-cli/cli/tui/**"
- "cmd/hatchet-cli/cli/testharness/**"

jobs:
test-templates:
runs-on: ubicloud-standard-4
timeout-minutes: 15
timeout-minutes: 30

steps:
- name: Checkout code
Expand All @@ -34,7 +43,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
python-version: "3.13" # pinned because of poetry dependency issue

- name: Set up Node.js
uses: actions/setup-node@v6
Expand All @@ -56,6 +65,9 @@ jobs:
- name: Install Bun
uses: oven-sh/setup-bun@v2

- name: Install tmux
run: sudo apt-get install -y tmux

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

On Ubuntu runners, apt-get install can intermittently fail without an apt-get update first (stale package index). Add a preceding 'sudo apt-get update' (or combine into one step) to make the workflow more reliable.

Suggested change
run: sudo apt-get install -y tmux
run: sudo apt-get update && sudo apt-get install -y tmux

Copilot uses AI. Check for mistakes.

- name: Build CLI
run: |
cd cmd/hatchet-cli
Expand All @@ -79,9 +91,17 @@ jobs:
cd cmd/hatchet-cli/cli
go test -tags e2e_cli -run TestQuickstartTemplates -v -timeout 15m
env:
# Ensure tests don't try to interact with stdin
CI: true

- name: Run resource E2E tests
run: |
cd cmd/hatchet-cli/cli
go test -tags e2e_cli \
-run 'Test(Runs|Workflows|Worker|RateLimits|Scheduled|Cron|Webhooks)' \
-v -timeout 10m
env:
HATCHET_CLI_PROFILE: local

- name: Show server logs on failure
if: failure()
run: |
Expand Down
4 changes: 2 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ linters:
paths:
- third_party$
- builtin$
- examples$
- ^examples/
- '(.+)_test\.go'
- "cmd/hatchet-loadtest/rampup/(.+).go"
formatters:
Expand All @@ -71,4 +71,4 @@ formatters:
paths:
- third_party$
- builtin$
- examples$
- ^examples/
26 changes: 24 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,27 @@ repos:
rev: v2.7.2
hooks:
- id: golangci-lint
args: ["--config=.golangci.yml"]
exclude: ^examples/
args: ["--config=.golangci.yml", "--allow-parallel-runners"]
exclude: ^(examples/|sdks/guides/go/)
- repo: local
hooks:
- id: golangci-lint-guides
name: golangci-lint (sdks/guides/go)
entry: bash -c 'cd sdks/guides/go && golangci-lint run ./... --config .golangci.yml --allow-parallel-runners'
language: system
files: ^sdks/guides/go/
- id: ruff-python-guides
name: ruff (sdks/guides/python)
entry: bash -c 'cd sdks/guides/python && poetry run ruff check .'
language: system
files: ^sdks/guides/python/
- id: eslint-typescript-guides
name: eslint (sdks/guides/typescript)
entry: bash -c 'cd sdks/guides/typescript && pnpm run lint:check'
language: system
files: ^sdks/guides/typescript/
- id: rubocop-ruby-guides
name: rubocop (sdks/guides/ruby)
entry: bash -c 'cd sdks/guides/ruby && bundle install && bundle exec rubocop'

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

Running 'bundle install' inside the pre-commit hook will be slow and can add network flakiness to commits. Prefer 'bundle check || bundle install' (to reuse an existing lock/vendor cache) or switch to a pre-commit ruby hook setup where dependencies are managed/cached outside the hook execution.

Suggested change
entry: bash -c 'cd sdks/guides/ruby && bundle install && bundle exec rubocop'
entry: bash -c 'cd sdks/guides/ruby && (bundle check || bundle install) && bundle exec rubocop'

Copilot uses AI. Check for mistakes.
language: system
files: ^sdks/guides/ruby/
41 changes: 35 additions & 6 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,21 @@ tasks:
fmt-docs:
dir: frontend/docs
cmd: pnpm run prettier:fix && pnpm run prettier:check
sync-agent-instructions:
desc: Sync agent-instructions MDX files into hatchet-cli skill-assets references
cmds:
- |
src="frontend/docs/pages/agent-instructions"
dest="cmd/hatchet-cli/cli/skill-assets/hatchet-cli/references"
for f in "$src"/*.mdx; do
Comment on lines +69 to +70

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

This task copies files but won’t remove destination files when a source MDX is deleted/renamed, so stale reference docs can accumulate. Consider ensuring the destination exists (mkdir -p) and using a sync mechanism that can delete extraneous files (e.g. rsync --delete or a clean step) so the skill-assets mirror stays accurate.

Suggested change
dest="cmd/hatchet-cli/cli/skill-assets/hatchet-cli/references"
for f in "$src"/*.mdx; do
dest="cmd/hatchet-cli/cli/skill-assets/hatchet-cli/references"
# Ensure destination directory exists
mkdir -p "$dest"
# Remove previously generated reference docs to avoid stale files
rm -f "$dest"/*.md
# Re-sync current MDX files as .md references
for f in "$src"/*.mdx; do
[ -e "$f" ] || continue

Copilot uses AI. Check for mistakes.
name=$(basename "$f" .mdx)
cp "$f" "$dest/$name.md"
done

pre:
aliases: [fmt]
cmds:
- task: sync-agent-instructions
- task: fmt-go
- task: fmt-app
- task: fmt-docs
Expand Down Expand Up @@ -160,11 +172,12 @@ tasks:
- task: generate-api-server
- task: generate-api-client
generate-docs:
deps: [venv]
dir: frontend/snippets
cmds:
- 'source {{.ROOT_DIR}}/.venv/bin/activate && python generate.py'
- task: fmt
deps: [venv]
dir: frontend/snippets
cmds:
- "source {{.ROOT_DIR}}/.venv/bin/activate && python generate.py"
- task: fmt
- task: sync-agent-instructions
generate-certs:
cmds:
- bash ./hack/dev/generate-x509-certs.sh ./hack/dev/certs
Expand Down Expand Up @@ -199,10 +212,26 @@ tasks:
lint:
cmds:
- task: lint-go
- task: lint-go-guides
- task: lint-python-guides
- task: lint-typescript-guides
- task: lint-ruby-guides
- task: lint-app
- task: lint-docs
lint-go:
cmd: golangci-lint run ./... --config .golangci.yml
lint-go-guides:
dir: sdks/guides/go
cmd: golangci-lint run ./... --config .golangci.yml
lint-python-guides:
dir: sdks/guides/python
cmd: poetry run ruff check .
lint-typescript-guides:
dir: sdks/guides/typescript
cmd: pnpm install && pnpm run lint:check
lint-ruby-guides:
dir: sdks/guides/ruby
cmd: bundle install && bundle exec rubocop
lint-app:
dir: frontend/app
cmd: pnpm run lint:check
Expand All @@ -228,7 +257,7 @@ tasks:
cmd: source .venv/bin/activate && pip install pre-commit && pre-commit install
pre-commit-run:
deps: [venv]
cmd: 'source .venv/bin/activate && pre-commit run --all-files || pre-commit run --all-files'
cmd: "source .venv/bin/activate && pre-commit run --all-files || pre-commit run --all-files"
docs:
cmds:
- |
Expand Down
6 changes: 6 additions & 0 deletions api/v1/server/oas/transformers/workflow_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func ToCronWorkflowsFromSQLC(cron *sqlcv1.ListCronWorkflowsRow) *gen.CronWorkflo
}
}

input := make(map[string]interface{})
if cron.Input != nil {
json.Unmarshal(cron.Input, &input) //nolint:errcheck
}
Comment on lines +69 to +72

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

Input is always set to a non-nil pointer, even when cron.Input is NULL, which changes the API surface from “field omitted/null” to “{}”. If you want to preserve prior semantics, only set res.Input when cron.Input != nil; also avoid swallowing json.Unmarshal errors (return nil input, or log/propagate the error) to prevent silent data loss.

Copilot uses AI. Check for mistakes.

res := &gen.CronWorkflows{
Metadata: *toAPIMetadata(cron.ID_2, cron.CreatedAt_2.Time, cron.UpdatedAt_2.Time),
WorkflowVersionId: cron.WorkflowVersionId.String(),
Expand All @@ -78,6 +83,7 @@ func ToCronWorkflowsFromSQLC(cron *sqlcv1.ListCronWorkflowsRow) *gen.CronWorkflo
Enabled: cron.Enabled,
Method: gen.CronWorkflowsMethod(cron.Method),
Priority: &cron.Priority,
Input: &input,
Comment on lines 85 to +86

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

Input is always set to a non-nil pointer, even when cron.Input is NULL, which changes the API surface from “field omitted/null” to “{}”. If you want to preserve prior semantics, only set res.Input when cron.Input != nil; also avoid swallowing json.Unmarshal errors (return nil input, or log/propagate the error) to prevent silent data loss.

Copilot uses AI. Check for mistakes.
}

return res
Expand Down
24 changes: 8 additions & 16 deletions build/package/servers.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,18 @@ COPY /cmd ./cmd

RUN go generate ./...

# OpenAPI bundle environment
# -------------------------
FROM node:22-alpine as build-openapi
# OpenAPI bundle environment (uses openapi-core only to avoid Redoc/React/styled-components)
# ----------------------------------------------------------------------------------------
FROM node:22-alpine AS build-openapi
WORKDIR /openapi

COPY /api-contracts/openapi ./openapi
COPY /hack/oas/bundle-openapi.mjs ./bundle-openapi.mjs

RUN echo '{ \
"name": "hack-for-redocly-cli-bug", \
"version": "1.0.0", \
"dependencies": { \
"@redocly/cli": "latest" \
}, \
"overrides": { \
"mobx-react": "9.2.0" \
} \
}' > package.json && \
npm install -g npm@8.1 @redocly/cli@latest && \
npx @redocly/cli@latest bundle ./openapi/openapi.yaml --output ./bin/oas/openapi.yaml --ext yaml && \
rm package.json
RUN echo '{ "type": "module", "dependencies": { "@redocly/openapi-core": "2.14.7", "yaml": "2.7.0" } }' > package.json && \
npm install && \
node bundle-openapi.mjs && \
rm -f package.json package-lock.json bundle-openapi.mjs

# Go build environment
# --------------------
Expand Down
62 changes: 59 additions & 3 deletions cmd/hatchet-cli/cli/client.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package cli

import (
"github.com/google/uuid"
openapi_types "github.com/oapi-codegen/runtime/types"
"github.com/rs/zerolog"
"github.com/spf13/cobra"

"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/cmd/hatchet-cli/cli/internal/config/cli"
"github.com/hatchet-dev/hatchet/pkg/client" //nolint:staticcheck
profileconfig "github.com/hatchet-dev/hatchet/pkg/config/cli"
clientconfig "github.com/hatchet-dev/hatchet/pkg/config/client"
"github.com/hatchet-dev/hatchet/pkg/config/shared"
)

// NewClientFromProfile creates a new Hatchet client from a profile configuration.
// It properly handles TLS settings, host/port, and authentication based on the profile.
func NewClientFromProfile(profile *profileconfig.Profile, logger *zerolog.Logger) (client.Client, error) {
func NewClientFromProfile(profile *profileconfig.Profile, logger *zerolog.Logger) (client.Client, error) { //nolint:staticcheck
// Construct a ClientConfigFile from the profile
configFile := &clientconfig.ClientConfigFile{
TenantId: profile.TenantId,
Expand All @@ -26,5 +30,57 @@ func NewClientFromProfile(profile *profileconfig.Profile, logger *zerolog.Logger
}

// Create client with the config file and logger
return client.NewFromConfigFile(configFile, client.WithLogger(logger))
return client.NewFromConfigFile(configFile, client.WithLogger(logger)) //nolint:staticcheck
}

// clientCmdConfig holds CLI flag values used to create a Hatchet client.
type clientCmdConfig struct {
Profile string
}

// readClientCmdConfig reads client-related flags from a command into a config struct.
func readClientCmdConfig(cmd *cobra.Command) clientCmdConfig {
profile, _ := cmd.Flags().GetString("profile")
return clientCmdConfig{Profile: profile}
}

// clientFromCmd selects a profile and returns a Hatchet client.
// The profile is read from the --profile flag if present, otherwise selected interactively.
func clientFromCmd(cmd *cobra.Command) (string, client.Client) { //nolint:staticcheck
cfg := readClientCmdConfig(cmd)

var selectedProfile string
if cfg.Profile != "" {
selectedProfile = cfg.Profile
} else {
selectedProfile = selectProfileForm(true)
if selectedProfile == "" {
selectedProfile = handleNoProfiles(cmd)
if selectedProfile == "" {
cli.Logger.Fatal("no profile selected or created")
}
}
}

profile, err := cli.GetProfile(selectedProfile)
if err != nil {
cli.Logger.Fatalf("could not get profile '%s': %v", selectedProfile, err)
}

nopLogger := zerolog.Nop()
hatchetClient, err := NewClientFromProfile(profile, &nopLogger)
if err != nil {
cli.Logger.Fatalf("could not create Hatchet client: %v", err)
}

return selectedProfile, hatchetClient
}

// clientTenantUUID parses and returns the tenant UUID from the client configuration.
func clientTenantUUID(hatchetClient client.Client) openapi_types.UUID { //nolint:staticcheck
parsed, err := uuid.Parse(hatchetClient.TenantId())
if err != nil {
cli.Logger.Fatalf("invalid tenant ID: %v", err)
}
return parsed
}
Loading