View raw

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:

  1. Capture the item at the top of the stream.
  2. Track its status while the work runs.
  3. 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:

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 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:

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: Project action items
#+TODO: TODO IN-PROGRESS TESTING TESTED BLOCKED | CANCELLED DONE
#+TAGS: [ Kind : feat fix refactor chore docs ]
#+TAGS: [ Scope : core app web ui ]
#+TAGS: [ Impact : minor major ]

# 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`.

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:

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:

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:

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:

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:

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

If you cannot complete an item, record the item and record the failure.