Skip to main content

Scenarios and usage guidelines

The quickstart shows the shortest path: capture, send, receive. This page shows why you'd run those (and the rest of the CLI), in three realistic situations, plus the guidance to decide when Ophiuchus is (and isn't) the right tool.

Ophiuchus ships ten commands: version, status, doctor, alpha-token-new, capture, inspect, export, send, receive, history. The scenarios below use most of them; run ophiuchus --help for the full list. There is no feature behind any of these scenarios that isn't one of those ten commands — only the situation and the fields you fill in change.

This is a hypothesis, not a proven result

The rediscovery-avoidance benefit these scenarios describe is not a measured result. It's the thing Ophiuchus's dogfooding is trying to find out, based on one real handoff so far. See the overview's note on this before treating any scenario below as a guaranteed time savings.

Contact Cards are not a persistent address book

capture accepts an optional contacts field (see Scenario A) — a bounded, sender-supplied "who to ask" pointer rendered clearly as unverified data, never as a confirmed identity. That is the entire feature. There is no persistent contact list, address book, org directory, or "who owns this" lookup anywhere in Ophiuchus, and no code path that infers, resolves, or verifies a contact_ref against anything — it's whatever string the sender typed (@bob, #incident-channel, an email address), rendered back exactly as given. If a capsule needs a contact and the sender didn't supply one, there is no fallback lookup — the field is simply absent.

Scenario A — cross-user, cross-tool handoff​

Alice is debugging a token-refresh bug with Claude Code on her laptop. She's found the real cause, ruled out a theory that looked plausible, and is handing the fix to Bob, who picks it up with Codex on a different machine.

Precondition: something both machines can reach over HTTPS is running the relay. The quickstart's loopback relay only works for one machine talking to itself — for a real Alice-and-Bob handoff, one of you stands up the relay (uv run python -c "from ophiuchus.transport.relay import run; run()", put your own TLS in front of it) and shares that URL with the other, out of band.

1. Alice captures. She knows Bob may still have questions once he's read her evidence, so she includes a Contact Card pointing at herself — optional, and just plain text she typed:

echo '{
"goal": "make the token refresh survive a clock skew",
"state": "reproduced the failure; the expiry is compared in local time",
"next_action": "switch the expiry comparison to UTC and add a regression test",
"rejected": ["the network-timeout theory - the request never leaves the client"],
"evidence": ["test_refresh_after_skew fails only when TZ=America/Chicago"],
"constraints": ["cannot change the token format - other services already parse it"],
"open_questions": ["does the mobile client hit the same comparison, or a separate one?"],
"contacts": [
{"display_name": "Alice", "role": "Original reporter", "preferred_channel": "github", "contact_ref": "@alice"}
]
}' | uv run ophiuchus capture --name handoff --out-dir .ophiuchus \
--repo /path/to/the/work/repo \
--producer-version "$(claude --version)"

