Recorded session

Nine prompts, one docs change, no agent merges.

A real Claude Code session against companion mcp — from the first question to a committed changeset. Nothing here is a mock-up: the tool calls, the JSON and the refusal at the end are what the client sent and received.

claude codecompanion mcpstdio

you

Use the companion MCP server for this. Call docs_get_context and tell me where our docs live for this repo. Do not guess paths.

companionok

docs_get_context{}
resultjson
{
  "context": {
    "project": "acme/api",
    "team": "payments",
    "languages": ["go"]
  },
  "area": "projects/acme/api",
  "docs": [
    { "path": "projects/acme/api/README.md",
      "kind": "project-doc", "status": "approved",
      "source": "main", "state": "main" },
    { "path": "projects/acme/api/flows/refund.md",
      "kind": "flow", "status": "approved",
      "source": "changeset cs-mztr4zb3emja", "state": "added" },
    { "path": "org/patterns/messaging/sqs.md",
      "kind": "pattern", "status": "approved",
      "source": "main", "state": "main" }
  ],
  "missing_required": ["projects/acme/api/architecture.md"],
  "active_changesets": [
    { "id": "cs-mztr4zb3emja", "title": "refund flow",
      "pr": 0, "state": "none" }
  ],
  "mirror_rev": "14285dc",
  "mirror_age": "1m ago",
  "warnings": ["GitHub token unavailable; open PRs were not checked"]
}

user, machine, repo_root and the per-field `sources` map are omitted.

agent

The docs for this repo don't live in the code repo. They're in the Companion docs store, under the area projects/acme/api.

PathKindStatusWhere it is
projects/acme/api/README.mdproject-docapprovedon main
projects/acme/api/flows/refund.mdflowapprovedadded in changeset cs-mztr4zb3emja, no PR yet
org/patterns/messaging/sqs.mdpatternapprovedon main (org-wide, not project-specific)

Missing required doc: projects/acme/api/architecture.md.

Canonical layout, relative to projects/acme/api/:

  • Root: only README.md and architecture.md
  • adr/YYYY-MM-DD-<slug>.md: one file per decision
  • flows/<slug>.md, runbooks/<slug>.md, patterns/<slug>.md, standards/<slug>.md
  • Every doc needs YAML frontmatter with id, kind, title, status, owners and last_reviewed

The local mirror is current: rev 14285dc, synced 1 minute ago. I could not check open PRs because no GitHub token is available.

you

Before I write an SQS consumer here: call docs_get_guidance with the task "implement an SQS consumer in Go" and lang go. Summarise the ranked results and cite the path of each one.

companionok

docs_get_guidance{ "task": "implement an SQS consumer in Go", "language": "go" }
resultjson
{
  "results": [
    {
      "id": "sqs-consumer",
      "title": "SQS consumer",
      "path": "org/patterns/messaging/sqs.md",
      "kind": "pattern",
      "status": "approved",
      "scope": "org",
      "score": 4.27,
      "matched_terms": ["sqs", "consumer", "go"],
      "why": "matched in body, headings, refs, title, topics",
      "code_context": [
        {
          "repo": "acme/api",
          "path": "internal/consumer/consumer.go",
          "symbol": "Consume",
          "commit": "e79d9d2",
          "path_status": "valid",
          "symbol_status": "valid",
          "drift_status": "fresh",
          "provider": "go",
          "line": 4,
          "qualified": "consumer.Consume",
          "signature": "func(body string) error"
        }
      ]
    }
  ],
  "task": "implement an SQS consumer in Go"
}

`topics`, `languages`, `last_reviewed` and `reference_impls` are omitted.

agent

Only one result came back, so there is nothing to rank against.

  • SQS consumer — org/patterns/messaging/sqs.md, score 4.27. An approved org-wide pattern for Go; "sqs", "consumer" and "go" appear in its title, headings, body, topics and code references.
  • Example code — internal/consumer/consumer.go:4, the function consumer.Consume with signature func(body string) error, on main at commit e79d9d2. Companion checked that the file and function exist and match the doc.

