# 07 — Agent Guide: Integrating with the Library over MCP

**Audience:** AI agents and developers integrating via MCP (Model Context Protocol / JSON-RPC).
**Measured against:** v0.22.188 — `docs/versions/v0.22.188/capability-manifest.json` (45 tools, 462 commands, describe coverage 0.89) and `docs/versions/v0.22.188/tools/*.md`, plus the config files cited inline. Every claim below carries its source; where a file:line is given it was read at v0.22.188. Nothing here is invented from memory — if a behaviour is not cited, verify it with `ux.docs_get` before relying on it.

---

## 1. The first call

### 1.1 `_hello` — the gateway payload

`_hello({})` needs no auth and has no side effects (`scripts/mcp-server/umbrellas/_hello.ts:38-47` description). The default payload is built at `_hello.ts:216-261` and carries:

| Field | Meaning | Source |
|---|---|---|
| `platform`, `version`, `status` | Identity + frozen-at-load `PKG_VERSION` + health status | `_hello.ts:217-219` |
| `tool_count`, `command_count` | What `tools/list` returns for **your** tier (not a constant) | `_hello.ts:222-223` |
| `message` | One-line platform blurb (static) | `_hello.ts:224` |
| `current_release` | Most recent published tag (live directory read) | `_hello.ts:225` |
| `available_versions` | `{current, count, latest[5], full_list_via: {tool:'ux', command:'versions_list'}}` | `_hello.ts:231-236` |
| `capabilities_summary`, `discovery`, `startup_hints` | Config-owned orientation blocks (`config/_hello.config.ts`) | `_hello.ts:239-241` |
| `session_convention` | How to tag your run (see §1.2) | `_hello.ts:244` |
| `run_report_convention` | End-of-run self-report contract (`ux.run_report`, §8) | `_hello.ts:247` |
| `refusal_convention` | The platform-wide `fix.kind` / `fix.args` refusal contract (§3) | `_hello.ts:252` |
| `next` | Back-compat pointer to the primary discovery target `{tool:'ux', command:'session_start'}`; mirrored by `discovery.full_orientation` | `_hello.ts:256-260` |
| `version_note` | Present only during the post-publish/pre-reload window when `version` lags `current_release.tag` (LIB-BUG-221) | `_hello.ts:281-285` |

Three startup patterns are served by that one call (manifest description): first-time agent → follow `startup_hints.first_time` / `discovery.full_orientation` to `ux.session_start`; returning agent → `startup_hints.returning_agent` (compact mode); status glance → `version` + `status` + `current_release` are already in the response.

`_hello({intent: "<natural language>"})` returns a workflow plan (archetype, tool chain, confidence, recovery hints) — no auth, no side effects. `_hello({command:'llm_smoke'})` is admin-only and **bills real LLM spend**; do not call it casually.

### 1.2 `session_label` and `agent_info` on every call

Two reserved gateway arguments are accepted on **every** `tools/call`, consumed and stripped before the target tool's Zod parse (`lib/mcp/sessionEnvelope.ts:1-30`, `lib/server/session-per-call.ts`). They are advertised on every tool's wire schema since v0.22.173 (LIB-BUG-1102).

| Argument | Rule | Source |
|---|---|---|
| `session_label` | Your logical-run identity. Grammar `^[A-Za-z0-9._-]{1,64}$`. Pass it on **every** call, not only the first, and re-declare after a reconnect — a call without it is attributed to whichever connection served it. Several labels on one connection accumulate (`metadata.labels`). | `config/session-tagging.config.ts:72,112-116,282-300` |
| `agent_info` | Voluntary, advisory self-identification; **never authorization-bearing**. Exactly four fields: `model`, `provider`, `version`, `effort`. | `session-tagging.config.ts:124,180-183` |

Recommended first call:

```json
health_check({ "session_label": "qa-run-2026-09-22a", "agent_info": { "model": "claude-opus-4", "provider": "anthropic", "version": "1.0", "effort": "high" } })
```

`health_check` `mode:'quick'` echoes a `session {session_id, label, agent_info}` field when tagging is enabled, so you can confirm attribution (`tools/health_check.md`).

