npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tickmarkr

v2.11.0

Published

Spec in, verified work out.

Readme

       ▄▖
     ▄█▀
▗▄ ▄█▀             tickmarkr
 ▝█▛               spec in, verified work out.

tickmarkr

Assertions are free. Tickmarks are earned.

tickmarkr is a spec-driven orchestration harness for AI coding agent CLIs. You write a spec with acceptance criteria; the engine routes tasks to the best installed agent CLI (claude-code, codex, cursor-agent, opencode, grok, pi, kimi) by cost and capability, dispatches work in git worktrees for change isolation — as interactive TUIs when running under herdr, headless subprocesses otherwise, or in Orca terminals when auto detects both Orca markers (name that driver explicitly outside one) — and independently verifies each committed result by checking for no new baseline failures per task, then strictly verifying the integration tip. Green tasks consolidate onto a tickmarkr/<runId> branch; merging to your mainline is always your call, never automated. Engage with full visibility into routing decisions, worker progress, and gate verdicts — or run headless and review the local execution log afterward.

Here, isolated git worktrees means change isolation: each task gets its own worktree and branch so its diff is separated from sibling tasks. It is not a process sandbox or host-containment boundary.

Invariants

These are law; the codebase fails closed around them:

  • acceptance[] required on every task; compile fails without it
  • new engagements consolidate to a tickmarkr/<runId> branch — never main
  • gates never trust worker claims; each task is checked for no new baseline failures and the merged integration tip is strictly re-verified
  • state is files + git only; no DB, no services
  • worker/judge/review/consult prompts end with machine-parseable trailers (structured JSON verdicts); parse defensively, fail closed

Any agent (or human) operating this repo must respect the same five rules above — they are not merely internal implementation details, they are the contract the gates enforce.

Install

npm i -g tickmarkr

This installs two identical bins: tickmarkr and its short alias tkr — use whichever you prefer, every example below works with either.

Requirements:

  • macOS or Linux. Every command shells out through bash, so native Windows is not supported — use WSL (untested).
  • Node ≥ 20.3 and git on PATH.
  • At least one agent CLI on PATH (claude, codex, cursor-agent, opencode, grok, pi, or kimi), authenticated through its own login. tickmarkr never handles vendor API keys itself.

Then verify your fleet:

tickmarkr doctor   # probes installed adapters, herdr, auth; prints the capability matrix

Expect doctor (and init, which runs it) to take a while on first run: auth detection is one real, short LLM call per configured model per installed CLI — honest, but not instant. A machine with three CLIs and several models each can take a minute or more; each probe is capped at 60s.

doctor also sweeps a catalog of known agent CLIs that have no adapter yet and prints an advisory detected — no adapter; not routable row for any found on PATH — a freshly installed harness is visible the day it lands, and never routed until an adapter ships for it.

Quickstart (5 minutes)

tickmarkr init                 # guided setup + doctor; scaffolds config and spec template
# edit tickmarkr.spec.md        # write your tasks and acceptance criteria
tickmarkr compile tickmarkr.spec.md # spec → task graph (fails without acceptance criteria)
tickmarkr plan                 # dry-run routing decisions + cost estimate
tickmarkr run                  # execute, route to best CLI, gate every result (--concurrency N)
tickmarkr report <runId> --md  # Markdown on stdout; redirect to save beside the spec

That's the flow: init scaffolds config, you write tasks with acceptance[] criteria, compile validates and builds the graph, plan shows routing decisions and cost, run dispatches work to installed CLIs and checks each task for no new baseline failures before strictly verifying the integration tip, and report documents the outcome. All green tasks land on tickmarkr/<runId> — merge to your mainline is your decision.

Agent-ready repos: tickmarkr init --agent

tickmarkr init --agent composes with the base init above and additionally installs the consumer-facing skills (tickmarkr-loop, tickmarkr-auto, tickmarkr-overseer) that ship in the npm tarball, so a coding agent working in your repo knows how to drive tickmarkr without reading its source:

tickmarkr init --agent            # installs host skills, offers CLAUDE.md/AGENTS.md notes
tickmarkr init --agent --force    # also overwrite skill files that already exist
tickmarkr init --agent --docs     # also append the agent-docs section without an interactive prompt

Consent rules (queue row 244; D-2141; D-2152):

  • an existing currently shipped host skill file (tickmarkr-loop, tickmarkr-auto or tickmarkr-overseer) is left untouched unless you answer yes to a per-file prompt, or pass --force
  • --agent removes retired skill directories only when their complete, nonempty tree, including a regular SKILL.md and no extra directory, equals one complete shipped snapshot path for path and byte for byte. This shipped-bytes proof works with or without the ownership marker; a marker alone never proves ownership. Inside today's skills it also removes retired files whose bytes were shipped at that same skill path. A complete earlier snapshot stays intact when its refresh is skipped; its retired files are removed only after a forced or consented refresh is applied. Each deletion says removed leftover <path>.
  • --remove sweeps those proven retired files before removing skill directories with the same complete-snapshot proof at both host locations. Unproven content is kept byte for byte and reported by path under reserved names or with the exact metadata.installed-by: tickmarkr marker; unmarked user skills outside reserved names stay untouched and unlisted. Today's reserved names keep the install and --force refresh behavior above; refresh preserves added files.
  • the no-follow boundary covers linked host roots and their ancestors within the consumer, skill entries, files and nested directories, including dangling links. Nothing is read, copied, written or deleted through them; each is named as kept <path> (symbolic link). tickmarkr version --check names every leftover <path> and unproven <path>, including when leftovers are the only installed guidance, and exits non-zero with a remedy naming each path. Move an added file out of the skill directory; modified or missing shipped files use tickmarkr init --agent --force.
  • a short "tickmarkr" block contains only the five invariants, the version check stop, the one-line green rule and the routing line naming the loop, auto and overseer skills. It is appended to each applicable host's guidance (AGENTS.md, plus CLAUDE.md when the repo uses Claude) only after you say yes, or pass --docs; it is wrapped in <!-- tickmarkr:agent-docs begin/end --> markers and never inserted twice. The loop and auto skills hold the commands, role check, preflight, seat hygiene, verified handoffs, full green rule and spec-template pointer; both point every host to the overseer skill's PROJECT MEMORY duty under Setup (queue row 241; D-2141; D-2152)
  • declining a prompt still lets the rest of init complete
  • non-interactive shells (no TTY) never prompt: missing skills are installed, eligible proven leftovers are removed, and existing shipped files needing overwrite consent are skipped with their complete earlier snapshots intact; use --force to refresh them. The summary names what was removed, kept or skipped and which flag would enable a refresh

Monitor and supervise

tickmarkr ui [runId] opens one cockpit with 1 Home, 4 Run, 5 Evidence. Without a run ID it selects the latest journal; an empty repository opens Home. Use tickmarkr ui <runId> --view run or --view evidence to open a delivered view directly. tickmarkr ui --setup <runId> opens Run Parks and preserves the requested run identity. Fleet/Bootstrap and Plan/Health are explicit follow-ons: keys 2/3/6 are not installed. Use the existing tickmarkr fleet, tickmarkr init, tickmarkr plan and tickmarkr doctor commands for those workflows.

? opens the shortcut sheet; Tab/Shift-Tab move focus through visible regions, Enter opens selection, and Esc closes the deepest overlay. q quits; text-entry mode keeps q1? literal. Run shows every task in the matching graph, its recorded attempt/path/pane/alarm, and current-attempt gate evidence. o requests focus of the recorded owned pane when the driver supports it; unavailable panes leave an evidence diagnostic. Evidence keeps original journal #L identities, full verdict paging, and a stable selection with Follow off.

