Foreman›Docs›Stories›Story files

Story files

Stories · page 1 of 5

The markdown story format: every frontmatter field, groups, progress notes and follow-ups.

Foreman 0.11.07 sections

A story is one markdown file with its facts in the frontmatter and its brief in the body. This page is for anyone who keeps stories in a folder, in the repo or in a notes vault outside it. It covers where the files live, every frontmatter field, the status values, groups, the progress notes Foreman writes, the follow-up stories it creates and every option of the stories block. Stories can also come from Jira or Salesforce Cases.

Where the files live

The default story source is a folder of markdown files:

"stories": { "adapter": "markdown", "dir": "stories" }

stories.dir is the folder, relative to the project or absolute. foreman init write creates it with one example story, unless you pass --no-example. Every .md file in it whose frontmatter has an id: line is a story; a file without one is ignored. Files are read in name order.

A full story file for the Acme project:

---
id: ACME-12
title: Add mean()
status: todo
depends_on: [ACME-11]
group: 2
group_name: Statistics
order: 2
---

# ACME-12 · Add mean()

## What

Add `mean(numbers)` to `sum.mjs`, returning the arithmetic mean, and export it.

## Done when

`node --test` passes, including a test that `mean([1, 2, 3])` is 2 and `mean([])` throws.

## Progress notes

_Newest first._

The body is the brief the agents read. The architect takes “Done when” as the check that says the story is finished, so write it as something a person could run. Everything else in the body is yours to shape.

A folder outside the repo, such as a notes vault, works the same way. Point dir at it, or use dirEnv so each machine names its own vault (see the options below). A story in a folder outside the project is edited in place; one inside the project is left alone until the story’s own commit marks it done (see Progress notes).

Frontmatter fields

Values can be bare, ‘single quoted’ or “double quoted”; quotes are removed when read. The five keys marked “renamable” can be given other names with stories.fields.

Field What it holds Who writes it
id The story’s id, such as ACME-12. It must match stories.idPattern. Required. You
title The story’s title. You
status One of the status values. Start a new story at todo: a file with no status is listed but never picked. You, then Foreman
depends_on The ids of stories that must be done first, in any form the id pattern can find: [ACME-10, ACME-11] or ACME-10, ACME-11. Renamable. You, and Foreman when you approve a proposal
group The group the story belongs to, such as 2. Renamable. You
group_name The group’s name, such as Statistics. Renamable. You
order A number; lower is picked first. Missing or not a number counts as 0. Renamable. You
blocked_by Why the story is blocked: story ids that must land first, or a reason in words. Renamable. Foreman, when a story is blocked or you take it over
follow_up_of The id of the story this one follows up. Foreman, on a story it creates
started The date the story was first started, YYYY-MM-DD. Foreman, on foreman story start
completed The date the story was marked done. Foreman, on foreman story done or finalize