---

## 2. Tiers and command floors

Single source of truth: `config/mcp-tiers.config.ts`. Hierarchy (each tier inherits the lower ones, header comment lines 11-12):

`PUBLIC → FREE → EDUCATION → DEVELOPER → TEAM → ENTERPRISE → ADMIN → SUPER_USER`

| Tier | Adds (umbrella visibility) | Daily call limit | Source |
|---|---|---|---|
| public (anonymous) | `FREE_TOOLS` (`_hello`, `health_check`, `workspace_info`, `ux`) + `dataset_catalog`, `dataset_data`, `fingerprint`, `rest`, `file_repo`, `workflow`, `search`, `edges` | 30 (comment: "per hour for anonymous") | lines 90-125; TIER_CONFIGS 308-316 |
| free | `admin` (per-command floors decide what a free key can actually do) | 100 | 156-163 |
| education | nothing extra | 500 | 166 |
| developer | `sessions`, `tokens`, `account`, `data_catalog`, `crawler`, `crawler_run`, `dataset_admin`, `dataset_analysis`, `dataset_ingest`, `dataset_lifecycle`, `analytics_config`, `story`, `evidence`, …, `git`, `artifact` | 1000 | 174-234 |
| team / enterprise | nothing extra (gating is per-command) | 5000 / unlimited | 237-240 |
| admin | `vault`, `mcp_log`, `mdm_project`, `stewardship` | unlimited | 264-291 |
| super_user | nothing extra | unlimited | 294 |

`ANON_DENIED_TOOLS` (line 251) removes tools from **anonymous** callers even when the tier lists them: `admin`, `crawler`, `generalization`, `dataset_data`, `git`. So `dataset_data` is in `PUBLIC_TOOLS` for a keyed public caller but unreachable anonymously — hold a key before relying on it.

**Per-command floors** live in `config/command-auth.config.ts` (header: "the single registry of PER-COMMAND authorization floors"): `ADMIN_COMMAND_AUTH` (line 35), `CRAWLER_COMMAND_AUTH` (78), `DATASET_ADMIN_COMMAND_AUTH` (88), `DATASET_LIFECYCLE_COMMAND_AUTH` (106), `MDM_COMMAND_AUTH` (115), and siblings. Umbrella visibility ≠ command permission: e.g. `admin` is reachable from free tier, but `settings_set`/`flag_set`/`approval_decide` floor at admin. Expect a typed refusal (§3), not silence, when you are under a floor.

**Quota telemetry:** metered successes carry `_meta.usage {used, limit, reset_at}`; the window is the UTC civil day and the refusal states the RFC 3339 `reset_at` (`config/quota.config.ts:14-42`). No other per-tool rate/pacing config was found under `config/` for the MCP surface at v0.22.188 (only `quota.config.ts` matched); the embed-lane pacing rules are worker-side and not part of the tool contract.

---

## 3. The canonical error envelope (DEC-1)

The MCP/JSON-RPC surface uses the **compact canonical MCP envelope**, not `application/problem+json`. RFC 9457 problem details are used only on the REST/HTTP surface (`lib/server/problem.ts:6`; `lib/server/processingConfigSchema.ts:141`: "DEC-1 `invalid_params` on MCP, RFC 9457 `details` on HTTP"; CLAUDE.md DEC-1 / LIB-BUG-318).

Shape (as asserted by `lib/mcp/__tests__/errorEnvelope.test.ts:327-328` and produced at `lib/mcp/umbrellaErgonomics.ts:1027-1059`):

```json
{
  "ok": false,
  "tool": "dataset_lifecycle",
  "error": {
    "code": "confirm_required",
    "message": "…what happened, what was NOT changed, what to do…",
    "retryable": false,
    "issues": [{ "path": ["confirm"], "message": "…", "code": "invalid_literal" }],
    "expected": { "…": "describeExpected(schema, command)" },
    "received": { "…": "redacted echo of your args" },
    "details": { "also_known_as": "alias" },
    "fix": { "tool": "dataset_lifecycle", "command": "delete", "kind": "replayable", "args": { "…": "…" }, "why": "…" }
  }
}
```