This writes .ophiuchus/handoff.capsule.json (the wire format) and .ophiuchus/handoff.capsule.md (Alice's own readable copy — Bob does not need this file).

1a. Alice previews it before sending (optional but recommended). inspect renders exactly what's about to leave the machine — including the Contact Card and the result of capture's deterministic quality checks (missing evidence, missing next action, that kind of thing) — without transmitting anything:

uv run ophiuchus inspect .ophiuchus/handoff.capsule.json

send --preview is a second way to see the same rendering without a separate inspect call — it still exits without transmitting anything, so you'd run plain send again afterward to actually send.

2. Alice sends it.

uv run ophiuchus send .ophiuchus/handoff.capsule.json --relay-url https://the-shared-relay
# location_id: bb0f870f…
# expires_at: 2026-09-06T21:15:09+00:00
# token: HNwRSQs8…

3. Exactly what Alice gives Bob. Two values, over two different channels, because they have different sensitivity:

  • location_id — a locator, not a secret. Fine in Slack, email, a Jira comment.
  • token — a secret. Anyone holding it can fetch the capsule until it expires or is consumed. Send it the way you'd send a password: a DM, not a public channel, not a commit message.

Nothing else. Not a file, not a URL to click, not a repo invite.

4. Bob receives it.

export OPHIUCHUS_CAPABILITY_TOKEN=…the token Alice sent…
uv run ophiuchus receive --location-id bb0f870f… --name handoff --with codex \
--relay-url https://the-shared-relay

5. What Bob should see. handoff.capsule.md rendered as a Codex-ready document: Alice's goal/state/next_action quoted as her claims, the rejected theory called out as already-attempted, Alice's Contact Card rendered as sender-supplied — not a verified identity — in case Bob's evidence review still leaves something only Alice can answer, and a freshness line comparing the capsule's grounding commit against Bob's own checkout.

6. Afterwards: ophiuchus history and ophiuchus export. Once the fetch completes, ophiuchus history lists it on both machines — a low-sensitivity local record (who sent/received, when, what status) that never stores the capsule body, the token, or the Contact Card value itself. If either of them wants a durable copy to paste into a PR description or a Jira comment, ophiuchus export handoff.capsule.json produces the same Markdown rendering as a file, byte-identical to what capture already wrote — it never talks to the relay again.

How Bob verifies he avoided rediscovery: before touching the code, Bob reads the rendered rejected section. If his first instinct was "maybe it's a network timeout," the document already tells him that's closed — he doesn't have to re-run an investigation Alice already closed off to find that out himself. Whether that reliably happens, and how much it's worth when it does, is exactly the open question noted above — watch for it happening or not happening rather than assuming it will.

TTL / delete-after-read: send --delete-after-read makes Bob's fetch the last one possible — a second receive with the same token 404s exactly like an expired one would. Without that flag, the capsule stays fetchable by anyone holding the token until expires_at.

Scenario B — handing a fix to a different repo​

Same mechanism as Scenario A. What's different: Alice's investigation happened in her repo, but the fix belongs in a repo she doesn't own.

The only thing that changes is which repo --repo points at: point it at the repo the fix belongs in, not the one Alice was debugging in. Ophiuchus does not know these are "different teams" or "different owners" — it has no concept of ownership at all. The organizational boundary Alice is crossing is real; the tool crossing it is exactly the same capture/send/receive triple, grounded against whichever checkout is relevant to the recipient's next action:

echo '{
"goal": "payments service 500s on a specific webhook payload shape",
"state": "confirmed in api-gateway logs; the malformed field originates in payments-service's webhook validator, not the gateway",
"next_action": "add validation for the missing optional field in payments-service's webhook handler",
"evidence": ["gateway access log shows the 500 originates downstream, not at the gateway"],
"rejected": ["gateway timeout theory - the gateway forwards successfully; payments-service returns the 500"],
"constraints": ["cannot change the webhook payload shape - it is set by the external provider"],
"open_questions": ["does payments-service already validate this field elsewhere and just miss this path?"]
}' | uv run ophiuchus capture --name webhook-500 --out-dir .ophiuchus \
--repo /path/to/payments-service \
--producer-version "$(claude --version)"

--repo here is payments-service's checkout — not api-gateway, where Alice actually found the bug — because that's the repo the receiving engineer's freshness check needs to be grounded against. Send and receive proceed exactly as in Scenario A; the receiving engineer gets Alice's evidence and the ruled-out gateway theory without having to independently re-diagnose which service the bug is actually in.

Scenario C — interrupted work, resumed later​

You (one person, one investigation) stop for the day, or switch from Claude Code to Codex on the same task without a live conversation window to hand off from.

What Ophiuchus preserves: exactly what you put in capture — goal, state, next_action, and whichever of evidence/rejected/constraints/ open_questions you filled in — plus the commit your checkout was on when you captured it.

What it does not preserve: anything you didn't write into those fields. Not your shell history, not the files you opened, not the tool calls Claude Code made, not a transcript. If it mattered enough to matter tomorrow, it has to be a sentence in the capsule.

# before stopping:
uv run ophiuchus send .ophiuchus/investigation.capsule.json
# note the location_id and token somewhere you'll have tomorrow

