frontmatter fields

The fields a person writes and the fields the dispatcher and the engine write are separate (The states a ticket passes through already drew that line). Do not fill an engine-side value into a field a person writes. Fill in session_id: and the ticket looks assigned, and once it does, no worker will pick it up.

Fields a person writes

FieldRequiredWhat it means
ticket✓The hash. Keep it the same as the filename (without it, the filename is used)
title✓The title a person reads
kindwork=an instruction / request=an ask / feedback=a report / answer=an answer file. For display; the value is not enforced
personaThe name of the persona to do it. Without it, a plain agent with no persona handles the ticket. Only letters, digits, -, and _ get through
squadThe name of one squad to do it. Unlike deps it is not a list ([...]). The engine resolves this value, starts that group's leader, and overwrites persona with that name, so do not write it alongside persona (Squads)
deps[<hash>...], a hard ordering. The ticket appears in the queue only when they are all done
priority1 to 5. Missing, unreadable, or out of range reads as 3. Higher goes first, and a prerequisite that a higher ticket waits on inherits that value
duedateAn ISO 8601 time (2026-08-14T18:00:00+09:00; drop the offset and it is local time). Five hours or less remaining (including already past) reads as 5, and seven days or more remaining, on a ticket that has its own duedate, reads as 1. Between the two, the priority as written stands
reqThe hash of the original request, written on a ticket split out of one. It is provenance rather than ordering, which is what makes it different from deps
epicOne number for the group this ticket belongs to (P273). Unlike deps it is not a list ([...]), and the value is not parsed either. The engine does not look at it; only the screen changes (Epics)
archivesOne hash of the target ticket, written by an archive ticket. Unlike deps it is not a list ([...]). With this value the ticket does not stand as its own card on the board; it sits on top of the target ticket
continuedOne hash of the follow-up ticket a session handed the rest of its range to. Like archives, not a list ([...]). With this value the done badge turns into Done (continued) and becomes a link to that follow-up (The states a ticket passes through)
handoffsThe number a handoff ticket writes to say which one it is. Add 1 to the previous ticket's value. A missing key reads as 0, so the first handoff is 1. Once this value goes past 3 - that is, from the fourth handoff on - the engine puts the ticket back down the moment it takes it, and locks it into awaiting answer (The states a ticket passes through)
pollingOne filename of the polling script this ticket waits on. It is not a field for a path, so a / in it makes the engine refuse. The script goes under polls/ below the queue root. While this value is not empty, that ticket stays out of the candidates even though it is open (The states a ticket passes through)
polling_untilThe cap on that wait. An ISO 8601 time (same format as duedate); past this time the engine stops polling and locks the ticket into awaiting answer

Fields the dispatcher and the engine write

