Kludex / Kludex/pgtask

Investigate relaxing the step-name character set

Open
#6 1 comment 0 reactions 0 assignees View on GitHub
question
Dominant language
Rust
Stars
21
Forks
1
Avg merge
10h 30m
Merged PRs (30d)
20

Description

## Context

`validate_name` (`crates/pgtask-core/src/identifier.rs:24-42`) applies one allowlist - ASCII alphanumerics plus `.`, `_`, `:`, `-` - to all five name types, and the DB re-checks it (`crates/pgtask-postgres/migrations/0001_initial.sql:199`: `step_name ~ '^[A-Za-z0-9._:-]+$'`).

That rule is well motivated for queue, task, and schedule names: they show up as CLI arguments, in the observer UI, and as OTel attributes (`pgtask.queue.name`, `pgtask.task.name`) that `docs/public-contracts.md` declares public and bounded.

Step names are different. They never leave the row - `step_name text` in the primary key `(task_id, handler_version, step_name, occurrence)`. They are not identifiers (the shared task table of ADR 0004 means no name is ever interpolated into SQL), they are not notify channels (`pgtask.result_channel` hashes the task id), and they are not metric attributes. They inherited the charset rule from the macro shared with the other name types.

## Why it matters

Step names are usually composed by a framework rather than typed by a person, so the composing library has to know pgtask's charset. Concretely: Pydantic AI names an agent's own toolset ``, so every agent-level tool under `pydantic-ai-pgtask` produced `...__function_toolset__.call_tool:x` and failed the task with `ValueError: step name contains unsupported character '<'` (Kludex/pydantic-ai-pgtask#1). Worse, the failure lands mid-task: the model request is already checkpointed, so every retry replays the cached response and dies at the same step, burning all `max_attempts`.

That integration now normalizes step names on its side, so this is not urgent - it is about whether the constraint should be there at all.

For comparison, absurd validates queue names only for what it structurally needs (non-empty, <= 57 bytes, because it derives `absurd.t_` table identifiers from them) and puts no rule at all on checkpoint names, which are plain text in a primary key. Its SDK even generates `name#2` for repeated steps.

## Questions to settle

- Does anything downstream actually depend on the step-name charset - the observer UI, CLI output, exports, log formatting, a future span attribute?
- If not, is relaxing `StepName` to non-empty + <= 255 bytes (leaving queue/task/schedule/signal names untouched) worth a storage-protocol bump? A changed CHECK predicate is a protocol increment per `docs/public-contracts.md`, so this is expand-and-contract, not a one-line patch.
- If the constraint stays, should the SDKs document the charset next to `task.step(...)` so integrators normalize up front instead of discovering it from a failed task?

Contributor guide

Open the contributing guide

Research direction

Start with validate_name in crates/pgtask-core/src/identifier.rs and the step_name CHECK in crates/pgtask-postgres/migrations/0001_initial.sql. Read docs/public-contracts.md, then trace the named observer UI, CLI output, exports, logging, and span-attribute paths for downstream charset assumptions. Done means a documented decision on relaxing the constraint or retaining it, including the required protocol and SDK documentation implications.

Written by the indexing model from the issue text.

Assessment

Tech stack
postgresql, rust
Domain
backend-api-design, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.