# tomorrow, same or different machine:
export OPHIUCHUS_CAPABILITY_TOKEN=…
uv run ophiuchus receive --location-id … --name investigation --with codex

How to verify the continuation is actually useful: open the rendered Markdown and check the freshness banner first. If your checkout has moved past the grounding commit (you or someone else committed something in the meantime), Ophiuchus says so loudly rather than pretending the context is still current — that banner, not the presence of the file, is what tells you whether to trust next_action as-is or re-verify state before continuing.

This is the same thing the product's internal experiment calls Time-to-Useful-Continuation: how fast you get back to doing correct work, not just to having a file open again.

Scenarios D–F: fidelity, validity and continuity​

Scenarios A–C cover the base fields. The three scenarios below exercise the optional critical, validity, negative_knowledge and --parent fields covered in Fidelity, validity, negative knowledge and continuity — each one is a real, runnable sequence against the loopback relay, not a hypothetical. Every command below was run against v0.0.0 before this page was written; the exact output shown is what that run printed.

Shared precondition for all three: the loopback relay running in its own terminal (quickstart step 2), and every command run from your Ophiuchus checkout.

Scenario D — a critical constraint and a rejected hypothesis survive a handoff​

A predecessor marks its rejected hypothesis as must-preserve, sends it, and a successor's rendering shows it came through.

1. Capture, with a Critical Context Unit pointing at rejected[0]:

echo '{
"goal": "stop the nightly inventory-reconciliation job from double-counting returned units after a partial retry",
"state": "reproduced against the fixture warehouse dataset: the retry path re-reads the batch from its start offset instead of resuming from the last committed checkpoint",
"next_action": "change the retry path to resume from the persisted checkpoint offset, then rerun test_partial_retry_no_double_count",
"evidence": ["test_partial_retry_no_double_count FAILED - expected on_hand=142, got on_hand=178 for SKU-40921 after simulated timeout+retry"],
"rejected": ["the queue-redelivery theory - ruled out because the broker'\''s delivery-count metric stays at 1 for every event in the reproduction, so nothing is being redelivered"],
"critical": [
{"unit_id": "cu-rejected-0", "kind": "rejected", "index": 0, "required": true}
]
}' | uv run ophiuchus capture --name handoff-d --out-dir .ophiuchus --producer-version demo

Expected intermediate state: inspect .ophiuchus/handoff-d.capsule.json shows a ## Critical context units section with cu-rejected-0 — rejected[0] (required). This is the sender-side preview only — the verdict itself is still unknown here, because no consumer path has evaluated it yet.

2. Send and receive (same machine is fine for this check):

uv run ophiuchus send .ophiuchus/handoff-d.capsule.json
# note location_id and token

export OPHIUCHUS_CAPABILITY_TOKEN=<token>
uv run ophiuchus receive --location-id <location_id> --name handoff-d --with codex

Final observable verification: the rendered ## Context fidelity section in handoff-d.capsule.md reads:

- `cu-rejected-0` (rejected[0], required): preserved — carried through to
this consumer's rendering.

inspect cannot show this verdict at all, with or without --repo: inspect takes only a capsule file and --repo (which affects validity — freshness against a checkout — not fidelity), and never renders against a consumer adapter. Running ophiuchus inspect .ophiuchus/handoff-d.capsule.json --repo <recipient's checkout> prints "Declared by the sender. No consumer path has been evaluated" for the critical unit regardless of --repo. A fidelity verdict is only ever computed against a specific consumer's rendering (see ophiuchus.capsule.fidelity), which only receive --with codex (used above) performs — there is no local-only shortcut for this particular check.

Cleanup: delete .ophiuchus/handoff-d.capsule.* (or rm -rf .ophiuchus) — capsule files under .ophiuchus/ are gitignored, so this is local-only cleanup, not a repository change.

Scenario E — a handoff arrives after the world moved on​

A predecessor declares a validity condition. By the time the recipient reads it, the declared condition has lapsed — the successor sees a loud stale verdict rather than silently trusting old context.

