Skip to content

Development

The repository is a uv workspace containing two independent runtime distributions and one code-free convenience metapackage.

uv sync --all-packages --group dev --frozen
uv run ruff format --check .
uv run ruff check .
uv run mypy packages/labtasker-client/src packages/labtasker-server/src
uv run pytest
uv build --all-packages

Build and preview the documentation with the pinned Zensical development dependency:

uv run zensical serve
uv run zensical build --clean

The generated site/ directory is ignored. CI builds the site from scratch. Pushes that change documentation on main publish the dev version to GitHub Pages. A vVERSION tag publishes VERSION, moves the latest alias, and updates the site's default redirect. Versioning uses Zensical's temporary Mike integration until native Zensical versioning is available.

Configure GitHub Pages to deploy from the root of the gh-pages branch. The documentation workflow retains older versions on that branch, so it deliberately does not use the single-artifact Pages deployment flow.

Dependency and release automation

Dependabot checks the uv workspace and GitHub Actions each week. GitHub reads .github/dependabot.yml from the repository's default branch.

Publishing a GitHub Release runs the complete ordinary gate, the real Linux distributed suite, clean-wheel smoke tests, and then publishes all three distributions through PyPI Trusted Publishing. Before the first run:

  1. Create protected GitHub environments named pypi, pypi-client, and pypi-server, preferably with required reviewers.
  2. Register luocfprime/labtasker and workflow release.yml as a Trusted Publisher for all three PyPI projects. Use environment pypi-client for labtasker-client, pypi-server for labtasker-server, and pypi for labtasker. Use a pending publisher for a project that does not exist yet.

The publishing workflow has no manual trigger. It accepts only a published GitHub Release whose vVERSION tag exactly matches every maintained version field. Separate jobs and environments publish the Client first, the Server second, and the code-free metapackage last. Existing files are skipped on a retry so a partially completed multi-project publication can resume without replacing immutable PyPI files.

Agent workflows

Repository-aware coding agents should start with the root AGENTS.md. It records the product boundary, architectural invariants, source-of-truth order, and validation expectations without loading the full specification into every task.

Repeatable, task-specific workflows live under .agents/skills/. Use the release skill for versioning and release readiness, and the public-contract-change skill when changing behavior across HTTP, Python, CLI, persistence, and documentation surfaces. Use documentation for maintained user guidance. Use skill-development when changing the public Agent Skill: it uses a separate examiner and fresh candidate agents restricted to the public skill, its bundled references, and task-facing public interfaces. The examiner provisions temporary Servers and databases, checks observable results, and keeps holdout questions and rubrics hidden from the reviser. Revisions are bounded; failed or unexecuted cases remain visible in the report.

The agent regression suite lives in tests/skill/, outside the installed skill and ordinary pytest discovery. Each case contains candidate.md, private examiner.md, and executable setup or checking scripts where applicable. Its README describes running the helpers and dispatching candidate attempts. Evaluation reports, metadata, and raw evidence belong in ignored tests/skill/runs/. Record the skill snapshot, software and case versions, candidate configuration, and results so later runs can compare the same cases.

Agent regression tests assess whether a short user request leads to correct operations: submitting experiments, checking progress, diagnosing waiting work, selecting and recovering Tasks, configuring a connection, or adapting a script. Questions give goals, necessary background, environment entry points, and real constraints. They do not prescribe commands, APIs, pagination, execution steps, or the expected conclusion. Detailed acceptance criteria stay with the examiner, who checks results, unintended changes, interpretation, and avoidable questions or detours while allowing different correct approaches.

For new features, extend the user workflow they enable. Low-level retry, fencing, type, process-tree, and database behavior belongs primarily in ordinary automated tests. The agent suite's scripts provision environments and verify operation results. Validate those checks before grading candidates, and keep fixture validation distinct from an agent pass. Record changed question versions rather than presenting results from more prescriptive prompts as evidence for new ones.

Package boundary

  • labtasker-client contains the Client, Worker runtime, public Python API, and CLI. It does not depend on FastAPI, SQLAlchemy, or the Server package.
  • labtasker-server contains the FastAPI/SQLite service and Server CLI. It does not depend on the Client package.
  • labtasker is a code-free convenience metapackage that installs matching Client and Server releases.

Build artifacts must preserve that independence and include the Apache-2.0 license.

Test suites

CI runs the ordinary test suite and independent Client/Server wheel installation checks on Ubuntu with Python 3.11, 3.12, 3.13, and 3.14. Each version has a separate result, and one failure does not cancel the other versions. Formatting, lint, type checks, and the documentation build run once on Python 3.11. Packages are built once on Python 3.11, then the same wheels are tested on all four versions. The real distributed launcher suite runs in the release workflow.

A focused macOS/Python 3.11 CI job checks database ownership, inherited locks, local daemon startup and recovery, Command Worker cancellation, and Worker fork/log isolation. This catches platform-specific regressions without requiring ML dependencies on macOS.

CI and the release workflow also check the previous published Client against the current Server over real HTTP. Run the same check from the repository root:

uv run python tests/compatibility/check_previous_client.py --client-version 2.1.0

The script uses uv to install Client 2.1.0 from PyPI into a temporary isolated environment and starts the current Server with a temporary SQLite database. It checks submission, retrieval, listing, claims, heartbeats, completion, failure reports, and an empty Queue. This check needs access to PyPI and runs separately from ordinary pytest collection. For the next release after 2.2.0, update the baseline in both workflows and this command to the immediately previous published v2 Client.

Ordinary unit and integration tests run without ML frameworks. The explicitly marked launcher suite exercises real torchrun and Accelerate installations:

uv run pytest -m distributed_integration

Launcher coverage includes the supported outer-wrapper topology, environment propagation, one completion reporter, at-fork context clearing, and rejection of an inner per-rank Worker loop.

Run the self-contained submission benchmark when changing Client transport, Task creation, Server transactions, or CLI startup behavior:

uv run python benchmarks/submission_throughput.py

It gives each scenario an isolated temporary database and real HTTP Server, excludes Server startup from the timed region, verifies the final Task counts, and prints JSON measurements for an explicit Python Client, the function-first Python API, and a Bash loop that starts one CLI process per Task. Use --python-count and --bash-count to adjust the sample sizes. Compare results on the same machine; the benchmark deliberately has no machine-dependent pass/fail threshold and is not part of the ordinary test gate.

Measure concurrent HTTP Clients and Worker-protocol claim/completion separately:

uv run python benchmarks/concurrent_throughput.py

The concurrent benchmark gives submission and execution phases separate SQLite files, warms each Client before timing, verifies final succeeded counts, and rejects missing or duplicate Task IDs. Adjust the workload with --tasks, --clients, and --workers; compare results only on the same machine.

The historical implementation remains available on branch v1. A maintainer may keep a separate v1 worktree for comparison, but contributors should not assume that local worktree exists or edit it as part of v2 work. V2 intentionally provides no protocol, database, configuration, or import compatibility with v1.