Rules an agent can rely on:

| Element | Contract | Source |
|---|---|---|
| `code` | Typed, snake_case, stable (e.g. `invalid_params`, `confirm_required`, `*_not_found`, `handler_failed`, `not_implemented`). Branch on `code`, never on `message`. | `errorEnvelope.test.ts:353,541` |
| `retryable` | Boolean; `true` means the same call may succeed later. | `test.ts:328,541` |
| `fix.kind` | One of `replayable` (send `fix.args` verbatim), `skeleton` (placeholders — edit first), `note` (no `args`; remedy is in `fix.note`). `args` is omitted above 32 768 serialised bytes. | `config/mcp-error-exposure.config.ts:310-324` |
| Sanitisation | Raw driver/OS messages never cross the wire; you get a `correlation_id` and the operator gets `original_message` in the log. | `test.ts:390,422` |
| Transport | Delivered as `content[0].text` (JSON string) with `isError: true`. | `umbrellaErgonomics.ts:1059` |

Successful responses are `{ "ok": true, … }` (paginated shape in §4).

---

## 4. Canonical parameters and pagination

Contract: `docs/contracts/canonical-params.md` (audited by `npm run audit:param-canonicalization`).

| Concern | Canonical | Deprecated aliases | Section |
|---|---|---|---|
| Entity identifier | `{entity}_id` (`dataset_id`, `provider_id`, `binding_id`, …) | bare `id` where an umbrella touches several entity types | §2.1 |
| Page size | `limit` (default/max per command) | `rows`, `max`, `count`, `take`, `page_size` | §2.2 |
| Page position | **either** `offset` (stable DB lists) **or** `cursor` (opaque; streams / eventually-consistent lists) — one per command | `start`, `from`, `after_cursor`, `before_cursor`, `page` | §2.3 |
| Detail level | `detail: enum` | — | §2.4 |
| Destructive gating | `confirm: true` required; `force: true` overrides guardrails | — | §2.5 |
| Preview | `dry_run: true` | — | §2.6 |
| Discriminator | `command` (alias `action`) | — | §2.7 |
| Soft-state | `include_{state}` (e.g. `include_deleted`, `include_archived`) | — | §2.8 |
| Time range | `since` / `until` (RFC 3339) | — | §2.9 |
| Extensible fields | `metadata` (object) + `tags` (string[]) | — | §2.10 |

Paginated list envelope (`canonical-params.md` §2.3):

```json
{ "ok": true, "data": { "items": [], "total_count": 1247, "has_more": true, "next_cursor": "opaque-or-null", "next_offset": 100 } }
```

`total_count` is required for offset pagination, best-effort for cursor pagination. Loop on `has_more`, feeding back `next_cursor` or `next_offset` — never compute the next page yourself. Note that older umbrellas (`dataset_admin.settings_get {id}`, `dataset_snapshot.list {id}`) still take `id`; the alias policy in §3 of the contract keeps both accepted through the v0.22.x cascade.

---

## 5. The 45 umbrellas, grouped by task

Every umbrella is called as `<umbrella>({command: '<cmd>', ...args})` except `_hello`, `health_check`, `workspace_info` (no discriminator). Command lists are the manifest's `commands[]`; "most used" picks are the commands the umbrella description itself leads with. Full per-command parameter tables: `docs/versions/v0.22.188/tools/<umbrella>.md`, or live via `ux.docs_get({topic:'<tool>.<command>'})`.

### 5.1 Gateway and session

| Umbrella | Purpose | Commands an agent uses most |
|---|---|---|
| `_hello` | Platform identity, status, capability surface, multi-version discovery | (no command); `intent` |
| `health_check` | DB connectivity, migrations, worker heartbeat; `mode` quick / full (admin) / worker (admin) | `mode` |
| `workspace_info` | Loaded datasets, storage, capacity, health (weight 0; redacted for anonymous) | (no command) |
| `ux` (14) | Per-caller session state, help, docs, run reports | `session_start`, `help`, `docs_get`, `versions_list`, `run_report` |
| `workflow` (4) | Executable multi-step recipes validated against the live tool surface | `list`, `get`, `run`, `save` |
| `search` (2) | One query across every searchable asset store; never widens access | `query`, `asset_types` |