1. Capture, with a time_window condition two seconds out and a declared_scope condition (always unverifiable):

NOT_AFTER="$(python3 -c 'import datetime; print((datetime.datetime.now(datetime.UTC)+datetime.timedelta(seconds=2)).isoformat())')"
echo "{
\"goal\": \"fix the nightly inventory-reconciliation retry offset bug\",
\"state\": \"root cause confirmed in the retry path'\''s checkpoint handling\",
\"next_action\": \"apply the checkpoint-resume fix and rerun the regression test\",
\"validity\": [
{\"condition_id\": \"vc-window\", \"kind\": \"time_window\", \"not_after\": \"$NOT_AFTER\", \"required\": true},
{\"condition_id\": \"vc-scope\", \"kind\": \"declared_scope\", \"subject\": \"the demo fixture only\", \"required\": true}
]
}" | uv run ophiuchus capture --name handoff-e --out-dir .ophiuchus --producer-version demo

Expected intermediate state: run ophiuchus inspect .ophiuchus/handoff-e.capsule.json immediately — vc-window reads valid ("before its deadline …"), vc-scope reads unverifiable (it always does).

2. Wait for the deadline to pass (3 seconds is enough), then send and receive:

sleep 3
uv run ophiuchus send .ophiuchus/handoff-e.capsule.json
# note location_id and token

export OPHIUCHUS_CAPABILITY_TOKEN=<token>
uv run ophiuchus receive --location-id <location_id> --name handoff-e --with codex

Final observable verification: receive prints this on stderr, and the same line appears at the top of handoff-e.capsule.md:

warning: CONTEXT VALIDITY — partially_stale (2 required condition(s) not
valid; ids: `vc-window, vc-scope`). Validity compares the sender's declared
conditions against this machine's checkout only. It is not a trust,
authority or correctness judgement, and it verifies no system outside this
repository.

The aggregate is partially_stale, not stale — one required condition (vc-window) is concretely stale and the other (vc-scope) is unverifiable; per the aggregation rule described in Semantics, a stale result mixed with anything else outranks an unrelated unverifiable rather than the two averaging out or unverifiable winning. Both per-condition verdicts are visible individually under ## Context validity in the same file. A capsule declaring only a git_paths condition against a file you then edit in your working tree (uncommitted is enough) demonstrates the same stale/partially_stale mechanism grounded in a real repository change instead of a clock — revert the edit afterward (git checkout -- <path>) if you try this against a tracked file.

Cleanup: delete .ophiuchus/handoff-e.capsule.*.

Scenario F — a second handoff supersedes evidence and revalidates a rejected path​

A predecessor rejects a theory, conditioned on a validity window. That window lapses before the theory is revisited. A successor capsule declares the first one as its parent, marks the negative-knowledge item revalidated, and supersedes a critical unit — inspect on the successor shows exactly what changed.

1. Capture the parent capsule. Omit --name so the file is written as <capsule_id>.capsule.json — this matters: inspect's continuity walk looks for a sibling file named after the declared parent_capsule_id, in the same directory as the capsule being inspected, so the child capsule must be captured into the same --out-dir as the parent, and the parent must keep its capsule-id filename.

NOT_AFTER="$(python3 -c 'import datetime; print((datetime.datetime.now(datetime.UTC)+datetime.timedelta(seconds=2)).isoformat())')"
echo "{
\"goal\": \"fix the nightly inventory-reconciliation retry offset bug\",
\"state\": \"root cause narrowed to the retry path'\''s checkpoint handling\",
\"next_action\": \"confirm the checkpoint offset is honored on retry\",
\"evidence\": [\"reconciliation.log line 2201: resuming batch b-2026-09-01-03 at offset 0 (expected 8400)\"],
\"critical\": [
{\"unit_id\": \"cu-p-next-action\", \"kind\": \"next_action\", \"required\": true}
],
\"validity\": [
{\"condition_id\": \"vc-window\", \"kind\": \"time_window\", \"not_after\": \"$NOT_AFTER\", \"required\": true}
],
\"negative_knowledge\": [
{
\"item_id\": \"nk-queue-redelivery\",
\"hypothesis\": \"the queue is redelivering duplicate events; add an idempotency key at the consumer\",
\"why_invalid\": \"the broker'\''s delivery-count metric stays at 1 for every event in the reproduction, so nothing is being redelivered\",
\"state\": \"rejected\",
\"evidence_refs\": [0],
\"applies_while\": [\"vc-window\"],
\"retry_if\": \"revisit if the broker'\''s delivery-count metric ever moves off 1 during a future reproduction\"
}
]
}" | uv run ophiuchus capture --out-dir .ophiuchus/chain --producer-version demo-parent
# wrote .ophiuchus/chain/<parent-id>.capsule.json — note <parent-id>

