Imported from weightwave/taskcast (
AGENTS.md). Install upstream withnpx skills add weightwave/taskcast. Copyright stays with the author.
Taskcast — Codex Instructions
What Is This
Taskcast is a unified long-lifecycle task tracking service for LLM streaming, agents, and similar async workloads. pnpm monorepo, 9 packages, TypeScript + ESM.
Commands
pnpm install # Install all deps
pnpm build # Build all packages (tsc -b)
pnpm test # Run all tests (vitest)
pnpm test:coverage # Coverage report
pnpm lint # Type check (tsc -b)
Run a single package's tests:
cd packages/core && pnpm test
Release Workflow
pnpm changeset # Create a changeset (run before PR)
pnpm changeset version # Bump versions (CI does this)
pnpm ci:publish # Build + publish (CI does this)
All 9 packages use fixed versioning — every release bumps all packages to the same version. Rust binaries share the same version number.
When you merge a PR that contains .changeset/*.md files, CI will:
- Open a "Release Packages" PR that bumps versions and generates changelogs
- When that PR is merged, CI publishes to npm, then:
- Builds Rust binaries for 5 platforms (linux/macOS amd64+arm64, Windows) and attaches to GitHub Release
- Builds and pushes multi-arch Docker image (
mwr1998/taskcast-rs) to DockerHub
- Rust
Cargo.tomlversions are synced at CI time viascripts/sync-rust-version.sh(not committed)
Package Map
| Package | Path | Purpose |
|---|---|---|
@taskcast/core |
packages/core |
Task engine, state machine, filtering, series merging. Zero HTTP deps. |
@taskcast/server |
packages/server |
Hono HTTP server — REST + SSE + auth + webhooks |
@taskcast/server-sdk |
packages/server-sdk |
HTTP client for remote server mode |
@taskcast/client |
packages/client |
Browser SSE subscription client |
@taskcast/react |
packages/react |
React hook useTaskEvents |
@taskcast/cli |
packages/cli |
npx @taskcast/cli standalone server |
@taskcast/redis |
packages/redis |
Redis broadcast + short-term store adapters |
@taskcast/postgres |
packages/postgres |
PostgreSQL long-term store adapter |
@taskcast/sqlite |
packages/sqlite |
SQLite local storage adapter (ShortTermStore + LongTermStore) |
@taskcast/sentry |
packages/sentry |
Sentry error monitoring hooks |
Key Files
packages/core/src/engine.ts— TaskEngine orchestrationpackages/core/src/types.ts— all type definitions (Task, TaskEvent, interfaces)packages/core/src/state-machine.ts— status transition validationpackages/core/src/filter.ts— event filtering (wildcard, level, since)packages/core/src/series.ts— series merging (accumulate/latest/keep-all)packages/core/src/cleanup.ts— cleanup rule matchingpackages/core/src/config.ts— config file loading + env var interpolationpackages/core/src/memory-adapters.ts— in-memory adapters for testingpackages/server/src/index.ts— createTaskcastApp factorypackages/server/src/routes/tasks.ts— REST endpointspackages/server/src/routes/sse.ts— SSE streamingpackages/server/src/auth.ts— JWT auth middlewarepackages/server/src/webhook.ts— webhook delivery + HMAC + retrypackages/cli/src/index.ts— CLI entry point
Design Principles
SDK-First Architecture
Core logic (@taskcast/core) has zero HTTP/infrastructure dependencies. The HTTP layer (@taskcast/server) is a thin wrapper. Storage adapters are pluggable via interfaces. This means:
- The engine can be embedded into any server framework
- Storage backends can be swapped without changing business logic
- Testing is simple — use in-memory adapters for unit tests
Three-Layer Storage
Each layer has a distinct responsibility and can be independently configured:
- BroadcastProvider — Real-time event fan-out. Fire-and-forget. (Redis pub/sub or memory)
- ShortTermStore — Event buffer + task state. Sync writes ensure ordering. (Redis or memory)
- LongTermStore — Permanent archive. Async writes, non-blocking. (PostgreSQL, optional)
Write path: publish → series processing → ShortTerm delta (sync) → Broadcast delta+acc (sync) → LongTerm accumulated (async)
For accumulate mode: ShortTermStore stores deltas, LongTermStore stores accumulated values. SSE subscribers choose format via seriesFormat query parameter (delta or accumulated).
Concurrent Safety
- Task status transitions use optimistic concurrency — if two requests race to complete a task, only one succeeds
- The state machine validates all transitions at the engine level, not just at the API boundary
- Series message merging is atomic within the engine
Coding Conventions
- ESM only — all packages use
"type": "module"and.jsextensions in imports - Workspace refs — internal deps use
workspace:* - No default exports — everything is named exports
- Zod validation — input validation at boundaries uses Zod schemas
- ULID IDs — all generated IDs use ULID via
ulidx - camelCase JSON — all API responses use camelCase field names
- Hono framework — HTTP layer uses Hono, not Express
Testing Philosophy
Coverage target: 100% where practical. Minimum: 90%.
- Every bug must produce a regression test — when you fix a bug, write a test that would have caught it first
- Test bad cases thoroughly — don't just test the happy path. Test invalid inputs, edge cases, race conditions, error states, boundary values, empty inputs, and overflows
- Unit tests — pure logic tests using in-memory adapters. No IO, no containers. Fast.
- Integration tests — use testcontainers for real Redis/Postgres. Test actual adapter behavior.
- Concurrent tests — verify safety under parallel access (e.g., 100 SSE subscribers, 10 concurrent status transitions)
- Code that truly doesn't need testing (trivial re-exports, type definitions) can be excluded, but everything else must be covered
- Always assert both the success case AND the rejection/error case
Test File Structure
packages/<pkg>/tests/
unit/ # Pure logic, no IO, memory adapters
integration/ # Real Redis/Postgres via testcontainers
Architecture Quick Ref
Task lifecycle: pending → running → completed|failed|timeout|cancelled
- No backward transitions
- Only one terminal transition allowed (concurrent-safe)
- TTL triggers automatic timeout
Event filtering: wildcard type matching (e.g. "llm.*"), level filtering, since cursor
Series modes: keep-all | accumulate (text concat) | latest (replace)
Series format (SSE): seriesFormat=delta (default) | accumulated
- Late-join: accumulate series collapsed to single snapshot (seriesSnapshot: true)
- Reconnect with since cursor: no collapse, deltas from breakpoint
SSE behavior:
pending → hold, auto-stream when running
running → replay history (collapse accumulate series) + stream live
terminal → replay collapsed history, then close
Auth modes: none | jwt | custom
Permission scopes: task:create, task:manage, event:publish, event:subscribe, event:history, webhook:create, *
Documentation Map
| Location | Content |
|---|---|
docs/plan.md |
Original project vision (Chinese) |
docs/plans/ |
Design specs and implementation plans |
docs/plans/2026-02-28-taskcast-design.md |
Full design document — the source of truth |
docs/plans/2026-02-28-rust-rewrite-design.md |
Planned Rust rewrite design |
docs/guide/ |
Human-readable guides (EN default, .zh.md for Chinese) |
docs/api/ |
API reference (EN default, .zh.md for Chinese) |
docs/skill/taskcast.md |
Codex skill for external projects |
Rust Rewrite (Planned)
Server-side packages (core, server, cli, redis, postgres) have a planned Rust rewrite using Axum + Tokio + sqlx. Client-side packages (client, react, server-sdk, sentry) stay TypeScript. See docs/plans/2026-02-28-rust-rewrite-design.md.
The Rust server must produce identical HTTP behavior — same paths, same JSON format, same SSE events, same status codes.
IMPORTANT: When changing any server-side feature, both the Node.js (TypeScript) and Rust implementations MUST be updated simultaneously. Do not merge a feature change that only modifies one side — the two implementations must stay in sync at all times.
Workflow Reminders
- Proactively check CI status for open PRs — Before starting new work, check whether existing open PRs have passing CI. If CI is failing, prioritize fixing CI issues before moving on to other tasks.
- PR merge requirements: 100% test coverage + all CI passing — PRs must achieve 100% test coverage and all CI checks must pass before merging. If CI fails, proactively investigate and fix. If a file needs to be excluded from coverage, get explicit user approval first.