@git.zone/tstask
@git.zone/tstask is a Linux-only, durable local task queue for coordinating bounded repository maintenance work. It provides a NodeNext ESM API and the tstask CLI, stores state in per-user machine-wide SQLite, and deliberately does not execute task content or mutate Git.
Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.
Requirements
- Linux
- Node.js 24.12.0 or newer
- Git available on
PATHfor repository inspection - An effective user ID and a supported durable local filesystem
Supported state filesystems are the ext family, XFS, Btrfs, ZFS, and overlayfs. tmpfs, network filesystems, FUSE filesystems, and unknown filesystem types are rejected.
Install
pnpm add --global @git.zone/tstask
For use as a library:
pnpm add @git.zone/tstask
There are no runtime package dependencies. SQLite is provided by Node's built-in node:sqlite module.
Quick Start
Add a task for the Git repository in the current directory:
tstask add \
--repo "$PWD" \
--classification deferred-maintenance \
--kind dependency-update \
--subject @git.zone/example \
--title "Update @git.zone/example" \
--reason "Keep the dependency baseline current." \
--target-version 2.0.0 \
--priority high \
--accept build \
--accept test \
--actor maintainer
Inspect and claim the next deferred-maintenance task:
tstask next --json
tstask claim TASK_UUID \
--owner local-worker \
--reason "Begin verified maintenance work." \
--allowed-root /absolute/path/containing/repository \
--json
The claim response is the only response that contains the plaintext 256-bit lease token. Keep it out of arguments, environment variables, logs, and task text. Every later token-authorized command requires --token-stdin:
printf '%s\n' "$LEASE_TOKEN" | tstask release TASK_UUID \
--token-stdin \
--actor local-worker \
--reason "Return unfinished work to the queue."
tstask does not lock the repository after claim. A worker must verify repository identity and state again immediately before editing.
Commands
| Command | Purpose |
|---|---|
add |
Add an immutable task intent or return its existing deduplicated task. |
list |
List tasks with optional state, classification, priority, and repository filters. |
show ID |
Show one redacted task, its typed transition history, and typed evidence. |
next |
Return the highest ordered ready task. Defaults to deferred-maintenance. |
claim ID |
Claim a ready queued task after strict repository preflight. |
renew ID |
Extend a live lease using its expected current expiry. |
release ID |
Return a live claimed task to queued. |
recover ID |
Resolve an expired claim as retry, completed, blocked, or cancelled. |
block ID |
Move a queued or live claimed task to blocked. |
unblock ID |
Move a blocked task back to queued. |
done ID |
Complete a live claimed task with evidence for every criterion. |
cancel ID |
Cancel a queued, blocked, or live claimed task. |
path |
Print the canonical state directory. |
--help |
Show the complete CLI grammar. |
--version |
Show the installed package version. |
Use --json with any command for a stable response envelope:
{"ok":true,"result":{}}
Errors use a stable code and a redacted message:
{"ok":false,"error":{"code":"INVALID_INPUT","message":"..."}}
Raw Git stderr, status filenames, and submitted lease tokens are never included in errors.
Add
Required options:
| Option | Meaning |
|---|---|
--repo PATH |
Existing canonical Git worktree or a canonical path inside it. |
--classification VALUE |
blocking-root-cause, deferred-maintenance, or optional. |
--kind SLUG |
Lowercase task-kind slug. |
--subject TEXT |
Stable task subject, such as a package name. |
--title TEXT |
Short human title. |
--reason TEXT |
Bounded rationale. |
--accept CRITERION |
Repeatable finite acceptance criterion. |
--actor TEXT |
Actor creating the task. |
Optional values include --id, --priority, --current-version, --target-version, --dedup-scope, --root-cause-chain, and repeatable --dependency. --id accepts a canonical UUID; otherwise tstask generates one. Tasks without a target version require a dedup scope. Dependencies must already exist, are immutable, and cannot form a cycle.
Supported acceptance criteria are:
artifact-verifybuilddedupedocker-buildgit-cleanlicense-checkreadme-checkrehearsaltesttypecheck
Acceptance criteria describe verification classes. They are not commands and cannot contain arguments.
List And Next
list supports --state, --classification, --priority, and --repository. next supports --classification and --repository. Use --classification any to remove the default deferred-maintenance classification filter.
Only completed dependencies satisfy readiness. Blocked and cancelled dependencies leave dependent tasks unready. Ready tasks are ordered by priority descending, creation time ascending, then UUID ascending.
Claim And Lease Renewal
Claim requires one or more explicit --allowed-root values. The current repository worktree, absolute Git directory, and common Git directory must all remain within those roots and exactly match the stored repository descriptor. Claim also requires a clean worktree and rejects merge, rebase, cherry-pick, revert, bisect, sequencer, and index-lock state.
Lease duration defaults to 15 minutes. --lease-ms accepts 30,000 through 86,400,000 milliseconds. Claims increment attempts and return a random 256-bit token. The database stores only SHA256 hashes of the owner and token.
Renewal is compare-and-renew. Supply the current expiresAt from the claim or preceding renewal as --expected-expiry:
printf '%s\n' "$LEASE_TOKEN" | tstask renew TASK_UUID \
--token-stdin \
--expected-expiry 2026-08-23T12:00:00.000Z \
--lease-ms 900000 \
--json
Retrying the same renewal after a lost response returns the already-issued expiry without another event or renewal count. A token loses authority exactly at expiry. Expiry never auto-requeues a task.
Recovery
Only an expired claimed task can be recovered. Recovery requires --outcome, --actor, and --reason.
tstask recover TASK_UUID \
--outcome retry \
--actor maintainer \
--reason "The prior worker expired before editing."
The outcomes are exact:
| Outcome | Result |
|---|---|
retry |
Return to queued; the next claim increments attempts. |
completed |
Complete only with evidence for every acceptance criterion. |
blocked |
Move to blocked. |
cancelled |
Move to cancelled. |
Completed recovery requires explicit allowed roots and the same evidence options as done.
Completion Evidence
Completion accepts only a fixed evidence union:
| Option | Evidence |
|---|---|
--evidence CRITERION |
Verification of one declared criterion. |
--evidence CRITERION=PATH |
Verification with an existing repository-relative file reference. |
--commit-sha SHA |
Full lowercase commit SHA. |
--release-version VERSION |
Bounded release version. |
--artifact-sha256 SHA256 |
Lowercase artifact SHA256. |
Example:
printf '%s\n' "$LEASE_TOKEN" | tstask done TASK_UUID \
--token-stdin \
--actor local-worker \
--reason "All declared checks passed." \
--allowed-root /absolute/path/containing/repository \
--evidence build=dist_ts/index.js \
--evidence test \
--commit-sha 0123456789abcdef0123456789abcdef01234567
Every declared acceptance criterion needs matching verification evidence. Referenced paths must resolve to existing files inside the re-verified worktree. Raw logs, commands, environment captures, notes, and arbitrary JSON are not accepted.
Task Model
Task states are queued, claimed, blocked, completed, and cancelled.
Allowed transitions are:
| From | To |
|---|---|
queued |
claimed, blocked, cancelled |
claimed with a live exact token |
claimed by renewal, queued, blocked, completed, cancelled |
expired claimed by explicit recovery |
queued, blocked, completed, cancelled |
blocked |
queued, cancelled |
Every transition writes a typed immutable event containing only event type, from/to state, actor, reason, attempt, and timestamp. No arbitrary event metadata exists.
Limits are 1,000 tasks, 64 dependencies, 16 criteria, 64 attempts, 128 renewals, and 256 events per task. At 240 events, routine claim, renewal, release, retry, and unblock transitions stop. Remaining capacity is reserved for completion, cancellation, or a final blocked outcome.
Deduplication
The versioned SHA256 deduplication preimage is the canonical JSON encoding of:
[version, repositoryIdentity, kind, subject, targetVersion|null, dedupScope|null]
The same key and byte-identical normalized immutable intent returns the existing task in any state. The same key with different immutable intent is a conflict. Recurrence therefore requires a changed target version or dedup scope.
The optional root-cause chain UUID is correlation only. It does not imply readiness or dependency authority.
State And Durability
The default state directory is:
${XDG_STATE_HOME:-~/.local/state}/tstask
TSTASK_STATE_DIR overrides it and must be absolute and canonical. The state path cannot traverse symlinks. The directory must be owned by the effective user with mode 0700; the database and any rollback journal must be owned regular single-link files with mode 0600.
First initialization uses O_CREAT|O_EXCL mode 0600. The winner creates an owned initialization sentinel and initializes in BEGIN EXCLUSIVE; concurrent openers wait for bounded completion. A zero database or sentinel left by an abandoned initializer fails closed and is never silently initialized by a later opener.
Existing databases must have the SQLite header, exact application ID, exact user version, and exact strict schema expected by this package. Unexpected WAL/SHM files are rejected. A validated hot rollback journal is left to SQLite for recovery and is never manually deleted by tstask.
Each connection enforces:
- defensive mode enabled
- extension loading disabled
foreign_keys=ONtrusted_schema=OFFtemp_store=MEMORYjournal_mode=DELETEsynchronous=EXTRA- bounded busy timeout
quick_check(1)after open and recovery
Short mutations use BEGIN IMMEDIATE with deterministic commit or rollback. Git and evidence path preflights run before the SQLite write transaction.
Repository Trust Boundary
tstask invokes only fixed read-only Git command forms with execFileSync, a 10-second timeout, and a 256 KiB output bound. It uses a scrubbed environment, disables terminal prompts, optional locks, fsmonitor, hooks, pagers, global config, and system config, and never invokes a shell.
HTTPS, SSH URL, and SCP-style origins normalize to credential-free host/path identities. Local and ambiguous remotes are rejected. HTTPS credentials and SSH passwords are rejected rather than stored.
A successful claim is a point-in-time preflight, not a Git lock and not proof that the repository remains unchanged. The worker owns the final pre-edit recheck.
Input Trust Boundary
Text is normalized to NFC and bounded. Control, format, surrogate, and terminal escape characters are rejected. Titles and reasons also receive deterministic defense-in-depth rejection for common PEM, bearer token, JWT, npm token, GitHub token, credential assignment, and URL-userinfo patterns.
This deterministic screening reduces accidental secret persistence. It is not proof that arbitrary prose is secret-free. Do not submit secrets, credentials, raw logs, command output, environment data, or sensitive filenames as task text.
Programmatic API
import { TstaskStore } from '@git.zone/tstask';
const store = new TstaskStore();
try {
const task = store.add({
repositoryPath: process.cwd(),
classification: 'deferred-maintenance',
kind: 'dependency-update',
subject: '@git.zone/example',
title: 'Update @git.zone/example',
reason: 'Keep the dependency baseline current.',
targetVersion: '2.0.0',
acceptanceCriteria: [{ type: 'build' }, { type: 'test' }],
actor: 'maintainer',
});
console.log(task.id);
} finally {
store.close();
}
The public barrel exports TstaskStore, task and evidence types, finite value constants, repository inspection helpers, state constants, TstaskError, and runCli.
Scope Boundaries
This package intentionally has no daemon, scheduler, cloud synchronization, remote service, command payload, command runner, OpenCode integration, release-tool integration, or automatic Git mutation. State is local durable filesystem state for one effective user on one Linux machine.
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.
Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
Company Information
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.