2. Wait for the window to lapse, then confirm the parent alone now needs revalidation:

sleep 3
uv run ophiuchus inspect .ophiuchus/chain/<parent-id>.capsule.json

Expected intermediate state: the ## Negative knowledge section reads:

- `nk-queue-redelivery` (`rejected`): revalidation_required — at least one
applicability condition is stale or partially stale — the assumption this
rejection depended on may no longer hold.

3. Capture the child, declaring the parent and revalidating the item:

NOT_AFTER_1H="$(python3 -c 'import datetime; print((datetime.datetime.now(datetime.UTC)+datetime.timedelta(hours=1)).isoformat())')"
echo "{
\"goal\": \"fix the nightly inventory-reconciliation retry offset bug\",
\"state\": \"checkpoint-resume patch verified on staging; re-checked the queue-redelivery theory after the earlier window lapsed and it still does not explain the double-count\",
\"next_action\": \"ship the checkpoint-resume fix and add the regression test\",
\"evidence\": [\"reconciliation.log line 2201: resuming batch b-2026-09-01-03 at offset 0 (expected 8400)\", \"staging soak: delivery-count metric held at 1 throughout; duplicate-count bug persisted regardless\"],
\"critical\": [
{\"unit_id\": \"cu-c-next-action\", \"kind\": \"next_action\", \"required\": true, \"supersedes\": \"cu-p-next-action\"}
],
\"validity\": [
{\"condition_id\": \"vc-window\", \"kind\": \"time_window\", \"not_after\": \"$NOT_AFTER_1H\", \"required\": true}
],
\"negative_knowledge\": [
{
\"item_id\": \"nk-queue-redelivery\",
\"hypothesis\": \"the queue is redelivering duplicate events; add an idempotency key at the consumer\",
\"why_invalid\": \"re-checked after the parent'\''s applicability window lapsed: the delivery-count metric held at 1 throughout a staging soak and the double-count bug persisted regardless, so redelivery still is not the cause\",
\"state\": \"revalidated\",
\"evidence_refs\": [1],
\"applies_while\": [\"vc-window\"]
}
]
}" | uv run ophiuchus capture --out-dir .ophiuchus/chain --producer-version demo-child --parent <parent-id>
# wrote .ophiuchus/chain/<child-id>.capsule.json

Final observable verification:

uv run ophiuchus inspect .ophiuchus/chain/<child-id>.capsule.json

prints a ## Continuity section:

Parent: `<parent-id>`
Chain: 1 ancestor(s) observed, termination: complete
- depth 1 `<parent-id>`: present — loaded from disk
- critical: inherited 0, added 1, dropped 1
- validity: inherited 1, added 0, dropped 0
- negative_knowledge: inherited 1, added 0, dropped 0
- rejected: carried_over 0, added 0, absent_here 0
- evidence: carried_over 1, added 1, absent_here 0
- constraints: carried_over 0, added 0, absent_here 0
- open_questions: carried_over 0, added 0, absent_here 0
- `cu-c-next-action` supersedes `cu-p-next-action`: resolved=True
- `nk-queue-redelivery`: `rejected` → `revalidated`

resolved=True confirms the successor's supersedes pointer actually resolved against a unit_id in the parent's critical list — the next_action Critical Context Unit was explicitly replaced, not silently dropped. The negative_knowledge transition line confirms the previously-rejected nk-queue-redelivery theory was formally revalidated, not merely restated: the child's own ## Negative knowledge section now shows it as retired (state revalidated always reads retired, regardless of any applicability condition), closing the loop the parent's revalidation_required opened.

Cleanup: rm -rf .ophiuchus/chain.

Scenario G — publishing a handoff as a Record or a Discussion​

Scenarios A–F move a capsule between two Ophiuchus sessions. This scenario is different: it materializes a verified handoff where your team already works — a GitHub issue, a Jira ticket, a ClickUp task, a Slack message, a Teams channel message — using ophiuchus publish (ADR-0025, the Workflow Handoff Bridge). Ophiuchus remains the handoff layer here too: it is never the system of record for GitHub/Jira/ClickUp/Slack/Teams, and it never reads anything back from them. There is no bidirectional sync, no polling, no webhook ingestion — see Semantics for the full non-goal list.

ophiuchus publish takes the capsule you already captured (same file capture wrote in Scenario A) and one of two profiles:

  • --profile record — a durable, management-friendly artifact: title, goal, current state, evidence, rejected hypotheses, constraints, requested next action. Destinations: GitHub (issues), Jira (issues/comments), ClickUp (tasks/comments).
  • --profile discuss — a shorter, conversation-friendly artifact: summary, why discussion is needed, trimmed evidence, rejected hypotheses, requested discussion. Destinations: Slack (messages), Teams (channel messages).

--preview is mandatory to run first, every time, for every provider: it composes the view and prints exactly what would leave this machine — --to/provider is never looked up, no credential is read, and no network call is made — so it is honestly side-effect-free regardless of destination. Contact Cards are excluded unless you pass --include-contacts explicitly.

Credential setup (any provider): set the OPHIUCHUS_<PROVIDER>_* environment variable(s) documented for that provider — never a literal example token in a capsule, a command, or this page. The capability matrix below names the exact credential/scope each provider needs.

This environment vs. yours: the commands below are real — they are the literal ophiuchus publish invocations you'd run — and GitHub's has real end-to-end evidence behind it (verified against a disposable sandbox repo, Chisanan232/ophiuchus-publish-sandbox). Jira, ClickUp, Slack, and Teams adapters are built and unit-tested, but this environment does not hold a live credential for any of them, so a mutating publish call to one of those four here would stop at a credential-missing error before any network call, not because the command is wrong. Run them for real once you've set the provider's credential in your own environment.

G1 — GitHub: create an issue from a RECORD view (real E2E evidence)​

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile record --to github --target <OWNER>/<REPO> --preview
# prints the composed RECORD view: title, goal, current state, the rejected
# hypothesis, requested next action -- nothing sent yet

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile record --to github --target <OWNER>/<REPO>
# creates a new GitHub issue in <OWNER>/<REPO>; the printed outcome carries
# the issue's external reference

To comment on an existing issue instead of creating one, add --operation append --target <OWNER>/<REPO>#<ISSUE_NUMBER>.

Afterwards: the Delivery Receipt sidecar (not ophiuchus history, which only ever knows about relay sends/receives) now holds one row for this publication — provider, operation, the GitHub issue reference, status published, and the idempotency_key this call used. Deleting the local capsule or history entry later does not delete the GitHub issue — its lifecycle now belongs to GitHub (ADR-0025 §6).

G2 — Jira: create an issue from a RECORD view​

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile record --to jira --target "<PROJECT_KEY>:Task" --preview

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile record --to jira --target "<PROJECT_KEY>:Task"

Jira's --target for --operation create (the default) is "<PROJECT_KEY>:<IssueType>" (e.g. "HORO:Task") — a project key alone is not enough, Jira always requires an issue type. For --operation append, --target is an existing issue key (e.g. "HORO-1520").

G3 — Slack: post a discussion message from a DISCUSS view​

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile discuss --to slack --target <CHANNEL_ID> --preview

