﻿# Ubiquitous Language

- **Status**: Reference
- **Authors**: Jean Mertz <git@jeanmertz.com>
- **Date**: 2026-05-06
- **Parent**: W-00dxybb
- **Summary**: The single-page glossary of JP's domain terms, holding every term
  not yet moved into a cluster.

This is JP's domain vocabulary: the shared, rigorous terms used across code,
documentation, commits, RFDs, CLI help, and error messages.
Every contributor (human or AI) should use these terms *as written* — don't
paraphrase or substitute near-synonyms.

When you encounter a new concept that doesn't fit existing terms, add it here.
When an existing term is contradicted by usage or misleading, update the
definition — don't paper over the drift with aliases or inline comments
explaining the mismatch.

In disagreements between code and docs, the code is authoritative.

## Table of Contents

<!--toc:start-->

- [Ubiquitous Language](#ubiquitous-language)
  - [Table of Contents](#table-of-contents)
  - [Terms](#terms)
    - [Attachment](#attachment)
    - [Background Task](#background-task)
    - [CommandConfig](#commandconfig)
    - [Compacted View](#compacted-view)
    - [Compaction](#compaction)
    - [Compaction Rule](#compaction-rule)
    - [Config Delta](#config-delta)
    - [Conversation](#conversation)
    - [Conversation Event](#conversation-event)
    - [EditorBackend](#editorbackend)
    - [Envelope](#envelope)
    - [Event Overlay](#event-overlay)
    - [External Login](#external-login)
    - [Fan-Out](#fan-out)
    - [InlineReply](#inlinereply)
    - [Inquiry](#inquiry)
    - [Invocation](#invocation)
    - [JP MCP Server](#jp-mcp-server)
    - [Match](#match)
    - [MCP Host](#mcp-host)
    - [Operation](#operation)
    - [Pinned Conversation](#pinned-conversation)
    - [Provider](#provider)
    - [RFD](#rfd)
    - [Search Hit](#search-hit)
    - [Service Tier](#service-tier)
    - [Signal Router](#signal-router)
    - [Subscription Flow](#subscription-flow)
    - [Summary](#summary)
    - [Thread](#thread)
    - [Tool Call](#tool-call)
    - [Tracking Ticket](#tracking-ticket)
    - [Turn](#turn)
    - [User-Workspace Directory](#user-workspace-directory)
    - [Workspace](#workspace)
    - [Workspace Projection](#workspace-projection)

<!--toc:end-->

## Terms

### Attachment

External content attached to a conversation to provide context: a file, URL
contents, command output, Bear note, etc. Implemented as `Attachment` in
`jp_attachment`.
Each attachment kind is a separate crate (`jp_attachment_file_content`,
`jp_attachment_cmd_output`, and so on).

### Background Task

Async work scheduled to run alongside a command and committed to the workspace
before JP exits.
Implemented by the `Task` trait and `TaskHandler` in `jp_task`.
A task is two-phase: `run` (off the main critical path, cancellable, accumulates
state) and `sync` (with `&mut Workspace`, commits the accumulated state).
The handler lives at `Ctx::task_handler` and is drained exactly once per `jp`
invocation, at the end of `jp_cli::lib::run`.

### CommandConfig

The shape of an external command JP runs on behalf of the user: a program,
arguments, and an optional `shell` flag.
Implemented as `CommandConfig` in `jp_config::types::command`, with
`CommandConfigOrString` providing a string-shorthand variant for TOML/JSON
config.

String-shorthand values (`command = "git log --oneline"`) are parsed with
shell-word semantics via [`shlex::split`], so quoting works (`"echo 'hello
world'"` is one `hello world` arg).
Malformed shell quoting is rejected at config-parse time.

Consumers (tools, conversation labels, the editor env-var fallback, the `cmd:`
attachment URL) share the same shape and parser.
The policy around *when* JP is allowed to run a `CommandConfig` (prompt or not,
confirm `shell = true` invocations) lives on each consumer, not on the shape
itself.

### Compacted View

What the LLM actually receives for a conversation: the raw event stream with
every [Compaction](#compaction) and [Event Overlay](#event-overlay) applied.
Produced by `ConversationStream::apply_projection` in `jp_conversation`, which
also returns a `TurnOrigin` per resulting turn mapping it back to the raw turn
number(s) it stands for.
`jp conversation print --compacted` renders it.

Turn numbering differs between the two: a [Summary](#summary) collapses its
range into a single turn, so the compacted view can be shorter than the
conversation it came from.

**Not the same as** a [Workspace Projection](#workspace-projection).
The word "projection" carries two unrelated meanings in the codebase: applying
conversation overlays (`jp_conversation::stream::projection`) and writing a
conversation into the workspace directory (`Projection` in `jp_storage`).

### Compaction

A non-destructive overlay that reduces what the provider sees for an inclusive
range of turns.
The original events are never removed: the overlay is appended to the
conversation stream and applied when the [Compacted View](#compacted-view) is
built.
Implemented as `Compaction` in `jp_conversation::compaction`, carrying up to
three independent policies over its range — a [Summary](#summary), a reasoning
policy, and a tool-call policy.
See [RFD-064].

A Summary supersedes the other two for the turns it covers.

### Compaction Rule

The configuration that produces a [Compaction](#compaction): how many turns to
preserve at each end, and which policies to apply to the rest.
Implemented as `CompactionRuleConfig` in `jp_config::conversation::compaction`;
each rule yields exactly one Compaction when applied.

**Not the same as** a Compaction.
A rule is durable configuration in relative terms ("keep the last turn"); a
Compaction is the event it produced, pinned to absolute turn indices and stored
in the conversation.

### Config Delta

A recorded change to a conversation's configuration: values to merge, and the
paths to clear before merging them.
Folding a conversation stream's deltas over its base config is what produces the
conversation's resolved config.
Implemented as `ConfigDelta` in `jp_conversation::stream`, either an
`ApplyDelta` (values plus the paths it clears) or a `ResetDelta` (discard the
accumulated state and restart from program defaults).

A delta is computed once, against the state its author means to diff from, and
recorded as given.

**Not the same as** a config snapshot, which states every value rather than what
changed.
Both are carried as a `PartialAppConfig`, so diffing a delta as though it were a
snapshot type-checks — and silently changes what its values mean.

### Conversation

A persistent sequence of events identified by a `ConversationId`, living within
a Workspace.
Implemented as `ConversationStream` in `jp_conversation`.
The user-facing notion of "a chat history with the assistant."

**Not to be confused with Thread.** A Conversation is the stored entity; a
Thread is what we build from it to send to an LLM.

### Conversation Event

The atomic unit of a conversation.
Implemented as `ConversationEvent` (with `EventKind`) in `jp_conversation`.
The variants are `TurnStart`, `ChatRequest`, `ChatResponse`, `ToolCallRequest`,
`ToolCallResponse`, `InquiryRequest`, `InquiryResponse`.

Not every event is sent to LLM providers.
`EventKind::is_provider_visible()` filters the stream down to the chat and
tool-call events; turn markers and inquiries are internal.

### EditorBackend

The frontend seam for invoking the user's configured editor.
Exposes `edit_text` (string in, edited string out) and `edit_file` (open the
editor on caller-owned paths), covering both editing shapes a frontend offers.
Each frontend is one implementation: `TerminalEditorBackend` spawns the editor
as a local process via `EditorConfig::command()`, and `MockEditorBackend`
scripts outcomes for tests.
Defined as the `EditorBackend` trait in `jp_editor`; call sites obtain one
through `build_editor_backend` in `jp_cli`.

### Envelope

The argument shape a [Fan-Out](#fan-out) tool is advertised with: an object
holding one `ops` array, each element of which is one complete set of the tool's
own arguments.
JP's construction rather than anything the tool declares; built by `envelope` in
`jp_mcp::server::fan_out`.

**Not the same as** the tool's own parameter schema, which describes one
[Operation](#operation) and is what each operation is validated against.

### Event Overlay

A non-destructive overlay that rewrites the metadata of the events it matches
when the [Compacted View](#compacted-view) is built.
Recorded when a provider rejects metadata on events already in the stream —
most often a reasoning signature that no longer covers the content beneath it.
The events themselves are never modified: the overlay is appended to the
conversation stream, so the stored history stays byte-identical.
Implemented as `EventOverlay` in `jp_conversation::patch`, matching events by
metadata value rather than by position.

**Not the same as** a [Compaction](#compaction), which is also a non-destructive
overlay.
An Event Overlay changes the metadata of individual events; a Compaction reduces
what a range of turns contributes.

### External Login

A subscription login whose credentials and refresh lifecycle are owned by an
external runtime.
JP registers its configuration directory and observed account identity without
storing its tokens.
Implemented by `CredentialSecret::External` in `jp_credentials`.

**Not the same as** a JP-stored token or a subscription flow: the login
identifies an account, while the flow selects how requests reach the provider.

### Fan-Out

A tool call carrying several independent [Operations](#operation) in an
[Envelope](#envelope), which the [JP MCP Server](#jp-mcp-server) runs as
separate [Invocations](#invocation) and folds into the single response the
caller is waiting for.
A property of a tool's configuration, not of its implementation, which still
receives one operation per execution; configured by `FanOutConfig` in
`jp_config::conversation::tool`.

**Not the same as** a tool that takes several targets as one action, such as
`fs_modify_file`, which applies them together and is approved as one.

### InlineReply

The `jp_inquire` widget for short replies: the interrupt-menu reply (`r` in both
the streaming and tool menus) and the tool argument / result / skip-reason
edits.
It renders on the `/dev/tty` prompt writer and accepts inline typing, with a
`Ctrl+X` escape to the configured editor (the `EditorBackend`) for longer edits.
Submitting produces a `ReplyOutcome`; the call site decides what an empty
submission or a `Ctrl+C` cancel means.
Built on a patched fork of reedline; the inline buffer's editing style is set by
`editor.inline.edit_mode`.

### Inquiry

A structured question-and-answer pair between the assistant, a tool, and/or the
user — distinct from a regular chat message.
Carried as `InquiryRequest` and `InquiryResponse` events within a conversation.
Used for mid-turn clarification that should not appear in the main chat stream
or be sent to the LLM provider as context.

### Invocation

One execution of one tool call inside the [JP MCP Server](#jp-mcp-server), from
the moment the call is admitted to the moment its result is recorded.
It carries an identity the server assigns itself, so two callers asking for the
same tool with the same arguments at the same time remain distinguishable.
Implemented as `InvocationId` in `jp_mcp::server::service`.

Each [Operation](#operation) of a [Fan-Out](#fan-out) call runs as a child of
the call's invocation, and ends by settling into it rather than by being
recorded.

**Not the same as** a [Tool Call](#tool-call), which is the pair of conversation
events an invocation produces, nor a transport request ID, which belongs to
whichever protocol carried the call.

A tool that asks for input ends its execution attempt and runs again with the
answer; both attempts belong to the same invocation.

### JP MCP Server

The in-process service that executes tool calls: it resolves the tool, validates
arguments, runs the local command, built-in, or upstream MCP tool, and produces
the result.
It owns no conversation and makes no policy decision about who answers a
question; it asks the [MCP Host](#mcp-host) for each decision it needs.
Implemented as `Service` in `jp_mcp::server::service`.

**Not the same as** a third-party MCP server, which is an external process
configured under `providers.mcp` and reached *through* this one.

### Match

A [Search Hit](#search-hit) whose line actually contains the pattern, as opposed
to one included only for surrounding context.
Recorded as `is_match` on the hit, along with the byte ranges of the matched
substrings.

Matches are the unit every count in `jp conversation grep` uses: the figure in a
heading, the `--output count` value, and the `--max-matches` cap.

**Not the same as.** A Search Hit, which also covers context lines.

### MCP Host

The side of a tool call that owns everything the [JP MCP Server](#jp-mcp-server)
deliberately does not: admission, argument and result editing, inquiry routing,
and writing the conversation.
The server asks; the Host decides and records.
In JP the Host is the CLI process, reached through the private channel a
`HostReceiver` carries.

**Not the same as** an MCP client, which is any caller that invokes tools over
the protocol.
The Host is one such caller, and a third-party client is another; only the Host
answers the server's decisions.

### Operation

One element of a [Fan-Out](#fan-out) call's `ops` array: one complete set of the
tool's arguments, run as its own child [Invocation](#invocation) and shown,
approved, and reviewed as a call of its own.
`Operation` in `jp_mcp::server::service` identifies one by its call's
invocation, its position, and the number of operations in the call.

**Not the same as** a [Tool Call](#tool-call): the operations of one call share
its tool call id, and the conversation records one response for all of them.
Nor the informal sense used by access policies, where an operation is whatever
one tool execution does to a path.

### Pinned Conversation

A conversation the user has marked as important, so it stays prominent and is
protected from casual removal.
Pinning is a property of the conversation itself, not of any session or view: it
persists with the conversation and means the same thing everywhere the
conversation appears.
Persisted as a `pinned_at` timestamp on the conversation metadata;
`Conversation::is_pinned()` in `jp_conversation` is the predicate.

**Not the same as** binding a session to a particular target.
Pinning marks one conversation as important; it does not make a session "keep
using" that conversation.
Keeping a session attached to a chosen target is a separate, per-session
concept, not conversation pinning.

### Provider

An LLM vendor integration — one of `anthropic`, `google`, `openai`,
`openrouter`, `llamacpp`, `ollama`, `cerebras`, `deepseek`.
Each implements the `Provider` trait in `jp_llm`.

### RFD

"Request for Discussion" — JP's design document format, stored in `docs/rfd/`.
Each RFD captures design rationale for a significant change.
Numeric-prefixed RFDs (`001-`, `002-`, …) are the accepted series; `D`-prefixed
RFDs (`D01-`, `D02-`, …) are drafts or abandoned proposals.
The process itself is defined in [RFD-001].

### Search Hit

One line of conversation content emitted by a search, together with the
coordinate that locates it: the conversation, the turn it came from, and which
part of the conversation it was found in (its scope — title, user, assistant,
reasoning, tool call, and so on).
Implemented as `Hit` in `jp_cli::cmd::conversation::grep`.

A hit is either a [Match](#match) or a context line pulled in by `--context`;
both are hits.

**Not the same as.** A Match (a hit whose line contains the pattern), a
Conversation Event (one hit is a single line from within an event, and one event
can yield many hits).

### Service Tier

The grade of capacity a request is served from, trading price against latency
and availability.
JP models four rungs (`off`, `flex`, `standard`, `priority`) as `ServiceTier` in
`jp_config::model::parameters`, and each [Provider](#provider) maps them onto
its own wire vocabulary.
`off` is the rung that asks for nothing, which every provider expresses by
sending no tier at all.

A provider that sells no equivalent of the requested tier refuses the query
rather than substituting a neighbouring rung, since the substitute would cost
something else.
A tier that is sent is still only a request: providers may shed it to another
tier under load, and bill at whatever actually served it.

**Not the same as** the model: a tier changes what the request costs and how
fast it is served, not which weights answer it.

### Signal Router

The process-wide owner of OS signal handling: `SignalRouter` in
`jp_cli::signals` (RFD 045).
It consumes SIGINT/SIGTERM/SIGQUIT once, tracks Ctrl+C **escalation** (first
press → topmost interrupt handler, second press within the cooldown → graceful
shutdown, any press after shutdown began → immediate exit), and owns the root
**shutdown token** — a `CancellationToken` cancelled when a graceful shutdown
is requested, observed cooperatively by teardown and long-running work.

Scopes that can act on a Ctrl+C register an **interrupt handler** on the
router's LIFO stack via `push_handler`, receiving an RAII guard and a
notification channel polled from their own event loop.
Only the topmost handler is notified; a handler may `decline` to pass the
interrupt down the stack.
The registered scopes are the streaming loop, the tool execution loop, and the
turn-level handler covering gaps between turn phases.

### Subscription Flow

The request implementation selected for subscription authentication within a
provider.
Represented by `SubscriptionFlow` in `jp_config::providers::llm::anthropic`.

**Not the same as** a credential, model, or service tier: selecting a flow does
not select an account, change the model ID, or authorize another billing kind.
API-key requests do not use this selection.

### Summary

Text that stands in for a range of turns in the [Compacted
View](#compacted-view): the turns it covers collapse into a single synthetic
request/response pair carrying the text.
Attached to a [Compaction](#compaction) as `SummaryPolicy` in
`jp_conversation::compaction`, whose `SummarySource` records whether the text
was *generated* (produced by a model reading the raw events in the range) or
*authored* (supplied verbatim by the user).

The distinction is operational: a generated summary can be re-derived for a
wider range, an authored one cannot.

**Not the same as** the mechanical compaction policies (reasoning stripping,
tool-call stripping), which filter events within their range rather than
replacing the range.

### Thread

The decomposed, provider-facing projection of a Conversation: a rendered system
prompt, rendered instruction sections, raw attachments, and a filtered event
stream, ready to be sent to an LLM provider.
Implemented as `Thread` in `jp_conversation::thread`.

A Conversation becomes a Thread at query time, via the config and conversation
pipeline.
A Thread is transient; a Conversation is persisted.

### Tool Call

An LLM-requested function invocation (`ToolCallRequest`) and its eventual
response (`ToolCallResponse`).
Tool calls are events within a Turn.
The tool itself can be a built-in, a local command, an MCP-provided tool, or a
plugin.

### Tracking Ticket

The single ticket that stands for an RFD's implementation as a whole, carrying
its implementation plan as the description.
It is the ticket labelled `tracking-rfd=NNN`; an RFD has at most one.
The key is a setting of `jp rfd`, `tracking.key`, as the phase tickets' is
`relevant.key`.
Whether that ticket is In Progress is what "this RFD is in development" means.

**Not the same as** a phase ticket, which is labelled `relevant-rfd=NNN` and
covers one slice of the work, as does any other ticket whose work belongs to the
RFD.

### Turn

A group of conversation events delimited by a `TurnStart` marker: one user chat
request through the assistant's final response for that request, including any
intermediate tool calls and inquiries.
Implemented as `Turn<'a>` in `jp_conversation::stream::turn_iter`.

A single Conversation contains many Turns, separated by `TurnStart` events.

### User-Workspace Directory

A workspace's per-user data directory,
`~/.local/share/jp/workspace/<slug>-<id>/`: this user's durable state for one
workspace — conversations, session mappings, locks, the roots registry, and the
user-workspace config search root.
Named for the user-workspace config scope: the *this user × this workspace*
point in the user-global / workspace / user-workspace scope taxonomy.
Located by workspace-ID suffix, never by exact name; `<slug>` is cosmetic,
display-only, and never renamed.
Implemented by `FsStorageBackend::with_user_storage` in `jp_storage`;
directory-name parsing lives in `jp_workspace::roots`.
See [RFD-031] and [RFD-087].

### Workspace

The top-level project unit, housing conversations, configuration, plugins, and
state for JP.
Identified by a `.jp/` directory at the project root.
Implemented as `Workspace` in `jp_workspace`.

### Workspace Projection

The copy of a conversation written into the workspace's `.jp/conversations/`
directory so it can be committed to version control alongside the project.
The durable source of truth is the user-local copy; the workspace copy is a
*projection* of it.
Non-local conversations are projected (written to both roots); `--local`
conversations live only in user-local storage and have no projection.
The write intent is carried by a conversation's lock (`Projection` in
`jp_storage`) and derived at load time from where the data lives
(`StoragePresence`).
See [RFD-031].

[RFD-001]: ../rfd/001-jp-rfd-process.md
[RFD-031]: ../rfd/031-durable-conversation-storage-with-workspace-projection.md
[RFD-064]: ../rfd/064-non-destructive-conversation-compaction.md
[RFD-087]: ../rfd/087-session-scoped-active-workspace.md
[`shlex::split`]: https://docs.rs/shlex
