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
2 changes: 2 additions & 0 deletions docs/architecture/02-webhook-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ interface ParsedWebhookEvent {
| `LinearRouterAdapter` | `src/router/adapters/linear.ts` | Linear team ID |
| `SentryRouterAdapter` | `src/router/adapters/sentry.ts` | CASCADE `projectId` (from URL) |

Sentry keeps the route shape `/sentry/webhook/:projectId`. The project ID selects the Cascade project, whose Sentry config includes a paired `organizationSlug`/`projectSlug`. The adapter filters payloads by matching Sentry project identifiers against the configured `projectSlug`; organization-level deliveries whose payload project does not match are acknowledged but do not dispatch agents.

## The 12-Step Pipeline

`src/router/webhook-processor.ts` — `processRouterWebhook()`
Expand Down
9 changes: 5 additions & 4 deletions docs/architecture/03-trigger-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ function registerBuiltInTriggers(registry: TriggerRegistry): void {

| Handler | Event | Agent |
|---------|-------|-------|
| `AlertingIssueTrigger` | Sentry issue alert | `alerting` |
| `AlertingMetricTrigger` | Sentry metric alert | `alerting` |
| `AlertingIssueTrigger` | Sentry `event_alert` resource | `alerting` |
| `AlertingMetricTrigger` | Sentry `metric_alert` resource | `alerting` |
| `SentryIssueLifecycleTrigger` | Sentry `issue` lifecycle resource | `alerting` |

## Trigger Configuration

Expand All @@ -147,7 +148,7 @@ Triggers use category-prefixed events from `src/triggers/shared/events.ts`. `TRI

- PM: `pm:status-changed`, `pm:label-added`, `pm:comment-mention`
- SCM: `scm:check-suite-success`, `scm:check-suite-failure`, `scm:pr-review-submitted`, `scm:review-requested`, `scm:pr-opened`, `scm:pr-comment-mention`, `scm:pr-merged`, `scm:pr-ready-to-merge`, `scm:pr-conflict-detected`
- Alerting: `alerting:issue-alert`, `alerting:metric-alert`
- Alerting: `alerting:issue-alert`, `alerting:metric-alert`, `alerting:issue-lifecycle`
- Internal: `internal:auto-chain`

New handlers should import `TRIGGER_EVENTS` instead of adding raw string literals. The static guard in `tests/unit/triggers/trigger-event-consistency.test.ts` fails when a handler gates on one event string and emits a different `agentInput.triggerEvent`.
Expand Down Expand Up @@ -203,7 +204,7 @@ Each trigger in a YAML agent definition can declare a `contextPipeline` — an o
| `prepopulateTodos` | Pre-populate todo list from work item checklists |
| `prContext` | Fetch PR details, GitHub changed-file metadata, locally verified compact per-file diffs from `origin/<base>...HEAD`, and CI checks; emit a `SKIPPED FILES` injection when files are omitted (over budget, deleted, binary/no patch, or local diff unavailable) |
| `prConversation` | Fetch PR comments and review threads |
| `pipelineSnapshot` | Fetch CI pipeline status |
| `pipelineSnapshot` | Fetch PM workflow/pipeline state and emit the single authoritative `PipelineSnapshotSummary` JSON context for backlog-manager |
| `alertingIssue` | Fetch Sentry issue and event details |

## Shared Agent Execution
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture/04-agent-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ Pipeline prompts receive separate PM identifiers for selection and creation:

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.

### Backlog-manager pipeline context

The `backlog-manager` agent requires the `pipelineSnapshot` context step on every run. That internal step key now emits exactly one context injection named `PipelineSnapshotSummary`; its `result` is structured JSON, not markdown. The JSON is the authoritative contract for active pipeline capacity, backlog ordering, per-status counts, `itemsById`, comments, checklists, labels, descriptions, attachments/media references, dependency signals, and provider or item-read errors.

The previous human-readable markdown `PipelineSnapshot` context was intentionally removed as a clean contract break. Prompt policy should read `PipelineSnapshotSummary.statuses.<status>.itemIds` and `PipelineSnapshotSummary.itemsById` directly instead of parsing formatted work-item text.

When capacity is available but every backlog item is blocked, backlog-manager posts the `Backlog Blocked` comment exactly once on the first item in `PipelineSnapshotSummary.statuses.backlog.itemIds` provider order. If BACKLOG is empty, it exits silently without posting that comment. Selected items are still moved only from BACKLOG to TODO with `MoveWorkItem.expectedSourceState` set to the configured backlog source.

### Alert task prompt context

Alerting task prompts can reference scalar alert fields passed through `AgentInput`:
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/06-integration-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ Each provider declares its credential roles — the mapping from logical role na

- `SentryAlertingIntegration` implements `AlertingIntegration`
- `sentryClient` — REST API client with Bearer token auth
- Supports issue alerts, metric alerts, and issue lifecycle webhooks
- Config: `organizationSlug` stored in `project_integrations.config` JSONB
- Supports Sentry webhook resources `event_alert`, `metric_alert`, and `issue` lifecycle
- Config: `organizationSlug` and `projectSlug` stored in `project_integrations.config` JSONB

## PM Abstraction

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 @@ -77,6 +77,16 @@ Backlog selection and work-item creation use different native concepts:

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.

Backlog-manager receives pipeline state through the required `pipelineSnapshot`
context step, which emits one structured `PipelineSnapshotSummary` JSON
injection. The JSON includes provider-source status counts, active pipeline
count, backlog item order, `itemsById`, comments, checklists, labels,
descriptions, attachments/media references, dependency signals, and provider
errors. The legacy markdown `PipelineSnapshot` context is intentionally not
emitted. If all backlog items appear blocked, backlog-manager posts the blocked
backlog comment only on the first BACKLOG item in `statuses.backlog.itemIds`
provider order; when BACKLOG is empty it exits without posting.

`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
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,15 +307,15 @@ node bin/cascade.js webhooks create my-project \
--callback-url https://your-tunnel.ngrok.io
```

This creates webhooks on GitHub, Trello, and Jira when those integrations are configured, reusing existing hooks when the canonical callback URL already exists. Linear and Sentry are informational/manual setup paths: the dashboard and API show the correct callback URL and whether a signing secret is stored, but you create the webhook in the provider UI.
This creates webhooks on GitHub, Trello, and Jira when those integrations are configured, reusing existing hooks when the canonical callback URL already exists. Linear and Sentry are informational/manual setup paths: the dashboard and API show the correct callback URL and whether a signing secret is stored, but you create the webhook in the provider UI. For Sentry, the URL remains `https://your-router-host/sentry/webhook/:projectId`; organization-level deliveries may reach that URL, but Cascade dispatches only payloads whose Sentry project matches the configured `projectSlug`.

| Provider | Setup behavior | Callback URL |
|----------|----------------|--------------|
| GitHub | Programmatic create/list/delete | `https://your-router-host/github/webhook` |
| Trello | Programmatic create/list/delete | `https://your-router-host/trello/webhook` |
| Jira | Programmatic create/list/delete plus label ensure | `https://your-router-host/jira/webhook` |
| Linear | Manual setup with optional `LINEAR_WEBHOOK_SECRET` | `https://your-router-host/linear/webhook` |
| Sentry | Manual setup with optional Sentry webhook secret | `https://your-router-host/sentry/webhook/my-project` |
| Sentry | Manual setup with optional Sentry webhook secret; paired with configured `organizationSlug`/`projectSlug` and filtered by payload project matching `projectSlug` | `https://your-router-host/sentry/webhook/my-project` |

---

Expand Down
9 changes: 5 additions & 4 deletions src/agents/definitions/backlog-manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ triggers:
# Required context — runs for EVERY invocation regardless of trigger source.
# Manual `cascade runs trigger --agent-type backlog-manager` would otherwise
# skip the per-trigger contextPipeline (triggerEvent is undefined), and the
# agent would freelance with no snapshot. Empty pipelineSnapshot result
# agent would freelance with no structured PipelineSnapshotSummary JSON. Empty pipelineSnapshot result
# aborts the run with a structured error + Sentry capture under tag
# `context_pipeline_required_step_failed` (closes the 2026-04-29 incident
# where MNG-422 was pulled from SPLITTING into TODO).
Expand All @@ -70,10 +70,11 @@ prompts:
# The system prompt (.eta file) contains full instructions.
# This taskPrompt provides the per-run context and directive.
taskPrompt: |
A Pipeline Snapshot has been pre-loaded into your context with the current state of all pipeline lists (BACKLOG, TODO, IN_PROGRESS, IN_REVIEW, DONE, MERGED).
A PipelineSnapshotSummary JSON object has been pre-loaded into your context with the current state of all pipeline statuses (BACKLOG, TODO, IN_PROGRESS, IN_REVIEW, DONE, MERGED).

1. Review the pre-loaded Pipeline Snapshot and count items currently in the active pipeline (TODO + IN PROGRESS + IN REVIEW).
2. If the count is below the capacity limit (see system prompt): use the pre-loaded BACKLOG data from the snapshot to select ALL eligible unblocked items to fill remaining capacity completely — always move the maximum number possible.
1. Parse PipelineSnapshotSummary. If activeCapacityReliable is false, abort immediately — capacity count is unreliable due to a fetch error; do not move any items. Otherwise use activePipelineCount for items currently in the active pipeline (TODO + IN PROGRESS + IN REVIEW).
2. If the count is below the capacity limit (see system prompt): use PipelineSnapshotSummary.statuses.backlog.itemIds in provider order plus itemsById details to select ALL eligible unblocked items to fill remaining capacity completely — always move the maximum number possible.
3. If already at or above capacity: exit immediately without taking action.
4. If every BACKLOG item is blocked, post one Backlog Blocked comment on the first BACKLOG item in PipelineSnapshotSummary.statuses.backlog.itemIds order. If BACKLOG is empty, exit silently.

hint: Only act if pipeline has capacity (items in TODO + IN PROGRESS + IN REVIEW < maxInFlightItems). When acting, ALWAYS fill ALL remaining capacity.
Loading
Loading