uv run ophiuchus publish .ophiuchus/handoff.capsule.json \
--profile discuss --to slack --target <CHANNEL_ID>

To reply within an existing thread instead of starting a new message, use --operation reply --target <CHANNEL_ID>:<THREAD_TS>.

Teams — honest current gate, not a fourth runnable scenario​

Teams' --profile discuss --to teams adapter is built and unit-tested (create a channel message / reply to one, against Microsoft Graph's real POST /teams/{team_id}/channels/{channel_id}/messages and .../messages/{message_id}/replies endpoints) but this environment has no live Microsoft Teams tenant to publish against — there is no real E2E evidence for Teams the way there is for GitHub. The exact credential it needs, when you have a tenant to point it at: a Microsoft Graph access token for an app or delegated identity granted the single scope ChannelMessage.Send (never a broader scope like Directory.Read.All) for the target team/channel, set via OPHIUCHUS_TEAMS_ACCESS_TOKEN. Until that credential exists somewhere this CLI can reach, treat ophiuchus publish ... --to teams as --preview-only in practice.

Capability matrix​

Built from each adapter's own declared DestinationCapability (src/ophiuchus/bridge/providers/*.py), not asserted from memory — a provider that cannot honestly do something declares that absence rather than claiming universal parity (ADR-0025 §3):

ProviderProfileOperationsIdempotencyMention handlingReal E2E evidence in this environment
GitHubrecordcreate, appendclient-managedinert only (mentions are neutralized, never active)Yes — Chisanan232/ophiuchus-publish-sandbox
Jirarecordcreate, appendclient-managedinert onlyNo — credential-gated Human Gate
ClickUprecordcreate, appendclient-managedinert onlyNo — credential-gated Human Gate
Slackdiscusscreate, replyclient-managedinert onlyNo — credential-gated Human Gate
Teamsdiscusscreate, replyunknown (not verified; no mitigation beyond never retrying internally)inert onlyNo — credential-gated Human Gate, no live tenant

No provider in this table is native idempotency today — none of the five providers' APIs are confirmed to accept the idempotency_key this adapter derives as a provider-native dedup key, so every one relies on the Delivery Receipt sidecar's (idempotency_key -> external_reference) record to avoid a duplicate external write on retry (ADR-0025 §5). mention_support is inert_only for every provider, by binding default — none of these adapters is authorized to let a capsule-derived @mention or <!channel> trigger a real notification; that would require its own ticket and its own security review (ADR-0025 §7).

Lifecycle asymmetry: deleting a local Ophiuchus Context/history entry never deletes the external GitHub issue, Jira ticket, ClickUp task, Slack message, or Teams message it produced. The external artifact's lifecycle is owned by its own provider, not by Ophiuchus (ADR-0025 §6).

Non-goals (binding, not an omission): no bidirectional sync, no polling external changes back into Ophiuchus, no webhook ingestion, no treating any of these five providers as a canonical Context store, and no Slack/Teams inbox or chat-client behavior — Ophiuchus posts one message and never reads the channel. See Semantics for the complete list.

What do I give the other person? (summary)​

You want to hand over...Concrete artifactSensitivity
Everything needed to continuelocation_idShare freely — it's a locator, not a credential
Permission to fetch ittoken (printed once by send)Secret — private channel only, like a password
Nothing else—No file, no URL to click, no repo invite is needed

Usage guidelines​

When to use Ophiuchus vs. Slack/Jira/PR: use it when the thing crossing the boundary is in-flight investigation state — a goal, ruled-out theories, evidence — that would otherwise get lossily summarized by a human, or not transferred at all. Use a PR when the artifact is the code change. Use Jira when the artifact is a planned, scoped work item. Use Slack when it's a synchronous question, not a structured handoff. These aren't mutually exclusive — Scenario A's location_id is exactly the kind of thing you'd also paste into a Slack message or a Jira comment.