No ADRs or standards came back, and nothing project-specific under projects/acme/api. The org pattern is the only documented guidance.

The returned snippet was just the doc's heading, so I haven't seen the pattern's actual rules yet. I can fetch org/patterns/messaging/sqs.md and read it before you start.

you

Read the top result with docs_read, and quote only the lines about dead-letter queues and idempotency.

companionok

docs_read{ "path": "org/patterns/messaging/sqs.md" }
resultdocument
---
id: sqs-consumer
kind: pattern
title: SQS consumer
status: approved
topics: [sqs, queue, consumer, messaging]
languages: [go]
owners: [payments]
last_reviewed: 2026-10-26
reference_impls:
  - repo: acme/api
    path: internal/consumer/consumer.go
    symbol: Consume
    kind: function
    ref: main
---
# SQS consumer

Use a dead-letter queue, idempotency keys and bounded concurrency.

## When to use

Use this pattern when a Go service consumes an SQS queue.

## Rules

- MUST configure a dead-letter queue.
- MUST make the handler idempotent.
- SHOULD bound worker concurrency.

## Reference implementations

| Repository | Path | Why |
|---|---|---|
| acme/api | `internal/consumer/consumer.go` | existing consumer in this repo |

Returned as a JSON envelope with `content`, `hash`, `source` and `state`.

agent

Use a dead-letter queue, idempotency keys and bounded concurrency.
- MUST configure a dead-letter queue. - MUST make the handler idempotent.

The first line is the doc's one-line summary; the other two are from the Rules section. That is all the doc has on these topics. It doesn't say how to configure the dead-letter queue or how to derive or store idempotency keys, so you'll have to decide those yourself.

you

Start a changeset called "sqs retry policy" using docs_change_start. Tell me the branch and worktree it created.

companionok

docs_change_start{ "title": "sqs retry policy" }
resultjson
{
  "id": "cs-7i3p5u3fegmq",
  "title": "sqs retry policy",
  "area": "projects/acme/api",
  "branch": "docs/bolacha/projects-acme-api/sqs-retry-policy",
  "path": "/tmp/companion-cli-demo/companion-home/changes/projects-acme-api--sqs-retry-policy",
  "pr_state": "none"
}

`area`, `created_at` and `updated_at` are omitted.

agent

I started changeset cs-7i3p5u3fegmq ("sqs retry policy") in the area projects/acme/api.

  • Branch: docs/bolacha/projects-acme-api/sqs-retry-policy
  • Worktree: /tmp/companion-cli-demo/companion-home/changes/projects-acme-api--sqs-retry-policy

It has no PR yet. Starting it made it the active changeset for the area, so docs writes there now go into it rather than the "refund flow" changeset, which still exists unchanged.

you

Add an ADR with docs_record_decision. Title: "SQS retry policy". Decision: retry three times with exponential backoff, then send to the dead-letter queue. Status: proposed. Show me the canonical path it wrote.

companionok

docs_record_decision{ "title": "SQS retry policy", "context": "The org SQS consumer pattern (org/patterns/messaging/sqs.md) requires that consumers configure a dead-letter queue and use idempotent handlers, but it does not define a retry policy. acme/api consumes SQS via internal/consumer/consumer.go (consumer.Consume).", "decision": "Retry a failed message three times with exponential backoff, then send it to the dead-letter queue.", "consequences": "A message can be processed up to four times (one initial attempt plus three retries), so handlers must be idempotent, as the org pattern already requires. Messages that keep failing go to the dead-letter queue instead of being retried forever, so the dead-letter queue needs monitoring and a way to redrive messages." }
resultjson
{
  "ok": true,
  "path": "projects/acme/api/adr/2026-10-26-sqs-retry-policy.md",
  "pending_sync": true,
  "changeset": {
    "id": "cs-7i3p5u3fegmq",
    "branch": "docs/bolacha/projects-acme-api/sqs-retry-policy",
    "pr_state": "none"
  }
}

