Skip to content

Second standalone skill: tickets and the agent queue (#1748) - #1749

Merged
suleimansh merged 1 commit into
mainfrom
skill-tickets
Aug 30, 2026
Merged

Second standalone skill: tickets and the agent queue (#1748)#1749
suleimansh merged 1 commit into
mainfrom
skill-tickets

Conversation

@suleimansh

Copy link
Copy Markdown
Contributor

Second standalone skill, per #1748: tickets and the agent queue as @gemstack/skill-tickets, with the tickets command. One PR: the skill, the branch primitive moved into skill-branches, and the framework adopting both. Dogfooded on an isolated rig (below) before opening.

Two things I decided while building, both in the PR:

  1. The framework's branch is agents-logs, not agent-logs. A directory .branches/agent-<x> is an agent checkout to the branches skill (that is its convention), so .branches/agent-logs showed up as a phantom agent logs and the reclaim sweep went after it. One letter away from the suggested name, same shape as the old agents-data.
  2. A lost push no longer applies an operation twice. The old data-branch.ts funnel rebased the losing attempt's commit onto origin and then re-ran the operation on top; a clean rebase kept the first commit, so a queue add could land twice. The moved primitive winds the operation's own commit back before re-applying. Found while writing its SPEC; covered by a test.

Temporary, on purpose (both labelled in code and dies when use-npm-skills commits the skills into the repo):

  • createCheckout/attachCheckout take a skills list, so the daemon can link tickets beside branches in every checkout it creates.
  • Agents outside a daemon-made checkout (terminal, Actions, cloud) get prompts/tickets_yourself.md + the package's SKILL.md text appended after branch_yourself.md, generated at build like the old inline skill was.
    A claim made that way names the agent's branch (no agent id exists there), so the dashboard shows it as written rather than resolving it to a session.

On every machine, after the merge (the old daemon would resurrect agents-data on origin, so do it everywhere the daemon ran):

# once, on origin (rehearsed verbatim on a throwaway remote shaped like origin/agents-data)
git fetch origin agents-data
git push origin origin/agents-data:refs/heads/tickets        # tickets + queue (+ agents/, removed next)
git fetch origin tickets
git worktree add /tmp/tk --detach origin/tickets && (cd /tmp/tk && git rm -rq agents && git rm -q tickets/2026-07-27_unified-session-timeline.lock.md \
  && sed -i '' '/^- \[x\]/d' TODO_AGENTS.md && git add -A && git commit -m "tickets: drop the archives, the stale lock and the done entries" && git push origin HEAD:refs/heads/tickets)
git worktree remove --force /tmp/tk
git push origin origin/agents-data:refs/heads/agents-logs    # archives + routine locks (+ tickets/, TODO_AGENTS.md, removed next)
git fetch origin agents-logs
git worktree add /tmp/al --detach origin/agents-logs && (cd /tmp/al && git rm -rq tickets TODO_AGENTS.md && git commit -m "agents-logs: drop the tickets and the queue" && git push origin HEAD:refs/heads/agents-logs)
git worktree remove --force /tmp/al
git push origin --delete agents-data
# per machine, per registered project (before restarting the daemon on the new build)
git worktree remove --force .branches/agents-data; rm tickets; git branch -D agents-data
# then pull, build, restart the daemon

The [x] lines: the queue on origin is nine done entries and nothing open; the queue format says done means deleted, and the code follows it now, so they go. The stale lock names a claude.ai session id; nothing holds it.


🤖 curated · Fable 5, effort high — what the agent built and how it was checked; I read it and I stand behind it.

What is in the PR

packages/skill-branchessrc/file-branch.ts: a branch used as a file store, with a branch argument (the old data-branch.ts, product-free): persistent checkout under .branches/<branch> with one serialized write cycle, the eager pull, reads off a ref from anywhere in the repo, a reader that fetches once, and a detached one-shot write from any clone (throwaway worktree on origin's tip, commit, push straight to the branch, re-apply on a lost race, never the persistent checkout) — the command's write path. skill-links.ts/checkout.ts: the temporary skills list. 130 tests.

packages/skill-tickets (new) — names (the conventions: branch tickets, checkout .branches/tickets, tickets/ + TODO_AGENTS.md, the path gates, a ticket's queue section, its issue ref), tickets (the read side over a filesystem seam, so it reads a checkout or a git ref), locks (CLAIMED: <holder>, claim batches for plan/drain, release, held-by release), queue (parse, insert by priority, delete on done, add/done through the caller's funnel), holder (the agent id off .branches/agent-<id>, else the branch name, detached = no-identity), store (the branch bound: paths, seams, the sync that seeds the queue and links tickets at the root), cli + bin/tickets (list, show, queue, queue add … [--priority N] [--ticket <file>], queue done, put, close, claim, release; JSON out, exit 0/1/2; refusals no-ticket, claimed+holder, not-holder+holder, no-lock, no-identity, no-remote, not-a-repo, invalid-path, no-entry), SKILL.md (the formats live there only). 57 tests, the claim race against a bare remote included. SPECs per sdd.md for every file.

packages/framework — deleted: data-branch.ts, ticket-locks.ts, the parsers of tickets.ts and todo-loop.ts, the reader in dashboard/tickets.ts, the prompts ticketing_format.md / todo_format.md / data_branch_protocol.md. Kept: planTicketPrompt/planAgentFor, runTodoLoop (over the skill's queue), routine-locks.ts and the archives on LOGS_BRANCH = 'agents-logs' through withFileBranch. Changed: daemon-services syncs both branches, claims/releases/queue-done through the package, passes the minted agentId to the start; auto-pm mints ids (mintAgentIds, a millisecond apart from its clock) and the pinned prompts say tickets show/put/release/close; daemon-runtime puts both bins on PATH and links the tickets skill; StartAgentOptions.agentId; agent-id.ts (pure, so the sweep stays browser-safe); dashboard/tickets.ts resolves a lock's holder to lockedByAgent { id, name? } and the ticket row/detail open the agent; system-prompt.ts drops the three formats, the bullets send to the skill, non-owned agents get the bridge; presets reworded to the skill; .the-framework/LAYOUT regenerated (tickets-branch, logs-branch); FEATURES-SPEC, prompts/README, root/packages SPEC, every touched SPEC.

Dogfood (isolated rig: bare remote + clone with three tickets on tickets, daemon on :4201 from this worktree's dist, Opus, target: local)

Every check has a positive signal:

  1. Both agent checkouts carry .claude/skills/{branches,tickets} and .agents/skills/{branches,tickets} → the packages; tickets on the PATH (the agents ran it).
  2. Hand-started Plan tickets: the agent used the skill (tickets list, tickets show, tickets queue add … --priority N); three queue add: commits on origin's tickets under the agent's git identity; TODO_AGENTS.md has the entries in ## Priority 8/6/4 as plain bullets.
  3. Fan-out (only: plan, concurrency 2): one claim 2 tickets commit with CLAIMED: 2026-08-30T14-48-39-412Z / …-413Z — the ids the two agents were then started with; onTickets returns lockedByAgent: { id }; each agent did put …plan.md then release …; locks gone, plans carry Effort/Uncertainty.
  4. Race: tickets claim for the same ticket from two clones back to back → the second exits 1 with {"ok":false,"reason":"claimed","holder":"agent-race-a"}; exactly one lock commit on origin.
  5. Drain (sendQueueTicket at priority 9, then only: drain): claim under the drain's id; the agent named its session, so the row resolves lockedByAgent: { id, name: "farewell-line" }; branch pushed to origin; the rig has no GitHub so the handoff's PR step fails and the entry (by design) stays open — with the archived record marked handoffReport: done by hand (what the unit test does) the next sweep landed queue done: … and the line is gone, not [x]. (Cosmetic: an emptied ## Priority 9 heading stays.)
  6. Non-owned: claude -p in a plain clone with the composed system prompt (the bridge present) and the Suggest-new-tickets preset → three tickets pushed to tickets with git, one commit, by the clone's identity.
  7. pnpm test in skill-branches (130), skill-tickets (57), framework (1386 node + dashboard vitest); root typecheck.
  8. Archives land on agents-logs after each run settles; the triage-quick lock file appeared on agents-logs during the triage and was released when it ended.

Not done: the live :4200 daemon on gemstack — the new build's sync would birth an empty tickets branch on gemstack's origin before the cutover, so that runs after the merge + cutover.

`@gemstack/skill-tickets`: the tickets and the queue on the `tickets`
branch, the `tickets` command, the skill. The branch primitive moves into
skill-branches as `file-branch.ts` with a branch argument; the framework
keeps its archives and routine locks on `agents-logs` and adopts the skill
for everything ticket-shaped.

🤖 curated · Fable 5, effort high
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant