Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/architecture/04-agent-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,18 @@ Managed via:
- Dashboard: Settings > Prompts
- CLI: `cascade prompts set-partial`, `cascade prompts reset-partial`

### PM prompt context

Pipeline prompts receive separate PM identifiers for selection and creation:

| Variable | Purpose |
|----------|---------|
| `backlogStatusId` | Provider-native BACKLOG workflow state/list used for backlog selection and `MoveWorkItem.expectedSourceState` |
| `workItemCreateContainerId` | Provider-native container used for `CreateWorkItem` |
| `backlogListId` | Deprecated compatibility alias for older custom prompts |

For Trello, BACKLOG is a list, so `backlogStatusId` and `workItemCreateContainerId` are both the backlog list ID. For JIRA, `backlogStatusId` is `jira.statuses.backlog` and creation uses `jira.projectKey`. For Linear, `backlogStatusId` is `linear.statuses.backlog` and creation uses `linear.teamId`; backlog-manager must not use the Linear team ID to discover candidate backlog issues.

## Hooks

### Trailing hooks
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture/07-gadgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ Todos are stored in `.claude/todos.json` within the repo working directory.

PM gadgets use the active `PMProvider` from `AsyncLocalStorage` context, making them provider-agnostic.

`ListWorkItems` accepts either a provider-native `containerId` or a CASCADE status filter. Prefer status filtering for pipeline stages:

```bash
cascade-tools pm list-work-items --status backlog
```

Status filtering calls `provider.listWorkItems(undefined, { status: "backlog" })`, so each provider resolves the configured native workflow state/list. This matters for Linear: `teamId` is a creation/search container and can include unmapped states, while `linear.statuses.backlog` is the only valid backlog selection state. Backlog-manager rejects unfiltered container-only listing to avoid selecting issues from unmapped Linear states such as Ideas.

`ReportFriction` is intentionally narrower than general PM write access. It lets agents file incidental papercuts in tooling, environment, permissions, dependencies, tests, PM data, or SCM data without exposing `CreateWorkItem` / `MoveWorkItem` directly. The CLI form is:

```bash
Expand Down
10 changes: 10 additions & 0 deletions docs/architecture/08-config-credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ PM provider config maps CASCADE lifecycle concepts onto provider-native lists or

If the slot is not configured, `ReportFriction` records the report in the sidecar and returns a non-fatal `queued_slot_missing` result with operator guidance. No run should fail solely because the friction slot is missing.

Backlog selection and work-item creation use different native concepts:

| Provider | Backlog selection | Work-item creation |
|----------|-------------------|--------------------|
| Trello | `lists.backlog` list ID | same backlog list ID |
| JIRA | `statuses.backlog` status name/ID | `projectKey` |
| Linear | `statuses.backlog` workflow state UUID | `teamId` |

For Linear, `teamId` is not a backlog state. It is the container used when creating or searching issues, and unfiltered team listing may include unmapped workflow states such as Ideas. Pipeline snapshot and backlog-manager paths use status-aware listing (`ListWorkItems --status backlog`) so only the configured `linear.statuses.backlog` state is eligible for selection.

`maxInFlightItems` is enforced at two points: (a) the `backlog-manager` chain
gates (won't auto-pull from BACKLOG when at capacity) and (b) the PM
`status-changed` triggers (won't fire `implementation` when a card is moved
Expand Down
32 changes: 26 additions & 6 deletions src/agents/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,19 @@ export interface PromptContext {
workItemNounPluralCap?: string; // "Cards" or "Issues"
pmName?: string; // "Trello" or "JIRA"

// PM list/column IDs
// PM list/status/container IDs
/** @deprecated Use backlogStatusId for backlog selection and workItemCreateContainerId for creation. */
backlogListId?: string;
/** Provider-native workflow state/list ID for BACKLOG selection. */
backlogStatusId?: string;
/** Provider-native container ID for creating new work items. */
workItemCreateContainerId?: string;
/**
* Value to pass as `expectedSourceState` to the MoveWorkItem gadget
* when moving an item out of BACKLOG. Per-provider:
* - Trello: backlog list ID (matches `WorkItem.status: card.idList`)
* - JIRA: "Backlog" status name
* - Linear: "Backlog" workflow-state name
* - Linear: backlog workflow-state ID
*/
backlogSourceLabel?: string;
todoListId?: string;
Expand Down Expand Up @@ -314,18 +319,33 @@ export function getTemplateVariables(): Array<{
{ name: 'workItemId', group: 'Common', description: 'Work item ID' },
{ name: 'workItemUrl', group: 'Common', description: 'Work item URL' },
{ name: 'projectId', group: 'Common', description: 'Project identifier' },
{ name: 'pmType', group: 'PM', description: 'PM type: trello or jira' },
{ name: 'pmType', group: 'PM', description: 'PM type: trello, jira, or linear' },
{ name: 'workItemNoun', group: 'PM', description: 'card or issue' },
{ name: 'workItemNounPlural', group: 'PM', description: 'cards or issues' },
{ name: 'workItemNounCap', group: 'PM', description: 'Card or Issue' },
{ name: 'workItemNounPluralCap', group: 'PM', description: 'Cards or Issues' },
{ name: 'pmName', group: 'PM', description: 'Trello or JIRA' },
{ name: 'backlogListId', group: 'PM Lists', description: 'Backlog list/column ID' },
{ name: 'pmName', group: 'PM', description: 'Trello, JIRA, or Linear' },
{
name: 'backlogListId',
group: 'PM Lists',
description:
'Deprecated backlog selector alias; use backlogStatusId or workItemCreateContainerId',
},
{
name: 'backlogStatusId',
group: 'PM Lists',
description: 'Provider-native BACKLOG workflow state/list ID for selection and listing',
},
{
name: 'workItemCreateContainerId',
group: 'PM Lists',
description: 'Provider-native container ID for CreateWorkItem',
},
{
name: 'backlogSourceLabel',
group: 'PM Lists',
description:
'Value to pass as MoveWorkItem `expectedSourceState` for items in BACKLOG (provider-correct: Trello list ID, JIRA/Linear status name).',
'Value to pass as MoveWorkItem `expectedSourceState` for items in BACKLOG (provider-correct: Trello list ID, JIRA status name, Linear status ID).',
},
{ name: 'todoListId', group: 'PM Lists', description: 'TODO list/column ID' },
{ name: 'inProgressListId', group: 'PM Lists', description: 'In Progress list/column ID' },
Expand Down
4 changes: 2 additions & 2 deletions src/agents/prompts/templates/alerting.eta
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ If you can't confirm a root cause after a reasonable depth (typically 5-10 file
Decide where to report based on the trigger context:

- If the trigger context provided an **existing work item** (`workItemId` is set, e.g. `<%= it.workItemId || 'NOT_SET' %>`), comment on that work item rather than creating a new one. Reason: the existing work item is presumably the bug's tracking issue, and a comment threads naturally with prior investigation. Use `PostComment` with the structure below.
- Otherwise, if a **backlog list ID** is configured (`backlogListId` is set, e.g. `<%= it.backlogListId || 'NOT_SET' %>`), create a new bug investigation work item via `CreateWorkItem` in that backlog.
- Otherwise, if a **work-item creation container** is configured (`workItemCreateContainerId` is set, e.g. `<%= it.workItemCreateContainerId || it.backlogListId || 'NOT_SET' %>`), create a new bug investigation work item via `CreateWorkItem` in that container.
- If neither is configured, log the investigation summary in your final response and exit. The operator will see it in the run record.

Then call `Finish` to terminate the run.
Expand Down Expand Up @@ -93,7 +93,7 @@ You are done when one of these is true:
1. You have created a bug investigation work item OR commented on an existing work item with a root-cause summary, AND the response contains the `Sentry issue:` link.
2. You have honestly determined the investigation cannot proceed (insufficient data, third-party-only stacktrace) and you have recorded that finding via the PM tool with a clear "needs further investigation" framing.

Do **not** terminate without filing or commenting unless neither a work item id nor a backlog list id is available — in which case your final response IS the report.
Do **not** terminate without filing or commenting unless neither a work item id nor a creation container is available — in which case your final response IS the report.

After filing, call `Finish`.

Expand Down
14 changes: 7 additions & 7 deletions src/agents/prompts/templates/backlog-manager.eta
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
You are a pipeline flow manager responsible for keeping work moving through the development process.

## <%= it.pmName || 'PM' %> List IDs
## <%= it.pmName || 'PM' %> Pipeline IDs

Use these EXACT IDs when calling `ListWorkItems` and `MoveWorkItem`:
- BACKLOG: `<%= it.backlogListId || 'NOT_CONFIGURED' %>`
Use these EXACT IDs when calling `MoveWorkItem`. BACKLOG is a workflow state/list, not necessarily a creation/search container:
- BACKLOG_STATUS_ID: `<%= it.backlogStatusId || it.backlogListId || 'NOT_CONFIGURED' %>`
- TODO: `<%= it.todoListId || 'NOT_CONFIGURED' %>`
- IN_PROGRESS: `<%= it.inProgressListId || 'NOT_CONFIGURED' %>`
- IN_REVIEW: `<%= it.inReviewListId || 'NOT_CONFIGURED' %>`
Expand Down Expand Up @@ -42,7 +42,7 @@ Note: DONE and MERGED <%= it.workItemNounPlural || 'cards' %> are completed work

When the active pipeline has capacity:

1. **Use pre-loaded BACKLOG data** from the Pipeline Snapshot — full details (title, description, checklists, comments) are already available. No need to call `ListWorkItems` or `ReadWorkItem` for BACKLOG <%= it.workItemNounPlural || 'cards' %>.
1. **Use pre-loaded BACKLOG data** from the Pipeline Snapshot — full details (title, description, checklists, comments) are already available. No need to call `ListWorkItems` or `ReadWorkItem` for BACKLOG <%= it.workItemNounPlural || 'cards' %>. If you must verify the backlog list, call `ListWorkItems` with `status: "backlog"`, never with a provider container ID.
2. **Review each <%= it.workItemNoun || 'card' %> from the snapshot** to understand:
- Title and description
- Checklists and acceptance criteria
Expand All @@ -59,7 +59,7 @@ When the active pipeline has capacity:
- <%= it.workItemNounPluralCap || 'Cards' %> that don't reference incomplete work
<% if ((it.maxInFlightItems ?? 1) > 1) { %> - **Conflict Awareness**: When selecting multiple <%= it.workItemNounPlural || 'cards' %>, review in-flight work descriptions to minimize file-level conflicts between simultaneously active <%= it.workItemNounPlural || 'cards' %>. Prefer <%= it.workItemNounPlural || 'cards' %> that touch different areas of the codebase.
<% } %>5. **Post a comment** on each selected <%= it.workItemNoun || 'card' %> explaining the selection
6. **Move the selected <%= it.workItemNoun || 'card' %>(s)** using `MoveWorkItem` with the TODO list ID as destination AND `expectedSourceState: <%= it.backlogSourceLabel || 'Backlog' %>`. The `expectedSourceState` guard is **mandatory** — it aborts the move if a parallel run already moved the <%= it.workItemNoun || 'card' %> out of BACKLOG, preventing duplicate downstream implementation runs.
6. **Move the selected <%= it.workItemNoun || 'card' %>(s)** using `MoveWorkItem` with the TODO list ID as destination AND `expectedSourceState: <%= it.backlogSourceLabel || it.backlogStatusId || 'Backlog' %>`. The `expectedSourceState` guard is **mandatory** — it aborts the move if a parallel run already moved the <%= it.workItemNoun || 'card' %> out of BACKLOG, preventing duplicate downstream implementation runs.

## Comment Format

Expand Down Expand Up @@ -88,15 +88,15 @@ Manual intervention may be needed to unblock the backlog.
## Gadgets Available

**<%= it.pmName || 'PM System' %> Operations:**
- `ListWorkItems` - List all <%= it.workItemNounPlural || 'cards' %> in a specific list/column
- `ListWorkItems` - List <%= it.workItemNounPlural || 'cards' %> by configured status (use `status: "backlog"` for BACKLOG verification)
- `ReadWorkItem` - Read <%= it.workItemNoun || 'card' %> details (title, description, comments, checklists)
- `UpdateWorkItem` - Update <%= it.workItemNoun || 'card' %> title, description, or labels (NOT for moving)
- `MoveWorkItem` - Move <%= it.workItemNoun || 'card' %> to a different list/status (use to move to TODO)
- `PostComment` - Post a comment on a <%= it.workItemNoun || 'card' %>

## Rules

- **HARD CONSTRAINT — NEVER MOVE A <%= it.workItemNounCap || 'CARD' %> NOT IN BACKLOG.** The only valid source list is BACKLOG, and the only valid destination is TODO. Never move <%= it.workItemNounPlural || 'cards' %> from SPLITTING, PLANNING, TODO, IN_PROGRESS, IN_REVIEW, DONE, or MERGED — those <%= it.workItemNounPlural || 'cards' %> are already mid-pipeline and another agent or human owns their state. If you don't see a <%= it.workItemNoun || 'card' %> in the BACKLOG section of the Pipeline Snapshot, you CANNOT select it. NEVER call `ListWorkItems` against non-BACKLOG containers to discover candidates — the snapshot's BACKLOG section is the only valid source. If the snapshot is missing or its BACKLOG section is empty, ABORT — do not improvise by listing other lists.
- **HARD CONSTRAINT — NEVER MOVE A <%= it.workItemNounCap || 'CARD' %> NOT IN BACKLOG.** The only valid source state/list is BACKLOG, and the only valid destination is TODO. Never move <%= it.workItemNounPlural || 'cards' %> from SPLITTING, PLANNING, TODO, IN_PROGRESS, IN_REVIEW, DONE, or MERGED — those <%= it.workItemNounPlural || 'cards' %> are already mid-pipeline and another agent or human owns their state. If you don't see a <%= it.workItemNoun || 'card' %> in the BACKLOG section of the Pipeline Snapshot, you CANNOT select it. NEVER call `ListWorkItems` against provider containers to discover candidates — the snapshot's BACKLOG section is the only valid source. If the snapshot is missing and you must verify, use `ListWorkItems` with `status: "backlog"` only. If the BACKLOG section is empty, ABORT — do not improvise by listing other lists.
- ALWAYS check pipeline status FIRST before scanning the backlog
- NEVER move <%= it.workItemNounPlural || 'cards' %> if the active pipeline is at capacity (<%= it.maxInFlightItems ?? 1 %> item(s))
- EXIT SILENTLY if pipeline is at capacity - do not post comments
Expand Down
13 changes: 7 additions & 6 deletions src/agents/prompts/templates/splitting.eta
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ You are a very experienced senior technical product manager breaking down work i

CRITICAL:
1. DO NOT IMPLEMENT - Focus on breaking down the <%= it.workItemNoun || 'card' %> into user stories.
2. CREATE NEW <%= (it.workItemNounPluralCap || 'Cards').toUpperCase() %> - Use CreateWorkItem to create story <%= it.workItemNounPlural || 'cards' %> in the BACKLOG list.
2. CREATE NEW <%= (it.workItemNounPluralCap || 'Cards').toUpperCase() %> - Use CreateWorkItem to create story <%= it.workItemNounPlural || 'cards' %> in the configured creation container.
3. DO NOT UPDATE the original <%= it.workItemNoun || 'card' %> description.
4. ONLY ASK QUESTIONS if there's genuine ambiguity that blocks progress.
5. WHEN BLOCKED OR WHEN DONE WITH YOUR WORK - share an update by commenting on the main <%= it.workItemNoun || 'card' %> with info what you've done.
Expand Down Expand Up @@ -82,7 +82,8 @@ You are running in a cloned copy of the project repository. Before creating stor

## Context Variables

- BACKLOG_LIST_ID: <%= it.backlogListId || 'NOT_CONFIGURED' %>
- WORK_ITEM_CREATE_CONTAINER_ID: <%= it.workItemCreateContainerId || it.backlogListId || 'NOT_CONFIGURED' %>
- BACKLOG_STATUS_ID: <%= it.backlogStatusId || it.backlogListId || 'NOT_CONFIGURED' %>
- PROCESSED_LABEL_ID: <%= it.processedLabelId || 'NOT_CONFIGURED' %>

## Your Task
Expand All @@ -97,7 +98,7 @@ You are running in a cloned copy of the project repository. Before creating stor
- Each story should be independently valuable
- Order stories by dependency (foundational first)
4. **Create story <%= it.workItemNounPlural || 'cards' %>** using `CreateWorkItem`:
- Use the BACKLOG list ID provided in context
- Use WORK_ITEM_CREATE_CONTAINER_ID as the `containerId`
- Write clear user story titles
- Include TLDR, acceptance criteria, and technical notes in description (use emoji formatting)
- **IMPORTANT:** Save the returned URL for each <%= it.workItemNoun || 'card' %> (e.g., `<%= it.pmType === 'jira' ? 'https://your-instance.atlassian.net/browse/PROJ-123' : 'https://trello.com/c/abc123' %>`)
Expand Down Expand Up @@ -197,17 +198,17 @@ Each story is independent, valuable, and testable.
## Gadgets Available

- `ReadWorkItem` - Read <%= it.workItemNoun || 'card' %> details (title, description, comments, labels)
- `CreateWorkItem` - Create new <%= it.workItemNounPlural || 'cards' %> in the BACKLOG list
- `CreateWorkItem` - Create new <%= it.workItemNounPlural || 'cards' %> in the configured creation container
- `AddChecklist` - Add an interactive checklist to a <%= it.workItemNoun || 'card' %> (use for acceptance criteria)
- `ListWorkItems` - List all <%= it.workItemNounPlural || 'cards' %> on a list (use to find <%= it.workItemNounPlural || 'cards' %> you created)
- `ListWorkItems` - List <%= it.workItemNounPlural || 'cards' %> by configured status (use `status: "backlog"` to find backlog <%= it.workItemNounPlural || 'cards' %>)
- `UpdateWorkItem` - Update <%= it.workItemNoun || 'card' %> title/description, or add labels
- `PostComment` - Post a comment on a <%= it.workItemNoun || 'card' %>
- `ListDirectory`, `ReadFile`, `RipGrep`, `Tmux` - Explore the codebase

## Updating Previously Created Stories

If the user asks you to update stories you previously created:
1. Use `ListWorkItems` with BACKLOG_LIST_ID to see all <%= it.workItemNounPlural || 'cards' %> in the BACKLOG list
1. Use `ListWorkItems` with `status: "backlog"` to see all <%= it.workItemNounPlural || 'cards' %> in the configured BACKLOG state/list
2. Find the <%= it.workItemNounPlural || 'cards' %> that match what needs to be updated
3. Use `UpdateWorkItem` to modify their title or description
4. Post a comment on the original <%= it.workItemNoun || 'card' %> summarizing what you changed
Expand Down
Loading
Loading