The echoed changeset object is trimmed to `id` and `pr_state`.

agent

I recorded the ADR at projects/acme/api/adr/2026-10-26-sqs-retry-policy.md. It went into the "sqs retry policy" changeset and has not been synced yet, so there is no PR.

docs_record_decision has no status parameter, so I could not pass "proposed" myself. I read the file back to check: the frontmatter says status: proposed, which is the default for new ADRs.

The tool also requires a Context and a Consequences section, which you had not given me, so I wrote them myself.

you

Patch that ADR's context section with docs_patch to mention that the payments team's queue standards apply. Pass the current base_rev. If it refuses because base_rev is stale, read the file again and retry.

companionok

docs_patch{ "path": "projects/acme/api/adr/2026-10-26-sqs-retry-policy.md", "base_rev": "4e42676c26dd289f04abe0ca30eb01c3e6448379", "edits": [ { "old": "acme/api consumes SQS via internal/consumer/consumer.go (consumer.Consume).", "new": "acme/api consumes SQS via internal/consumer/consumer.go (consumer.Consume). The payments team's queue standards also apply to this consumer." } ] }
resultjson
{
  "ok": true,
  "pending_sync": true,
  "changeset": {
    "id": "cs-7i3p5u3fegmq",
    "branch": "docs/bolacha/projects-acme-api/sqs-retry-policy",
    "pr_state": "none"
  }
}

The echoed changeset object is trimmed to `id` and `pr_state`.

agent

The patch went through on the first try. The base_rev I passed was 4e42676, the hash returned when I read the ADR back after creating it. It was not stale, so there was no refusal and no retry.

…acme/api consumes SQS via internal/consumer/consumer.go (consumer.Consume). The payments team's queue standards also apply to this consumer.

The new sentence does not link to the standards. The docs list for this project did not include any payments queue standard, so I would need the path to add one to the ADR's related: field.

you

Call docs_change_status and tell me exactly what is staged and on which branch.

companionok

docs_change_status{ "changeset": "cs-7i3p5u3fegmq" }
resultjson
{
  "changeset": {
    "id": "cs-7i3p5u3fegmq",
    "title": "sqs retry policy",
    "branch": "docs/bolacha/projects-acme-api/sqs-retry-policy",
    "pr_state": "none"
  },
  "dirty": null,
  "frozen": false
}

`area`, `path`, `created_at` and `updated_at` are omitted.

agent

Nothing is staged. docs_change_status reports dirty: null, and git status in the worktree shows no staged or modified files. Both changes are already committed locally.

CommitMessageChange
95cf207docs(api): sqs retry policyadded the ADR (+29 lines)
43e90aadocs(api): sqs retry policyedited the same file (+1/−1): the Context patch

PR: none. Frozen: no. Not pushed either — both commits exist only on docs/bolacha/projects-acme-api/sqs-retry-policy until a sync runs.

you

Call docs_change_submit with the message "docs(api): SQS retry policy". Report the result verbatim, including any warnings.

companionblocked

docs_change_submit{ "changeset": "cs-7i3p5u3fegmq", "summary": "docs(api): SQS retry policy" }
resulterror
cannot submit without GitHub authentication

agent

Here is the result verbatim. It returned no warnings and no other output:

cannot submit without GitHub authentication

docs_change_submit has no "message" parameter, so I passed the text as summary. Nothing was pushed, no PR was created, and the changeset is not frozen.

The two commits are still only local, on the changeset branch. This is the same problem as the earlier warning: "GitHub token unavailable; open PRs were not checked".

you

In three sentences: what did you change, where did the guidance come from, and who merges it?

agent

I added one ADR, projects/acme/api/adr/2026-10-26-sqs-retry-policy.md, with status "proposed": it says to retry three times with exponential backoff and then send to the dead-letter queue, and its context says the payments team's queue standards apply.