What belongs in a capsule — least-sufficient context: goal, state, and next_action always. evidence, rejected, constraints, and open_questions only when they exist and matter to the next step. Don't paste a full log — cite the one line that proves the point. A capsule that tries to carry everything is the raw transcript this product exists to avoid sending.

Sensitive information: a sent capsule leaves your machine and sits on whatever relay you pointed it at, unencrypted at rest by default. The relay operator can read it. Never put credentials, secrets, or personal data into a capsule field. capture runs an advisory local secret scan and refuses by default if it trips; --allow-secrets overrides that and still reports every finding, masked, to stderr — read Privacy and safe use before you reach for that flag.

Evidence vs. raw transcript: evidence is a specific, citable fact — a test name, a log line, a repro command's output — not a pasted conversation. If you're tempted to paste the whole chat, that's the sign you should be summarizing into state or evidence instead.

Rejected hypotheses / negative knowledge: the highest-value field on the whole capsule. Naming what you tried and ruled out is the one thing a normal handoff note almost always drops, and it's exactly what stops the recipient from re-walking a dead end you already closed.

Constraints and open questions: constraints is a boundary already decided — the recipient must not cross it while continuing. open_questions is something you didn't resolve — naming it explicitly is different from silently omitting it, because an omitted question reads as answered and an open one reads as still unknown.

Contact Cards: include one only when you already know who the recipient should ask if the capsule alone isn't enough — a Contact Card is sender-supplied data, not a directory lookup, so there's nothing to include if you don't already know. Every field in it leaves your machine when the capsule is sent, same as everything else in the payload; inspect shows you exactly what that looks like before you send.

Preview before you send: ophiuchus inspect <capsule> (or send --preview) is the honest way to answer "what exactly am I about to transmit?" without a network call — it renders the same content a recipient will see, plus the deterministic quality checks (missing evidence, missing next action, capsule looks otherwise thin) that capture itself doesn't block on.

Local history and durable export: ophiuchus history is a low-sensitivity local log of your own sends/receives (who, when, what status) — it never stores the capsule body, a capability token, or a Contact Card value, and it does not talk to the relay, so it only knows what happened on this machine. Clear it with ophiuchus history clear. ophiuchus export <capsule> --format markdown turns a capsule you already have locally into a durable Markdown file — the same rendering capture already wrote, just retrievable again later, for pasting into a PR, a Jira comment, or an incident doc. Neither command changes what's stored on the relay or how long a capsule survives there.

Troubleshooting: ophiuchus doctor is the first thing to run when anything above doesn't behave as expected — it checks your install, config, relay reachability, schema compatibility, and local write permissions in one pass, and reports secrets only as present/absent, never their value.

TTL and lifecycle:

A capsule is temporary by construction. The relay enforces its own expiry ceiling (one hour by default) regardless of what a longer-lived capsule claims for itself — send warns you when the two disagree. --delete-after-read makes the first successful fetch the last one. There is no CLI command to delete a capsule early in this build; the relay's DELETE /capsules/{location_id} HTTP endpoint (same bearer token send printed) does it directly — see the quickstart's deletion note.

Retry / expired / revoked behavior: a missing, expired, already-consumed, or wrong-token fetch all return the same 404-shaped response, on purpose — Ophiuchus does not tell an unauthorized caller why a capsule is unreachable, only that it is. If receive fails this way, the right move is to ask the sender to check whether it already expired or was consumed, and resend if so — not to assume the token was mistyped.

When NOT to use Ophiuchus:

  • Continuing your own work in the same session — you don't need a handoff tool to keep talking to the same agent.
  • Durable team knowledge. A capsule expires, history only knows this machine's own sends/receives, and export produces one file at a time — none of these add up to a knowledge base or a search index over past handoffs.
  • Finding out who owns something you don't already know. A Contact Card only carries what the sender already knew and chose to type; there is no lookup to fall back on.
  • Anything that needs encryption-at-rest or a compliance guarantee — Ophiuchus doesn't provide either.
  • A recipient whose checkout has no relationship to the sender's repo at all — the freshness check will always report the histories as unrelated, and the handoff degrades to "read this text," which a normal message would do just as well.