Issue Board
The Issue Board is a single, global surface over work happening across every Workspace — a Linear-style list a human reads and an agent writes. There is no central store: the board is assembled live by scanning each workspace's own .alice/issues/ directory on every read. An issue is just a markdown file the agent commits inside its checkout.
The design goal is self-description. In Linear, urgency and ownership are part of the issue itself, so a worker can look at the ticket and know what to do next. OpenAlice uses the same idea for human+agent trading work: status, priority, assignee, context, comments, and links describe the task to the people and agents working it.
OpenAlice then extends that self-description to scheduling. One object spans two roles. A plain issue is a tracked work item — something to do, with a status and a priority. Add a when and what, and the same file additionally self-schedules: the workspace automation scanner fires it as a headless run. It's the action counterpart to the Inbox ("here's a finished thing") and the Tracked index ("here's what I'm following") — the Issue Board is "here's the work, and what's scheduled to happen."
An issue is a markdown file
Each issue is one markdown file at <wsDir>/.alice/issues/<id>.md inside a workspace's own checkout — YAML frontmatter plus a markdown body. The directory is .alice/issues/ (ISSUES_DIR_REL), relative to the workspace root. The id is the filename stem (kebab-case slug); it is stable and keys the scanner's last-fired marker for scheduled issues.
Read the frontmatter as two self-describing layers:
- Board fields (
title,status,priority,assignee) describe how the work should be triaged. - Schedule fields (
when,what,agent) describe how the work should be fired when nobody is watching.
---
title: Re-underwrite the VST thesis
status: in_progress
priority: high
assignee: human
when: { kind: cron, cron: "30 8 * * 1-5" }
what: Pull pre-market movers for [[stock-vst]], re-check the invalidation level in thesis.md, and push a brief to the inbox.
---
The PJM capacity auction cleared 9× higher. Track whether the nuclear fleet
stays uncontracted into 2026.
## Comments
**ws:research** — 2026-06-28: invalidation level still intact at the open.
The agent writes these files (a coding task — the bundled self-scheduling skill teaches the format) and the launcher reads them live each scan. Because an agent can edit any file in its checkout, a read is never trusted: every file is re-validated, size-capped at 64 KB, and isolated — one malformed file is reported (it lands in an invalid list), never propagated, and one bad workspace can't break the scan for the rest. Nothing in the reader throws.
Frontmatter
| Field | Required | Default | Meaning |
|---|---|---|---|
title | ✓ | — | Short human title (min 1 char). An issue with no title has no board row. |
status | — | todo | One of backlog, todo, in_progress, done, canceled. |
priority | — | none | One of urgent, high, medium, low, none. |
assignee | — | unassigned | Free string — e.g. human, ws:<tag>, or unassigned. |
when | — | (absent) | Optional schedule. Present iff the issue self-schedules. See Scheduling. |
what | — | (falls back to title+body) | The prompt a scheduled fire hands to the headless run. |
agent | — | (workspace default) | Adapter id (e.g. claude / codex) to run the scheduled fire. |
Everything below the closing --- is the body: the markdown description, which also carries the ## Comments block. id and body are not frontmatter fields — id comes from the filename, body from below the fence.
Statuses & priorities
Status moves an issue through the board; priority orders it within a group.
- Statuses —
backlog → todo → in_progress → done → canceled.doneandcanceledare terminal. - Priorities —
urgent,high,medium,low,none.
There is no enabled field. A terminal status (done or canceled) is how a self-scheduled issue is turned off: the scanner stops firing it but the issue stays visible on the board. An issue is fireable only when it has a when and a non-terminal status.
Scheduling: an issue with a when
A when connects a tracked item to an automated run. With it present, the issue self-schedules; without it, the scanner ignores the file and it's a pure board work item.
when: { kind: at, at: "2026-09-01T13:30:00Z" } # run once
when: { kind: every, every: "1h" } # repeat on an interval
when: { kind: cron, cron: "0 9 * * 1-5" } # 5-field cron, wall-clock
When a scheduled issue fires, the launcher hands the headless run the what prompt — or, if what is absent, the title + body. The firing cadence, the last-fired and next-due markers, restart-safety, and the "write what for a headless run" guidance all live in Workspace Automation. The board surfaces the result: a cadence pill, and (on the detail view) the last-fired / next-due times.
Scheduling frontmatter —
when,what,agent— is owned by the agent, edited by writing the file. None of the CLI, MCP, or HTTP write paths touch it; they patch only the board fields below.
The alice-workspace issue CLI
The issue verbs are alice-workspace subcommands. They read global, write local: a read scans the whole board across every workspace, but a write only ever touches the caller's own .alice/issues/. There is no delete verb — remove an issue by deleting its file. Each verb is registered once and is therefore reachable over MCP too (the tool names mirror the verbs).
| Verb | Reach | Flags | Effect |
|---|---|---|---|
issue list | global | (none) | One row per issue across the whole board. |
issue show | global | --id <name> (req) | Resolve a name (id or title, case-insensitive) across the board; returns full detail, or a candidate list if ambiguous. |
issue create | local | --title <t> (req), --id, --status, --priority, --assignee, --when, --what, --agent, --body | Create an issue in the caller's workspace. |
issue update | local | --id <id> (req) + at least one of --status / --priority / --assignee | Patch board fields only. |
issue comment | local | --id <id> (req), --text <md> (req) | Append a comment under ## Comments. |
Notes on the write verbs:
create— only--titleis required;--idis derived as a kebab slug from the title when omitted.--whenis JSON, e.g.--when '{"kind":"cron","cron":"0 9 * * *"}'. Defaults:status=todo,priority=none,assignee=ws:<tag>. Creating over an existing id is refused.update— patches onlystatus/priority/assignee;when/what/agent/bodyare not updatable here (write the file for those).comment— appends to the stable## Commentssection in the body, authoredws:<tag>. The file stays the single source of truth — there is no separate comment store.
The matching MCP tools carry the same shapes (one shared registration), with the wsId closed over from the gateway URL so the agent never names its own workspace:
issue_list({})
issue_show({ id })
issue_create({ title, id?, status?, priority?, assignee?, when?, what?, agent?, body? })
issue_update({ id, status?, priority?, assignee? })
issue_comment({ id, text })
From the Web UI
Issues is a pinned top-level activity, peer to Inbox and Tracked — a Linear-style, full-width, sidebar-less view.
The board is read-only. Issues are grouped by status into collapsible groups in the order in_progress → todo → backlog → done → canceled (not kanban columns). Each row shows a priority glyph, the mono id, the title, the assignee, and a workspace-tag pill — plus a dup pill when the title collides with an issue in another workspace, and a cadence pill when the issue has a when. A workspace whose issues dir is unreadable shows as a red error card; the empty state tells you to write .alice/issues/<id>.md.
The issue detail view has a back-to-board link, a main column, and a Properties rail:
- Main column —
id+ cadence pill + title, the rendered markdown body (which carries the## Commentsblock), a comment composer (textarea, ⌘/Ctrl+↵, authorhuman), an Activity feed of the headless runs linked to this issue, and an Inbox reports section (the issue → inbox cross-link). - Editable from the UI —
status,priority,assignee. - Read-only —
when/ cadence,agent, Last fired, Next due. These are driven by the frontmatter the agent owns.
[[name]] wikilinks in the body resolve across both the entity and the issue namespaces.
API
The board is served read-and-write over HTTP. Reads scan; writes go through the same shared mutation helper the agent surfaces use, so the human and agent paths can never drift on file format or validation. Writes are working-tree only (no auto-commit).
| Method & path | Effect |
|---|---|
GET /api/issues | The global board — every workspace, each with its issues (or an invalid marker). |
GET /api/issues/:wsId/:id | One issue's detail: the issue + body + markers, its runs history, and its inbox reports. |
PATCH /api/issues/:wsId/:id | Patch status / priority / assignee only — not scheduling frontmatter. |
POST /api/issues/:wsId/:id/comments | Append a comment under ## Comments, author human. |
The detail and both write routes return the same detail shape, so the UI can swap its cache after an edit.
Cross-links: the [[ ]] graph, runs, and inbox
An issue is a first-class node in the cross-workspace knowledge graph:
[[name]]graph — issues join the same[[name]]namespace as entities. A wikilink in an issue body resolves across both the entity and issue namespaces. Because a name is a global team object, a title shared by issues in two different workspaces is flagged as a duplicate-name collision — surfaced as theduppill and a top-levelduplicateNameslist. This is detection only: a duplicate name is never rejected, and access stayswsId-precise.- Runs — when a scheduled issue fires, the resulting headless run is tied back to the issue. The detail view's Activity feed lists that issue's run history.
- Inbox — a report pushed during a scheduled run is stamped with its
origin.issueId, so the issue detail's Inbox reports section gathers every push the issue produced — the issue ↔ inbox cross-link in both directions.
Next Steps
- Workspace Automation — Add
whenandwhatto turn an issue into a run. - Inbox — Read the reports an issue produces.
- Workspaces — See where issue files live.
