Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## [Unreleased]

### Added
- Runs can now be given a custom `display_name` at trigger time (via any SDK's run/run-many/child-spawn options or the REST trigger endpoint), so fanned-out child runs are identifiable at a glance in the dashboard instead of sharing a generated `<readableId>-<timestamp>` label. Single-task runs carry the name on the task; multi-step DAG runs carry it on the DAG (steps keep their generated names). Omitting the field (or passing empty/whitespace) preserves the previous generated-name behavior, and names longer than 255 characters are stored truncated rather than rejected ([#4259](https://github.com/hatchet-dev/hatchet/issues/4259)).

### Highlights

- Fixes timeouts in `UserSession` cleanup jobs by adding an index via a new `hatchet-migrate` migration.
Expand Down Expand Up @@ -596,7 +599,7 @@
- Cleanup old workers via daily gocron by @mnafees in [#3663](https://github.com/hatchet-dev/hatchet/pull/3663)

### Removed
- Remove "account" dropdown', add notifications dropdown by @TehShrike in [#3365](https://github.com/hatchet-dev/hatchet/pull/3365)

Check warning on line 602 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".


## [0.83.33] - 2026-04-21
Expand Down Expand Up @@ -826,7 +829,7 @@

### Changed
- Move OTel tables to OLAP repo by @mnafees in [#3369](https://github.com/hatchet-dev/hatchet/pull/3369)
- Don't flicker the runs table when refetching by @TehShrike in [#3367](https://github.com/hatchet-dev/hatchet/pull/3367)

Check warning on line 832 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".


## [0.82.0] - 2026-03-23
Expand All @@ -853,7 +856,7 @@
- Add latency plots to load test by @juliusgeo in [#3259](https://github.com/hatchet-dev/hatchet/pull/3259)

### Changed
- Only return non-archived tenants in TenantMember queries by @TehShrike in [#3317](https://github.com/hatchet-dev/hatchet/pull/3317)

Check warning on line 859 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".


## [0.81.0] - 2026-03-18
Expand All @@ -877,9 +880,9 @@
## [0.80.8] - 2026-03-18

### Changed
- Guarantee that organization tenants will always be an array by @TehShrike in [#3316](https://github.com/hatchet-dev/hatchet/pull/3316)

Check warning on line 883 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".
- New organizations+tenants screen by @TehShrike in [#3198](https://github.com/hatchet-dev/hatchet/pull/3198)

Check warning on line 884 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".
- Improved invitation accept screen by @TehShrike in [#3151](https://github.com/hatchet-dev/hatchet/pull/3151)

Check warning on line 885 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"Teh" should be "The".

### Removed
- Remove dispatch backlog, replace with timeout lock acquisition by @juliusgeo in [#3290](https://github.com/hatchet-dev/hatchet/pull/3290)
Expand Down Expand Up @@ -911,7 +914,7 @@
## [0.79.44] - 2026-03-15

### Added
- Add support for additional RBAC configruation via YAML configuration by @grutt in [#3285](https://github.com/hatchet-dev/hatchet/pull/3285)

Check warning on line 917 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / spelling

"configruation" should be "configurations" or "configuration".

### Changed
- Gracefully handle empty bulk scheduled deletes by @avirajkhare00 in [#3279](https://github.com/hatchet-dev/hatchet/pull/3279)
Expand Down
3 changes: 3 additions & 0 deletions api-contracts/openapi/components/schemas/v1/workflow_run.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ V1TriggerWorkflowRunRequest:
priority:
type: integer
description: The priority of the workflow run.
displayName:
type: string
description: A custom display name for the run. Falls back to a generated name if unset.
return_only_id:
type: boolean
description: A boolean flag indicating whether to only return the id of the created run.
Expand Down
3 changes: 3 additions & 0 deletions api-contracts/v1/shared/trigger.proto
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,7 @@ message TriggerWorkflowRequest {

// (optional) the desired worker labels for the workflow run, which will be used to determine which workers can pick up the workflow's tasks. if not set, defaults to an empty set of labels, which means any worker can pick up the tasks.
map<string, DesiredWorkerLabels> desired_worker_labels = 10;

// (optional) a custom display name for the run; falls back to "<name>-<timestamp>" if unset
optional string display_name = 11;
}
3 changes: 3 additions & 0 deletions api-contracts/v1/workflows.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ message TriggerWorkflowRunRequest {
bytes additional_metadata = 3;
optional int32 priority = 4;
map<string, DesiredWorkerLabels> desired_worker_labels = 5;

// (optional) a custom display name for the run; falls back to a generated name if unset
optional string display_name = 6;
}

message TriggerWorkflowRunResponse {
Expand Down
1 change: 1 addition & 0 deletions api/v1/server/handlers/v1/workflow-runs/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (t *V1WorkflowRunsService) V1WorkflowRunCreate(ctx echo.Context, request ge
Input: inputBytes,
AdditionalMetadata: additionalMetadataBytes,
Priority: priority,
DisplayName: request.Body.DisplayName,
}

resp, err := t.proxyTrigger.Do(
Expand Down
433 changes: 218 additions & 215 deletions api/v1/server/oas/gen/openapi.gen.go

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions frontend/app/src/lib/api/generated/data-contracts.ts

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

1 change: 1 addition & 0 deletions frontend/docs/pages/v1/_meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default {
concurrency: "Concurrency",
"rate-limits": "Rate Limits",
priority: "Priority",
"run-names": "Run Names",
"--durable-workflows-section": {
title: "Durable Execution",
type: "separator",
Expand Down
6 changes: 6 additions & 0 deletions frontend/docs/pages/v1/additional-metadata.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Hatchet allows you to attach arbitrary key-value string pairs to events and task
runs.
</Callout>

<Callout type="info">
If you only want a human-readable label to tell fanned-out runs apart in the
dashboard, see [Run Names](./run-names.mdx) — it sets the run's `display_name`
directly instead of relying on metadata.
</Callout>

You can attach additional metadata when pushing events or triggering task runs using the Hatchet client libraries:

<Tabs items={['Event Push', 'Task Run Trigger']}>
Expand Down
6 changes: 6 additions & 0 deletions frontend/docs/pages/v1/child-spawning.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import FanoutDiagram from "@/components/FanoutDiagram";

A task can spawn child tasks at runtime — including other durable tasks or entire DAG workflows. Children run independently on any available worker, and the parent can wait for their results before continuing. You can spawn children out of any task (durable or not).

<Callout type="info">
Spawning many children at once? Give each a [display name](./run-names.mdx) so
they're identifiable at a glance in the dashboard instead of sharing a
generated name.
</Callout>

<Callout type="info">
While child spawning is not unique to durable tasks, we often recommend using
durable tasks when spawning children is the main responsibility (or one of the
Expand Down
85 changes: 85 additions & 0 deletions frontend/docs/pages/v1/run-names.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Callout, Tabs } from "nextra/components";
import UniversalTabs from "../../components/UniversalTabs";

# Naming runs with a custom display name

By default, each run in Hatchet is labelled with a generated name of the form `<readableId>-<timestamp>` (for example, `resolve-subprocessor-info-1782146942603`). When a parent fans out many child runs in a single batch, they all share the same step id and batch timestamp, so the runs list renders several identically-named rows and you have to open each run's input to tell them apart.

You can instead set a human-readable **display name** at trigger time. The dashboard renders it automatically in the runs list, the run detail header, and the children list — no other configuration required.

<Callout type="info" emoji="🪓">

The display name is applied to the **run**:

- For a **single-task** workflow, it lands on the task (the task _is_ the run).
- For a **multi-step DAG**, it lands on the DAG run; the DAG's step tasks keep their generated names.

Omitting the field (or passing an empty/whitespace value) preserves the default generated name. A name longer than 255 characters is stored truncated, never rejected.

</Callout>

## Setting a display name

Pass the display name in the options you already use to trigger a run. This works for top-level runs, `run_many` / bulk triggers (each item gets its own name), and child spawns (each child gets its own name).

<UniversalTabs items={['Python', 'Typescript', 'Go', "Ruby"]}>
<Tabs.Tab>

```python
# Top-level run
workflow.run(input, display_name="Acme Corp")

# Per-item names in a bulk run
workflow.run_many([
workflow.create_bulk_run_item(input=input_a, display_name="Acme Corp"),
workflow.create_bulk_run_item(input=input_b, display_name="Globex"),
])
```

</Tabs.Tab>
<Tabs.Tab>

```typescript
// Top-level run
await workflow.run(input, { displayName: "Acme Corp" });

// Spawning a child with its own name
await ctx.runChild(childWorkflow, input, { displayName: "Acme Corp" });
```

</Tabs.Tab>
<Tabs.Tab>

```go
// Top-level run
_, err := client.Run(ctx, "my-workflow", input, hatchet.WithRunDisplayName("Acme Corp"))
```

</Tabs.Tab>
<Tabs.Tab>

```ruby
# Top-level run
workflow.run(input, options: { display_name: "Acme Corp" })
```

</Tabs.Tab>
</UniversalTabs>

## REST

The REST trigger endpoint accepts the same field:

```json
POST /api/v1/stable/tenants/{tenant}/workflow-runs/trigger
{
"workflowName": "my-workflow",
"input": { ... },
"displayName": "Acme Corp"
}
```

## Related

- [Additional metadata](./additional-metadata.mdx) — attach arbitrary key/value metadata to a run, which is filterable and searchable (the display name is display-only).
- [Child spawning](./child-spawning.mdx) — fanning out child runs, the most common place a per-run display name helps.
1 change: 1 addition & 0 deletions internal/services/admin/v1/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ func (a *AdminServiceImpl) newTriggerOpt(
WorkflowName: req.WorkflowName,
Data: req.Input,
AdditionalMetadata: req.AdditionalMetadata,
DisplayName: v1.NormalizeDisplayName(req.DisplayName),
}

if len(req.DesiredWorkerLabels) > 0 {
Expand Down
35 changes: 35 additions & 0 deletions internal/services/admin/v1/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package v1

import (
"context"
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

contracts "github.com/hatchet-dev/hatchet/internal/services/shared/proto/v1"
)

func Test_newTriggerOpt_normalizes_display_name(t *testing.T) {
a := &AdminServiceImpl{}
name := " Acme Corp "

opt, err := a.newTriggerOpt(context.Background(), uuid.New(), &contracts.TriggerWorkflowRunRequest{
WorkflowName: "my-workflow",
DisplayName: &name,
})
require.NoError(t, err)
require.NotNil(t, opt.DisplayName, "REST display name should be carried onto the trigger opt")
assert.Equal(t, "Acme Corp", *opt.DisplayName, "REST display name is trimmed via NormalizeDisplayName")
}

func Test_newTriggerOpt_omits_display_name_when_unset(t *testing.T) {
a := &AdminServiceImpl{}

opt, err := a.newTriggerOpt(context.Background(), uuid.New(), &contracts.TriggerWorkflowRunRequest{
WorkflowName: "my-workflow",
})
require.NoError(t, err)
assert.Nil(t, opt.DisplayName, "omitted display name stays unset so the run falls back to a generated name")
}
73 changes: 43 additions & 30 deletions internal/services/shared/proto/v1/trigger.pb.go

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

Loading
Loading