tickmarkr status <runId>        # preserved printed engagement state
tickmarkr status <runId> --oneline # compact snapshot, then exit
tickmarkr status <runId> --watch # TTY: Run cockpit; non-TTY: line output
tickmarkr status <runId> --watch --plain # preserved line/ANSI fallback, including on a TTY
tickmarkr resume <runId>       # continue an engagement from the local execution log
tickmarkr approve <runId> <taskId> --park <line>@<ts>   # decide the park status printed; see below
tickmarkr report <runId>       # cost/quality report
tickmarkr report <runId> --md > feature.record.md # explicit file write beside your spec
tickmarkr profile              # show the learned routing profile
tickmarkr profile --explain <shape> <channel>  # why a channel ranks where it does for a shape

For machines, tickmarkr status <runId> --watch --events replays and follows projected decision events as one JSON document per stdout line. --jsonl and --decision-events are aliases; keep stderr keepalives separate (never 2>&1). This projection is distinct from raw journal.jsonl. Webhook delivery remains opt-in via --webhook <url>. Preserved printed twins also include separate fleet --print and fleet --why outputs, plan, doctor, report --compare <baseline-runId>, report --bundle <path> (an explicit proof-bundle write), and stats (all runs, no run ID). Report retains its learning preview and comparison warnings; absent metering stays “not measurable,” never an invented $0.

A manual cockpit keeps its final receipt at run-end and follows a later resume of that run. By default, the daemon-owned board requests graceful shutdown at run-end and closes only its owned pane after checking its watch presence stood down; unconfirmed cleanup is reported. The existing visibility.keepPanes: forever debug override preserves panes. Herdr keeps task/gate grouping, short titles, and the board beside its caller without taking focus. Quitting or orderly signals restore raw mode, pointer tracking, title and alternate screen, and release only that observer's presence. Green tasks land on tickmarkr/<runId>; merge to your mainline is always your call.

Escalation and consults

When gates or tasks stall, tickmarkr escalates to a frontier-model consult for a structured verdict. The consult can recommend rerouting (exclude a failed channel/adapter and try the next ranked option) or human approval if deadlock persists. Exclusions persist across resume — a task that escalated away from a failing adapter will never retry it in subsequent tickmarkr resume calls.

Approving tasks

The recorded partial-human-park case has 1/3 merged, human T2, blocked T3, and a passed tip verify. It is PARTIAL, not green: T2's humanGate: true parks it before dispatch. In Run (4, or tickmarkr ui --setup <runId>), select T2, press a for Actions, choose Approve with Enter, and review the confirmation: run/task, original park #L, actor/reason, exact tickmarkr approve argv, append-only consequence and enactor. Only y confirms; n/Esc cancel, and Enter never confirms.

Read the receipt's newly appended task-approved line, actor/reason and disposition back from the journal. Approval records permission; it does not dispatch work, pass a gate or mark the task done. With no live owner the receipt says approved; resume required. Exit the observer and run tickmarkr resume <runId> explicitly. A matching live daemon can enact the release at its next task boundary; a different live run must end before this one resumes. Keep any resume refusal and its remediation visible, including a deny/prefer config conflict; repair the source/config as directed, never edit the compiled graph to force success. After resume, CURRENT TIP is PENDING until fresh evidence arrives. Completion requires the latest run-end, a nonfailed known tip result, empty failed, human, blocked and pending buckets, and owed checks outstanding empty AND known in CURRENT tickmarkr status <runId> (outstanding 0); the completed case has 3/3 recorded merges. Empty buckets alone are not completion (D-660): 3/3 merged with every bucket empty and the tip passed, after the operator waived T2's review, leaves one accepted-risk review check owed. run/resume exit 0 on execution alone, their final line says execution complete; outstanding 1 (T2 review) rather than verified, and status reads outstanding 1. That run completes only when tickmarkr verify --record <runId> discharges the check and CURRENT status reads outstanding 0 — a discharge after run-end counts, while the run-end record keeps its old count; outstanding unknown never completes. An unrelated graph says “not comparable” and supplies no borrowed denominator. Historical GATES RAN does not establish current completion.

