Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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)`).
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/
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ 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/)
16 changes: 16 additions & 0 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,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 Down
2 changes: 1 addition & 1 deletion cmd/hatchet-cli/cli/templates/typescript/bun/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"typescript": "^5.9.3"
},
"dependencies": {
"@hatchet-dev/typescript-sdk": "^1.10.3"
"@hatchet-dev/typescript-sdk": "1.10.3"
}
}
2 changes: 1 addition & 1 deletion cmd/hatchet-cli/cli/templates/typescript/npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"typescript": "^5.9.3"
},
"dependencies": {
"@hatchet-dev/typescript-sdk": "^1.10.3"
"@hatchet-dev/typescript-sdk": "1.10.3"
}
}
2 changes: 1 addition & 1 deletion cmd/hatchet-cli/cli/templates/typescript/pnpm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"typescript": "^5.9.3"
},
"dependencies": {
"@hatchet-dev/typescript-sdk": "^1.10.3"
"@hatchet-dev/typescript-sdk": "1.10.3"
},
"pnpm": {
"overrides": {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/hatchet-cli/cli/templates/typescript/yarn/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"typescript": "^5.9.3"
},
"dependencies": {
"@hatchet-dev/typescript-sdk": "^1.10.3"
"@hatchet-dev/typescript-sdk": "1.10.3"
}
}
71 changes: 71 additions & 0 deletions examples/go/guides/ai-agents/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"log"

"github.com/hatchet-dev/hatchet/pkg/cmdutils"
hatchet "github.com/hatchet-dev/hatchet/sdks/go"
)

func main() {
client, err := hatchet.NewClient()
if err != nil {
log.Fatalf("failed to create hatchet client: %v", err)
}

// > Step 02 Reasoning Loop
agentReasoningLoop := func(query string) (map[string]interface{}, error) {
messages := []map[string]interface{}{{"role": "user", "content": query}}
for i := 0; i < 10; i++ {
resp := CallLLM(messages)
if resp.Done {
return map[string]interface{}{"response": resp.Content}, nil
}
for _, tc := range resp.ToolCalls {
args := make(map[string]interface{})
for k, v := range tc.Args {
args[k] = v
}
result := RunTool(tc.Name, args)
messages = append(messages, map[string]interface{}{"role": "tool", "content": result})
}
}
return map[string]interface{}{"response": "Max iterations reached"}, nil
}

// > Step 01 Define Agent Task
agentTask := client.NewStandaloneDurableTask("reasoning-loop-agent", func(ctx hatchet.DurableContext, input map[string]interface{}) (map[string]interface{}, error) {
query := "Hello"
if q, ok := input["query"].(string); ok && q != "" {
query = q
}
return agentReasoningLoop(query)
})

// > Step 03 Stream Response
streamingTask := client.NewStandaloneDurableTask("streaming-agent-task", func(ctx hatchet.DurableContext, input map[string]interface{}) (map[string]interface{}, error) {
tokens := []string{"Hello", " ", "world", "!"}
for _, t := range tokens {
ctx.PutStream(t)
}
return map[string]interface{}{"done": true}, nil
})

// > Step 04 Run Worker
worker, err := client.NewWorker("agent-worker",
hatchet.WithWorkflows(agentTask, streamingTask),
hatchet.WithSlots(5),
hatchet.WithDurableSlots(5),
)
if err != nil {
log.Fatalf("failed to create worker: %v", err)
}

interruptCtx, cancel := cmdutils.NewInterruptContext()
defer cancel()

if err := worker.StartBlocking(interruptCtx); err != nil {
cancel()
log.Fatalf("failed to start worker: %v", err)
}
}
40 changes: 40 additions & 0 deletions examples/go/guides/ai-agents/mock_agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

// CallLLM is a mock - no external LLM API.
// First call returns tool_calls; second returns final answer.
var llmCallCount int

type LLMResponse struct {
Content string
ToolCalls []ToolCall
Done bool
}

type ToolCall struct {
Name string
Args map[string]interface{}
}

func CallLLM(messages []map[string]interface{}) LLMResponse {
llmCallCount++
if llmCallCount == 1 {
return LLMResponse{
Content: "",
ToolCalls: []ToolCall{{Name: "get_weather", Args: map[string]interface{}{"location": "SF"}}},
Done: false,
}
}
return LLMResponse{Content: "It's 72°F and sunny in SF.", ToolCalls: nil, Done: true}
}

// RunTool is a mock - returns canned results.
func RunTool(name string, args map[string]interface{}) string {
if name == "get_weather" {
loc := "unknown"
if v, ok := args["location"]; ok {
loc = v.(string)
}
return "Weather in " + loc + ": 72°F, sunny"
}
return "Unknown tool: " + name
}
67 changes: 67 additions & 0 deletions examples/go/guides/batch-processing/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"log"

"github.com/hatchet-dev/hatchet/pkg/cmdutils"
hatchet "github.com/hatchet-dev/hatchet/sdks/go"
)

type BatchInput struct {
Items []string `json:"items"`
}

type ItemInput struct {
ItemID string `json:"item_id"`
}

func main() {
client, err := hatchet.NewClient()
if err != nil {
log.Fatalf("failed to create hatchet client: %v", err)
}

// > Step 03 Process Item
childTask := client.NewStandaloneTask("process-item", func(ctx hatchet.Context, input ItemInput) (map[string]string, error) {
return map[string]string{"status": "done", "item_id": input.ItemID}, nil
})

// > Step 01 Define Parent Task
parentTask := client.NewStandaloneDurableTask("spawn-children", func(ctx hatchet.DurableContext, input BatchInput) (map[string]interface{}, error) {
inputs := make([]hatchet.RunManyOpt, len(input.Items))
for i, itemID := range input.Items {
inputs[i] = hatchet.RunManyOpt{Input: ItemInput{ItemID: itemID}}
}
runRefs, err := childTask.RunMany(ctx, inputs)
if err != nil {
return nil, err
}
results := make([]interface{}, len(runRefs))
for i, ref := range runRefs {
result, err := ref.Result()
if err != nil {
return nil, err
}
var parsed map[string]interface{}
if err := result.TaskOutput("process-item").Into(&parsed); err != nil {
return nil, err
}
results[i] = parsed
}
return map[string]interface{}{"processed": len(results), "results": results}, nil
})

// > Step 04 Run Worker
worker, err := client.NewWorker("batch-worker", hatchet.WithWorkflows(parentTask, childTask), hatchet.WithSlots(20))
if err != nil {
log.Fatalf("failed to create worker: %v", err)
}

interruptCtx, cancel := cmdutils.NewInterruptContext()
defer cancel()

if err := worker.StartBlocking(interruptCtx); err != nil {
cancel()
log.Fatalf("failed to start worker: %v", err)
}
}
63 changes: 63 additions & 0 deletions examples/go/guides/document-processing/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"log"

"github.com/hatchet-dev/hatchet/pkg/cmdutils"
hatchet "github.com/hatchet-dev/hatchet/sdks/go"
)

type DocInput struct {
DocID string `json:"doc_id"`
Content []byte `json:"content"`
}

func main() {
client, err := hatchet.NewClient()
if err != nil {
log.Fatalf("failed to create hatchet client: %v", err)
}

// > Step 01 Define DAG
workflow := client.NewWorkflow("DocumentPipeline")

// > Step 02 Parse Stage
ingest := workflow.NewTask("ingest", func(ctx hatchet.Context, input DocInput) (map[string]interface{}, error) {
return map[string]interface{}{"doc_id": input.DocID, "content": input.Content}, nil
})

parse := workflow.NewTask("parse", func(ctx hatchet.Context, input DocInput) (map[string]interface{}, error) {
var ingested map[string]interface{}
if err := ctx.ParentOutput(ingest, &ingested); err != nil {
return nil, err
}
content := ingested["content"].([]byte)
text := parseDocument(content)
return map[string]interface{}{"doc_id": input.DocID, "text": text}, nil
}, hatchet.WithParents(ingest))

// > Step 03 Extract Stage
extract := workflow.NewTask("extract", func(ctx hatchet.Context, input DocInput) (map[string]interface{}, error) {
var parsed map[string]interface{}
if err := ctx.ParentOutput(parse, &parsed); err != nil {
return nil, err
}
return map[string]interface{}{"doc_id": parsed["doc_id"], "entities": []string{"entity1", "entity2"}}, nil
}, hatchet.WithParents(parse))

_ = extract

// > Step 04 Run Worker
worker, err := client.NewWorker("document-worker", hatchet.WithWorkflows(workflow))
if err != nil {
log.Fatalf("failed to create worker: %v", err)
}

interruptCtx, cancel := cmdutils.NewInterruptContext()
defer cancel()

if err := worker.StartBlocking(interruptCtx); err != nil {
cancel()
log.Fatalf("failed to start worker: %v", err)
}
}
Loading
Loading