Efficient agent tools
Discover operation contracts, use compact reads, preview atomic planning, and verify readiness and usage safely.
On this page
Use BernOps tools to discover exact contracts, select only relevant records, plan in batches, and verify the result without repeatedly loading large descriptions or guessing mutation fields. Efficiency does not remove project permissions, claim ownership, human approval, or concurrency checks.
This guide describes the changes in feat/agent-tool-efficiency; it is not a claim that they are deployed in production or available in every installed client. Check bernops app --help and the deployed operation descriptions. Older hosts may not expose the new operations, and older backends may reject them. Keep using supported calls rather than guessing parameters or automatically upgrading or signing in. CLI distribution remains private.
Choose the transport
| Interface | Authentication and purpose | Compact response support |
|---|---|---|
bernops_app in a personal BernOps session | The host derives project and actor identity; use the current session's permitted board, wiki and roadmap tools. | Supported operations can negotiate query.response_view: "compact-v1". |
bernops app for an external agent | A separately provisioned project API key with the required scopes, not user OAuth or provider credentials. | Direct REST responses; do not pass response_view. |
| Public docs search/get | No login, key, provider or installed agent component required. | Public section retrieval, not access to private project data. |
For CLI app calls, use the approved BERNOPS_API_KEY environment variable or --api-key-stdin; never put a key in command arguments or documentation. App commands default to the cloud target. Use --target local only for an explicitly intended local environment. See API keys and scopes.
Discover exact operations before calling them
On supporting managed tools, describe the operation you need instead of searching source files or testing guessed mutations:
{"operation":"operation.describe","params":{},"query":{"operation":"roadmap-planning.bulk-replace"}}External agents can read the same deployed contract through the CLI:
bernops --json app operation describe --project "$PROJECT_ID" \
--query operation=roadmap-planning.bulk-replaceThe authenticated REST equivalent is GET /api/projects/{projectId}/app-control/operations?operation=roadmap-planning.bulk-replace. Project access is required. Discovery itself needs no additional domain scope, but executing the described operation still requires its advertised scopes and permissions.
To list names, omit query.operation. The index defaults to 20 names, at most 50 per page. While has_more is true, pass the returned next_cursor as query.after. Describe one selected name separately; do not combine operation with index pagination.
Read params, query_keys, body_allowed, body_fields, body_required, semantics, permissions, concurrency, pagination, and compact_view_supported. In particular:
paramsis required even when{}. UUID and numeric path parameters are strings. Do not supply project/actor identity overrides.body_allowed: falsemeans omitbody, not null or an empty object. Read conditional rules as well as required fields: a milestone needs either a point date or both range dates.querycontains nonempty scalar values or scalar arrays, not nested objects. Roadmap cursors are JSON-encoded strings; board status cursors aresort_key:UUIDstrings.- Examples contain synthetic IDs and preconditions. Replace them with current records; discovery does not grant a lease, approval, admin rights or permission to execute. Binary operations are outside this JSON catalog.
Public documentation revisions can differ from the running backend. Use the deployed description for supported fields; a partial documentation search match is not proof that an exact operation exists.
Use compact reads for selection, not implementation
A supporting personal dispatcher accepts this narrow board read:
{"operation":"work-item.list","params":{},"query":{"status":"todo","limit":14,"response_view":"compact-v1"}}Use discovery's compact_view_supported flag before adding the option. Supported families include board/label selection and ordinary receipts, claims, roadmap snapshots/candidates and ordinary planning receipts, and wiki page list summaries. Omit the option, or use "full" on supported operations, for the existing full response.
Compact responses omit resource descriptions with description_omitted: true and duplicated event.post_state with post_state_omitted: true. They retain IDs, names, dates, revisions, resource ETags, claim credentials, mutation/event identity, outcomes, replay flags, event.changes, warnings and pagination/dependency completeness indicators. Omission is not an empty description or evidence that all dependencies were returned. Existing size limits apply before projection: use filters, smaller limits and pagination rather than expecting compact mode to rescue an oversized response.
Canonical work-item.get, work-item.get-by-number, work-item.events, wiki content/history reads and high-impact approval operations remain full-only. Do not send response_view to them. Always read the full item and acceptance criteria before claiming or implementing; read current full wiki content before editing.
Do not add response_view to direct REST or bernops app list calls. If an older backend rejects the option on a read, use the supported full read. Never replay an uncertain mutation merely to change its response format.
Keep resource ETags, cursors and leases separate
A compact resource can return an etag such as "7". Pass that exact quoted token as if_match, without adding another quote layer. A snapshot or event cursor is not a resource ETag.
For external CLI/REST work-item mutations, follow discovery's concurrency.etag_read_operation and etag_response_header: read canonical work-item.get and use its ETag response header. The direct REST detail is the item itself; command receipts may instead wrap an item. Default collections and ordinary mutation receipts do not promise ETag headers.
Wiki head tokens are page-qualified, such as "wiki-page:<page UUID>:rev:7", not "7". Compact wiki-page.list can provide these tokens, but historical revisions are not current-head write preconditions. Labels, claims and attachments do not have their own resource revision token.
Claims require their returned private lease token and generation, not an ETag. Lease mutations take generation as a JSON integer even when responses encode it as a decimal string. ttl_seconds is a JSON integer from 60 to 3600. claim.next selects and claims work; it is a mutation, not an inspection shortcut.
For an ambiguous transport outcome, retain the logical idempotency key, mutation UUID, body and preconditions. A new CLI invocation generates a new mutation UUID unless you explicitly retain --mutation-id. Refusals such as 403, 409, 412 or 428 require inspection and reconciliation, not blind retries or refreshed ETags under the same logical write. A deliberately revised proposal is a new logical mutation.
Preview and apply atomic bulk planning
Use roadmap-planning.bulk-replace to replace planning for 1–50 unique items in one project. Its body has exactly dry_run and items. Each item must contain its work-item ID, current quoted if_match, and all four nullable planning fields. Null clears a field; omission does not preserve it. This is full replacement, not a patch.
Replace the placeholder IDs and illustrative ETag below with current values before requesting a preview:
{
"operation": "roadmap-planning.bulk-replace",
"params": {},
"body": {
"dry_run": true,
"items": [{
"work_item_id": "<existing item UUID>",
"if_match": "\"7\"",
"roadmap_id": "<existing roadmap UUID>",
"roadmap_goal_id": null,
"planned_start_on": "2026-11-02",
"planned_end_on": "2026-11-03"
}]
}
}Dates are inclusive YYYY-MM-DD pairs, both null or both valid with start no later than end. With no roadmap, all other planning fields must be null. A selected goal must belong to that roadmap; dates need not fit its window. Every active claim blocks the batch, including a claim held by the caller. There is no lease override. Final dependency dates involving actually changed items must put a predecessor's end strictly before its successor's start.
Preview runs the validation in a rollback-only transaction. It saves no revisions, events or idempotency entries and reserves nothing; it returns expected_revision / expected_etag plus proposed values, not simulated committed revisions. Apply uses dry_run: false, a different logical idempotency key, and revalidates all items. An invalid item rolls back the whole batch. Bulk receipts are already compact; do not send response_view.
The external CLI accepts the same body files. Provision the required work_items:write scope separately. Generate and retain a different mutation UUID/key for preview and apply; the variables below must already contain those values:
bernops --json app roadmap-planning bulk-replace --project "$PROJECT_ID" \
--json-file plan-preview.json \
--idempotency-key "$PREVIEW_KEY" --mutation-id "$PREVIEW_MUTATION_ID"
bernops --json app roadmap-planning bulk-replace --project "$PROJECT_ID" \
--json-file plan-apply.json \
--idempotency-key "$APPLY_KEY" --mutation-id "$APPLY_MUTATION_ID"plan-preview.json contains only the body above; plan-apply.json contains the reviewed replacements with dry_run: false. Preserve the exact apply file, key and mutation UUID to reconcile an ambiguous apply. Exact replay returns the original historical receipt without applying again; fetch a new snapshot for current state.
An efficient planning sequence is snapshot/candidates → preview → apply → verification snapshot. That is four planning-domain calls only when all required records fit the reads and current preconditions are already available. Count discovery, additional pages and canonical ETag/detail reads separately. It is not a promise of four total cold-client calls, lower provider bills or authority to implement the selected work.
Verify readiness, not just linked cards
On supporting deployments, roadmap.snapshot includes authoritative readiness in full and compact views. It covers all linked non-archived work, not just loaded pages or locally visible bars. Read the effective window, scheduled/unscheduled work, unfinished work and dependency warnings together. Completed undated history should not be treated as upcoming work that needs dates.
If a timeline has no bars, inspect server readiness, dates, the snapshot window, completed history, local filters/collapsed groups and pagination. outside_window_count means scheduled work outside the window, not missing dates. A visible/loaded-bar count is not a server total.
For only linked undated unfinished work, zero_visible_work, zero_window_work, unscheduled_work and propose_dates_or_confirm_intentional_unscheduling distinguish a link-only plan from a dated delivery plan. Propose dates for review or explicitly report intentional unscheduling; do not declare a dated plan complete merely because items are linked.
Bulk receipts include readiness once per affected old/target roadmap after the complete batch. readiness_state distinguishes proposed_final_batch from committed_final_batch; replay is historical. After ordinary planning mutations, or whenever current state matters, refetch the snapshot. Warnings do not assign dates, move successors, authorize execution or add permission to mutate.
Interpret usage attribution without inventing billing
On supporting deployments, the session usage dialog shows request/turn/tool attribution, parent versus delegated-child scope, missing data and collection gaps. Refresh starts at page one, next-page navigation replaces the page, and JSON export contains only the current validated page, not a session total.
- Reported model input/output/cache/context counters remain separate; provider categories can overlap. Do not add every displayed category together.
- SDK catalog cost is an estimate, not a provider invoice. Tool-text tokens use
ceil(UTF-8 bytes / 4): an approximation of newly introduced text, not historical context reprocessing or billed model tokens. - Missing usage stays unavailable, not zero. An absent record or collection gap is not proof of no cost. Existing aggregate totals are not increased by adding the attribution rows.
- The collector stores bounded identities, counters and closed tool/operation categories, not prompt content, arguments, tool text, file paths, credentials or raw provider payloads. Delegated usage is distinct from the parent's Task result.
The private GET /api/agent-sessions/{sessionId}/usage-attribution endpoint requires the session creator and current project access. It is paginated with at most 100 records per page and uses Cache-Control: no-store; it is not a public docs asset or a project-key discovery endpoint.
A practical efficient workflow
- Search public docs, then retrieve the actual section ID with the same locale. Use the public revision as reference, not as proof of deployed support.
- Discover only the operation contracts needed for the task. Use permitted narrow reads and follow every relevant page/cursor.
- Use managed compact selection/snapshot results where supported; expand canonical detail before claiming, implementing or editing content.
- Keep resource ETags, private claim leases and idempotency identities intact. Preview batches only within the user's authorized planning scope.
- Apply reviewed changes once, then verify a fresh snapshot, readiness and conflicts. Record limitations, intentional unscheduling and unresolved access instead of hiding extra calls or claiming unmeasured savings.
For installation and command availability, see Coding agent CLI. For identity and capability boundaries, see API keys and scopes.