### 5.2 Discover and read datasets

| Umbrella | Purpose | Most used |
|---|---|---|
| `dataset_catalog` (10) | Read-only metadata: search with 30+ facets, get, stats, tables | `search`, `get`, `fields_search`, `stats`, `catalog_stats` |
| `dataset_data` (2) | Read-only row access through masking views | `query`, `sample` |
| `file_repo` (11) | File repos behind a dataset: metadata, content, analysis runs, derivatives, drift | `list`, `read_file`, `analysis_runs`, `derivative_create`, `upload` |
| `edges` (5) | Typed relationship traversal from one asset (provenance, citations, lineage, governance) | `neighbors`, `traverse`, `impact`, `predicates` |
| `fingerprint` (6) | Identify the source system (Salesforce, SAP, …) from columns; 95 built-in | `detect`, `list`, `validate` |
| `dataset_snapshot` (7) | Point-in-time snapshots of a source table | `list`, `create`, `compare`, `restore` |

### 5.3 Bring data in

| Umbrella | Purpose | Most used |
|---|---|---|
| `dataset_ingest` (9) | Inline / URL / batch ingest, load to Query DB, progress, cancel (SSRF-guarded) | `url_ingest`, `url_and_wait`, `load`, `status_get`, `cancel` |
| `crawler` (13) | Open-data crawler projects, sources, runs, portal discovery | `project_list`, `project_create`, `source_create`, `project_run`, `portal_discover` |
| `crawler_run` (2) | Per-run error ledger + history | `errors_list`, `history_list` |
| `watch_source` (6) | Poll S3/Azure/GCS/SFTP/FTP/SMB subtrees; optional auto-ingest | `create`, `poll`, `list`, `disable` |
| `connection` (5) | Stored credentials, AES-256-GCM at rest, write-only config | `create`, `list`, `test`, `delete` |
| `rest` (6) | Server-side REST client; secrets never leave the server | `endpoint_create`, `execute`, `endpoint_list`, `responses_diff` |
| `data_catalog` (29) | Estate map of system objects in connected providers; scan, profile, bind, remaster | `provider_create`, `scan_run`, `assets_list`, `columns_search`, `profile_get` |

### 5.4 Manage, transform, govern datasets

| Umbrella | Purpose | Most used |
|---|---|---|
| `dataset_admin` (13) | Settings, masking, joins/unions, exports, sync, refresh | `settings_get`, `settings_update`, `join_create`, `union_create`, `masking_set` |
| `dataset_lifecycle` (6) | Soft/hard delete, undelete, archive/restore — all by `dataset_id` | `delete`, `undelete`, `archive_create`, `restore` |
| `dataset_analysis` (8) | Read/store analysis, submit analysis jobs, analysis-run location | `get`, `submit`, `preflight`, `runs_list` |
| `dataset_transform` (6) | Server-side JSON→CSV / processing utilities (DataDoc Dev Studio) | `json_to_csv`, `json_process`, `transform_preview` |
| `generalization` (7) | Privacy-preserving column transforms; view-mode masking works, physical mode is `not_implemented` (FEAT-033) | `list`, `preview`, `apply`, `stats` |
| `vault` (3, admin) | Detokenise `TOK_*` values; fail-closed audited | `lookup`, `stats`, `audit_list` |
| `classification` (6) | UNSPSC-style catalog lookup + AI dataset tagging with review | `lookup`, `dataset_classify`, `proposals_list`, `proposal_decide` |
| `domain` (5) / `topic` (10) | Governance domains and domain-scoped topics (datasets, knowledge, discussions) | `domain.list/get`; `topic.create/get/search/resource_link` |
| `job` (8) | Unified async-job lifecycle across both queues | `get`, `list_mine`, `cancel`, `retry`, `stats` |

### 5.5 Master data and stewardship

