fix: updated docs to align with reality

This commit is contained in:
2026-08-02 16:54:27 -04:00
parent f361f05f96
commit b0749467c9
3 changed files with 251 additions and 80 deletions

100
AGENTS.md
View File

@@ -2,7 +2,9 @@
## What this is ## What this is
A Pi coding agent extension that registers the `/ralpi` slash command. Not a standalone app — it runs inside Pi's extension host. A Pi coding agent extension that registers the `/ralpi` slash commands
(`/ralpi`, `/ralpi-run`, `/ralpi-plan`, `/ralpi-resume`, `/ralpi-reset`).
Not a standalone app — it runs inside Pi's extension host.
## Type checking ## Type checking
@@ -10,6 +12,8 @@ A Pi coding agent extension that registers the `/ralpi` slash command. Not a sta
npm run typecheck # tsc --noEmit npm run typecheck # tsc --noEmit
``` ```
Tests: `bun test` (parser and DAG suites in `tests/`).
No build step needed — Pi loads extensions via [jiti](https://github.com/unjs/jiti), which compiles TypeScript at runtime. `index.ts` is the entry point directly. No build step needed — Pi loads extensions via [jiti](https://github.com/unjs/jiti), which compiles TypeScript at runtime. `index.ts` is the entry point directly.
## Entry point ## Entry point
@@ -23,22 +27,41 @@ The extension imports from Pi SDK packages (not in `package.json` — provided b
- `@earendil-works/pi-coding-agent``ExtensionAPI`, `ExtensionContext`, `createAgentSession`, etc. - `@earendil-works/pi-coding-agent``ExtensionAPI`, `ExtensionContext`, `createAgentSession`, etc.
- `@earendil-works/pi-tui``Box`, `Text` for custom message renderer - `@earendil-works/pi-tui``Box`, `Text` for custom message renderer
The only real npm dependency is `yaml` (^2.4.0). The only real npm dependency is `yaml` (^2.4.0). It is used for parsing YAML
task files (`src/parser.ts`) and config files (`parseSimpleYaml` in
`src/utils.ts`, which falls back to a flat key:value parser when the package
is unavailable).
## Source structure ## Source structure
- `index.ts` — extension entry, command routing, UI registration, reload detection - `index.ts` — extension entry, command registration (`ralpi`, `ralpi-run`,
`ralpi-plan`, `ralpi-resume`, `ralpi-reset`), execution-mode + loop-option
prompts, reload auto-resume via `session_start`, progress message renderer
- `src/` — all logic modules: - `src/` — all logic modules:
- `parser.ts` — task file parsing (Fio, checkbox, YAML formats) - `parser.ts` — task file parsing (Fio/README numbered, phased, checkbox,
- `dag.ts` — Kahn's algorithm dependency resolution, batch planning YAML formats), dependency + parallel-group + timeout parsing,
- `executor.ts` — task execution, retry, parallel/sequential modes `updateTaskInFile()` for PRD checkbox updates
- `progress.ts``.ralpi/progress.json` state management - `dag.ts` — Kahn's algorithm dependency resolution, group-aware batching,
cycle detection, sequential/parallel plan builders
- `executor.ts` — task execution, parallel/sequential modes, model
round-robin + failover, review-gated loop, worktree orchestration,
batch-level conflict resolution
- `review.ts` — review verdict extraction (`## REVIEW VERDICT`), review
save/load to `.ralpi/reviews/`
- `worktree.ts` — git worktree create/merge/cleanup helpers, stale-worktree
cleanup, `finalizeCommittedWorktrees()`
- `progress.ts``.ralpi/progress.json` state management (multi-PRD)
- `prompts.ts` — prompt generation for spawned agent sessions - `prompts.ts` — prompt generation for spawned agent sessions
- `reflection.ts` — reflection extraction from agent output - `reflection.ts` — reflection extraction from agent output
- `utils.ts` — config loading, progress discovery, `runAgentSession()` - `utils.ts` — config loading, progress/PRD discovery, `runAgentSession()`,
model resolution (`resolveModelSpec`), loop-active marker
- `types.ts` — all interfaces and `DEFAULT_CONFIG` - `types.ts` — all interfaces and `DEFAULT_CONFIG`
- `widget-batcher.ts` — debounced widget updates for parallel tasks - `widget-batcher.ts` — debounced widget updates for parallel tasks
- `constants.ts` — static constants - `task-manager-prompt.ts` — loads and expands the bundled
`prompts/task-manager.md` template for `/ralpi-plan`
- `constants.ts` — static constants (slash command, task file names,
reflection/review patterns)
- `tests/` — bun test suites for parser and DAG behavior
- `skills/ralpi-use.md` — Pi skill definition for task execution - `skills/ralpi-use.md` — Pi skill definition for task execution
- `prompts/task-manager.md` — Pi prompt for task planning - `prompts/task-manager.md` — Pi prompt for task planning
@@ -47,27 +70,70 @@ The only real npm dependency is `yaml` (^2.4.0).
All runtime state lives in `.ralpi/` in the **project directory** (not this extension directory): All runtime state lives in `.ralpi/` in the **project directory** (not this extension directory):
- `.ralpi/progress.json` — execution progress, supports multiple PRDs - `.ralpi/progress.json` — execution progress, supports multiple PRDs
- `.ralpi/loop-active.json` — marker written while a loop runs; drives
auto-resume after a session reload
- `.ralpi/reflections/` — per-task reflection JSON files - `.ralpi/reflections/` — per-task reflection JSON files
- `.ralpi/reviews/<prdKey>/` — full review output JSON (only when
`saveReviews` is on)
- `.ralpi/prompts/` — generated prompts (timestamped, for debugging) - `.ralpi/prompts/` — generated prompts (timestamped, for debugging)
- `.ralpi/sessions/` — full session transcripts - `.ralpi/config.yaml` — project-level config (optional)
There is no `.ralpi/sessions/` directory anymore — full task output is shown
inline via expandable `ralpi-progress` chat messages, and review output is
persisted under `.ralpi/reviews/`.
## Task ID convention ## Task ID convention
Task IDs are zero-padded strings (`"01"`, `"02"`, etc.). The parser prepends `0` to parsed digits. Never use raw numeric IDs. Task IDs are zero-padded strings (`"01"`, `"02"`, etc.) with an optional
single lowercase letter suffix for sub-tasks (`"02b"`, `"02c"`). The parser
normalizes `2b``02b` (see `normalizeTaskId` in `src/parser.ts`). Never
use raw numeric IDs.
## Command routing ## Command routing
`/ralpi` with no args → plan. First token looks like a path (`@path`, `./path`, `.md`, etc.) → run. Otherwise dispatches to subcommand (`run`, `plan`, `resume`, `reset`). - `/ralpi` no args → show plan for `README.md`; first token looks like a
path (`@path`, `./path`, `.md`, `.yaml`, etc.) → run; anything else →
error suggesting the dash commands
- `/ralpi-run [task-file]` — run tasks (auto-resumes when progress already
exists for the file; otherwise prompts for execution mode + loop options)
- `/ralpi-plan [prompt]` — loads the bundled `prompts/task-manager.md`
template and sends it as a user message. Pi's `sendUserMessage()` sends
with `expandPromptTemplates: false`, so the extension does its own
frontmatter stripping and `$@`/`$1` arg substitution
(`loadTaskManagerPrompt` in `src/task-manager-prompt.ts`)
- `/ralpi-resume [task-file]` — resume from persisted progress; prompts for
the PRD when multiple loops have progress. Reuses the loop snapshot in
`loop-active.json` (mode + autoCommit/autoReview/saveReviews) to resume
non-interactively
- `/ralpi-reset [task-file]` — reset execution progress (does not modify the PRD)
The old `/ralpi plan|resume|reset` subcommand dispatch, plus `status` and
`next`, were removed.
## Config ## Config
Read from `.ralpi/config.yaml` in project directory (and global `~/.pi/ralpi/config.yaml`). Falls back to `DEFAULT_CONFIG` in `src/types.ts` when files are missing. Config is loaded at `projectDir` level, not extension level. Read from `.ralpi/config.yaml` in project directory (and global
`~/.pi/ralpi/config.yaml`), project overrides global. Falls back to
`DEFAULT_CONFIG` in `src/types.ts` when files are missing. Config is loaded
at `projectDir` level, not extension level. Execution keys explicitly
present in a loaded YAML are tracked in `execution.explicitKeys` so the
loop-startup interactive prompts (`selectLoopOptions` in `index.ts`) can be
skipped for fields the user already set.
Key config fields in `execution`: Key config fields in `execution`:
- `autoCommit` / `autoReview` — toggle follow-up commit and review agent sessions (also selectable at loop startup via `selectLoopOptions`) - `autoCommit` / `autoReview` / `saveReviews` — loop options (selectable at
- `models` — round-robin model list for parallel mode loop startup via `selectLoopOptions`; review is asked FIRST, commit is
- `implModel` / `commitModel` / `reviewModel``<provider>/<model>` strings resolved via `resolveModelSpec` in `utils.ts` mandated when review is on)
- `models` — slot-aware round-robin model list for parallel mode, with
automatic failover to the next model per task
- `implModel` / `commitModel` / `reviewModel``<provider>/<model>` strings
resolved via `resolveModelSpec` in `utils.ts`
- `maxReviewRetries` / `reviewBlockOnFail` — review-gated loop retry behavior
- `worktrees``"never" | "parallel" | "always"` git worktree isolation
(default `"parallel"`; see `shouldUseWorktrees` in `src/executor.ts`)
- `commitTimeoutMs` / `reviewTimeoutMs` — timeouts for follow-up sessions - `commitTimeoutMs` / `reviewTimeoutMs` — timeouts for follow-up sessions
- `loopTimeoutMs` — max total loop duration in ms (0 = no limit; checked between batches in `executePlanBatches`) - `loopTimeoutMs` — max total loop duration in ms (0 = no limit; checked
between batches in `executePlanBatches`)
- `timeoutMs` — per-task execution timeout - `timeoutMs` — per-task execution timeout
- `prompts.projectContext` / `prompts.reflectionPrompt` — prompt-level settings

189
README.md
View File

@@ -8,35 +8,59 @@ pi install npm:@mikefreno/ralpi
## Features ## Features
- **Parallel batching**: Independent tasks in each batch can run concurrently - **DAG-based execution**: Tasks ordered via dependencies (arrow notation, natural language, "must be done before", or YAML)
- **Persistent progress**: Execution state saved to `.ralpi/progress.json` - **Parallel batching**: Independent tasks in each batch run concurrently, round-robin across configured models
- **Persistent progress**: Execution state saved to `.ralpi/progress.json`, supporting multiple PRDs simultaneously
- **Resume & auto-resume**: `/ralpi-resume` continues paused execution; a session reload mid-loop auto-resumes via `.ralpi/loop-active.json`
- **Reflection system**: Each task produces a reflection for downstream tasks - **Reflection system**: Each task produces a reflection for downstream tasks
- **Retry with backoff**: Failed tasks retry with exponential backoff - **Phased plans**: `## Phase N — Title` sections add implicit phase-boundary dependencies
- **Multiple formats**: Supports simple checkboxes, and YAML - **Model failover**: Unreachable providers cycle to the next model in the list before a task fails
- **Tool usage tracking**: Detects and reports tool usage (read, write, edit, bash) from task execution - **Auto-commit / auto-review loop**: Optional per-task commit and review-gated re-execution until pass
- **Configurable timeouts**: Task-level timeouts via meta blocks, with global fallback - **Worktree isolation**: Parallel tasks run in separate git worktrees so they can't stomp each other, with batch-level merge-conflict resolution
- **Session saving**: Saves full task output for expandable session review - **Multiple formats**: Fio README (numbered + dependencies), phased, simple checkboxes, and YAML
- **Resume auto-discovery**: Automatically finds and resumes interrupted execution - **Tool usage tracking**: Reports read/write/edit/bash usage from task execution
- **Configurable timeouts**: Task-level timeouts (inline, meta block, or YAML) with global fallback
## Usage ## Usage
``` ```
/ralpi [task-file] # Execute all tasks /ralpi [task-file] # No args → show plan for README.md; path arg → run tasks
/ralpi plan # Alias to /task-manager to plan new tasks /ralpi-run [task-file] # Execute tasks from a task file
/ralpi resume # Resume paused execution /ralpi-plan [prompt] # Open the Task Manager to plan tasks
/ralpi reset # Reset progress and .ralpi directory - does not modify PRD /ralpi-resume [task-file] # Resume paused/interrupted execution
/ralpi-reset [task-file] # Reset execution progress — does not modify the PRD
``` ```
### Highly recommended to use the task-manager prompt for prd construction, it's output pairs perfectly - /task-manager or /ralpi plan `/ralpi` with no arguments shows the execution plan for the default task file. When the first token looks like a path (`@path`, `./path`, `path/file.md`, `.yaml`, etc.) it routes to `/ralpi-run`. Everything else is handled by the dedicated dash commands above (the old `/ralpi plan|resume|reset` subcommand syntax is gone).
> The task-manager prompt (`/ralpi-plan`) pairs perfectly with ralpi's task file formats — use it for PRD construction.
## Tasks ## Tasks
### Simple Checkbox Format ### Simple Checkbox Format
```markdown ```markdown
- [ ] 01: Setup project structure - [ ] Setup project structure
- [ ] 02: Implement auth - [ ] Implement auth
- [ ] 03: Build API - [ ] Build API
```
Checkbox-only files get sequential IDs (`01`, `02`, ...). Status characters: `[ ]` pending, `[x]` done, `[~]` in progress, `[!]` failed, `[-]` skipped.
### Fio Format (numbered tasks + dependencies)
```markdown
# Build a web application
## Tasks
- [ ] 01 — Setup project structure
- [ ] 02 — Implement auth
- [ ] 03 — Build API
## Dependencies
01 -> 02, 03
``` ```
### YAML Format ### YAML Format
@@ -72,40 +96,98 @@ Use lettered sub-tasks when you discover mid-stream that a step needs to be
split. They let you preserve sibling numbering (`01`, `02`, `03`, ...) while split. They let you preserve sibling numbering (`01`, `02`, `03`, ...) while
adding granularity between two existing steps. adding granularity between two existing steps.
## Phases
`## Phase N — Title` headings group tasks into phases and add an implicit
dependency from the first task of each phase to the last task of the
previous one, so phases always run in order:
```markdown
## Phase 1 — Push-to-Talk MVP
- [ ] 01 — Voice capture
- [ ] 02 — Transmission
## Phase 2 — Group Chat
- [ ] 03 — Channels
- [ ] 04 — Presence
```
## Dependencies ## Dependencies
Dependency lines live in a `## Dependencies` section (or a plain
`Dependencies` heading). Multiple formats are supported and can be mixed.
### Arrow Notation (recommended) ### Arrow Notation (recommended)
```
1 -> 2,3,4 1 -> 2,3,4
5 -> 6 5 -> 6
This means: "Task 1 must complete before tasks 2, 3, and 4 can start." ```
"Task 1 must complete before tasks 2, 3, and 4 can start." Also supports
chains (`03 -> 04 -> 05`) and multi-prereq sources (`05, 07, 08 -> 13`).
### Natural Language ### Natural Language
```
13 depends on 17, 18, 19, 20 13 depends on 17, 18, 19, 20
14 depends on 13, 15, 16 14 depends on 13, 15, 16
22, 23, 24 depend on 21
```
This means: "Task 13 depends on tasks 17, 18, 19, and 20." "Task 13 depends on tasks 17, 18, 19, and 20." `also depends on` is accepted.
### Parallel Groups (informational only) ### "must be done before"
1, 2, 3, 4 can be done in parallel ```
21 must be done before 22, 23, 24
02, 03 must be done before 04
```
### Parallel Groups
```
1, 2, 3, 4 can be done in parallel (Play Store prep)
5, 6, 7, 8 can be done in parallel 5, 6, 7, 8 can be done in parallel
```
Note: These lines are ignored by the parser. Use explicit dependencies to control execution order. Tasks listed in a parallel group are allowed to run concurrently. Group
declarations imply no cross-group dependencies, and intra-group
dependencies are still respected — group-aware batching produces a plan
where tasks from any group run as soon as their dependencies are
satisfied.
## Configuration ## Configuration
### Task-Level Timeout ### Task-Level Timeout
You can set a timeout for individual tasks using a meta block in the task file: Timeouts can be set inline on the task line, as an inline comment, via a
meta block in the Dependencies section, or in YAML:
```markdown ```markdown
- [ ] 01: Setup project structure - [ ] 01 Setup project structure timeout: 10m
timeout: 10m - [ ] 02 — Implement auth # timeout=30s
``` ```
Supported formats: `10m` (minutes), `600s` (seconds), `3600000` (milliseconds) ```markdown
## Dependencies
01 -> 02
01 [timeout] = 10m
```
```yaml
tasks:
- id: "01"
title: Setup project structure
timeout: 15m
```
Supported units: `m` / `min` (minutes), `s` (seconds), `ms` (milliseconds).
Bare numbers default to minutes; in YAML, numeric values ≥ 1000 are treated
as milliseconds.
### Config files ### Config files
@@ -114,23 +196,31 @@ Supported formats: `10m` (minutes), `600s` (seconds), `3600000` (milliseconds)
| **Global** | `~/.pi/ralpi/config.yaml` | | **Global** | `~/.pi/ralpi/config.yaml` |
| **Project** | `./.ralpi/config.yaml` | | **Project** | `./.ralpi/config.yaml` |
Project config overrides global, which overrides defaults. Keys set
explicitly in YAML skip the corresponding loop-startup prompt.
```yaml ```yaml
execution: execution:
maxParallel: 3 # ralpi-level concurrency only maxParallel: 3 # ralpi-level concurrency only (0 = unlimited)
models: # round-robin in <provider>/<model> format models: # round-robin for parallel tasks, <provider>/<model>
- google/gemini-3.5-flash # 1st and 3rd task in parallel - anthropic/claude-sonnet-4
- openai/gpt-5.5 # 2nd task in parallel - openai/gpt-4o
autoCommit: true # commit after each task (mandated when autoReview is on; standalone toggle when off) autoCommit: true # commit after each task (mandated when autoReview is on)
autoReview: false # commit → review → loop on fail → merge on pass autoReview: false # commit → review → loop on fail → merge on pass
implModel: "" # model for task impl (sequential mode, empty = inherit parent) saveReviews: false # persist full review output to .ralpi/reviews/ (only with autoReview)
maxReviewRetries: 2 # re-executions on a 'fail' verdict before giving up
reviewBlockOnFail: false # true = mark task failed after retries exhausted instead of merging
implModel: "" # model for task impl (empty = inherit parent session model)
commitModel: "" # model for commit sessions (empty = inherit task model) commitModel: "" # model for commit sessions (empty = inherit task model)
reviewModel: "" # model for review sessions (empty = inherit task model) reviewModel: "" # model for review sessions (empty = inherit task model)
timeoutMs: 0 # per-task timeout in ms (0 = inherit Pi's defaults) timeoutMs: 0 # per-task timeout in ms (0 = inherit Pi's defaults)
commitTimeoutMs: 60000 # timeout for auto-commit agent sessions commitTimeoutMs: 0 # timeout for auto-commit agent sessions (0 = inherit)
reviewTimeoutMs: 120000 # timeout for auto-review agent sessions reviewTimeoutMs: 0 # timeout for auto-review agent sessions (0 = inherit)
loopTimeoutMs: 0 # max total loop duration in ms (0 = no limit) loopTimeoutMs: 0 # max total loop duration in ms (0 = no limit; checked between batches)
worktrees: parallel # "never" | "parallel" (default) | "always" — git worktree isolation
prompts: prompts:
projectContext: "Additional context for all tasks" projectContext: "Additional context for all tasks"
reflectionPrompt: "" # custom suffix for reflection extraction
``` ```
> `execution.models` uses slot-aware round-robin: with 3 models and 2 concurrent > `execution.models` uses slot-aware round-robin: with 3 models and 2 concurrent
@@ -140,8 +230,9 @@ prompts:
> **Automatic failover**: if a provider/API is unreachable (rate limit, 503, etc.), > **Automatic failover**: if a provider/API is unreachable (rate limit, 503, etc.),
> the task automatically cycles to the next model in the list without counting it > the task automatically cycles to the next model in the list without counting it
> as a task failure. Each model is tried once before the task is marked as failed. > as a task failure. Each model is tried once before the task is marked as failed.
> **NOTE**: this is only used in parallel execution, in sequential mode the > **NOTE**: model lists are only used in parallel execution. In sequential mode
> parent pi session's model is used > (or parallel mode with no `models` list) the parent pi session's model is used,
> unless `implModel` is set.
#### Auto-review and Auto-commit #### Auto-review and Auto-commit
@@ -158,19 +249,27 @@ state — original work plus fixes. On pass, the changes are already
committed and the worktree merges. committed and the worktree merges.
When `autoReview` is disabled, `autoCommit` runs a follow-up commit When `autoReview` is disabled, `autoCommit` runs a follow-up commit
agent after each task with no review. Both options can be overridden at agent after each task with no review. With `autoReview` on, the user is
loop startup via a selection prompt (config YAML values are honored also asked whether to persist full review output to
without prompting when set explicitly). `.ralpi/reviews/<prdKey>/<task-id>.json` (`saveReviews` — this is what
enables review feedback recovery when resuming interrupted loops). Both
options can be overridden at loop startup via a selection prompt (config
YAML values are honored without prompting when set explicitly).
`commitModel` and `reviewModel` accept `<provider>/<model>` strings (e.g. `commitModel` and `reviewModel` accept `<provider>/<model>` strings (e.g.
`anthropic/claude-sonnet-4`) resolved via the model registry. When empty, the `anthropic/claude-sonnet-4`) resolved via the model registry. When empty, the
task's model is inherited. `implModel` sets the model for task implementation task's model is inherited. `implModel` sets the model for task implementation
in sequential mode (overridden by `execution.models` round-robin in parallel (used whenever no round-robin model is assigned — sequential mode, or parallel
mode). mode with an empty `models` list; overridden by `execution.models` round-robin
in parallel mode).
## State Files ## State Files
- `.ralpi/progress.json` - Execution progress ```
- `.ralpi/reflections/` - Per-task reflections .ralpi/progress.json # Execution progress (supports multiple PRDs)
- `.ralpi/prompts/` - Generated prompts .ralpi/loop-active.json # Active-loop marker used for auto-resume after a reload
- `.ralpi/sessions/` - Full task output for review .ralpi/reflections/ # Per-task reflections
.ralpi/reviews/<prdKey>/ # Full review output (when saveReviews is on)
.ralpi/prompts/ # Generated prompts (timestamped, for debugging)
.ralpi/config.yaml # Project-level config (optional)
```

View File

@@ -1,38 +1,44 @@
--- ---
description: Executes individual tasks from ralpi task files using DAG-based dependency resolution, with progress tracking and reflection support description: Execute tasks from ralpi task files / PRDs using DAG-based dependency resolution, with persistent progress tracking and reflection support
--- ---
# ralpi-task # ralpi-task
Execute a single task from a ralpi task file. Execute tasks from a ralpi task file (checkbox, Fio, phased, or YAML) using DAG-based dependency resolution, with persistent progress tracking and reflection support.
## When to Use ## When to Use
- User asks to execute a specific task from a task file - User asks to execute a task file, PRD, or task list (e.g. "run the tasks", "execute the plan")
- User provides a task ID and wants to run it - User wants to run a full ralpi loop from a task file in the project
- User wants to run the next task in sequence - User wants to resume an interrupted or paused ralpi run
- User wants to plan new tasks with the task-manager prompt
## Usage ## Usage
``` ```
/ralpi run [task-file] # Run all tasks /ralpi [task-file] # No args → show plan; path arg → run all tasks
/ralpi next [task-file] # Run next batch /ralpi-run [task-file] # Run all tasks (auto-resumes if progress exists)
/ralpi status [task-file] # Check progress /ralpi-resume [task-file] # Resume paused/interrupted execution
/ralpi-plan [prompt] # Open the Task Manager to plan tasks
``` ```
Note: ralpi runs whole task plans — there is no single-task or next-batch subcommand. To execute only part of a plan, edit the task file and remove/adjust the tasks first.
## Task File Location ## Task File Location
Default: `README.md` in current directory. Can be overridden with explicit path. Default: `README.md` in the current directory. Can be overridden with an explicit path (`@path`, `./path`, `*.md`, `*.yaml`, `*.yml`).
## Reflection Format ## Reflection Format
After completing a task, include: After completing a task, the task agent ends its response with a reflection block, which the extension parses and passes to downstream tasks:
``` ```
## REFLECTION ## REFLECTION
SUMMARY: [what was done] SUMMARY: [1-2 sentence description of what was accomplished]
FILES: [files changed] FILES: [comma-separated list of files created or modified]
LEARNINGS: LEARNINGS:
- [key learning] - [key decision, pattern, or architectural choice]
BLOCKERS: [issues or 'none'] - [important API or interface details]
- [anything downstream tasks need to know]
BLOCKERS: [any unresolved issues, or 'none']
``` ```