Skip to content

Structured model and effort for addressed comments

This protocol is opt-in. A text-only @Bot comment keeps the existing dispatch behavior. Rule and schedule model/reasoning fields never supply defaults for an addressed comment.

Direct-comment YAML scope

A bot operator can add a verified, exact target-runtime inventory to the live kardbrd.yml:

comment_execution:
  defaults:
    model: <exact-model-id>
    effort: high
  models:
    - id: <exact-model-id>
      efforts: [low, medium, high]
  verification:
    source: operator_probe
    verified_at: "2026-10-05T16:00:00Z"
    executor_version: "<exact CLI version observed in target runtime>"

The example is a schema illustration, not a claim that any model is available to this bot. Replace every placeholder only after an exact model and effort probe succeeds in the target container/account. source accepts operator_probe or runtime_probe; the timestamp must be past RFC 3339. The verification record describes how the administrator established the configured choices; the daemon cannot guarantee future provider access. A provider rejection remains visible and withdraws the rejected model/effort pair from subsequent publication. Config without a verified model inventory does not publish selectable capabilities. An absent comment_execution block leaves plain mentions on executor CLI defaults. If defaults.model or defaults.effort is absent, the exact CLI default is unknown and registered as JSON null; the UI keeps selectors unavailable. No rule is selected as a substitute default.

kardbrd agent validate <path> checks strict nested fields, duplicate models/efforts, default membership and verification metadata. The daemon reads the live file on startup and accepted hot reload. No change to this repository's sample kardbrd.yml activates a model in a running bot.

Shared v1 wire contract

Web accepts an optional comment POST execution_request:

{"version":1,"target_bot_id":"<bot public_id>","capability_revision":"<server revision>","model":"<exact executor ID>","effort":"high"}

version, target_bot_id, and capability_revision are required when the object exists. model and effort are independently optional. Omitted means: valid first-line directive field, then the Web-frozen accepted default for that field, then unresolved CLI default with no flag. Explicit null, empty, wrong type, unsupported value, duplicate key or unknown field is an error; it never falls back. A plain comment has no object and keeps the legacy route. Web validates the sole addressed bot and fresh registration before persistence, returns HTTP 400 VALIDATION_ERROR with a field path for malformed values, or HTTP 409 CAPABILITIES_STALE for unavailable/stale registration. It persists the original object, including omissions, and sends that object on the existing comment_created event. Sibling server-only accepted_defaults:{"model":null,"effort":null} and accepted_models:[{"id":"<exact ID>","efforts":["high"]}] are frozen by Web at acceptance and cannot be supplied by a commenter. Web checks each explicit field; the Agent checks the resolved final pair against the accepted snapshot and current advertisement. Null in the effective selection represents a genuinely unresolved CLI default and sends no corresponding CLI flag. The event envelope remains v1; there is no second dispatch.

The bot-token authenticated PUT /api/bots/execution-capabilities/ publishes version, server-issued live WebSocket instance_id, executor, board_id, exact models:[{id,efforts}], nullable defaults:{model,effort}, and provenance:{source,config_fingerprint,verified_at}. The server derives bot identity from the bearer token and issues a revision, published_at, and expires_at. A same-instance, same-content heartbeat extends expiry without changing revision. GET /api/bots/<bot public_id>/execution-capabilities/?board_id=<board public_id> is board scoped. On connect and accepted YAML reload the client registers; it refreshes before expiry. Older Web lacks instance_id, so the client runs text/directive comments without registration or selectors. An older client cannot register v1.

The client validates structured request and server-only snapshots before slash commands or exact command rules. Wrong-target events do not run. It stores the original request, accepted defaults and models, author attribution, resolved model/effort and source of each field (explicit, directive, accepted_defaults, or cli_unknown) under the base repository's ignored state/agent-claims/ directory. The durable identity binds board, card, comment and target bot. The accepted capability revision remains source-board/socket provenance after a move or reconnect; it need not equal the current reader's revision. The current sole reader, supported pair, and Web claim are checked before spawn without changing the frozen selection. Structured comments queue FIFO for a busy card; plain comments freeze applicable YAML defaults before queueing. The base repository mount must be persistent across container restarts.

On initial connection and registration refresh, the Agent pages GET /api/bots/execution-requests/?board_id=<public board ID>&after=<public comment ID optional> using the bot bearer token. HTTP 200 returns {data:{requests:[<canonical comment_created events>],next_after:<public comment ID|null>}}, at most 100 events per page in creation/database order. A page includes original author ID/name/bot flag, event fields, unchanged execution_request, and server-only accepted_defaults and accepted_models. next_after names the last returned comment only when another page remains. The Agent starts each scan at the beginning and advances within that scan only after recording each item's accepted or rejected disposition locally. Rejected intake records a stable idempotent error comment key. After the page scan, local recovery processes accepted requests and retries pending comments and receipts; a job or publication failure does not block later page intake. A page failure leaves already recorded items on disk, and the next scan starts at the beginning so no later item is skipped. Live events and replay use the same handler and local claim. Web returns 409 CAPABILITIES_STALE for unavailable registration and 400 VALIDATION_ERROR for an invalid cursor; no other bot's requests are returned.

