rafa pages
- rafa
- rafa issue
rafa issue
The tracker subject: list, show, create, comment on and move issues, mark a spec ready, unblock it, check its references and edit it.
rafa issue works on the project's issue tracker, GitHub Issues today. It is the subject for specs that live as issues: reading them, filing them, moving them between states and checking that a spec is complete before a plan is made from it.
Spends: nothing. Changes: issues, their labels and comments on the tracker; edit and ready refuse before writing when a gate fails. Needs: gh signed in, and for every write an account with the access each action names; see Prerequisites.
Things to know before you use it:
- An issue's text ends up in an agent's prompt, so
readyrefuses an issue opened by an account without write access to the repository, andeditweighs four gates (who edits, who wrote it, no leaks, a complete spec stays complete) before it writes. - rafa never adds
spec:readyby itself:rafa issue readyasks first, and--yesanswers for a script. unblockis offered only on a board inlabelsmode; innativemode GitHub's own blocked-by links clear when the blocker closes.
Specs and plans explains the labels and the spec template.
In the target layout this code lives in the private package rafa-board, bundled into @open-tomato/rafa (Repository layout). In 0.47.0 it is already a package, @open-tomato/rafa-board in packages/rafa-board, but only as a facade: it re-exports this code from the root package's src/, where the files still live until their move. You install and type the same either way. [verify: the release the move lands in]
Actions
| Action | Does |
|---|---|
list | list the issues on the tracker, or the Roadmap's lines in its order, narrowed by type, module or text |
show | show one issue on the tracker: its fields, then its body |
create | create an issue on the tracker from a title and a body, a bug from its fields |
comment | comment on one issue on the tracker |
move | move one issue on the tracker to a state |
ready | mark an issue spec:ready once its author and its body check out |
unblock | take spec:blocked off an issue whose blockers have all closed |
check | read the references a spec's saved copy names, each with its state |
edit | append a dated update to an issue body, or replace its body or title, after four gates |
rafa issue list
List the issues on the tracker, or the Roadmap's lines in its order, narrowed by type, module or text.
rafa issue list [--roadmap] [--all] [--full] [--check] [--labels] [-t|--texts]
[--refresh] [--state=<string>] [--type=<string>] [--module=<string>]
[--search=<string>] [--limit=<number>]
Lists the issues on the tracker tracker.default names in .rafa/config.yaml, or on the first tracker.fallback kind whose preflight passes when it fails, each kind passed over warned about. Prints the tracker, then one row per issue: its id, state, type and title; under --type=bug, a header and the columns id, state, module, action and summary, read back from each title. Each flag narrows the list, and a flag left out narrows by nothing. The github tracker holds an issue open or closed, so it refuses --state, and lists 30 issues unless --limit says otherwise. With --roadmap it lists the unticked lines of the Roadmap issue (roadmap.issue, else the lowest-numbered open type:roadmap board, else the open issue titled Roadmap) in its order instead, read off the GitHub board, as a table adding four columns: spec, whether the issue can be planned (every section of the spec template filled, and the spec:ready label on it), as a symbol; blocked by, the blockers grouped by whether they are still open; has, a plan, a branch or a pull request already made for it; and refs, how many references of the issue's saved copy under specs.dir read suspect or dangling, - with no copy. A legend names each symbol, --texts (-t) spells them in words, and --labels prints the labels on a row under each issue. A Roadmap naming epics prints them alone, grouped by horizon under a Roadmap #<n> · <horizon> heading with the columns #, state, done/total, blocked, date and title, the now horizon only unless --all, with one line counting the lines naming no epic; --full prints each epic's issues under its row, and the lines naming no epic as the table. The board listing is kept under .rafa/cache/ and read for what changed since; --refresh reads the whole board again. --check exits 1 when any type:epic issue's stored state disagrees with its computed one. --type, --module, --search and --limit then narrow the spec rows, keeping their order, and an unreachable board is warned about with the rows still printed. With --output=json the tracker, the query and every issue, or under --roadmap the roadmap, the rows, the epics and the warnings, are the data of the terminal result event.
| Flag | Type and default | What it does |
|---|---|---|
--roadmap | boolean | List the unticked lines of the Roadmap issue instead, in its order, with the spec, blocked by, has and refs columns, and the epics it names grouped by horizon. Reads the GitHub board whatever tracker the chain lands on. |
--all | boolean | With --roadmap, keep the ticked lines too, each in its place, and show the epics of every horizon, not only now. Refused without --roadmap. |
--full | boolean | With --roadmap, print each epic's issues under its row, on two rows each: number, state and title, then labels and blockers. Refused without --roadmap. |
--check | boolean | With --roadmap, exit 1 when any type:epic issue on the board is open with its work done, or closed with it not done, or when the board could not be read, naming each. Refused without --roadmap. |
--labels | boolean | With --roadmap, print each issue's labels on a row of their own under it, and under each --full member; they are no column. Refused without --roadmap. |
-t, --texts | boolean | With --roadmap, spell the spec and blocked by columns in words instead of symbols, with no legend. Refused without --roadmap. |
--refresh | boolean | With --roadmap, read the whole board again instead of only what changed since the listing kept under .rafa/cache/. Refused without --roadmap. |
--state | string | Only issues in this state: one of backlog, todo, in-progress, in-review, done, released, cancelled. Refused on the github tracker and beside --roadmap. |
--type | string | Only issues of this type: one of code, bug, spike, adr, chore, package-api, epic, spec. |
--module | string | Only issues of this module. |
--search | string | Only issues whose title or body holds this text. |
--limit | number | At most this many issues, a positive whole number. The limit of the tracker when left out, and none under --roadmap. |
Examples:
rafa issue list: Lists the issues on the tracker the chain lands on, one row each.rafa issue list --type=bug --search=timeout: Lists the bugs whose title or body holds the word timeout, in the columns id, state, module, action and summary.rafa issue list --limit=5 --output=json: Writes a start event, then a result event whose data holds the tracker, the query and five issues at most.rafa issue list --roadmap: Prints the Roadmap's unticked lines in its order, with the spec, blocked by, has and refs of each.rafa issue list --roadmap --full: Prints the Roadmap's now epics with each epic's issues underneath, then its spec lines.rafa issue list --roadmap --check: Prints the Roadmap, then exits 1 when an epic is open with its work done or closed with it not done.rafa issue list --roadmap --all --type=bug: Prints the bugs on the Roadmap, ticked lines included, in the Roadmap's order, beside its epics of every horizon.
Output formats: text, json.
rafa issue show
Show one issue on the tracker: its fields, then its body.
rafa issue show <id>
Reads one issue on the tracker tracker.default names in .rafa/config.yaml, or on the first tracker.fallback kind whose preflight passes when it fails, each kind passed over warned about. The id is the issue number on that tracker, so on a tracker fallen back to it names a different issue. Prints the tracker, the id, the URL when there is one, the title, state, type, module and priority, then the body. With --output=json the tracker and the issue are the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
id | string, required | The issue id on the tracker: a GitHub issue number, or the number of a file under .rafa/issues/. |
Examples:
rafa issue show 12: Prints issue 12 on the tracker the chain lands on, its fields and then its body.rafa issue show 12 --output=json: Writes a start event, then a result event whose data holds the tracker and the issue.
Output formats: text, json.
rafa issue create
Create an issue on the tracker from a title and a body, a bug from its fields.
rafa issue create [--title=<string>] [--body=<string>] [--body-file=<string>]
[--type=<string>] [--module=<string>] [--priority=<string>]
[--summary=<string>] [--action=<string>] [--cause=<string>]
[--proposed=<string>] [--reproduce=<string>] [--rerun=<string>]
[--env=<string>] [--project=<string>] [--path=<string>] [--dry-run]
Files one issue on the tracker tracker.default names in .rafa/config.yaml, or on the first tracker.fallback kind whose preflight passes when it fails, each kind passed over warned about. --title is required for every type but bug. The issue is of type code and module unassigned unless --type and --module say otherwise, and with no --priority the tracker marks it needs-triage. A --type=bug line is filed from its fields: --module, --action, --summary, --cause and --reproduce, each also read from a section of the body, with rafa filling the versions and the place, rendering the title through issues.titles.bug and refusing a record missing a field, naming each. The body is --body, or what the file --body-file names holds, standard input for -. A spec whose body carries a Blocked by: #24 line is filed blocked, and refused when the line names no issue, the issue itself or an issue the board does not hold. With board.project.number set, a GitHub issue is added to the project and its fields filled, any failure there a warning that keeps the exit code. Prints the tracker and id of the issue filed, and its URL when there is one. With --output=json the tracker and the ref are the data of the terminal result event. --dry-run prints the title and body the line would file, resolves no tracker and files nothing.
| Flag | Type and default | What it does |
|---|---|---|
--title | string | The title of the issue. Required for every type but bug, whose title is rendered from its fields. |
--body | string | The body of the issue, as markdown. Empty when left out. Not with --body-file. |
--body-file | string | A file holding the body of the issue, read whole; - reads standard input. Not with --body. |
--type | string, default "code" | What the issue is for: one of code, bug, spike, adr, chore, package-api, epic, spec. |
--module | string | The module the issue belongs to; unassigned when left out. A bug names one, or unknown. |
--priority | string | How urgent the issue is: one of urgent, high, medium, low. Marked needs-triage when left out. |
--summary | string | A bug: one line saying what is wrong, the last part of its title. |
--action | string | A bug: what was running, a command, a step or a test; or unknown. |
--cause | string | A bug: its cause code from rafa bug codes, or <family>:new-context when no leaf fits. |
--proposed | string | A bug: beside a new-context cause, the leaf you would have used, as <family>:<leaf>. |
--reproduce | string | A bug: the steps, or the condition that revealed it. Never unknown. |
--rerun | string | A bug: one command that shows it again. |
--env | string | A bug: the place it was seen, one of local, ci, prod, a place issues.environments adds, or unknown. Left out, ci when the CI variable is set and local otherwise. |
--project | string | A bug: the project, where one tracker holds several. |
--path | string | A bug: the file the error points at, which names the package its Versions section holds. Left out, the first file of the project the artifact names. |
--dry-run | boolean | Print the title and the body the line would file, resolve no tracker and file nothing. |
Examples:
rafa issue create --type=bug --module=loop --action=start --summary="resume does not find the worktree branch" --reproduce="rafa loop start --continue after a stop" --cause=git:new-context --proposed=git:branch-not-found --dry-run: Prints the titleloop - start: resume does not find the worktree branchand the body of the bug, and files nothing.rafa issue create --title="Document issue move" --priority=low --output=json: Writes a start event, then a result event whose data holds the tracker and the ref of the issue.rafa issue create --title="Faster plan show" --body-file=spec.md: Files an issue whose body is what spec.md holds; --body-file=- reads it from standard input.
Output formats: text, json.
rafa issue comment
Comment on one issue on the tracker.
rafa issue comment <id> --body=<string>
Posts --body as a comment on one issue on the tracker tracker.default names in .rafa/config.yaml, or on the first tracker.fallback kind whose preflight passes when it fails, each kind passed over warned about. The id is the issue number on that tracker, so on a tracker fallen back to it names a different issue. With --output=json the tracker and the ref commented on are the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
id | string, required | The issue id on the tracker: a GitHub issue number, or the number of a file under .rafa/issues/. |
| Flag | Type and default | What it does |
|---|---|---|
--body | string, required | The comment, as markdown. Required. |
Examples:
rafa issue comment 12 --body="Seen again after the retry change.": Posts one comment on issue 12 on the tracker the chain lands on.rafa issue comment 12 --body=Reproduced --output=json: Writes a start event, then a result event whose data holds the tracker and the ref commented on.
Output formats: text, json.
rafa issue move
Move one issue on the tracker to a state.
rafa issue move <id> <state>
Moves one issue on the tracker tracker.default names in .rafa/config.yaml, or on the first tracker.fallback kind whose preflight passes when it fails, each kind passed over warned about, to one of the states backlog, todo, in-progress, in-review, done, released, cancelled. The id is the issue number on that tracker, so on a tracker fallen back to it names a different issue. The github tracker holds an issue open or closed, so it warns for backlog, in-progress, in-review and released, which it cannot hold, and still exits 0. With --output=json the tracker, the ref, the state and the warning are the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
id | string, required | The issue id on the tracker: a GitHub issue number, or the number of a file under .rafa/issues/. |
state | string, required | The state to move the issue to: one of backlog, todo, in-progress, in-review, done, released, cancelled. |
Examples:
rafa issue move 12 done: Moves issue 12 to done, which closes it on the github tracker.rafa issue move 12 in-progress --output=json: Writes a start event, then a result event whose data holds the tracker, the ref, the state and any warning.
Output formats: text, json.
rafa issue ready
Mark an issue spec:ready once its author and its body check out.
rafa issue ready <n> [--yes] [--dry-run] [--no-hint]
Reads the issue on the GitHub board, refuses one opened by an account without write access to the repository, refuses a body that does not fill the spec template, refuses an issue carrying two epic: labels (with board.relationships set to labels, the default), and then asks whether to mark it spec:ready. On a yes it swaps the labels in one write, adding spec:ready and removing spec:needs-work. --yes makes the swap with no question once every check passes, with or without a terminal, and refuses everything the checks refuse without it; without a terminal and without --yes it prints both readings and adds no label. A run that marks the issue, or finds it marked already, ends by naming the one step that follows, which --no-hint turns off. --dry-run makes every check and prints both readings, asking nothing and writing nothing whatever the terminal, exits 2 on a check that refuses, and is refused beside --yes. With --output=json the report is the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
n | string, required | The issue number on the GitHub board. |
| Flag | Type and default | What it does |
|---|---|---|
--yes | boolean | Marks the issue spec:ready without asking, once its author and its body check out; a check that refuses still refuses. Needed where standard input is no terminal. |
--dry-run | boolean | Prints both readings and writes nothing, asking no question whatever the terminal; a check that refuses still exits 2. Not with --yes. |
--hint | boolean, default true | Ends by naming the one step that follows, asking it where there is a terminal to answer on; --no-hint reads nothing and prints nothing. |
Examples:
rafa issue ready 57: Checks #57 and asks whether to mark it spec:ready.rafa issue ready 57 --yes: Checks #57 and marks it spec:ready with no question, from a script or an agent.rafa issue ready 57 --dry-run: Checks #57 and reads its status in one line, from a script, an agent or CI, marking nothing.
Output formats: text, json.
rafa issue unblock
Take spec:blocked off an issue whose blockers have all closed.
rafa issue unblock [n] [--all] [--yes]
Reads the "Blocked by: #24 #26" line of an issue labelled spec:blocked on the GitHub board, asks the board for each blocker's state, and when every blocker is closed asks whether to remove spec:blocked and removes it on a yes. With a blocker still open it names it and changes nothing. An issue labelled spec:blocked whose "Blocked by:" line is missing, names itself or names an id the board has no issue for is reported and never guessed at. Without a terminal and without --yes it asks nothing and writes nothing. --yes answers the question yes without putting it, terminal or not, and names the blockers it read closed; it never removes spec:blocked from an issue with a blocker open or a line it refuses to guess at. With --output=json the outcome of each issue is the data of the terminal result event. With board.relationships set to native it reads nothing and writes nothing: GitHub clears a blocker by itself when the blocking issue closes.
| Argument | Type | What it is |
|---|---|---|
n | string | The issue number on the GitHub board. Left out, --all reads every blocked issue. |
| Flag | Type and default | What it does |
|---|---|---|
--all | boolean | Read every open issue labelled spec:blocked, asking about each one in turn. |
--yes | boolean | Answer the question yes without putting it, so spec:blocked comes off every issue whose blockers are all closed, with no terminal needed. An open blocker or an unreadable "Blocked by:" line still changes nothing. |
Examples:
rafa issue unblock 57: Asks whether to remove spec:blocked from #57 when every issue its "Blocked by:" line names is closed.rafa issue unblock 57 --yes: Removes spec:blocked from #57 with no question when every blocker it names is closed.rafa issue unblock --all: Walks every open blocked issue on the board, asking about each one whose blockers have all closed.
Output formats: text, json.
rafa issue check
Read the references a spec's saved copy names, each with its state.
rafa issue check <n> [--stamp]
Reads the saved copy of issue <n> under specs.dir and prints every reference it names — an issue, a file, a declared symbol, a command, a flag, a config key — with its state against the stamp the copy keeps: ok, new, dangling, suspect, resolved or unknown. A reference the copy keeps no stamp for is stamped on this read. Exits 0 whatever the states are, and plans nothing. A missing saved copy is refused, naming the rafa plan create --issue=<n> that writes one. With --output=json every reference, with its kind, line, state and fingerprint, is the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
n | string, required | The issue number on the GitHub board whose saved copy is read. |
| Flag | Type and default | What it does |
|---|---|---|
--stamp | boolean | Re-stamp every reference with what its target holds now, so the next check reads each one ok. |
Examples:
rafa issue check 151: Prints each reference the saved copy of #151 names with its state, and a count per state.rafa issue check 151 --stamp: Prints the same reading, then re-stamps every reference as reviewed without planning.rafa issue check 151 --output=json: Writes a start event, then a result event whose data lists every reference with its kind, line, state and fingerprint.
Output formats: text, json.
rafa issue edit
Append a dated update to an issue body, or replace its body or title, after four gates.
rafa issue edit <n> [--append-file=<string>] [--append=<string>]
[--reason=<string>] [--replace-file=<string>] [--title=<string>]
[--priority=<string>] [--while-in-development] [--dry-run]
Edits one issue on the tracker the chain lands on, after four gates weighed in order, the first refusal ending the run with nothing written and exit code 2: the account editing must have write access or be in board.trustedAuthors; so must the issue's author; the text added must leak nothing, and a complete spec must stay complete; and the issue must be open, its labels and saved spec copies allowing the edit. An append (--append-file or --append, with --reason) adds a block **Updated <YYYY-MM-DD>, <reason>:** below the body, leaving the original part byte for byte; the same block already closing the body answers already. --replace-file replaces the body and --title the title. --priority sets the priority, removes every other and takes the issue out of triage in one write, alone or beside --title, weighed by the gates as a title change; a priority the issue already holds alone answers already. After the write the issue is read again: a body that no longer holds what was written answers conflict with exit code 1, the bodies read before and after saved under .rafa/scratch/. An append on an issue already planned answers stale-copy and names rafa plan create --issue=<n> --refresh, which it never runs. --dry-run prints each gate's reading and the added part and writes nothing. With --output=json the report and the tracker are the data of the terminal result event.
| Argument | Type | What it is |
|---|---|---|
n | string, required | The issue number on the tracker. |
| Flag | Type and default | What it does |
|---|---|---|
--append-file | string | A file holding the text to append, read whole; - reads standard input. Needs --reason. Not with --append, --replace-file, --title or --priority. |
--append | string | The text to append. Needs --reason. Not with --append-file, --replace-file, --title or --priority. |
--reason | string | Why the update was made, written into its heading. Required with an append, refused otherwise. |
--replace-file | string | A file holding the new body, read whole; - reads standard input. Not with an append or --priority. |
--title | string | The new title, alone or beside --replace-file or --priority. Not with an append. |
--priority | string | The new priority, one of urgent, high, medium, low: it replaces the one the issue holds and takes the issue out of triage. Alone or beside --title. Not with an append or --replace-file. |
--while-in-development | boolean | Allows an append on an issue a loop has claimed, answering stale-copy. Only with an append. |
--dry-run | boolean | Prints the added part and each gate's reading and writes nothing; a refusal still exits 2. |
Examples:
rafa issue edit 57 --append-file=update.md --reason="scope narrowed": Appends what update.md holds to #57 below a dated heading naming the reason.rafa issue edit 57 --append="Also covers --dry-run." --reason=clarified --dry-run: Prints each gate's reading and the block it would append, and writes nothing.rafa issue edit 57 --title="Faster plan show": Changes the title of #57 and leaves its body alone.rafa issue edit 57 --priority=high --dry-run: Prints each gate's reading and the swap, such aspriority: needs-triage → high, and writes nothing.rafa issue edit 57 --replace-file=spec.md --output=json: Replaces the body of #57; the report and the tracker are the data of the result event.
Output formats: text, json.