| Umbrella | Purpose | Most used |
|---|---|---|
| `mdm` (99) | Golden entities, source links, relationships, match configs, normalisation, enrichers | `entity_list`, `entity_get`, `match_decide`, `config_trial`, `resolution_run` |
| `mdm_project` (15) | Conversational MDM onboarding (interview → spec → model) | `project_start`, `interview_next`, `interview_answer`, `spec_generate`, `gate_status` |
| `stewardship` (29) | Proposal review front door, disposition rules, masked golden reads, dashboard | `worklist_get`, `proposal_decide`, `golden_read`, `recommendations`, `workbench` |

### 5.6 Analytics, evidence, reporting

| Umbrella | Purpose | Most used |
|---|---|---|
| `story` (4) | FDR-gated storylines over governed aggregates (find→prove) | `scan`, `get`, `list`, `watch` |
| `evidence` (4) | Content-hashed proofs (RFC 8785 canonical JSON) behind claims | `show`, `verify`, `list` |
| `timeline` (8) | Compile a storyline into an animated timeline archetype | `make`, `render`, `publish`, `list` |
| `report` (7) | Agent-generated reports with dataset lineage | `create`, `get`, `list`, `versions_list` |
| `analytics_config` (4) | Versioned write-once policy store for the analytics engine | `get`, `list`, `validate` |
| `rag` (12) | RAG over document corpora | `corpus_create`, `corpus_add`, `status`, `search`, `corpus_docs` |

### 5.7 Platform, identity, engineering

| Umbrella | Purpose | Most used |
|---|---|---|
| `artifact` (15) | BUG/FEAT/SPR lifecycle, atomic IDs, bug submit/list/update | `bug_submit`, `bug_get`, `bug_list`, `bug_update`, `search` |
| `git` (17) | Repo state, history, diffs, doc-file writes on the library repo | `status`, `log`, `diff`, `read_file` |
| `admin` (21) | Tiers, usage, settings, feedback, flags, approvals (per-command floors) | `settings_get`, `flags_list`, `feedback_list`, `approvals_list` |
| `account` (10) / `tokens` (9) / `sessions` (3) | Accounts, API keys, web sessions (own-account at dev tier; deprecation notice → auth-mcp, `remove_after: null`) | `tokens.create/list/delete`; `account.token_verify` |
| `mcp_log` (2, admin) | Read `registry.mcp_call_log` for audit/efficiency | `query`, `stats` |

---

## 6. Task recipes

Parameter names below are copied from the v0.22.188 per-tool tables; `confirm:true` marks irreversible steps (§4).

### 6.1 Catalogue a new database

1. `connection({command:'create', …})` — store the credential once (write-only; `test` afterwards).
2. `data_catalog({command:'provider_create', kind, name, connection_id, role, scan_cadence, sample_policy})` — register the provider over the connection.
3. `data_catalog({command:'provider_test', provider_id})`, then `data_catalog({command:'crawler_create', provider_id, purpose, name, scope})`.
4. `data_catalog({command:'scan_run', crawler_project_id, acknowledge_sweep?, reason?})` — inspect failures with `crawler_run.errors_list {run_id}`.
5. Browse: `assets_list {provider_id, kind, q}` → `asset_get {asset_id}` → `columns_list {asset_id}` → `profile_get {asset_id, sections}`.
6. Optionally bind to master data: `bind_inbound {asset_id, entity_type_id, field_map, role}`.
Saved recipe: `workflow.get {id:'W35'}` ("map what exists in a connected database").

### 6.2 Find PII in an estate

- Catalogue-level: `dataset_catalog({command:'search', has_pii:true, compliance_level?, semantic_type?})` and `dataset_catalog({command:'catalog_stats'})` (PII/PHI counts, needs-classification counts).
- Field-level: `dataset_catalog({command:'fields_search', field_query, semantic_type})`.
- Connected systems: `data_catalog.profile_get {asset_id}` and the provider summary, which separates `columns_with_format_label` (shape detections) from `columns_with_secret_label` (credentials) and reports `columns_with_stored_samples` + `sample_disclosure` (`tools/data_catalog.md`). `columns_search {provider_id, q}` for name-based sweeps.
- Re-classify stale datasets: `dataset_analysis.submit_batch {scope:'vocabulary_stale', dry_run:true}` first, then real.
- Mask what you find: `generalization.preview` → `generalization.apply` (view-mode), or `dataset_admin.masking_set {id, mode, confirm}`. Masking is OFF by default and legally-required only (STANDARD-009).
Saved recipes: `W14` (catalog-wide PII field inventory), `W38` (apply compliance masking and verify).