The CLI twin for the same decision is tickmarkr approve <runId> T2 --park <line>@<ts> --by operator --reason 'ready to proceed', followed by the receipt check and explicit resume above. Every decision is bound to one park: status, the park notice and the Run confirmation print its <line>@<ts> token (the park row's physical journal line and timestamp), and --park names it — without --park the park open when the command starts is bound. A waive is also bound to that park's failed gate, named explicitly with --gate (tickmarkr approve <runId> T2 --waive --park <line>@<ts> --gate review). A decision queued behind another approval is revalidated under serialization, and the daemon revalidates it again before enactment: once a newer park opens, a stale token, an unbound row or a mismatched gate is refused (the daemon journals approval-refused) and never waives the newer gate. A failed task's recheck keeps working with its own bound token: status prints failed — T3 — failure <line>@<ts>, and tickmarkr approve <runId> T3 --recheck --park <line>@<ts> re-gates its landed commits with no worker. Other parks have different permitted decisions:

| Park | Decision and effect | |---|---| | Human gate / other non-gate park | Plain approve records permission to dispatch. | | Attempt cap | Plain approve grants a fresh attempt budget; prior routing exclusions remain. | | Infrastructure | Plain approve or --recheck; recheck reruns the declared battery and satisfies no gate. | | Stall with a recorded reapFailure (the worker's process census was unreadable or had survivors) | --recheck bound to that park's token re-verifies the parked attempt's owned census: only an explicitly recorded empty survivors array ([]) releases its harvested commits to the declared battery, with no worker; a missing, unreadable or surviving census re-parks the stall (new token, still approve/recheck). Plain approve dispatches a worker. | | Ordinary stall (no reapFailure) | Plain approve only — it dispatches a worker; --recheck refuses. | | Failed review gate | --waive satisfies only that identified gate; --uphold funds one fixed attempt carrying findings; --recheck reruns the battery. | | Other failed gate | --waive or --recheck; plain approve refuses. | | Tombstone / gate failure without identifying evidence | Diagnostic only; no invented decision. |

Decisions are append-only and cannot be undone. Unknown tasks, duplicate decisions and changed parks refuse; no success receipt is claimed without reading back the append.

Help and recovery

tickmarkr ui --help, tickmarkr eval --help, tickmarkr unlock --help and tickmarkr profile reset --help print guidance without opening a UI, seeding fixtures, unlocking or resetting history. Help flags are recognized before --; arguments after it are literal data. Help examples describe operations; printing them never executes them.

Recovery remains explicit: unlock <runId> targets a matching provably dead lock; unlock --garbage handles malformed lock bytes without inventing a run ID. Both require confirmation (--yes for non-TTY) and refuse live, inaccessible or changed holders. doctor --cached reads cached diagnostics; doctor --probe-preflight discloses probe counts/files; doctor --fix-only repairs locally without model probes or catalog refresh. Default doctor and --fix still probe. doctor --refresh-catalog refreshes only the catalog. scope <intent-file> --preview is a local, non-writing preview; authoring requires TTY confirmation or --yes and can make model calls.

Choosing your fleet: tickmarkr fleet

Configure which agent CLIs and models tickmarkr may route before your first run:

tickmarkr doctor          # probe auth + capabilities (run after install or credential changes)
tickmarkr fleet           # interactive fleet browser (requires a TTY) — review diff to write
tickmarkr fleet --print   # effective fleet state (repo > global > defaults), non-interactive
tickmarkr plan            # lint the resolved routing table against your spec
tickmarkr run             # dispatch with the fleet you confirmed

tickmarkr doctor probes and records health; tickmarkr fleet edits routing config. The browser is one two-pane surface: the left rail lists views (All models, Shapes, Steering) and every installed agent CLI with its auth state and model count; the right pane is a searchable model list with tier, context, price, and probe-latency columns. Space allows/denies, Enter classifies an unclassified model (with a required benchmark-provenance note) or pins a classified one to a shape, m opens the routing-mode presets, and w renders the unified diff of the one overlay the save lands in, named in the review: an allow/deny membership edit goes to the layer that holds it (the repo overlay when it declares that family, else your user overlay), everything else to the user overlay; a batch needing both is refused — save each half in its own session. Nothing is written until you confirm with y. A worker-only deny on a partially excluded adapter takes two rail presses: out · all seats, then out · workers. The Shapes view pins from a candidate picker ranked by the production router.

Routing-mode semantics, pin/floor/prefer precedence, review and consult steering syntax, provenance rules, and --quality / --mode flags are documented in FLEET.md (advanced reference).

Steering

The fleet browser's Steering view sets review.prefer and consult.prefer; routing modes (risk-based, partner-led, staff-led) are applied from the presets overlay (m). Full grammar — including when review prefer may name a bare adapter versus adapter:model, and why consult prefer entries require adapter:model form — plus tickmarkr run --supersedes rerun control, is in FLEET.md.

Model scoping and auth detection

Each agent CLI exposes a list of available models. tickmarkr's routing works only with classified models — those you explicitly enter into the config under tiers. The tickmarkr doctor command probes these models to detect auth status and records the results, which routing consumes to avoid 401/403 dispatch failures.

Model terminology:

  • Listed models: All models a CLI advertises as available (e.g., pi advertises both zai/glm-5.2 and anthropic/claude-opus-4-8, but not all are authed)
  • Scoped models: Listed models with an explicit provider prefix (e.g., zai/glm-5.2, zai-coding-plan/glm-5.2). The design rule is to classify scoped models primarily, not unscoped listed models
  • Classified models: Models you've entered into tiers — the routing-eligible set, regardless of scope

When you run tickmarkr doctor, it:

  1. Probes each classified model exactly once (one minimal headless API call per model per adapter)
  2. Records results locally: authed: true or authed: false with the failure reason and probe timestamp
  3. Prints a model-status table for classified models only, showing tier, auth verdict, denial status, and prefer rank

Routing and auth:

  • Routing discovers channels only from authed classified models (unauthed channels are dropped)
  • tickmarkr plan lints each excluded model, naming the probe reason and timestamp
  • If a task's floor can only be satisfied by unauthed models, the plan fails loudly — never a silent fallback

When to re-run doctor:

  • After classifying new scoped models into tiers (e.g., onboarding a new vendor or aggregator CLI)
  • Before a run, if credentials may have changed (API key rotation, quota reset, subscription renewal)

Operator model control — deny.models: You can bench a classified model without removing it from tiers:

routing:
  deny:
    models:
      - pi:zai/glm-5.2            # stays in tiers, never routes (key is adapter:model)
      - cursor-agent:composer-2.5 # reason in git commit

tickmarkr plan lints denied models identically to unauthed ones. Re-enable by deleting one line.

Run output and local execution log

When you execute tickmarkr run, the daemon records every event in an append-only local journal: task dispatch, gate verdicts, worker status, and merges. Narration streams to stdout; you can also --watch or tail the journal directly. If interrupted, tickmarkr resume <runId> replays the journal and continues from the last stable state — deterministic, not ephemeral.

Usage and cost

Every engagement record includes a Usage & efficiency section showing token/window consumption, cost estimates, and first-attempt success rate. Cost reporting follows strict honesty rules and never infers absent data:

Cost model — two channel economics

API channels (real marginal cost):

  • Formula: tokens × price
  • Pricing data sourced from LiteLLM model prices
  • Each estimate carries its basis: input tokens, output tokens, cache reads (if applicable), rate per Mtok, and rate date

Subscription channels (flat monthly, no marginal token cost):

  • Two metrics reported when computable:
    • Amortized window cost: plan monthly cost ÷ usable windows per month — a range accounting for time-varying quotas
    • API-equivalent counterfactual: metered tokens × API price of the same/nearest model — e.g., a $100/month Claude Max user who consumes tokens worth $500 API-equivalent has paid $500 API-equivalent for flat $100
  • Both metrics appear in the report alongside their sample count and range bounds

Honesty rules (no guessing allowed)

  • Ranges, never single numbers: quota multipliers and monthly-window variation make subscription costs a range, not a point estimate
  • "Not measurable" never becomes $0: if a channel lacks pricing or metering data, the report explicitly states "not measurable"
  • Basis always shown: every cost figure prints the token count, rate used, and date so estimates can be audited
  • No network calls: pricing config is operator-maintained locally, seeded with dated comments and LiteLLM's JSON file named as the copy-from source; tickmarkr never calls home to fetch rates
  • Attribution from journal: token counts come from tickmarkr's own telemetry spans in the local journal, never from provider invoices or dashboards

Visibility: optional supervised workspace

When running under herdr, tickmarkr creates a labeled pane-and-tab workspace for real-time visibility (optional — omit --driver herdr or run headless if preferred).

Orca: a detected-or-named execution surface

Orca is the third execution surface. auto resolves herdr first when HERDR_ENV=1, then Orca only when both Orca-authored markers TERM_PROGRAM=Orca and ORCA_TERMINAL_HANDLE are present, then subprocess. This environment-only choice executes no binary or runtime probe. Outside an Orca terminal, name it explicitly with --driver orca or driver: orca in config. Once selected either way, an unreachable Orca stays a loud Orca driver failure and is never silently replaced by a hidden subprocess worker.

What Orca supplies is terminals. What tickmarkr keeps is everything that decides whether work ships: it creates and owns the git worktree for every task (Orca is told which checkout to bind its terminal to, and never makes one), it runs the full gate battery — build, test, lint, evidence, scope, acceptance, review — against the commits that land there, and it holds merge authority, consolidating only green tasks onto the run's tickmarkr/<runId> integration branch. Orca is given no say over any of the three. Merging that branch to your mainline remains your call, exactly as with every other driver.

tickmarkr borrows audit-firm vocabulary for its roles: you are the Partner (final sign-off), workers are the field team, the acceptance judge is the EQR (engagement quality reviewer), and the frontier-model consult is the National Office. The terms below use that vocabulary:

Tab vocabulary

  • <taskId>: one tab per TASK. A task's worker pane and every gate pane it earns — judge, review, consult — live together in that task's tab, so everything happening to one task is in one place. The tab header shows the task token plus one state glyph:
    • ↻ — the member is a retry attempt (attempt > 0)
    • ✋ — the driver detected the member is blocked
    • (bare token) — member is running normally
  • cleanup · : overflow/teardown generation tabs. When a new generation starts (on retry escalation), a new cleanup tab opens labeled with the newest live member's task ID; it auto-closes when the generation completes
  • watch: a single owned pane running tickmarkr ui <runId> --view run — the same Run cockpit opened by TTY status --watch

Pane naming (when visibility.llm = pane)

  • judge · : the EQR evaluating acceptance criteria
  • review · : the cross-vendor code review gate
  • consult · : National Office escalation on deadlock (gates or worker stall)

Notification tiers

Only operator-decision events notify (with sound):

  • ✓ Gate failure escalations (move to next channel, human approval needed)
  • ✓ National Office verdicts (deadlock resolved or escalation recommended)
  • ✓ Human gates (task parked, awaiting tickmarkr approve)
  • ✓ Quota failover (channel exhausted, routed to next)
  • ✓ Engagement end (unqualified or qualified opinion)

Routine events do NOT notify:

  • ✗ Task dispatch (tab already shows progress)
  • ✗ Task done (tab state updated, already visible)

This keeps the Partner focused on decisions that require attention, not noise.

Reconciliation model: level-triggered pane lifecycle

tickmarkr closes exactly what it owns and no longer needs, no matter how any process died.

Tabs use short human labels (at most 20 characters); panes carry durable ownership names:

  • <taskId> — the task's tab, holding its worker and its judge/review/consult panes
  • cleanup · <taskId> — teardown generation tab for overflow attempts
  • tickmarkr:watch:run:0:<runId> — daemon-owned Run board
  • tickmarkr:<role>:<taskId>:<attempt>:<runId> — durable judge, review, consult and worker pane names; display titles may be shorter

Reconciliation stays within the run's workspace. It can retire another run's panes only when this repository's journal evidence proves that run ended; live or unknown runs and other workspaces remain protected. Pane names outside the ownership contract are foreign. Board replacement additionally checks repository/run ownership and its acknowledged watch presence; matching a short tab label is never sufficient.

Desired-state reconciliation: A pure function computes the exact set of panes that should exist from the local journal at any moment:

  • Worker panes for all in-flight task attempts
  • Gate panes for unread judge/review/consult verdicts
  • The watch pane (if running)
  • Empty set (after engagement end)

The daemon reconciles at every safe point:

  1. Run start — reconcile this run and older runs proven ended in this repository
  2. Resume — reconcile the restarted journal state and close panes for superseded attempts
  3. After terminal events (task done, failed, human gate) — close the corresponding worker/gate pane and its emptied tab
  4. At engagement end — close remaining owned panes and tabs, with graceful board shutdown

visibility.keepPanes: forever disables this sweep. Reconciliation failures (herdr unavailable, a pane vanished mid-sweep) do not replace gate verdicts; unconfirmed board cleanup is journaled and reported.

Worktree lifecycle

Queue row 240 (D-2138): at run end, every journaled task checkout is removed once its recovery ref is recorded, including parked and failed tasks. Clean merged checkouts are removed silently. Dirty work is preserved under refs/tickmarkr/preserved/; unmerged clean heads are pinned there too. The journal records the recovery ref and removed checkout, and resume rebuilds from that work. Task branches and refs remain.

A crash or SIGTERM leaves checkouts until the next daemon start in the same checkout. After its lifecycle row and reconciliation, that start sweeps other runs in this checkout's runs directory only when their newest lifecycle row records a proven dead pid, regardless of how they ended. Recovery and removal rows go into the owning run's journal and name the sweeping run; outstanding checks and measured wall windows stay unchanged. Only journaled task checkouts are touched: there is no worktree directory listing or in-use check. Live or unrecorded pids, this run's checkouts, verification checkouts, hand-made directories and sibling linked checkouts remain untouched; a refused start sweeps nothing. visibility.keepPanes: forever disables both removal boundaries.

Staged bytes missing from the recovery tree, preservation failures and deletion failures keep the checkout and journal a run-level removal failure in the current run. Cleanup never changes the run's outcome or adds another run end.

Workspace trust

tickmarkr creates fresh git worktrees for change isolation. That is separate from each CLI's own workspace-trust behavior:

  • tickmarkr doctor invokes each installed adapter's trust hook where supported and seeds trust where possible
  • Some CLIs show a "Workspace Trust" dialog; tickmarkr auto-answers only a recognized fingerprint once per slot
  • Other blocked dialogs page you for manual approval

Spec formats

tickmarkr compiles several formats into its internal task graph. The most common:

| Format | Example | |---|---| | tickmarkr native (default) | tickmarkr init template with native spec marker | | Markdown PRD | Any .md file with task sections and acceptance[] criteria | | Spec Kit | A Spec Kit spec directory | | GSD | A GSD .planning/ phase plan |

Compile fails loudly if it cannot recognize the format or if acceptance[] is missing. Use --type native|prd|speckit|gsd to force a specific format.

Acceptance criteria (assertions)

Every task requires explicit acceptance[] criteria; compile fails without them. Three oracle types:

  • command <cmd> — pass if exit code 0; most reliable
  • test <name> — run a named test from your suite (must exist)
  • judge <rubric> — LLM verdict against your rubric; fail-closed, never overrides failed command/test

At runtime, the scope gate derives git diff --name-only from the task base and compares it with the spec-declared files. Out-of-scope edits fail unless the operator config explicitly allowlists them; worker-declared deviations are recorded as notes, not authority.

Claude Code and Claude Desktop

The optional tickmarkr plugin adds the /tickmarkr-board pane and a session-start waiter:

  • Claude Code: claude plugin install tickmarkr --marketplace alzahrani-khalid/tickmarkr.
  • Claude Desktop: Plugins → Add marketplace (alzahrani-khalid/tickmarkr) → Add plugin (tickmarkr).

Enabling the plugin runs tickmarkr wait --timeout 23h as a background task at every session start and resume and loads the board module (hooks/board/register.tsx); both execute with the user's permissions. The waiter wakes the session when the run ends, parks, a task fails or the daemon dies. The board shows the current run's status and decisions.

The plugin and tickmarkr init --agent use the same skills:

  • /tickmarkr-loop — compile a spec, review the routing plan, run the engagement, and commit the Markdown record
  • /tickmarkr-auto — autonomous multi-phase runs (GSD milestones, etc.)

These are optional — the CLI works standalone. Skills are repo-scoped and ship in the npm tarball for agents working in projects that have run tickmarkr init --agent. skills/ stays the single source for the plugin and installed skills; this repository's .claude/skills/ links resolve there. The overseer skill links to the same loop walkthrough for cockpit and decision guidance; skill names remain unchanged.

tickmarkr acp

tickmarkr acp is an ACP v1 agent that an editor launches over stdio. It runs no model and accepts only console commands:

| Command | Behavior | |---|---| | /attach [run] | Watch the named run, or the current/default run. | | /status [run] | Read the named run's current status, or the current/default run. | | /compile <spec> | Run tickmarkr version --check first; compile only if it succeeds. | | /plan | Run tickmarkr version --check first; plan only if it succeeds. | | /run | Launch tickmarkr run --driver subprocess as a detached daemon, then watch it. | | /resume <run id> | Launch tickmarkr resume <runId> --driver subprocess as a detached daemon, then watch it. |

Journalled runs appear as sessions. Both launch commands also check the version before launching; they accept no driver override or other flags.

A park shows its exact tickmarkr approve commands, who enacts each decision, and the approval window's close time when a window is open. The live daemon enacts a release at its next task boundary; without a live owner, run the printed tickmarkr resume command after approving. If another run holds the repository lock, wait for it to end before resuming. Window expiry is a lapse: approvals are swept again before final close, and the daemon owns the run until it exits. A park with no permitted decision shows its reason. ACP never decides: no ACP answer or client setting releases a park. Run a printed approval command explicitly in a terminal.

Closing the editor or a session stops only the watch; nothing is signalled to the daemon or workers. ACP never stops a run.

Dock-launched editors may lack your shell's PATH. Use absolute paths to both Node and the tickmarkr script. Replace both example paths below with your installed paths: find Node with node -p 'process.execPath', then run npm root -g and append /tickmarkr/dist/cli/index.js to find the global script (or use your checkout's dist/cli/index.js).

For Zed, merge this entry into settings.json under agent_servers:

{
  "agent_servers": {
    "tickmarkr": {
      "type": "custom",
      "command": "/opt/homebrew/bin/node",
      "args": ["/opt/homebrew/lib/node_modules/tickmarkr/dist/cli/index.js", "acp"],
      "env": {}
    }
  }
}

For JetBrains, add this entry to ~/.jetbrains/acp.json:

{
  "agent_servers": {
    "tickmarkr": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/opt/homebrew/lib/node_modules/tickmarkr/dist/cli/index.js", "acp"],
      "env": {}
    }
  }
}

Other ACP clients use the same command and arguments. tickmarkr is not in the ACP registry; configure it as a custom agent.

Contributing

See CONTRIBUTING.md for development setup, the green bar (build/test/lint), and design invariants. Pull requests welcome; non-trivial changes should include test coverage.

Boundaries: this repo is a squashed export of private development — each release is a verified snapshot, not a live mirror of every commit. Before tickmarkr 2.0, minor versions may break with every break noted in CHANGELOG.md. Support is best effort for the latest version only; accepted contributions are credited via Co-authored-by: on the release commit. Details in CONTRIBUTING.md.

Documentation