# list_promises

Page the index of current owner-approved promises for this token-bound repository. Use a task-shaped read when planning or changing known behavior, even before files are chosen: one targeted `keyword` lookup over titles and scope markers, or `get_promise_context` for an exact capability marker already known from the task. Keyword lookup is substring matching, not semantic search. Reuse relevant context for an unchanged plan; do not repeat reads at every message or run an unfiltered catalog read at startup. The person may also explicitly ask what this repository has promised, or you may check `brokenSinceLastSeen` before saying what broke while they were away. When the change touches known paths, give `paths`, the paths your change touches, and you get the 12 promises whose scope overlaps them, closest first, plus up to 3 without path-specific scope in the legacy `repositoryWide` field. Those are uncertain-applicability leads, not automatically global obligations; check their behavioral scope before using them. `total` counts every promise that overlaps, `truncated` says whether rows were left out, and a selection does not continue with a cursor. Without `paths` this is an unranked page you filter by path prefix, by keyword, by `unverified` (no Balladeer verifier is bound), by `mine` (the person who connected this agent owns or agreed to it), by `brokenSinceLastSeen` (the promises that person owns that stopped holding since they last read them), or not at all, and continue with a cursor. `paths` on a page says which repository paths carry promises at all, bounded, so you can compare the files you are about to change against it before you read anything else; when you do not know which paths your change touches, use a task-shaped keyword or exact marker lookup, or expand promises by id with `get_promise` when supplied. Never treat an unfiltered page as though it were about your change. `total` always counts every promise the filters match, not the length of the page, and `scope` says in one sentence what it is a total of. Each row carries what it takes to rule that promise out without fetching it: the repository, the paths it covers, its one-sentence claim, what protects it and why, and the latest applicable recorded Balladeer check. It also carries `seal`, which says whether this promise's sealed files are intact, broken, unknown when evidence is inconclusive, or not sealed at all, and `sealedPath` where a verifier is bound: the directory whose files are sealed, which an ordinary edit would break. Expand one with `get_promise`, and only one you could not rule out: the expansion answers with everything on the row plus the meaning, so nothing you read here is lost by expanding it.

Reviewed 2026-10-01. Read the [current operating instructions](https://app.balladeer.ai/agent) and the [tool index](/docs/mcp-reference.md). This reference does not authorize the action.

## Parameters

```json
{
  "type": "object",
  "properties": {
    "repository": {
      "description": "The repository to answer for, by id or display name. This connection is bound to exactly one repository, so naming any other is refused rather than silently answered from the bound one.",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "paths": {
      "description": "The repository-relative paths your change touches, for example src/import/preview.ts or apps/web/cart/. Repository-relative and nothing else: an absolute path off your own machine is refused rather than answered with an empty selection, because it could not overlap a scope. Naming them turns this read into a selection: the promises whose scope overlaps them, longest matching prefix first, then protected before agreed-but-unchecked, then most recently checked. Paths only. Never send prompt, transcript, or source text.",
      "minItems": 1,
      "maxItems": 100,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 200
      }
    },
    "pathPrefix": {
      "description": "Keep only promises with a surface or label that starts with this, for example src/import/ or supplier-. Matched against the scope markers, never against your working directory. Ignored when `paths` is given.",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "keyword": {
      "description": "Case-insensitive substring of the promise's title or scope markers. Not a ranked search: it filters, and everything it keeps is returned in id order.",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "unverified": {
      "description": "True keeps agreed promises with no Balladeer verifier bound. This does not say whether independent tests or CI checks cover their behavior, so never render or write these as unchecked, untested or with nothing checking them: say no Balladeer verifier.",
      "type": "boolean"
    },
    "mine": {
      "description": "Whose, from the person's point of view rather than yours: true keeps only the promises the person who connected this agent owns or agreed to, and excludes their teammates'. Use it for anything you are about to say to that person unasked; drop it when they ask what the rest of the team has promised.",
      "type": "boolean"
    },
    "brokenSinceLastSeen": {
      "description": "True keeps only the promises the person who connected this agent owns that stopped holding since they last read them, each row carrying `brokeAt`, the moment it broke. It is already scoped to that person, so it needs no `mine` beside it. Make this read once at the start of a session: say in one line that they broke, offer to fix them, and wait. Never start on one because nobody said no.",
      "type": "boolean"
    },
    "countOnly": {
      "description": "True answers with how many promises the filters match and nothing about them: no rows, no claims, no paths. Use it when the number is all you need, such as telling a person at the start of a session how many of their agreed promises have no verifier without reading any of them. It wins over every row-shaping option: with it, no row comes back.",
      "type": "boolean"
    },
    "limit": {
      "description": "Rows on this page. Default 25, ceiling 100.",
      "default": 25,
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "cursor": {
      "description": "The `nextCursor` from the previous page. Opaque: do not construct one.",
      "type": "string",
      "minLength": 1,
      "maxLength": 512
    },
    "session": {
      "description": "The session id `npx -y balladeer@latest session` prints, which stays the same for the whole of this piece of work. Pass it on every read, and write `Balladeer-Session: <it>` into the commit or pull-request body you produce, so a check that goes red can be read back against what you were told before you started.",
      "type": "string",
      "pattern": "^bs_[0-9a-f]{32}$"
    },
    "pathsFrom": {
      "description": "Where the paths you named came from: `change` for the files in front of you, `touch-map` when `npx -y balladeer@latest affected` named them out of what this repository's verifiers actually executed. Balladeer never receives the map, so this is the only way it can tell the two apart, and saying `touch-map` when the map did not name them makes a measurement out of a guess.",
      "type": "string",
      "enum": [
        "change",
        "touch-map"
      ]
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false
}
```

## Effect annotations

```json
{
  "readOnlyHint": true,
  "destructiveHint": false,
  "idempotentHint": true,
  "openWorldHint": false
}
```