The local recovery scan checks each record against the bot identity supplied by its authenticated WebSocket connection. It retains that verified identity when the socket disconnects so the original bot can finish a saved comment and owning receipt; an unknown or different bot cannot publish or execute the record. Replay writes every item's disposition, then records the page's observed public comment ID order before advancing its cursor. A scan marks its previous collection snapshot stale before requesting the first page. If a page fails, only the durable prefix observed in that scan can start; earlier accepted requests absent from that prefix wait for a complete scan rather than taking Web ownership prematurely. Already owned outcomes and receipts remain independently recoverable. If Web reports 404 NOT_FOUND for an observed request before spawn, the Agent leaves its accepted claim and frozen selection retryable, reports a local actionable error, and does not post or react to a card that may have moved off board. A complete scan records the current collection and holds omitted unstarted claims until they reappear. Deletion or a move off board therefore cannot change an old request's identity or block later intake. The collection has no immutable absolute rank. A cursor that disappears during pagination causes a bounded scan restart from the beginning; local claims make repeated intake idempotent. Recovery uses creation time and the durable identity sequence to preserve same-card order across pages and restarts. Older ordinal sidecars can seed historical state, but ranks from different mutable scans cannot prove equal-timestamp same-card order; recovery holds ambiguous unstarted work until canonical replay establishes its order. Other cards and owned publications continue.

Immediately before executor spawn, after preparation and a fresh card/registration check, the Agent sends exactly {version:1,board_id,card_id,comment_id,instance_id,effective:{model:<exact ID|null>,effort:<exact ID|null>}} to bearer-authenticated POST /api/bots/execution-claims/. The first atomic claim returns HTTP 201 with {data:{board_id,card_id,comment_id,instance_id,effective,status:"claimed",receipt:null}}. An identical same-instance retry returns HTTP 200 with stored data; the Agent inspects status and never respawns completed, failed or uncertain work. Web rejects a different instance or effective selection with 409 CLAIM_UNCERTAIN, a stale reader or unsupported current choice with 409 CAPABILITIES_STALE, an invalid final pair with 400 INVALID_EFFECTIVE_PAIR, malformed JSON/schema with 400 VALIDATION_ERROR, and a missing or wrong target with 404 NOT_FOUND. A newly connected socket cannot prove that a disconnected executor stopped. The accepted request and immutable selection remain available for operator reconciliation; the Agent does not take over uncertain work. An accepted local claim with an orphan lock file can resume only if it has not reached the started state and Web grants the claim. An orphan started state never auto-runs again.

For a first claim, Web also returns 409 CLAIM_ORDER_PENDING if an earlier currently visible accepted v1 request for the same authenticated bot and card has no terminal receipt or trusted rejection marker. An identical owned claim retry is handled before this guard. The Agent keeps its accepted request and frozen selection, starts no executor, sends no terminal receipt or error comment, and schedules a coalesced from-start replay and recovery with bounded backoff. Repeated holds do not create another job. Local directive/final-pair validation failures retain a durable rejected tombstone and publish the existing idempotent bot-authored comment with key execution-rejected:<board_id>:<card_id>:<comment_id>; Web recognizes only matching persisted bot/card/comment metadata as a terminal rejection for ordering, with an existing nonterminal claim taking precedence. A deleted rejection comment is republished on recovery. A target with this trusted marker and no existing claim returns 409 CLAIM_REJECTED; the Agent marks its accepted local sidecar terminal rejected without spawning, receipting, or posting another error. A claim for the same bot/card/comment from a prior board retains ownership and blocks a new first claim as CLAIM_UNCERTAIN; its owned terminal receipt remains valid. No effective pair is claimed for invalid input. Legacy comments without execution_request remain outside this guard. Older clients that do not recognize these 409 codes fail closed with the Web code in an actionable error; upgraded clients against old Web remain in legacy mode without a socket instance_id. Deploy Web guard support and verify the sole v1 reader before enabling structured selection.

After executor completion, including when its deadline or socket context was canceled before it returned, the Agent stores the known outcome body and status locally before publication. It uses a bounded context for bookkeeping after cancellation and never resumes canceled work to obtain a missing summary. It posts the card comment with a stable client_request_id; an identical retry returns the saved comment. It then sends exactly {version:1,board_id,card_id,instance_id:<original owner>,status:"completed"|"failed",message?:<up to 1000 characters>} to PUT /api/bots/execution-claims/<public comment ID>/receipt/. HTTP 200 returns the claim with its stored receipt, including on an identical retry. Conflicting receipts return 409 RECEIPT_CONFLICT; another owner returns 409 CLAIM_UNCERTAIN; an unknown claim returns 404 NOT_FOUND; malformed JSON/schema returns 400 VALIDATION_ERROR. A lost comment or receipt response leaves the local outcome pending for retry without rerunning the executor. The original owner may receipt after its socket disconnects. A started claim with no returned result remains uncertain and requires operator reconciliation.

Rollout gate

  1. Review both the Agent and Web PRs and their CI at the exact heads. Do not enable the composer yet.
  2. Install the Agent reader and Web registration/instance endpoints. The Web UI remains disabled until exactly one authenticated v1 socket exists for the target bot and board. Drain old bot sockets; targeting cannot force a legacy board-wide reader to ignore a matching text mention.
  3. Paul alone verifies the actual MBP container, live config path, mount persistence and account. In that container, run codex --version, codex exec --help, kardbrd agent validate <live-kardbrd.yml-path>, and the exact model/effort probe from Addressed Comment Dispatch. A rebuild does not establish gpt-6.1-sol access; a prior ChatGPT-backed CLI rejected it. Do not edit credentials or live YAML as part of this PR.
  4. After approval, Paul alone drains and rebuilds/restarts MBP using his deployment's actual service name and paths. The repository's example Compose service is agent (docker compose -f examples/docker/docker-compose.yml ps agent), but the live MBP service must be verified before using any build or restart command.
  5. Confirm one live v1 registration, its revision and expiry, then submit one Web-board addressed comment with an exact verified model and effort. Check the persisted request, event, claim, actual executor argv, provider outcome, and one terminal response. Only after this gate may any other board or VPS rollout proceed. Resolve the referred-to CPA/CBA VPS identity before touching it.