{
  "reviewedAt": "2026-10-01",
  "appSha": "e369451d01076fe1472e6aa14ed09bd86fa080bc",
  "tools": [
    {
      "name": "get_promise_context",
      "title": "Get approved behavior context for this task",
      "description": "Return a bounded, marker-matched slice of this repository's approved promises: up to 25 catalog rows and up to 10 fully expanded promises, with the true totals. Every row and every expansion here carries the same facts an index row carries, including where each promise's seal stands and the latest applicable recorded Balladeer check. This is not the whole catalog and never was safe to treat as one: read `catalogCount`, and use `list_promises` to page the index and `get_promise` to expand one by ID. Proposed and retired meaning is never returned. Do not make an unscoped startup read. When a substantive task supplies meaningful markers, use them before planning, including on the first turn of a session. Reuse the result while the task scope is unchanged. If meaningful markers are not yet known, use a task-shaped list_promises keyword lookup or a supplied promise ID rather than inventing markers or scanning the unfiltered catalog.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "markers": {
            "type": "object",
            "properties": {
              "surfaces": {
                "default": [],
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 96,
                  "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                }
              },
              "capabilities": {
                "default": [],
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 96,
                  "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                }
              },
              "events": {
                "default": [],
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 96,
                  "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                }
              },
              "labels": {
                "default": [],
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 96,
                  "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                }
              }
            },
            "additionalProperties": false
          },
          "taskDigest": {
            "description": "Optional client-computed digest. Do not send a prompt, transcript, source, path, or raw task text.",
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          }
        },
        "required": [
          "markers"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "discover_intent",
      "title": "Find existing intent relevant to this behavior",
      "description": "Before planning behavior, discover potentially relevant agreed promises and separately shared intentions and recorded promises. Describe requested behavior, known actor, trigger and conditions; distinguish unresolved choices. Use this route instead of parallel lexical lookups; IDs go directly to get_promise/get_proposal. `answer` states the outcome. With coverage.judgment \"available\", agreed/shared/recorded hold only leads judged to apply (judgment.reason says why) and adjacent near misses are context, never rules; otherwise every lead is unjudged: check it. Each lead carries only what decides applicability: title, beneficiary, trigger, non-goals and scope. Rule a lead out from those and say only that it does not apply: a promise you ruled out gives no rule, threshold, wording or approver to code, tests, README, questions or advice. For one that applies, read its rule through fullRead and validate revision/digest before use. Suggest optional coordination for concrete overlap. Recommendations stay unchosen. Explain consequential influence before acting. Check every applicable guarantee, not only listed examples or green tests. Reuse context; refresh for distinct behavior or changed/withdrawn relied-on meaning, not startup/every-turn. Shared intent is not approval or current activity. A recorded promise is intent nobody has agreed, never protected: treat it as its `use` says, and flag a teammate's that is wrong with report_promise_context_feedback flagWrong. For implementation_impact use local touch-map impactPromiseIds as dependency leads, not applicability. Repository defaults to this connection; selectors never grant access. Read coverage/semantic status: bounded misses prove no absence. Send bounded meaning, never source, credentials, raw prompts or transcripts.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "intent": {
            "description": "Brief user-requested behavior and known actor, operation, trigger and conditions; distinguish unresolved choices from established intent. Do not present recommendations as decisions or source clues as verified facts. No source code, credentials, raw prompts or transcripts.",
            "type": "string",
            "minLength": 8,
            "maxLength": 1200
          },
          "purpose": {
            "default": "behavior_overlap",
            "type": "string",
            "enum": [
              "behavior_overlap",
              "implementation_impact"
            ]
          },
          "repositoryId": {
            "description": "Defaults to this connection's repository. Selection never broadens the connection's authorization.",
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "markers": {
            "description": "Optional exact existing scope values (surfaces or labels) observed in repository metadata, previous Balladeer results, or explicitly supplied by the user. Omit when unknown; do not invent tags or copy search keywords into this field. Marker matches are retrieval leads, not proof of applicability.",
            "maxItems": 12,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          "paths": {
            "description": "Repository-relative paths the work will touch, when known (for example src/import/preview.ts). A promise whose scope covers one is always considered and never silently dropped. Paths only; they stay on Balladeer's server and are never sent to a model provider.",
            "maxItems": 20,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          "impactPromiseIds": {
            "description": "For implementation impact only: promise IDs obtained from the local verifier touch map. These are execution-dependency leads, not proof of behavioral applicability.",
            "maxItems": 20,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^prom_[a-z0-9]{8,64}$"
            }
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8
          }
        },
        "required": [
          "intent"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "list_shared_work",
      "description": "Find up to three tentative intentions explicitly shared by a human for coordination in this connection's repository. Before settling a design or changing shared behavior, use exact scope markers already known from your task, or a short task-shaped keyword over current shared titles, observable claims, triggers and explicit scope surfaces/labels when paths are unknown or return no useful match. Keywords are literal case-insensitive substrings, not semantic search; prefer a distinctive term over a whole question. If markers and keyword are both given, both must match. Empty queries cannot browse all shared work. Returns short revision-bound leads with their marker/keyword match basis, separately from agreed promises. A match is not proof of duplicate effort, contradiction, applicability, or current activity. Fetch a relevant get_proposal selectively; require coordination.sharedCurrentRevision and the same proposalDigest; an older digest is stale after an edit, but the edited successor inherits sharing until withdrawn. Original human sharer attribution does not mean that person reviewed the edited meaning; current revision provenance identifies its actual actor. A withdrawn share is no longer current. No automatic discovery of unshared drafts; this tool cannot share or agree anything.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "markers": {
            "minItems": 1,
            "maxItems": 12,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 96,
              "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
            }
          },
          "keyword": {
            "description": "Case-insensitive literal substring of a shared proposal title, observable claim, trigger, or explicit scope surface/label. Use a short task phrase, not source or a transcript. With markers, both filters must match.",
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        },
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "list_promises",
      "title": "List this repository's approved promises",
      "description": "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.",
      "inputSchema": {
        "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
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "get_promise",
      "title": "Expand one approved behavior",
      "description": "Return one current owner-approved promise by ID, with its full meaning, the name of the person who owns it, and its page. It carries every fact the index row for the same promise carries and never fewer: what protects it and why in one sentence, whether a verifier is bound, where its sealed files stand (`seal`: intact, broken, unknown when evidence is inconclusive, or none where nothing is sealed), the directory those files live in, and `lastCheckedAt`, the latest applicable recorded Balladeer check timestamp available in this read, or `unknown` when no such timestamp is available. Unprotected or no bound verifier describes Balladeer's evidence state; it does not establish that independent tests or CI checks are absent. The meaning states a commitment, not proof that the current implementation keeps it. Its owner has authority over this meaning; that does not establish required approvers for separate work. When implementing it, check every applicable guarantee against delivered behavior; success and failure cases illustrate the meaning rather than exhaust it. A case passing does not establish other guarantees in the same promise. Do not silently narrow agreed behavior to existing callers or cases; report unmet or unverified guarantees. Report what those checks exercised and what remains unverified; do not infer universal coverage or absence of tests. A broken seal is never reported as protected, whatever the last run claimed. This is the read to make whenever you have an ID: from a person, who can copy one off any promise page, from a failing check, or from the code you are working on. Ask a person for the id rather than searching for the promise they meant. A promise that is not currently holding also carries `threat`: the commit the failing run checked, the bounded reason it could not report green, and the failing cases in the agreed meaning that run was checking. Read it before you change anything, and repair the behavior rather than the test. Proposed, retired, and superseded meaning is unavailable.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "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}$"
          },
          "signal": {
            "description": "Why you are reading this promise: `named` when a person or a failing check gave you the id, `path` when an index row's paths matched your change, `touch` when `npx -y balladeer@latest affected` named it out of what its verifier actually executed, `keyword` when a text match found it. Absent reads as `named`. It is what lets somebody find out later which of these signals was worth keeping.",
            "type": "string",
            "enum": [
              "named",
              "path",
              "touch",
              "keyword"
            ]
          },
          "unattended": {
            "description": "True only when a program is reading on your behalf and no person is looking, such as a judge run before a push or a behavior map being built. An unattended read is not the person having seen the promise, so it never clears what they are still to be told about it, and it is left out of the retrieval bookkeeping. Leave it out for every read you make while working with a person.",
            "type": "boolean"
          }
        },
        "required": [
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "mark_promise_used",
      "title": "Say which promises actually mattered",
      "description": "Name the promises you cited or acted on in this session, out of the ones an index read handed you. It is the only thing that tells Balladeer which rows were worth sending: expanding a promise and then ignoring it is not the same as using it, and a row nobody uses is a row the index should have kept back. Call it once, near the end of the work, with the ids you actually used. It changes nothing about any promise, and naming none is a fine answer: say nothing rather than naming ids you did not use. An id this session was never offered comes back under `notServed` rather than being recorded.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "session": {
            "description": "The session id `npx -y balladeer@latest session` printed, the same one you passed to the reads and wrote into your commit as Balladeer-Session.",
            "type": "string",
            "pattern": "^bs_[0-9a-f]{32}$"
          },
          "promiseIds": {
            "description": "The promises you cited or acted on, at most 12, which is one selection's worth. Ids only: never a reason, a file name or anything out of your conversation.",
            "minItems": 1,
            "maxItems": 12,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^prom_[a-z0-9]{8,64}$"
            }
          }
        },
        "required": [
          "session",
          "promiseIds"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "record_session_commit",
      "title": "Tie this session's reads to the commit it wrote",
      "description": "Record that this session wrote this commit, so a check that goes red on it can be read back against what Balladeer told you before you started. Write `Balladeer-Session: <session id>` into the commit message or the pull-request body as well, for the people who read them; this call is what Balladeer itself joins on, because it never receives a commit message. Call it once per commit you author, after the commit exists. Recording the same pair twice records it once.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "session": {
            "description": "The session id `npx -y balladeer@latest session` printed for this work.",
            "type": "string",
            "pattern": "^bs_[0-9a-f]{32}$"
          },
          "commitSha": {
            "description": "The full forty-character commit SHA, exactly as `git rev-parse HEAD` prints it. An abbreviated SHA is refused: a run names the full one, and a join on a prefix is a join on a guess.",
            "type": "string",
            "pattern": "^[0-9a-f]{40}$"
          }
        },
        "required": [
          "session",
          "commitSha"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "report_promise_check",
      "title": "Record what a local check concluded about one promise",
      "description": "Record one verdict `balladeer judge` reached on this machine about one promise for one change: the commit it judged, the base it compared against, and whether the promise was kept, broken, or could not be told. Call it once for each promise the judge ruled on, with the fields from its verdict line, after the verdict is printed. It records evidence about a change and nothing more: it never changes a promise's protection, its agreed meaning, its owner or its verifier, and a kept verdict is not a passing check. A broken verdict needs the digest of the reproduction the runner ran both ways; it tells the promise's owner that a change would break their promise, and a later kept verdict on a commit never judged broken clears that. Recording the same promise, commit and verdict twice records it once. Send only these fields: never code, commands, output, file names or anything from the conversation.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "promiseId": {
            "description": "The promise the judge ruled on, exactly as its verdict line names it.",
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "sourceSha": {
            "description": "The full forty-character commit the judge ran on: the verdict's `head`. An abbreviated SHA is refused.",
            "type": "string",
            "pattern": "^[0-9a-f]{40}$"
          },
          "baseSha": {
            "description": "The full forty-character commit it compared against: the verdict's `base`. It must differ from sourceSha.",
            "type": "string",
            "pattern": "^[0-9a-f]{40}$"
          },
          "verdict": {
            "description": "The judge's verdict, unchanged. kept only after every passing case was exercised against the change; broken only with a reproduction that failed on the change and passed on its base; anything else is could_not_tell. Never upgrade one.",
            "type": "string",
            "enum": [
              "kept",
              "broken",
              "could_not_tell"
            ]
          },
          "reproductionDigest": {
            "description": "The digest the runner recorded for the reproduction it ran both ways (`reproduction.digest`), as sha256 and 64 hex characters. Required with broken. The command itself never leaves this machine.",
            "type": "string",
            "pattern": "^sha256:[0-9a-f]{64}$"
          },
          "briefRevision": {
            "description": "The judge brief revision the verdict names, such as judge-brief/v1.",
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{0,47}\\/v[1-9][0-9]{0,5}$"
          },
          "sessionId": {
            "description": "The session id `npx -y balladeer@latest session` printed, when the judge ran inside a session that has one. Omit it otherwise.",
            "type": "string",
            "pattern": "^bs_[0-9a-f]{32}$"
          },
          "client": {
            "description": "What ran the judge: claude or codex for that agent's hook, git for a pre-push hook, other for anything else.",
            "type": "string",
            "enum": [
              "claude",
              "codex",
              "git",
              "other"
            ]
          }
        },
        "required": [
          "promiseId",
          "sourceSha",
          "baseSha",
          "verdict",
          "briefRevision",
          "client"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_promise",
      "title": "Propose a behavior for owner review",
      "description": "File one pending proposal after an explicit request to record, or after you made a specific capture offer and the reply accepted it: a yes or a generic go-ahead permits recording. A decline, carrying on without answering, no reply at all, or a request to hear more first does not. Never file what you did not offer. Afterwards tell the person in one line that you recorded it, with the review link. Say who the behavior applies to and who it does not, in preconditions and nonGoals, from what the person stated or the work shows. Use context first. Ask a separate question only for a consequential choice the scoped offer cannot resolve. An earlier unanswered question is not itself a blocker if later context or acceptance resolves it. Never guess a still-unresolved choice or broaden the confirmed behavior. Preserve actual human words in saidWords; if you worded the offer, record your offer and their literal acceptance separately in explicitIntent, never as an invented human quote. A rule you chose while building, and the person only approved as code, is yours: do not offer to record it as theirs. Send wrongOutcome as a concrete failure of the requested or confirmed behavior; it is refused without one. Surface concrete conflicts with relevant agreed promises before writing. No outstanding questions are stored and no automatic conflict gate runs. This never approves meaning, activates a verifier, or enters CI/retrieval; a named person still agrees the meaning. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "True only for an explicit request to record, or a yes or generic go-ahead to an identifiable scoped capture offer you made. Without such an offer, a factual answer or permission to implement is not permission to file. This is agent-reported capture permission, not named-owner agreement.",
            "type": "boolean",
            "const": true
          },
          "source": {
            "type": "string",
            "enum": [
              "protect_behavior",
              "incident_closure",
              "release_acceptance"
            ]
          },
          "explicitIntent": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "meaning": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "oneSentenceOutcome": {
                "description": "The whole promise in one sentence a person could say to a customer, at most 160 characters. Write it in their words, and never as a restatement of the name.",
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "beneficiary": {
                "description": "Who relies on this behavior, named the way a teammate would: a buyer, a promise owner, a person who installed the CLI.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "trigger": {
                "description": "The situation that sets this behavior off, in plain words a teammate would use for the work that meets it, such as 'a buyer edits an approved order'. Later work finds this promise by its trigger and beneficiary: name the situation in the software, not the product.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "preconditions": {
                "description": "What must be true for this behavior to apply, one condition each: which customers, accounts or plans; which role or actor; which environment, region or locale; which feature flag or rollout stage; which versions; from or until when. Write only limits the person stated or the work plainly shows. Never invent one, and leave the list empty when the behavior applies to everyone.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "observableOutcome": {
                "type": "string",
                "minLength": 1,
                "maxLength": 8000
              },
              "allowedVariations": {
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "nonGoals": {
                "description": "Who and what this promise does not cover, one each: the customers, roles, environments or situations a reader might wrongly assume are included. Later work is ruled in or out by these.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "passingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "failingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "refactorExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "saidWords": {
                "description": "The person's own sentence the claim was drawn from, quoted exactly. Never a paraphrase, and never composed.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "scope": {
                "type": "object",
                "properties": {
                  "repositoryId": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "surfaces": {
                    "description": "One or more real repository-relative paths or one-token capability markers. Reuse an existing marker when known. For a new capability the person named, format its words as a hyphenated marker yourself: for example, supplier import becomes supplier-import. This is a descriptive marker, not a registered identifier; do not ask the person to supply its format. Use a file path only when you know it exists in this repository. Never invent paths or repository IDs; get the repositoryId from get_promise_setup.",
                    "minItems": 1,
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  },
                  "labels": {
                    "default": [],
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  }
                },
                "required": [
                  "repositoryId",
                  "surfaces"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "title",
              "beneficiary",
              "trigger",
              "preconditions",
              "observableOutcome",
              "allowedVariations",
              "nonGoals",
              "passingExamples",
              "failingExamples",
              "scope"
            ],
            "additionalProperties": false
          },
          "unresolvedQuestions": {
            "default": [],
            "maxItems": 0,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            }
          },
          "proposedOwnerId": {
            "description": "Membership ID of the person who should be asked to agree to this meaning, from the members list on get_promise_setup. It must name an active member of this workspace; a name that is not on that list is refused and the refusal says who is. Omit it when the person did not name an owner: the person who connected this agent will own it unless you name someone else.",
            "type": "string",
            "maxLength": 2000
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "wrongOutcome": {
            "description": "Required concrete failure of the behavior the person requested or confirmed in a scoped capture offer, for example \"a second payment must not go out\". Preserve their failure wording when supplied; otherwise derive the bounded failure without adding guarantees or pretending they dictated it. Keep literal human source in saidWords and the offer/acceptance in explicitIntent. Refused when missing.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "leastSure": {
            "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "recordingBasis": {
            "description": "State truthfully how the person answered before you recorded: asked (they asked you to record it), yes (they said yes to your specific offer), go_ahead (a generic go-ahead to the offer), or carried_on (they carried on without answering). It is stored on the proposal and shown to its owner.",
            "type": "string",
            "enum": [
              "asked",
              "yes",
              "go_ahead",
              "carried_on"
            ]
          }
        },
        "required": [
          "explicitHumanAction",
          "source",
          "explicitIntent",
          "meaning",
          "confidence",
          "wrongOutcome"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "update_proposal",
      "title": "Change a proposal you filed",
      "description": "Rewrite parts of a proposal this connection filed, while nobody has agreed to it yet. Name only the fields you are changing; everything you leave out is kept exactly as it stands. Refused once a named person has agreed to, rejected, or merged the proposal, and refused outright on a proposal a different connection filed: agreed meaning changes only through a revision that person signs off. Clarify material changes with the person before writing, and incorporate their answer in the meaning and cases. Outstanding questions and question retraction are retired; do not send them.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal this connection filed, from propose_promise.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "reason": {
            "description": "Why you are changing it, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "explicitIntent": {
            "description": "The actual request or the precise agent-worded offer and literal human acceptance, attributed separately. Replaces the stored intent; never invent a human quote.",
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "meaning": {
            "description": "The whole meaning replaces the stored object: omitted nested fields are not kept. Read get_proposal's complete meaning, copy it, and change only what the person asked to change. Retain every unchanged field, including original claim and case saidWords; do not rebuild from presentation fields. When a correction makes an existing quote misleading, replace it with an exact contiguous span from one actual human message supporting the corrected claim or case. Never stitch separate spans, paraphrase inside saidWords, or silently drop quotes while rewriting cases. For a newly derived or corrected case with no literal supporting human span, omit its optional quote rather than invent one, and preserve the confirmed interpretation and acceptance in explicitIntent. Do not drop unchanged quotes. Existing absent quotes on historical or noncapture meanings need not be invented. For a title-only edit, change only meaning.title in the complete copied object; keep every other field exactly. This field does not change consent or approval.",
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "oneSentenceOutcome": {
                "description": "The whole promise in one sentence a person could say to a customer, at most 160 characters. Write it in their words, and never as a restatement of the name.",
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "beneficiary": {
                "description": "Who relies on this behavior, named the way a teammate would: a buyer, a promise owner, a person who installed the CLI.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "trigger": {
                "description": "The situation that sets this behavior off, in plain words a teammate would use for the work that meets it, such as 'a buyer edits an approved order'. Later work finds this promise by its trigger and beneficiary: name the situation in the software, not the product.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "preconditions": {
                "description": "What must be true for this behavior to apply, one condition each: which customers, accounts or plans; which role or actor; which environment, region or locale; which feature flag or rollout stage; which versions; from or until when. Write only limits the person stated or the work plainly shows. Never invent one, and leave the list empty when the behavior applies to everyone.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "observableOutcome": {
                "type": "string",
                "minLength": 1,
                "maxLength": 8000
              },
              "allowedVariations": {
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "nonGoals": {
                "description": "Who and what this promise does not cover, one each: the customers, roles, environments or situations a reader might wrongly assume are included. Later work is ruled in or out by these.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "passingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "failingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "refactorExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "saidWords": {
                "description": "The person's own sentence the claim was drawn from, quoted exactly. Never a paraphrase, and never composed.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "scope": {
                "type": "object",
                "properties": {
                  "repositoryId": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "surfaces": {
                    "description": "One or more real repository-relative paths or one-token capability markers. Reuse an existing marker when known. For a new capability the person named, format its words as a hyphenated marker yourself: for example, supplier import becomes supplier-import. This is a descriptive marker, not a registered identifier; do not ask the person to supply its format. Use a file path only when you know it exists in this repository. Never invent paths or repository IDs; get the repositoryId from get_promise_setup.",
                    "minItems": 1,
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  },
                  "labels": {
                    "default": [],
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  }
                },
                "required": [
                  "repositoryId",
                  "surfaces"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "title",
              "beneficiary",
              "trigger",
              "preconditions",
              "observableOutcome",
              "allowedVariations",
              "nonGoals",
              "passingExamples",
              "failingExamples",
              "scope"
            ],
            "additionalProperties": false
          },
          "unresolvedQuestions": {
            "description": "Retired. Omit this field; legacy callers may send an empty array.",
            "maxItems": 8,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            }
          },
          "retractQuestions": {
            "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
            "minItems": 1,
            "maxItems": 8,
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 32
            }
          },
          "retractReason": {
            "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "proposedOwnerId": {
            "description": "A different person to ask, from the members list on get_promise_setup. A name that is not on that list is refused and the refusal says who is. Refused once a person has routed this proposal to somebody: handing it on is then theirs.",
            "type": "string",
            "maxLength": 2000
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        },
        "required": [
          "proposalId",
          "reason"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "add_case",
      "title": "Add one case to a proposal you filed",
      "description": "Append one case to a proposal this connection filed: a situation and what must happen in it, or what must never happen. The case joins the end of its list, so the numbers a person is already reading do not move. Same limits as update_proposal: only your own proposal, and only while nobody has decided it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal this connection filed, from propose_promise.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "reason": {
            "description": "Why you are changing it, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "kind": {
            "description": "Which numbered list: passing is what must keep working, failing is what must be caught.",
            "type": "string",
            "enum": [
              "passing",
              "failing"
            ]
          },
          "label": {
            "description": "What this case is, in a few words. It is the row's name on the page.",
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "setup": {
            "description": "The situation, in the words the person used for it.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "expectedOutcome": {
            "description": "What must happen. On a failing case write what must NOT happen, in those words: \"a second charge must not appear\", never \"a second charge appears\".",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        },
        "required": [
          "proposalId",
          "reason",
          "kind",
          "label",
          "setup",
          "expectedOutcome"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "remove_case",
      "title": "Remove one case from a proposal you filed",
      "description": "Take one numbered case out of a proposal this connection filed. Name the number the review page shows, so P2 is caseNumber 2 of the passing list. A promise has to say both what must happen and what must never happen, so the last case in either list cannot be removed. Same limits as update_proposal.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal this connection filed, from propose_promise.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "reason": {
            "description": "Why you are changing it, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "kind": {
            "description": "Which numbered list: passing is what must keep working, failing is what must be caught.",
            "type": "string",
            "enum": [
              "passing",
              "failing"
            ]
          },
          "caseNumber": {
            "description": "The number on the review page, counting from 1 within its own list.",
            "type": "integer",
            "minimum": 1,
            "maximum": 12
          }
        },
        "required": [
          "proposalId",
          "reason",
          "kind",
          "caseNumber"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "withdraw_proposal",
      "title": "Take back a proposal you filed",
      "description": "Withdraw a proposal this connection filed, when the person says they do not want it after all or you filed it in error. The proposal stays readable and says it was withdrawn; nobody can agree to it afterwards. This is not a rejection: rejecting is a named person deciding against meaning they read. Refused once anybody has decided it, and refused on a proposal a different connection filed. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal this connection filed, from propose_promise.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "reason": {
            "description": "Why you are changing it, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        },
        "required": [
          "proposalId",
          "reason"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "report_seam_offer",
      "title": "Record that you offered, and what came back, or that you dropped one",
      "description": "Record one offer you made at a seam and its answer: yes, no, or silent when the reply did not address it or nobody answered. This files nothing, proposes nothing, and stores no word of what anybody said. Call it every time you offer, including when the answer was no, because a no is the reading that says the offers are landing at the wrong moments. Never record an offer you did not make. Use kind 'drop' for the other outcome: an entry you were holding that the work went on to contradict, so you never offered it at all. Report drops when the work settles, one call each, never the moment the build disagrees with an entry: nothing about this is worth interrupting the task for.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "identity": {
            "description": "For reliable accounting, assign one UUID and the ISO startedAt for the offer (first reports accepted up to seven days later). Reuse both on retries and later answers. To change an answer, include previousAnswer with the last reported answer; a conflict requires checking your report, not inventing a new offer. After propose_promise actually succeeds, include its proposalId on the yes or silent report to link the persisted proposal; a no never carries one. A reported answer is agent-reported capture permission, never independently verified consent or owner agreement. Omit identity only for legacy unlinked reporting. Identity is refused for drops.",
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "previousAnswer": {
                "type": "string",
                "enum": [
                  "yes",
                  "no",
                  "silent"
                ]
              },
              "proposalId": {
                "type": "string",
                "pattern": "^cand_[a-z0-9]{8,64}$"
              }
            },
            "required": [
              "id",
              "startedAt"
            ],
            "additionalProperties": false
          },
          "seamKind": {
            "description": "The kind of conversation this happened in. Nothing here describes the rule itself.",
            "type": "string",
            "enum": [
              "new_feature",
              "bug_fix",
              "refactor",
              "incident_closure",
              "config_or_copy",
              "review_verdict",
              "planning",
              "explicit_ask"
            ]
          },
          "kind": {
            "description": "'offer' when you put it to the person, which is the default and needs an answer. 'drop' when you were holding an entry and the work contradicted it, so nothing was ever put to anybody: leave the answer out.",
            "default": "offer",
            "type": "string",
            "enum": [
              "offer",
              "drop"
            ]
          },
          "answer": {
            "description": "What the reply said about recording, whatever you then did: yes if they asked you to keep it, no if they declined, silent if the reply did not address the offer (a generic go-ahead, carrying on with the work) or nobody answered. Required on an offer, and refused on a drop, because nobody answered something you never said.",
            "type": "string",
            "enum": [
              "yes",
              "no",
              "silent"
            ]
          }
        },
        "required": [
          "seamKind"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "get_proposal",
      "title": "Read one proposal whole",
      "description": "Read one proposal by id, including complete canonical meaning, status, owner, cases and historical reasoning. Describe it plainly. To edit, copy the complete meaning and change only what the person requested, preserving quotes and unchanged scope and cases. Clarify material ambiguity before writing. There is no stored question workflow or question-based agreement gate. A proposal still requires its named owner's agreement.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal id, as it appears on its review page and in a propose answer.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "proposalId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "resolve_question",
      "title": "Retired question command",
      "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "proposalId": {
            "description": "The proposal this connection filed, from propose_promise or get_proposal.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "questionNumber": {
            "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
            "type": "integer",
            "minimum": 1,
            "maximum": 8
          },
          "answer": {
            "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          }
        },
        "required": [
          "proposalId",
          "questionNumber",
          "answer"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_semantic_revision",
      "title": "Propose an intentional meaning change",
      "description": "Propose changed customer meaning for named-owner review. The current active revision remains authoritative until the new agreed revision is independently qualified and atomically activated.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "Must be true only when the person explicitly asked to protect or change this behavior. Ambient inference is forbidden.",
            "type": "boolean",
            "const": true
          },
          "reason": {
            "description": "Why, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "meaning": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "oneSentenceOutcome": {
                "description": "The whole promise in one sentence a person could say to a customer, at most 160 characters. Write it in their words, and never as a restatement of the name.",
                "type": "string",
                "minLength": 1,
                "maxLength": 160
              },
              "beneficiary": {
                "description": "Who relies on this behavior, named the way a teammate would: a buyer, a promise owner, a person who installed the CLI.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "trigger": {
                "description": "The situation that sets this behavior off, in plain words a teammate would use for the work that meets it, such as 'a buyer edits an approved order'. Later work finds this promise by its trigger and beneficiary: name the situation in the software, not the product.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "preconditions": {
                "description": "What must be true for this behavior to apply, one condition each: which customers, accounts or plans; which role or actor; which environment, region or locale; which feature flag or rollout stage; which versions; from or until when. Write only limits the person stated or the work plainly shows. Never invent one, and leave the list empty when the behavior applies to everyone.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "observableOutcome": {
                "type": "string",
                "minLength": 1,
                "maxLength": 8000
              },
              "allowedVariations": {
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "nonGoals": {
                "description": "Who and what this promise does not cover, one each: the customers, roles, environments or situations a reader might wrongly assume are included. Later work is ruled in or out by these.",
                "maxItems": 24,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "passingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "failingExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "refactorExamples": {
                "minItems": 1,
                "maxItems": 12,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 160
                    },
                    "setup": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "expectedOutcome": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    },
                    "saidWords": {
                      "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "required": [
                    "label",
                    "setup",
                    "expectedOutcome"
                  ],
                  "additionalProperties": false
                }
              },
              "saidWords": {
                "description": "The person's own sentence the claim was drawn from, quoted exactly. Never a paraphrase, and never composed.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "scope": {
                "type": "object",
                "properties": {
                  "repositoryId": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "surfaces": {
                    "description": "One or more real repository-relative paths or one-token capability markers. Reuse an existing marker when known. For a new capability the person named, format its words as a hyphenated marker yourself: for example, supplier import becomes supplier-import. This is a descriptive marker, not a registered identifier; do not ask the person to supply its format. Use a file path only when you know it exists in this repository. Never invent paths or repository IDs; get the repositoryId from get_promise_setup.",
                    "minItems": 1,
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  },
                  "labels": {
                    "default": [],
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 96,
                      "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                    }
                  }
                },
                "required": [
                  "repositoryId",
                  "surfaces"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "title",
              "beneficiary",
              "trigger",
              "preconditions",
              "observableOutcome",
              "allowedVariations",
              "nonGoals",
              "passingExamples",
              "failingExamples",
              "scope"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "explicitHumanAction",
          "reason",
          "promiseId",
          "meaning"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_verifier_repair",
      "title": "Propose a verifier-only repair",
      "description": "Propose new customer-local verifier identities while keeping the approved semantic digest exact. This cannot revise meaning or activate protection.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "Must be true only when the person explicitly asked to protect or change this behavior. Ambient inference is forbidden.",
            "type": "boolean",
            "const": true
          },
          "reason": {
            "description": "Why, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "approvedSemanticDigest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "packageDigest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "verifierDigest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "fixturesDigest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          },
          "workflowDigest": {
            "type": "string",
            "pattern": "^sha256:[a-f0-9]{64}$"
          }
        },
        "required": [
          "explicitHumanAction",
          "reason",
          "promiseId",
          "approvedSemanticDigest",
          "packageDigest",
          "verifierDigest",
          "fixturesDigest",
          "workflowDigest"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_supersession",
      "title": "Propose replacing a promise",
      "description": "Propose that one approved promise is replaced by another, for the named meaning owner to decide. Name an existing successor promise, or describe the successor's meaning and it is filed as a proposal the owner must agree to first. This never retires anything.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "Must be true only when the person explicitly asked to protect or change this behavior. Ambient inference is forbidden.",
            "type": "boolean",
            "const": true
          },
          "reason": {
            "description": "Why, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "successorPromiseId": {
            "description": "An existing agreed promise in this repository that replaces this one.",
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "successorMeaning": {
            "description": "The successor's meaning, when no promise holds it yet. It is filed as a proposal; a named person must agree to it before this promise can point at it.",
            "type": "object",
            "properties": {
              "meaning": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160
                  },
                  "oneSentenceOutcome": {
                    "description": "The whole promise in one sentence a person could say to a customer, at most 160 characters. Write it in their words, and never as a restatement of the name.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160
                  },
                  "beneficiary": {
                    "description": "Who relies on this behavior, named the way a teammate would: a buyer, a promise owner, a person who installed the CLI.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "trigger": {
                    "description": "The situation that sets this behavior off, in plain words a teammate would use for the work that meets it, such as 'a buyer edits an approved order'. Later work finds this promise by its trigger and beneficiary: name the situation in the software, not the product.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "preconditions": {
                    "description": "What must be true for this behavior to apply, one condition each: which customers, accounts or plans; which role or actor; which environment, region or locale; which feature flag or rollout stage; which versions; from or until when. Write only limits the person stated or the work plainly shows. Never invent one, and leave the list empty when the behavior applies to everyone.",
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "observableOutcome": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 8000
                  },
                  "allowedVariations": {
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "nonGoals": {
                    "description": "Who and what this promise does not cover, one each: the customers, roles, environments or situations a reader might wrongly assume are included. Later work is ruled in or out by these.",
                    "maxItems": 24,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2000
                    }
                  },
                  "passingExamples": {
                    "minItems": 1,
                    "maxItems": 12,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 160
                        },
                        "setup": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "expectedOutcome": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "saidWords": {
                          "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        }
                      },
                      "required": [
                        "label",
                        "setup",
                        "expectedOutcome"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "failingExamples": {
                    "minItems": 1,
                    "maxItems": 12,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 160
                        },
                        "setup": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "expectedOutcome": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "saidWords": {
                          "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        }
                      },
                      "required": [
                        "label",
                        "setup",
                        "expectedOutcome"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "refactorExamples": {
                    "minItems": 1,
                    "maxItems": 12,
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 160
                        },
                        "setup": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "expectedOutcome": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        },
                        "saidWords": {
                          "description": "The person's own sentence this case was drawn from, quoted exactly. Leave it out rather than composing one.",
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 2000
                        }
                      },
                      "required": [
                        "label",
                        "setup",
                        "expectedOutcome"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "saidWords": {
                    "description": "The person's own sentence the claim was drawn from, quoted exactly. Never a paraphrase, and never composed.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "scope": {
                    "type": "object",
                    "properties": {
                      "repositoryId": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "surfaces": {
                        "description": "One or more real repository-relative paths or one-token capability markers. Reuse an existing marker when known. For a new capability the person named, format its words as a hyphenated marker yourself: for example, supplier import becomes supplier-import. This is a descriptive marker, not a registered identifier; do not ask the person to supply its format. Use a file path only when you know it exists in this repository. Never invent paths or repository IDs; get the repositoryId from get_promise_setup.",
                        "minItems": 1,
                        "maxItems": 24,
                        "type": "array",
                        "items": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 96,
                          "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                        }
                      },
                      "labels": {
                        "default": [],
                        "maxItems": 24,
                        "type": "array",
                        "items": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 96,
                          "pattern": "^[A-Za-z0-9_:.@/[\\]-]+$"
                        }
                      }
                    },
                    "required": [
                      "repositoryId",
                      "surfaces"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "title",
                  "beneficiary",
                  "trigger",
                  "preconditions",
                  "observableOutcome",
                  "allowedVariations",
                  "nonGoals",
                  "passingExamples",
                  "failingExamples",
                  "scope"
                ],
                "additionalProperties": false
              },
              "unresolvedQuestions": {
                "default": [],
                "maxItems": 0,
                "type": "array",
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 2000
                }
              },
              "proposedOwnerId": {
                "description": "Membership ID of the person who should be asked to agree to the successor's meaning, from the members list on get_promise_setup. A name that is not on that list is refused and the refusal says who is. Omit it when the person did not name an owner.",
                "type": "string",
                "maxLength": 2000
              },
              "confidence": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "wrongOutcome": {
                "description": "What going wrong looks like for the successor, in the person's own words, as a must-not. Refused when it is missing, exactly as it is for propose_promise.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "leastSure": {
                "description": "Balladeer no longer stores outstanding questions. Before writing, ask the person only about ambiguity that changes the commitment, then put their answer in the meaning and cases. Do not invent an answer or change agreed meaning. Nothing was recorded; refresh your Balladeer instructions before retrying.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              }
            },
            "required": [
              "meaning",
              "confidence"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "explicitHumanAction",
          "reason",
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_retirement",
      "title": "Propose retiring a promise",
      "description": "Propose that an approved promise is finished and should stop being protected, for the named meaning owner to decide. This never retires it.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "Must be true only when the person explicitly asked to protect or change this behavior. Ambient inference is forbidden.",
            "type": "boolean",
            "const": true
          },
          "reason": {
            "description": "Why, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "explicitHumanAction",
          "reason",
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "propose_ownership_transfer",
      "title": "Propose a new meaning owner",
      "description": "Propose that a promise should belong to a different named person, from the members list on get_promise_setup. Ownership moves only when the owner or an administrator proposes the transfer and the new owner accepts it; this does neither.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "explicitHumanAction": {
            "description": "Must be true only when the person explicitly asked to protect or change this behavior. Ambient inference is forbidden.",
            "type": "boolean",
            "const": true
          },
          "reason": {
            "description": "Why, in the person's own words where they gave you any. It is recorded on the proposal and read by whoever decides it. Name nobody in it: Balladeer records who connected you as the person behind this act, and a name from git, a commit or a file is not the person in the conversation. Say \"the person\" or quote their words.",
            "type": "string",
            "minLength": 1,
            "maxLength": 2000
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "newOwnerId": {
            "description": "Membership ID of the person who should own this promise, from the members list on get_promise_setup. A name that is not on that list is refused and the refusal says who is.",
            "type": "string",
            "maxLength": 2000
          }
        },
        "required": [
          "explicitHumanAction",
          "reason",
          "promiseId",
          "newOwnerId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "prepare_qualification",
      "title": "Prepare the one-time qualification setup for one promise",
      "description": "Mint the one-time qualification setup for a promise whose meaning a named person has agreed and which has no qualified Balladeer verifier yet, so you can build its verifier and get it protected. You need no sign-off and no code for this: agreeing the meaning was the person's act, and building the check that proves it is yours. Qualification proves itself in CI, so nobody activates anything. What comes back is the same packet the promise page downloads: write `packet.qualificationMetadata` byte for byte to `metadataPath` with no other keys, author the acceptance package at `packagePath` from `packet.revision.meaning` without changing its digest, build and seal the verifier, and push to the default branch. Build it in the shape your instructions describe under \"The shape of a check that lasts\": against the behavior at the boundary the promise names, through the product's own entry point, asserting only what the agreed cases say, with a refactor control that is a genuine restructuring and a known-bad control that is the failure the owner named. A check that copies the product's logic or pins its exact output goes red on the next rewrite and catches nothing. The packet carries no credential and grants no access. It is one-time: while an unspent packet exists this refuses and names who prepared it and when, and `again` mints a replacement that INVALIDATES the earlier one, so a run publishing the earlier identities afterwards is refused. Both are written into the promise's history with you as the actor and the person who connected you as the carrier.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "again": {
            "description": "Prepare a replacement even though an unspent packet exists, invalidating that earlier packet. Only when the person asked for one, or the earlier packet is genuinely lost: an agent that sets this to get past a refusal invalidates a packet somebody may be halfway through using.",
            "default": false,
            "type": "boolean"
          }
        },
        "required": [
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "request_owner_signoff",
      "title": "Ask the owner to sign off one act",
      "description": "Return the page to give the promise's named owner so they can sign off ONE act, and the name of the person who has to be the one to sign it. This asks; it never acts, and it never signs. Call it when the person you are talking to has asked for an act you do not have a code for, or when a code was refused.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "act": {
            "description": "The one act to be signed off. A sign-off covers this act and no other.",
            "type": "string",
            "enum": [
              "agree_meaning",
              "activate_protection",
              "revise_meaning",
              "supersede",
              "retire",
              "transfer_owner",
              "disconnect_repository"
            ]
          },
          "proposalId": {
            "description": "The proposal, when the act is agreeing to its meaning.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "candidateId": {
            "description": "Deprecated alias for `proposalId`, still accepted so a caller written against the older tool keeps working. Send `proposalId`; sending both names at once is refused.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "promiseId": {
            "description": "The promise, for every act on one.",
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "act"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "agree_promise_meaning",
      "title": "Agree a proposal's meaning, on the owner's sign-off",
      "description": "Turn one proposal into a promise, because its named owner read it and agreed to it. The promise enters agreed-but-unprotected: it is context, not protection, until a verifier qualifies. Protection then starts on its own, with Balladeer recorded as the actor and the qualification receipt as the cause, so do not offer to turn it on for anybody. When the person you are talking to is the proposal's named owner and this is their own connection, they can agree here: show them the proposal in full from get_proposal, ask plainly whether they agree its meaning, and on a clear yes call this with shownDigest and their quoted words; no code and no browser. Then, because building the check is your work and needs no further sign-off, prepare_qualification and build the verifier unless they said not to. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "shownDigest": {
            "description": "Agreeing from the conversation, with no code: the `proposalDigest` from the get_proposal answer you showed the person, verbatim. Only the person this connection belongs to can agree this way, and only when they are the proposal's named owner; put their exact words in signOff.quotedWords and their membership id or email in signOff.who. The server refuses if the proposal changed since you showed it. Anyone else, or any other act, needs the sign-off page and its code.",
            "type": "string",
            "pattern": "^(?:sha256:)?[a-f0-9]{64}$"
          },
          "proposalId": {
            "description": "The proposal whose meaning the owner agreed to.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          },
          "candidateId": {
            "description": "Deprecated alias for `proposalId`, still accepted so a caller written against the older tool keeps working. Send `proposalId`; sending both names at once is refused.",
            "type": "string",
            "pattern": "^cand_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "signOff"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "activate_promise_protection",
      "title": "Protection starts on its own",
      "description": "No longer an act. Protection starts automatically the moment a verifier qualifies for a promise whose meaning is agreed, so nobody activates anything and there is nothing to sign off. This tool changes nothing and is kept for one release so an agent on older instructions gets a clear answer; it will be removed after that. If a promise you expected to be protected is not, read the promise to see which evidence or lifecycle state prevents protection.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "receiptId": {
            "description": "Ignored. Kept so an older caller's arguments still parse.",
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        },
        "required": [
          "signOff",
          "grantCode",
          "promiseId",
          "receiptId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "revise_promise_meaning",
      "title": "Agree a revised meaning, on the owner's sign-off",
      "description": "Agree the revision waiting on one promise, because its named owner read the new wording and signed it off. File the revision first with propose_semantic_revision: the sign-off page shows the person the exact wording they are agreeing to, so there has to be one there to read. The meaning currently in force stays authoritative until a verifier qualifies against the new one and protection is activated on it, which is why a deliberate revision can read as a regression in between. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "signOff",
          "grantCode",
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "supersede_promise",
      "title": "Replace one promise with another, on the owner's sign-off",
      "description": "Retire one promise onto the promise that replaces it, because its named owner signed that off. The successor has to be an existing promise in this repository whose meaning a named person has already agreed; a proposal nobody has agreed to yet is not a successor. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "successorPromiseId": {
            "description": "The agreed promise in this repository that replaces it.",
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "signOff",
          "grantCode",
          "promiseId",
          "successorPromiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "retire_promise",
      "title": "Retire a promise, on the owner's sign-off",
      "description": "Stop protecting one promise, because its named owner signed that off. Its history stays readable and its meaning leaves planning context; nothing about the customer's own tests changes. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "signOff",
          "grantCode",
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "transfer_promise_owner",
      "title": "Hand a promise to a new owner, on the current owner's sign-off",
      "description": "Offer one promise to a different named person, because its current owner signed that off. Ownership does not move yet: the person receiving it accepts in their own browser, and until they do the current owner still owns it. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them. When a result includes forThePerson, tell the person what it says in your own words, in about one line.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          },
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          },
          "newOwnerId": {
            "description": "Membership ID of the person who should own it, from the members list on get_promise_setup. A name that is not on that list is refused and the refusal says who is.",
            "type": "string",
            "maxLength": 2000
          }
        },
        "required": [
          "signOff",
          "grantCode",
          "promiseId",
          "newOwnerId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "disconnect_repository",
      "title": "Disconnect this repository, on an administrator's sign-off",
      "description": "Disconnect the repository this connection is bound to, because a workspace administrator signed that off. Its promises stop being checked and every credential issued for it, including this one, stops working. The tests, fixtures, and workflow in the repository are the customer's and keep running exactly as they do now. Obtain explicit sign-off for THIS act in THIS conversation before calling this. Never call it on your own initiative, and never on a person's general approval of some earlier act. Quote the person's own words in signOff.quotedWords, not your summary of them.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "signOff": {
            "type": "object",
            "properties": {
              "who": {
                "description": "The person who signed this off: their membership ID from get_promise_setup, or the email address they signed in with. It must be the named owner of the thing being acted on; anybody else is refused.",
                "type": "string",
                "minLength": 1,
                "maxLength": 320
              },
              "quotedWords": {
                "description": "What the person actually said, in their words. Not your paraphrase, not a restatement of the act, and never words you supplied for them to agree to.",
                "type": "string",
                "minLength": 1,
                "maxLength": 2000
              },
              "at": {
                "description": "When they said it, in this conversation.",
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              }
            },
            "required": [
              "who",
              "quotedWords",
              "at"
            ],
            "additionalProperties": false
          },
          "grantCode": {
            "description": "The one-time code the person was shown after they signed off on the sign-off page. Ask them for it; you cannot produce one, and a wrong or missing code is refused with the link to hand them rather than something to retry.",
            "type": "string",
            "minLength": 8,
            "maxLength": 200
          }
        },
        "required": [
          "signOff",
          "grantCode"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "invite_teammate",
      "title": "Ask an administrator to invite one teammate",
      "description": "Ask for one person to be brought into this workspace by email. This does not send anything: it answers with the page to give a workspace administrator, with the address already filled in for them to read, and with whether that address is already here. Inviting somebody is an administrator's act, so the person reads the address in their own browser and presses Send. Call it when somebody asks you to add a teammate, give them the page it returns, and tell them nothing has been sent yet. Call it again afterwards to find out whether the invitation is now open.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "email": {
            "description": "The person's email address, exactly as the person you are talking to gave it to you. Never one you worked out. It must be an address: anything else is refused rather than filled into an administrator's invitation box.",
            "type": "string",
            "minLength": 3,
            "maxLength": 320,
            "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
          },
          "role": {
            "description": "A viewer can read this workspace. A contributor can also propose promises. An administrator can also change setup and invite people. Ask if they did not say; contributor is the ordinary answer.",
            "default": "contributor",
            "type": "string",
            "enum": [
              "contributor",
              "viewer",
              "administrator"
            ]
          }
        },
        "required": [
          "email"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "list_promise_proposals",
      "title": "List lifecycle proposals on one promise",
      "description": "Return the open lifecycle proposal on one promise, if there is one, and what people decided about recent ones. This is how an agent learns the outcome; Balladeer sends no notification.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "promiseId": {
            "type": "string",
            "pattern": "^prom_[a-z0-9]{8,64}$"
          }
        },
        "required": [
          "promiseId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "list_promise_attention",
      "title": "List what this repository is waiting on people for",
      "description": "Page what is waiting on a named person in this repository: proposals nobody has read, promises with no bound Balladeer verifier, evidence that could not decide, and revisions and repairs waiting on an owner. Each row carries the one sentence the promise or proposal claims, the repository, the reason in words, and the name of the person it is waiting on, so you can say what is waiting without expanding anything. `total` counts findings across current subjects, including unresolved recorded findings; one subject can have several findings. It does not count only the rows on this page; `truncated` says whether rows were left out, and `nextCursor` continues. Default 20 rows, ceiling 100. It exposes no customer source code, no logs and no raw output. Read it once when somebody asks what is outstanding, say the count and name the first few; never walk every page to summarise a repository. With `countOnly` and `mine` it answers three numbers for the person this connection belongs to and no row: proposals waiting for their agreement, agreed promises with no verifier, and promises of theirs that broke, plus two about promises they recorded and nobody agreed: how many reached teammates' agents this week and how many a teammate flagged as wrong (for both, the person acts with \"show proposals\"). That is the one read a session opens with.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "limit": {
            "description": "Rows on this page. Default 20, ceiling 100.",
            "default": 20,
            "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
          },
          "mine": {
            "description": "True keeps only what waits on the person this connection belongs to: their proposals to agree, their agreed promises with no verifier, their promises that broke.",
            "type": "boolean"
          },
          "countOnly": {
            "description": "True answers with counts and no row: `counts.proposalsAwaitingAgreement`, `counts.agreedPromisesUnchecked`, `counts.promisesBroken`, and with `mine` `counts.recordedPromisesSurfacedToTeammates` and `counts.recordedPromisesFlaggedWrong` (act on both with \"show proposals\"). Nothing about any proposal or promise leaves the server.",
            "type": "boolean"
          }
        },
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "report_promise_context_feedback",
      "title": "Report context relevance corrections",
      "description": "Attach explicit corrections or misses to a get_promise_context or discover_intent receipt. For discover_intent: corrected = returned leads that did not apply, confirmed = returned leads that did (proposal ids for shared and recorded leads), missed = promises that apply but were not returned, adjacent ones included. flagWrong = a teammate's recorded promise that answer served does not hold; the person who recorded it is told. Feedback is telemetry only and never suppresses future context.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "receiptId": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "correctedPromiseIds": {
            "default": [],
            "maxItems": 128,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^prom_[a-z0-9]{8,64}$"
            }
          },
          "missedPromiseIds": {
            "default": [],
            "maxItems": 128,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^prom_[a-z0-9]{8,64}$"
            }
          },
          "confirmedPromiseIds": {
            "default": [],
            "maxItems": 8,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^prom_[a-z0-9]{8,64}$"
            }
          },
          "correctedProposalIds": {
            "default": [],
            "maxItems": 8,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^cand_[a-z0-9]{8,64}$"
            }
          },
          "confirmedProposalIds": {
            "default": [],
            "maxItems": 8,
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^cand_[a-z0-9]{8,64}$"
            }
          },
          "flagWrong": {
            "description": "A teammate's recorded promise this discover_intent answer returned in `recorded` does not hold. Not for your person's own: they change or drop it.",
            "type": "object",
            "properties": {
              "proposalId": {
                "type": "string",
                "pattern": "^cand_[a-z0-9]{8,64}$"
              },
              "reason": {
                "description": "Optional: one plain sentence, in the person's words, on why it does not hold. No source code.",
                "type": "string",
                "minLength": 1,
                "maxLength": 280
              }
            },
            "required": [
              "proposalId"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "receiptId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    },
    {
      "name": "get_promise_setup",
      "title": "Get customer-local setup instructions",
      "description": "Read current Balladeer workflow and capture policy in the guidance field, including its revision and digest. Return bounded package and runner instructions for this repository, plus the active members who may be named as a proposal's meaning owner and the current workspace's optional Slack connection status and browser setup link. An installation record does not verify owner delivery. Balladeer does not request source access or repository credentials. It takes no arguments; anything sent is refused by name rather than quietly dropped.",
      "inputSchema": {
        "type": "object",
        "properties": {},
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "additionalProperties": false
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      },
      "execution": {
        "taskSupport": "forbidden"
      }
    }
  ]
}