Second standalone skill: tickets and the agent queue (#1748) - #1749
Merged
Conversation
`@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
This was referenced Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second standalone skill, per #1748: tickets and the agent queue as
@gemstack/skill-tickets, with theticketscommand. One PR: the skill, the branch primitive moved intoskill-branches, and the framework adopting both. Dogfooded on an isolated rig (below) before opening.Two things I decided while building, both in the PR:
agents-logs, notagent-logs. A directory.branches/agent-<x>is an agent checkout to the branches skill (that is its convention), so.branches/agent-logsshowed up as a phantom agentlogsand the reclaim sweep went after it. One letter away from the suggested name, same shape as the oldagents-data.data-branch.tsfunnel 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/attachCheckouttake askillslist, so the daemon can linkticketsbesidebranchesin every checkout it creates.prompts/tickets_yourself.md+ the package'sSKILL.mdtext appended afterbranch_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-dataon origin, so do it everywhere the daemon ran):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-branches—src/file-branch.ts: a branch used as a file store, with abranchargument (the olddata-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 temporaryskillslist. 130 tests.packages/skill-tickets(new) —names(the conventions: branchtickets, 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 linksticketsat 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; refusalsno-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 oftickets.tsandtodo-loop.ts, the reader indashboard/tickets.ts, the promptsticketing_format.md/todo_format.md/data_branch_protocol.md. Kept:planTicketPrompt/planAgentFor,runTodoLoop(over the skill's queue),routine-locks.tsand the archives onLOGS_BRANCH = 'agents-logs'throughwithFileBranch. Changed:daemon-servicessyncs both branches, claims/releases/queue-done through the package, passes the mintedagentIdto the start;auto-pmmints ids (mintAgentIds, a millisecond apart from its clock) and the pinned prompts saytickets show/put/release/close;daemon-runtimeputs both bins on PATH and links theticketsskill;StartAgentOptions.agentId;agent-id.ts(pure, so the sweep stays browser-safe);dashboard/tickets.tsresolves a lock's holder tolockedByAgent { id, name? }and the ticket row/detail open the agent;system-prompt.tsdrops the three formats, the bullets send to the skill, non-owned agents get the bridge; presets reworded to the skill;.the-framework/LAYOUTregenerated (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:
.claude/skills/{branches,tickets}and.agents/skills/{branches,tickets}→ the packages;ticketson the PATH (the agents ran it).tickets list,tickets show,tickets queue add … --priority N); threequeue add:commits on origin'sticketsunder the agent's git identity;TODO_AGENTS.mdhas the entries in## Priority 8/6/4as plain bullets.only: plan, concurrency 2): oneclaim 2 ticketscommit withCLAIMED: 2026-08-30T14-48-39-412Z/…-413Z— the ids the two agents were then started with;onTicketsreturnslockedByAgent: { id }; each agent didput …plan.mdthenrelease …; locks gone, plans carry Effort/Uncertainty.tickets claimfor 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.sendQueueTicketat priority 9, thenonly: drain): claim under the drain's id; the agent named its session, so the row resolveslockedByAgent: { 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 markedhandoffReport: doneby hand (what the unit test does) the next sweep landedqueue done: …and the line is gone, not[x]. (Cosmetic: an emptied## Priority 9heading stays.)claude -pin a plain clone with the composed system prompt (the bridge present) and the Suggest-new-tickets preset → three tickets pushed toticketswith git, one commit, by the clone's identity.pnpm testin skill-branches (130), skill-tickets (57), framework (1386 node + dashboard vitest); root typecheck.agents-logsafter each run settles; thetriage-quicklock file appeared onagents-logsduring 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
ticketsbranch on gemstack's origin before the cutover, so that runs after the merge + cutover.