Foreman sets or replaces one frontmatter line at a time and leaves every other line of the file exactly as it was. A value that YAML would misread (one with : or #) is written in double quotes.

Status values

Status Meaning
todo Open and not started. Picked when its dependencies are done.
in-progress A slot is building it. A story left in progress by a run that died is picked first.
blocked Not buildable now. When blocked_by starts with story ids (“ACME-3, ACME-4 must land first”), the story is picked again once they are done. A reason in words waits for you.
ready-to-promote Its release is parked and finishes later. See Parked releases.
done Shipped, or marked done by you. Final.
retired Dropped from the backlog. Final, and left out of every count.

Only todo, in-progress and blocked stories can be picked, and never an owner task. The full rules are on How Foreman picks. A done or retired status in the file is the truth: in state mode it beats whatever Foreman recorded on the side.

foreman story start, block, done, todo and finalize change the status for you; the agents never edit a story file by hand.

Groups and order

A group is a set of stories that belong together, such as a phase of a plan. group is its key and group_name its name; order ranks stories within and across groups. The picker takes the story with the lowest order first, then the lowest number at the end of the id, then the id. Dependencies cross groups: a story waits for every id in depends_on, whatever group it is in.

In the agents’ prompts the group is called by stories.groupLabel, “Group” by default; a plan that calls them phases sets "groupLabel": "Phase".

stories.groupSummary asks for a summary when a group finishes. It is a path relative to dir with {group}, {groupName} and {groupLabel} filled in, such as "summaries/{groupLabel} {group}.md". When the last live story of a group ships, the session runs foreman story summary <id>, which says whether the summary is due: every story in the group that is not retired is done, the file exists, and it does not yet say “<label> <group> is finished”. When it is due, the session appends a section headed ## <groupLabel> <group> is finished with what the group delivered, one line per story, and the date. Unset, no summaries are written.

stories.resourcesByGroup names an exclusive resource every story in some groups needs: { "schema": ["2", "3"] } means no two stories of groups 2 and 3 build at once. See Exclusive resources.

Progress notes

Foreman records what happened to a story as dated lines under a heading, ## Progress notes by default (stories.progressHeading changes it). Each line is - **2026-10-17** — text, newest first. When the heading is followed by an italic hint line such as _Newest first._, new lines go after the hint; with no heading, Foreman adds one at the end of the file.

The lines are “Started.”, “Blocked: <reason>”, “Failed: <reason>”, the note foreman story done or finalize is given, and, with features.jiraComments on (the markdown source follows the Jira switch), one line for each note Foreman keeps on a story: a question it asked you and your answer, the architect’s design, each round of review, how a session stopped, a release that stopped and a follow-up it created. Each is the note’s title and a short summary folded to one line of at most 500 characters. A story source that holds comments gets the full text instead; see The story’s own record.

Where the lines go depends on stories.track:

  • file: Foreman edits the story file in place, the frontmatter and the progress lines, and touches nothing else in it. This is the default for a folder outside the project.
  • state: Foreman never edits the file while the story runs. Status, blocked_by, other fields and the notes go to an overlay, .foreman/stories/<id>.json, and the agents read a brief at .foreman/briefs/<id>.md that is the file with the overlay applied. When the story is committed, foreman story finalize <id> in the story’s worktree writes status: done, completed and every note from the overlay into that checkout’s copy of the file, so the story’s own pull request carries them and the base branch reads done exactly when it merges. Run twice, it adds no line the file already has. This is the default for a folder inside the project.

Follow-up stories

foreman learn turns the minor findings a shipped story left behind into a follow-up story. By default it asks first (“Follow up on ACME-12: 2 minor finding(s)?”); with features.autoCreateFollowUps on it creates the story at once and tells you, with an Undo. The switches are on How Foreman learns.

A follow-up is a new file in the layout of the others:

---
id: F-1
title: Follow-ups from ACME-12
status: todo
group: 2
group_name: Statistics
order: 2
depends_on: []
follow_up_of: ACME-12
---

# F-1 · Follow-ups from ACME-12

## What

- <finding> (`<where>`): <observed> — fix: <fix>

## Done when

Each listed finding is fixed, with a test where it is a behaviour, and `npm test` passes.

## Progress notes

_Newest first._

The id comes from stories.newId (default F-{n}): {n} is one more than the highest number already used by ids of that shape, and {group} is the origin’s group, for templates such as P{group}-F{n}. The id must match idPattern. The group fields are copied from the origin, and “Done when” names the checks that always run. The file is <id>.md, or <id> <title>.md when the origin’s file is named that way.

In file mode the new file goes straight into dir. In state mode it waits in .foreman/stories/new/, where it is listed and picked like any other story, and finalize in its own worktree writes it into dir, so the follow-up’s pull request is what adds the file. Once dir has a file with that id, the waiting copy is deleted.

Options: dir, idPattern, fields, track, newId

Every key of the stories block for the markdown source. An unknown key is a config error.

Name What it does Default
adapter "markdown". markdown
dir The folder of story files, relative to the project or absolute. Required. stories
dirEnv The name of an environment variable that, when set and not empty, replaces dir, such as NOTES_VAULT_DIR for a vault that lives somewhere else on each machine. unset
dirEnvSuffix A path joined to dirEnv’s value, such as Stories when the variable names the vault root. Needs dirEnv. unset
idPattern What an id looks like, as a regular expression without ^ or $ (ids are found inside text, such as depends_on). [A-Z]+-\d+
fields Which frontmatter key holds each of group, groupName, order, dependsOn and blockedBy. group, group_name, order, depends_on, blocked_by
ownerTask { "id": <pattern>, "title": <pattern> }: a story whose id or title matches is yours to do and is never picked. {} turns detection off. title matches (owner task), ignoring case
resourcesByGroup { "<resource>": ["<group>", …] }: every story in those groups needs that exclusive resource. {}
groupLabel What a group is called in the agents’ prompts. Group
groupSummary Where a finished group’s summary goes, relative to dir, with {group}, {groupName} and {groupLabel}. unset: no summaries
progressHeading The heading the dated progress lines go under. ## Progress notes
track file edits the files in place; state keeps changes in .foreman/ and only the story’s own commit touches the file. state when dir is inside the project, else file
newId The id template for follow-up stories, with {n} exactly once and {group} as wanted. F-{n}

A vault on two machines:

"stories": {
  "adapter": "markdown",
  "dir": "/Users/acme/Notes/Stories",
  "dirEnv": "NOTES_VAULT_DIR",
  "dirEnvSuffix": "Stories",
  "idPattern": "P\\d+[a-z]?-\\d+",
  "groupLabel": "Phase",
  "groupSummary": "Phase {group} summary.md",
  "newId": "P{group}-F{n}",
  "ownerTask": { "title": "\\(owner task\\)|\\[owner\\]" }
}

foreman config check reports a bad value with the key that holds it, and foreman.config.json lists the block beside the Jira and Salesforce ones. Owner tasks have their own page: Owner tasks.