Request
Request
A request (
ContextRequest, id prefix req_) is a consumer asking for context — a question an agent couldn’t answer on its own. It carries a kind, a topic, the question text, the submitting consumer, and optional context, dedup_key, and routing overrides.A request moves through a lifecycle: queued → asked → escalated → proposed → accepted | rejected | failed. You can read a request’s current status and its accepted answer at any time via GET /requests/{id}.Proposal
Proposal
A proposal (
ContextProposal, id prefix prop_) is an expert’s answer, structured and attributed. It links back to its request_id and carries the expert’s verbatim answer, an optional LLM-structured form of that answer, a confidence score, and provenance (who answered, how, and when).Every proposal starts as pending in the approval queue. A human accepts or rejects it before it is ever delivered to a consumer — unless that consumer explicitly opted into auto_accept.Expert
Expert
An expert is a human in the registry who can answer questions on one or more topics. Each expert has an
id (their Slack user ID), a name, a list of topics, and an optional escalation_to — the expert to re-ask if they don’t reply in time.Experts are defined in shouldertap.yaml and carry live state the engine tracks: whether they’re muted, how many open_asks they have outstanding, and how many they’ve received today. Rate limits and quiet hours protect them from being over-asked.Consumer
Consumer
A consumer is anything that submits requests and receives answers — typically an AI agent, but also the CLI, the MCP server, or your own service. A consumer is identified by a string id (for example
bi.assistant) and declares which handles_kinds it deals with and how it wants answers delivered.Consumers receive answers through delivery: a webhook POST, or an inprocess callback when the consumer is embedded in the same process as the engine via the SDK.Kind
Kind
A kind is the type of question being asked, and it determines the schema the expert’s answer is captured into. Two kinds ship built in:
A consumer can register its own kind with a custom JSON Schema at registration time; a custom schema takes precedence over a built-in of the same name. If structuring fails or confidence is below 0.3, the raw
answer is still preserved — it is never dropped.Topic
Topic
A topic is the routing key that connects a request to an expert. When a request arrives, ShoulderTap normalizes its topic (lowercase, whitespace-collapsed) and matches it against each expert’s
topics.Routing tries, in order: an exact topic match (load-balanced to the expert with the fewest open asks), a fuzzy token-overlap match, then the topic’s configured fallback expert. If nothing matches, the request fails with no_expert_found. A request can also name target_experts to bypass topic routing entirely.Transport
Transport
A transport is how ShoulderTap reaches experts. Two are built in:
console prints the DM to your terminal and reads replies from stdin (ideal for a first look, no Slack needed), and slack sends real DMs through a Slack app and captures replies from the Events API. You choose one with shtap serve --transport.Adapter
Adapter
An adapter is a consumer-side helper that writes an accepted proposal back into a system of record. Two ship with ShoulderTap: an OpenMetadata adapter that patches a glossary term’s description with a provenance footer, and a generic webhook adapter that POSTs the accepted proposal anywhere. Adapters implement a single method,
on_accepted(proposal).Deduplication
If a request carries adedup_key that matches another request from the same consumer within its dedup window (default 24 hours), ShoulderTap deduplicates it. If the matching request already resolved to an accepted proposal, that proposal is delivered to the new consumer immediately — no expert is asked again. If the matching request is still open, the new consumer is attached as an additional subscriber and gets the answer when it lands. An expert is never asked twice for the same thing.