FieldWhen it is writtenWhat it means
session_idassign - before the engine startsIf it is there the ticket counts as assigned and drops out of the queue. This is the only value that judgment looks at
assigned_atat the same time as assignThe time of assignment (used by reap's grace-period judgment)
ownerassign (automatic dispatch) or handclaim (taking it by hand)Automatic gives <persona or agent> / <worker>-<first 8 of the sid>; a hand claim gives whatever identifier the caller passed
pidsetpid - after the engine starts in the background, or handclaim recording the ancestor claude pidFor the liveness check. It has nothing to do with dropping out of the queue
inboxsetinbox - after the first prompt has been fed inThe path of the interject FIFO. The FIFO file disappears when the session ends, but this value itself survives a normal completion and an automatic stale reclaim. Only unassign or a failed dispatch clears it
claimed_athandclaimThe time it was taken by hand (used to grant a grace period against the idle check)
transcripthandclaimThe path of the newest transcript at that moment (for the idle check)
attemptsreap (up by one on every reclaim)It counts up to two automatic reclaims, and resets to 0 when the ticket turns into a request for an answer. It counts different events from the Reassigned line at the top right of the ticket detail
awaitingwhen reap escalates it into a request for an answer (the third automatic reclaim, or immediately and regardless of the count if the last section of the body is a ## 블록, the block section), and when a person cuts the session with unassign --forceA hash that does not exist. The same value goes into deps as well, and it is that deps that locks the queue. It clears itself once an answer file of the same name (<awaiting>.done.md) appears
polled_atevery time a worker runs the polling script onceThe time of the last run. It is what keeps the interval written at the head of the script. No value means it has not run even once yet
polling_failswhen the polling script ends in an errorHow many errors in a row. One success and the engine deletes this value. Reach 3 and that ticket is locked into awaiting answer too

Sessions write awaiting too. A session splitting a request writes this value itself when it cannot go further without guessing. It is not deleted after the answer arrives. It stays as a record, and whether the wait is still on is judged by whether the answer file exists.

Four rules outside the tables

Writing 5 in priority is a person's call. That value pushes aside a ticket that is already running, so sessions only write 1 to 4.

duedate does not rewrite priority. When a ticket is picked, the clock is read once and the value goes up or down for that moment only. The number in the file stays as it is.

kind: answer is born done (<awaiting>.done.md). A person or the app creates it. Sessions never create this file. Born open, it would dispatch to any worker at all as a ticket with no persona, and nobody performs this file.

There is no field in these tables for what the ticket is waiting for. The reason for a wait goes in the body, not the frontmatter. The session that hangs the wait and takes its hands off writes it as one sentence in the first line of the ## 결과 (the result section). The line under the badge on the board card, and the Reason row in the Polling section of the ticket detail, are that sentence copied over (The states a ticket passes through). No need to hunt for a key. There isn't one, by design.

polling and polling_until sit among the fields a person writes, but they are not values you open the file and fill in by hand. The session that decided to wait has the worker script write them (workers/<worker>.sh poll <hash> <script filename> <cap>). The engine then runs five checks before it puts the values in. It starts by looking for that file actually under polls/, and it finishes by running the script once on the spot. If a single one fails it refuses without changing a character of the file. Write them by hand and you skip all five, and a ticket pointing at a file that is not there sits quietly open until the cap arrives.

Where you fix them on screen - the frontmatter section of the ticket detail

There is one place to fix the keys in these tables without opening the file: the frontmatter section at the top left of the ticket detail. It is the same row editor as the ontology cards, with a key field and a value field on every line, and it works exactly as Archiving and the ontology describes. When you have made your change, press Save at the bottom right of the section.

Only open tickets can be fixed. In progress (.wip) and done (.done) get a read-only table instead of rows. It is the same door that locks the body editing form - if the screen changes a file from outside while a session is holding it, the values that session just read no longer match.

What comes up as rows is deps, req, epic, awaiting, continued, archives, handoffs, and any key you added yourself that is not in these tables. The rest fall into two groups.

  • The six the editing form already holds - title, kind, persona, squad, priority, duedate. The editing form on the right is the source, so they do not appear in the frontmatter section. Fix the same key in both places and whichever you saved later quietly wins.
  • The execution keys the engine stamps - visible in the table below, but not editable.

Keys you can see but cannot fix

Below the row list sits one more small table. These are not values a person decides but facts the engine stamped, so they are read-only. Change one by hand and the claim of the session running right now breaks on screen.

The keys here are ticket, session_id, assigned_at, pid, owner, inbox, attempts, claimed_at, transcript, polled_at, default_answer, and the three that start with polling (polling, polling_until, polling_fails).

polling and polling_until are here for the reason written in 'Four rules outside the tables' above. Those values go in only after the worker script has passed all five checks.

The app does edit the three polling keys, but somewhere else. Not in this table — in the two handles in the Polling section of the ticket detail (see The screens). Dispatch now empties polling and polling_fails; Extend deadline writes polling_until again with a new time. Neither one clears polled_at, because how long the ticket waited has to stay on the record.

The table shows down to assigned_at and you open the rest with Expand. A key that is not in the file has no line at all. On a ticket that was never claimed, this table does not appear at all.

No checking here either

The value field of deps: and req: offers the ticket hashes in this queue as candidates. The candidates are only there to make picking easier, so a hash that does not exist saves fine if you type it in. The screen does not check the rules in these tables - it will not stop you writing squad and persona together, or putting a list in epic. How a wrong value gets read is exactly what the two tables above say.

The ticket screen has no Switch to plain text handle for seeing the whole source. When you have to see the frontmatter source as it is, open the file yourself.


That is the manual. The place where you end up writing these tables by hand is Writing a ticket yourself.