The guidance came from the org pattern org/patterns/messaging/sqs.md, which requires a dead-letter queue and idempotent handlers, and from its reference implementation, consumer.Consume in internal/consumer/consumer.go.

Companion doesn't let agents merge, so a person has to review and merge the PR — most likely someone from the payments team, since they own these docs. There is no PR yet because the submit failed without GitHub authentication.

One real session, 2026-10-26: Claude Code 2.1.308 against companion v0.2 in a throwawayacme/api checkout. Prose is trimmed for length; tool calls and results are as sent. Run without a GitHub token, so the last step is refused.9 promptsstdio

What to notice

01

It never guesses paths

The first call returns the store layout, the required docs and what is missing. So the agent reads projects/acme/api/README.md instead of inventing docs/ in the code repo.

02

Guidance arrives cited

docs_get_guidance returns a path, a score and a resolved code reference — internal/consumer/consumer.go:4, drift fresh — so the pattern can be checked, not just trusted.

03

Writes land in a changeset

Every write goes to a branch in a worktree, not to main. The agent can read its own diff and status; only a person can open and merge the pull request.

Run it against your own repo

Install the binary, point it at a docs store, then register the MCP server with your client. The agent picks it up on the next session — no daemon and no port.

claude mcp add --scope user companion -- companion mcp

Claude Code · codex mcp add companion -- companion mcp for Codex

No agent required

Every tool above is a thin wrapper over the CLI, so the same run works in a terminal — no client, no MCP handshake. This is the offline demo script, from store init to a changeset waiting on the same GitHub token.

companion — offline demo

companion init --remote /tmp/companion-cli-demo/docs-store.git
store: /tmp/companion-cli-demo/companion-home/repo.git
mirror: /tmp/companion-cli-demo/companion-home/main
config: /tmp/companion-cli-demo/companion-config.yaml
ssh: ok
warning: GitHub token unavailable; set COMPANION_FORGE_TOKEN, configure forge.token, or run `gh auth login`
companion context
project acme/api ← .companion.yaml
team payments ← .companion.yaml
languages go ← .companion.yaml
area projects/acme/api
companion guidance "implement an SQS consumer in Go" --lang go
1. SQS consumer [pattern/approved] score=4.27
org/patterns/messaging/sqs.md
# SQS consumer
ref: acme/api@main:internal/consumer/consumer.go
code: internal/consumer/consumer.go:4 (consumer.Consume) [go] path=valid symbol=valid drift=fresh
func(body string) error
companion validate-code-refs --all
org/patterns/messaging/sqs.md
internal/consumer/consumer.go (Consume): path=valid symbol=valid drift=fresh — resolved consumer.Consume at internal/consumer/consumer.go:4
1 documents, 1 references: 1 valid paths, 0 missing, 0 external, 0 unverified, 0 stale
companion change start "refund flow"
changeset cs-dodwswveg7la
area projects/acme/api
branch docs/bolacha/projects-acme-api/refund-flow
worktree /tmp/companion-cli-demo/companion-home/changes/projects-acme-api--refund-flow
PR - (none)
companion status
mirror 1c752f5111 (just now)
ID PR STATE DIRTY AREA
* cs-dodwswveg7la - none 0 projects/acme/api
refund flow
companion change sync
changeset cs-dodwswveg7la
files
projects/acme/api/flows/refund.md
warning branch pushed, but no GitHub token is available; draft PR was not created/updated
Real output of docs/demo-cli-only.sh. IDs, scores, hashes and timestamps vary per run.no network

This run had no GitHub token, so the final submit was refused and the two commits exist only on the changeset branch. Companion looks for one in COMPANION_FORGE_TOKEN,forge.token in the config, or gh auth login; with it the same call opens a draft PR and marks it ready — still merged by a person. IDs, scores, hashes and timestamps differ on every run.