### 6.3 Build a derivative dataset

- Join: `dataset_admin({command:'join_create', sources, join_type, join_keys, output_name, qualify?, where_clause?, output_format?, visibility?, auto_analyze?})`.
- Union: `dataset_admin({command:'union_create', sources, output_name, dedup?, dedup_key?, survivorship?})`.
- The derivative is born with strictest-of-parents governance (`compliance_level`/`has_pii`/`has_phi`) and masked parent columns stay masked (`tools/dataset_admin.md`). `dataset_transform` materialises nothing — it is a JSON utility (`docs/audit` writeup 05).
- File-level derivative pipelines: `file_repo.derivative_create`. Track the async job with `job.get`.

### 6.4 Search documents with RAG

1. `rag({command:'corpus_create', name, description?, topic_id?, tags?, visibility?, hierarchical_split?})` → returns a corpus `dataset_id`.
2. `rag({command:'corpus_add', dataset_id, files? | file_ids? | from_urls? | from_connection?})`.
3. `rag({command:'status', dataset_id})` until indexed; `corpus_docs {dataset_id, cursor, limit}` to list.
4. `rag({command:'search', dataset_id, query, top_k?, max_chunks_per_doc?, filters?})`.
5. Cross-store discovery without knowing the owner umbrella: `search({command:'query', q})`.
`corpus_promote`/`corpus_demote` flip personal↔internal visibility and carry a disclosure; `corpus_doc_remove` and `reindex` need `confirm`. Guide: `ux.docs_get {topic:'rag_skill_pack'}`.

### 6.5 File a bug

`artifact({command:'bug_submit', title, description, severity, umbrella, sprint?, discovered_by?, reproducer?, affected_versions?, related_bugs?, metadata?})` → returns the `LIB-BUG-n` id (IDs come only from the writer; `next_id {type:'BUG'}` previews without consuming). Then `bug_get {id}`, `bug_update {id, status, severity, metadata, body_append}`, `bug_list {umbrella, status, severity, limit, cursor}`.

### 6.6 Check platform health

`health_check({})` (quick: DB latency, `migrations.applied/latest/pending/pending_files`, worker heartbeat); `workspace_info({})` for capacity; `job.stats` for queue state; admin: `health_check {mode:'full'|'worker'}`, `mcp_log.stats`. Saved recipe: `W28` (health sweep with a saved report).

### 6.7 Run a saved REST endpoint through the vault

1. `connection.create` the credential (never readable back).
2. `rest({command:'endpoint_create', name, url, method, headers?, body_template?, auth_type, auth_connection_id, variables?, catalog?})`.
3. `rest({command:'execute', endpoint_id, overrides?, auto_save?, dataset_id?})` — executed server-side; the response, not the secret, is returned. `auto_save`/`dataset_id` land it as a dataset (recipe `W22`).
4. `rest({command:'responses_diff', execution_id_1, execution_id_2})` before trusting a changed response (recipe `W25`).

---

## 7. `workflow` recipes

87 executable recipes live as JSON in `library/workflows/` (loaded by `lib/mcp/workflowRegistry.ts:86-91`, ids `W01`…), each validated against the live tool surface by `audit:workflow-recipe-integrity`. Use `workflow.list {archetype?, domain?, complexity?, destructive?, accessible_only?}` (tier-filtered via `requiredTierFor`, `workflowRegistry.ts:180`), `workflow.get {id}`, `workflow.run {id, params, confirm}` and `workflow.save {id, definition}`. `ux.help {view:'patterns'}` projects the top recipes per category from the same registry. Examples: W01 ingest-URL-and-analyze, W07 snapshot-before-destructive, W17 assess an unknown file end-to-end, W22 wire a REST API into a dataset, W35 map a connected database, W40 assemble a compliance-audit evidence pack.

