[Markdown] Tools for working with agents.
Table of Contents
--- name: rail description: Manage a rolling action item list in an Org mode file with dedicated Emacs MCP tools. ---
RAIL — Rolling Action Item List
Overview
RAIL keeps every action item in one Org file, `RAIL.org`, at the root of a project. Dedicated Emacs tools write that file. The work follows three steps:
- Capture the item at the top of the stream.
- Track its status while the work runs.
- Close it with commit evidence and test evidence.
The command for this skill is `/rail`.
Usage
Use this skill when the user does one of these things:
- The user runs `/rail`.
- The user states an action item, a feature request, a defect, or a change to make.
- The user asks you to record or capture an item.
- The user asks for the status of an item, or asks to change it.
- The user asks you to close, cancel, or block an item.
- The user asks what items are open.
Treat each prompt as one separate action item. Do not merge two items. Do not build, test, commit, or start an agent for a capture.
The current prompt is the action item:
$ARGUMENTS
If the prompt is empty, ask the user for one line that describes the item. Then stop. Capture the next prompt as the next item.
Core Concepts
The stream
`RAIL.org` is a flat stream. Each action item is a top-level heading. The newest item comes first. The file holds no container heading and no topical heading. Tags on the heading give the axes of the item. The position of a heading gives only its age.
The file owns the vocabulary and the workflow
The tools carry no project vocabulary and no project workflow. The stream file owns both:
- The `#+TODO:` line declares the status keywords.
- The `#+TAGS:` lines declare the tag vocabulary, grouped into axes.
The tools read both from the file. One tool file therefore serves every project. To change the states or the tags, edit the preamble of `RAIL.org`. Run `rail-inspect` to read the current vocabulary.
The Org ID is the handle
Every item carries an Org ID. Each tool that changes an item needs that ID. Never select an item by its title text, because two titles can match. Run `rail-list` to get the ID of each item.
Evidence, not silence
Three transitions demand a written reason or written evidence:
- `BLOCKED` needs a reason.
- `CANCELLED` needs a reason.
- `DONE` needs a commit hash and a test recap.
The tools refuse these transitions without that text. A stalled item or a dropped item therefore always states why.
Each closing transition writes the text as a closing note in the `:LOGBOOK:` drawer, next to the state timestamp. The note reads `CLOSING NOTE` for `DONE`, `BLOCKED NOTE` for `BLOCKED`, and `CANCELLED NOTE` for `CANCELLED`. This mirrors Org's own closing note, and it replaces the older body-line evidence.
The tag vocabulary is grouped by axis
The `#+TAGS:` lines group the tags into named axes. A common shape holds a `Kind` axis and a `Scope` axis, plus optional axes. Each axis holds a short set of bare tags. Distinct words across axes keep a bare tag unambiguous. This is a convention, not a tool rule. The tool accepts any tag that a `#+TAGS:` line declares, in any axis the project chooses.
Infer the tags from the item text when you capture it. You can re-tag an item later with `rail-retag`, as its shape changes.
Set up the stream file
A project needs one `RAIL.org` file at its root before the tools run. Create it with this preamble, then edit the axes to fit the project:
#+TITLE:
#+TODO:
#+TAGS:
#+TAGS:
#+TAGS:
# Local Variables:
# org-log-done: time
# End:
The `#+TODO:` keywords before the bar are open states. The keywords after the bar are closed states. The `org-log-done` local variable makes Org write a `CLOSED` timestamp on the transition to `DONE`, which `rail-complete` requires. A file with no `#+TAGS:` line accepts any tag.
Install the tools once per session
The tools live in `rail-tools.el`, beside this `SKILL.md`. The skill can sit in one project, or in a shared global directory that serves every project. Load the tool file one time in the running Emacs. Use the generic `eval-elisp` tool for the load only.
Load the tool file from the directory of this `SKILL.md`, and pass the directory of the project you work in as the project root. These are two separate places. The tool file has one fixed home. The project root changes with each project.
(let ((tools "/absolute/path/to/this/skill/rail-tools.el")
(project "/the/project/directory/you/work/in"))
(unless (file-readable-p tools)
(error "No RAIL tools at %s" tools))
(load tools nil t)
(setq rail-project-root (file-name-as-directory project)))
Set `rail-project-root` to the project you work in. Every tool then uses that project by default, wherever the tool file itself lives. Do not write an absolute path from a home directory as a fixed constant in a committed file, because that path differs on every machine.
The tool file also runs an upward search for `RAIL.org` at load time, from its own directory and then from `default-directory`. That search finds the project only when the tool file sits inside the project. For a shared install, or when `default-directory` sits outside the project, set `rail-project-root` as shown, or pass `root` to each tool call.
Then verify that the tools are present. This expression returns the thirteen tool names:
(seq-filter (lambda (name) (string-prefix-p "rail-" name))
(mcp-server-tools-list-names))
Confirm that the tools resolve the right project. This expression returns the stream file path:
(alist-get 'file (json-parse-string
(alist-get 'text (aref (mcp-server-tools-call
"rail-inspect" nil) 0))
:object-type 'alist))
If the load fails, stop and report the problem. Do not edit `RAIL.org` as raw text instead.
If the tools are not callable
The tools register inside Emacs. The MCP client lists its tools when it connects, so a mid-session load can leave the tools absent from your own tool list. In that case, call each tool through the dispatch path:
(mcp-server-tools-call "rail-list" '((state . "TODO")))
This path runs the same handler as a direct tool call. To make the tools callable directly, load `rail-tools.el` from the Emacs init file.
Why dedicated tools
The generic `eval-elisp` tool sends its code through the Emacs security form walker. The walker prompts for each file function, such as `find-file-noselect` and `save-buffer`. The RAIL tools register as normal MCP tools, and tool dispatch does not use the walker. The read-only tools also carry a `readOnlyHint` annotation, so the client can approve them without a prompt.
You **MUST** use these tools for every change to `RAIL.org`.
- You **MUST NOT** edit the file as raw text, because raw edits break the timestamps, the IDs, and the tag alignment.
- You **MUST NOT** use a shell tool or a file tool for an item, because those tools apply no validation.
The RAIL tools
Each tool accepts an optional `root` argument. `root` names the project directory that holds `RAIL.org`. When you omit `root`, the tools use `rail-project-root`. Set `root` only for a different project.
| Tool | Purpose | Read-only | |---|---|---| | `rail-inspect` | Report the keyword sequence and the tag axes. | Yes | | `rail-list` | List items, newest first, with ID, title, state, and tags. Accepts a `state` or `tag` filter. | Yes | | `rail-show` | Report one item in full: body, logbook, checklist, and result. | Yes | | `rail-verify` | Report the heading, state, `CLOSED` time, and tags of one item. | Yes | | `rail-capture` | Create a `TODO` item at the top of the stream. | No | | `rail-set-status` | Set the keyword of an item. Refuses `DONE`. | No | | `rail-block` | Set `BLOCKED` and record a required reason. | No | | `rail-cancel` | Set `CANCELLED` and record a required reason. | No | | `rail-check` | Add, toggle, or list the checklist items of one item. | No | | `rail-log` | Append a timestamped note to the `:LOGBOOK:` drawer. | No | | `rail-retag` | Replace the tags of an item with a validated set. | No | | `rail-set-result` | Pre-stage the DONE closing note in the `:LOGBOOK:` drawer. | No | | `rail-complete` | Set `DONE` after it records the result evidence. | No |
Use `rail-show` to read one item, and `rail-retag` to re-tag it. These are the correct tools for those two actions, because the generic Emacs `org-*` tools cannot operate on this file.
For the exact arguments and the result shape of each tool, read `references/tools.md`.
Step 1 — Capture the item
Run `rail-capture` with these arguments:
- `title` — a short imperative title under 60 characters.
- `body` — the full item text.
- `tags` — the tags that fit the item, from the file vocabulary.
The tool inserts the item at the top of the file. It records the capture time as an inactive `SCHEDULED` timestamp. It applies the tags, wraps the body to 72 columns, and assigns an Org ID. Report that ID to the user.
Keep the item text unchanged. If the text holds separate ideas, write the body as a list. Pass the body as plain text, because the tool wraps it.
If `rail-capture` rejects a tag, run `rail-inspect` to read the vocabulary. Then fix the tag and re-run the capture.
Then proceed to Step 2 when work starts on the item.
Step 2 — Track the status
First find the item. Run `rail-list` to read the ID, the title, the state, and the tags of each item. Pass a `state` filter or a `tag` filter to narrow the list. Use the ID for every later call.
Read the current state with `rail-verify` before you change a keyword. Then pick the tool from the target state:
- Target an open keyword, such as `IN-PROGRESS` → `rail-set-status`
- Target `BLOCKED` → `rail-block`, with a reason
- Target `CANCELLED` → `rail-cancel`, with a reason
- Target `DONE` → `rail-complete`, with commit and test evidence
Run `rail-verify` again after the change to confirm the keyword.
Block or cancel with a reason
Run `rail-block` when work cannot continue. Run `rail-cancel` when you stop work on purpose. Each tool needs a `reason`, and writes it as a closing note in the `:LOGBOOK:` drawer of the item.
You **MUST NOT** use `rail-set-status` here, because it records no reason.
Split a large item with a checklist
Run `rail-check` when one item is large enough to track in parts:
- `action` `add` — append an unchecked item. Needs `item`.
- `action` `toggle` — flip one matching item. Needs `item`.
- `action` `list` — report the items and their state.
The tool keeps the items in a `Checklist [/]:` block in the body. The `[/]` cookie counts the completed items against the total. The items stay inside the one action item. They are not separate stream entries, and they carry no keyword of their own. For independent work, capture a new action item instead.
Record progress in the logbook
Run `rail-log` with a `note` to record progress during a session. The tool prepends a timestamped item to the `:LOGBOOK:` drawer. The logbook is append-only. The tool never rewrites an earlier note, and never rewrites the body.
Then proceed to Step 3 when every closing criterion is met.
Step 3 — Close the item
Check each criterion before you close an item. Copy this list and mark each item:
- [ ] The build passes.
- [ ] The tests pass.
- [ ] The files have the required format.
- [ ] A commit exists.
If one criterion fails, do not close the item. Set the state with `rail-set-status` instead. Then fix the failure and re-run this check.
When every item is marked, run `rail-complete` with these arguments:
- `id` — the Org ID of the item.
- `commit` — the commit hash of the completed work.
- `tests` — a short test recap, for example `215 pass`.
- `model` — optional. The agent that did the work.
- `notes` — optional. A short tail, such as a root cause.
The tool sets `DONE`, writes the closing note into the `:LOGBOOK:` drawer, then confirms that Org recorded the `CLOSED` timestamp. Org writes that timestamp, because the file sets `org-log-done` to `time`.
You **MUST NOT** write or edit that timestamp, because Org owns it.
The closing note lands in the `:LOGBOOK:` drawer, next to the `CLOSED` timestamp. It replaces the old body-line evidence. It stays on one line, because the reader reads one line:
:LOGBOOK:
- CLOSING NOTE [2026-09-07 Mon 19:31] :: model=example-agent commit=3051af0 tests=243 pass; short root cause
:END:
The item stays in place in the stream. There is no refile step.
Run `rail-verify` last. Verify that the state is `DONE` and that the `CLOSED` timestamp is present. To pre-stage the closing note before you close the item, run `rail-set-result` on its own.
Test the tools
The tools carry a test suite. Run it after any change to `rail-tools.el`:
.kiro/skills/rail/run-tests.sh
The suite needs no MCP framework and no configuration. Each test uses a temporary stream file, so the project stream file stays unchanged.
Common mistakes
- **Editing the file as raw text.** The tools keep the timestamps, the IDs, and the tag alignment correct. Raw edits do not.
- **Selecting an item by title.** Two titles can match. Use the Org ID from `rail-list`.
- **Using `rail-set-status` for `DONE`.** That call fails by design. Use `rail-complete`, which demands the evidence.
- **Writing a `CLOSED` timestamp.** Org writes it. The tool only confirms it.
- **Merging two items.** Record each prompt as its own action item.
If you cannot complete an item, record the item and record the failure.