---

## 8. The `ux` umbrella and the tester protocol

| Command | Use | Source |
|---|---|---|
| `ux.help {view:'search'\|'patterns', query?, version?, limit?, offset?}` | Tool search against a **pinned** manifest (`version` = `current`/`latest`/`vX.Y.Z`; unknown versions error with the list — no silent fallback) or browse recipe patterns | `tools/ux.md:22` |
| `ux.docs_get {topic?, version?}` | `release_notes`, `test_prompt`, `rag_skill_pack`, or dotted `<tool>.<command>` for per-parameter reference read live from the server | `tools/ux.md:28` |
| `ux.versions_list {}` | Snapshots in `docs/versions/`, the current default, `has_release_notes`/`has_test_prompt` per version | `tools/ux.md:30` |
| `ux.run_report {session_label?, metrics?, narrative?}` | ONE end-of-run self-report; server reconciles your `metrics {elapsed_s, total_calls, error_retries, tokens_spent, per_tool}` against `mcp_call_log` and echoes `cross_check` | `tools/ux.md:31` |

**DEV-GOV-TESTING protocol for QA agents** (`docs/governance/DEV-GOV-TESTING.md` §4):

- **T-5** — Declare `session_label` = the run's identity and `agent_info {model, provider, version, effort}` on the FIRST call (`health_check` or `_hello`) and carry `session_label` on EVERY subsequent call (line 34). **T-5a**: one principal per role tier — do not mix keys in one run (line 36).
- **T-6** — Self-report per BLOCK as structure inside ONE `ux.run_report {narrative: {blocks: [{block, …}], final_summary}}`; the verb is capped at 5 reports per session (`RUN_REPORT_CONFIG.rowCaps.maxReportsPerSession`, line 37-38).
- **T-7** — File findings through `artifact.bug_submit` with the root cause as you read it; set severity via `bug_update`; never edit the prompt doc (line 41).
- The release test prompt itself is fetched with `ux.docs_get {topic:'test_prompt', version:'vX.Y.Z'}` (§3 of the standard).

---

## 9. Pacing and limits an agent should respect

- Daily call limits per tier (§2) with `_meta.usage` telemetry on success and an RFC 3339 `reset_at` on refusal (`config/quota.config.ts`).
- `session_label` aggregation window for cross-checks is `PT24H` (ISO 8601); a longer run should use a fresh label (`session-tagging.config.ts` `labelAggregation`).
- `dataset_data.query` is cost-guardrailed; `force:true` overrides (manifest description). `data_catalog.crawler_create` and `crawler.project_create`/`project_update` accept `rate_limit_rps` / `max_requests_per_run` so **you** declare the outbound crawl pace (`rest.endpoint_create` carries no such parameter — pace REST executions yourself).
- Destructive verbs do nothing without `confirm:true`; the refusal ships a `fix` you can replay (§3).
- Tool weights and token estimates per tool are declared in `config/mcp-tiers.config.ts` (`TOOL_WEIGHTS` 460-533, `TOKEN_ESTIMATES` from 538); `TOOL_ENTRY_SIZE_BUDGET_BYTES = 100_000` (line 664) bounds each advertised tool entry.

---

## 10. Where to look next

| Need | Source |
|---|---|
| Every command + parameter for one umbrella | `docs/versions/v0.22.188/tools/<umbrella>.md` or `ux.docs_get {topic:'<tool>.<command>'}` |
| What changed in a release | `ux.docs_get {topic:'release_notes', version}` / `docs/versions/vX.Y.Z/RELEASE-NOTES.md` |
| Authorisation model | `docs/standards/STANDARD-004-server-side-authorization.md`, `config/command-auth.config.ts` |
| Privacy and masking rules | `docs/standards/STANDARD-009-privacy-classification-and-masking.md` |
| Connection paths and ownership | `docs/standards/STANDARD-010-connection-paths-and-ownership.md` |
| Canonical params contract | `docs/contracts/canonical-params.md` |
| Tester governance | `docs/governance/DEV-GOV-TESTING.md` |
