Skip to content

Commit 1bf7007

Browse files
authored
docs: fix stale facts found in a docs accuracy audit (#80)
Correct twelve places where user docs or package GoDoc disagreed with the code or with each other (Linear preemption status, tutorial log line and step reference, example README working directory and optional message.im, NATS in backend lists, Linear example expected behavior, Slack retry coverage and bot user id, Linear single-install default, DetachTimeout requirements, REDIS_URL, UpdateSession external URLs). _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
1 parent 3497603 commit 1bf7007

13 files changed

Lines changed: 71 additions & 65 deletions

File tree

‎adapters/linear/doc.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Package linear provides a Linear app-actor adapter for Chat SDK Go.
22
//
33
// The adapter participates as a Linear app-owned actor on App-Actor Client
4-
// Credentials (a Single-Install Adapter). It supports two Linear interaction
4+
// Credentials (a Single-Install Adapter by default; see Multi-tenant installs
5+
// below for serving many organizations). It supports two Linear interaction
56
// models, both reached through the small chat.Adapter interface and the opaque,
67
// versioned Thread ID:
78
//

‎adapters/slack/doc.go‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@
2424
//
2525
// The install credential rides as the adapter-specific slack.SlackInstall payload
2626
// on chat.Install.Credential (a Platform Escape Hatch for credentials): the
27-
// per-workspace bot token plus an optional bot user id used for tenant-correct
28-
// self-filtering. Thread Handle reconstruction (out-of-webhook posting) resolves
29-
// the same way, keyed by the Platform Tenant decoded from the opaque Thread ID.
27+
// per-workspace bot token plus the bot user id used for tenant-correct
28+
// self-filtering. The bot user id is optional in the type but should be treated
29+
// as required: without it the bot's own posts are not filtered, and a
30+
// subscribed thread can loop on its own replies. Thread Handle reconstruction
31+
// (out-of-webhook posting) resolves the same way, keyed by the Platform Tenant
32+
// decoded from the opaque Thread ID.
3033
//
3134
// # Message history (ADR 0009)
3235
//
@@ -41,15 +44,17 @@
4144
//
4245
// # Rate-limit retry (ADR 0005)
4346
//
44-
// Outbound posts are hardened against Slack throttling by default. The adapter
45-
// wraps its Slack Web API call site (chat.postMessage / chat.postEphemeral /
46-
// conversations.open) with bounded retry on a Slack 429 (honoring the Retry-After
47-
// header, in seconds) and the ratelimited API error. Retry is bounded three ways:
48-
// an attempt cap (Options.RetryPolicy.MaxAttempts), a cumulative backoff ceiling
49-
// (MaxElapsed), and the caller's context deadline. The single load-bearing
50-
// invariant is that in-line synchronous retry never sleeps past the caller's
51-
// context deadline, so retry under the default DispatchSync stays inside Slack's
52-
// 3-second ack window and cannot trigger a platform redelivery storm.
47+
// Outbound calls are hardened against Slack throttling by default. The adapter
48+
// wraps every Slack Web API call (including auth.test, chat.postMessage,
49+
// chat.postEphemeral, conversations.open, views.open, and history reads) and
50+
// every response_url post with bounded retry on a Slack 429 (honoring the
51+
// Retry-After header, in seconds) and the ratelimited API error. Retry is
52+
// bounded three ways: an attempt cap (Options.RetryPolicy.MaxAttempts), a
53+
// cumulative backoff ceiling (MaxElapsed), and the caller's context deadline.
54+
// The single load-bearing invariant is that in-line synchronous retry never
55+
// sleeps past the caller's context deadline, so retry under the default
56+
// DispatchSync stays inside Slack's 3-second ack window and cannot trigger a
57+
// platform redelivery storm.
5358
//
5459
// The RetryPolicy is per-adapter platform config in Options, never Runtime
5560
// Options. Its zero value is a conservative default that keeps MaxElapsed under

‎docs/how-to/choose-a-state-backend.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ import (
4545
chatredis "github.com/coder/chat/state/redis"
4646
)
4747

48+
redisOptions, err := redis.ParseURL(os.Getenv("REDIS_URL")) // e.g. redis://127.0.0.1:6379/0
49+
if err != nil {
50+
return err
51+
}
4852
redisState, err := chatredis.New(ctx, chatredis.Options{
49-
Client: redis.NewClient(&redis.Options{Addr: os.Getenv("REDIS_ADDR")}),
53+
Client: redis.NewClient(redisOptions),
5054
Prefix: "mybot", // see "One namespace per bot application" below
5155
})
5256
```

‎docs/how-to/deferred-dispatch.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ bot, err := chat.New(ctx,
4646

4747
- `Dispatch: chat.DispatchDeferred` turns on ack-then-work. The default is
4848
`chat.DispatchSync`, which runs the handler before acknowledging.
49-
- `DetachTimeout` bounds how long a detached handler may run after the webhook
50-
request has ended.
49+
- `DetachTimeout` (required under `DispatchDeferred`; `DefaultRuntimeOptions()`
50+
leaves it at zero, so `chat.New` fails until you set it) bounds how long a
51+
detached handler may run after the webhook request has ended.
5152
- `Concurrency: chat.ConcurrencyQueue` is the natural companion: while a
5253
detached handler holds the thread lock, follow-up events on the same thread
5354
wait instead of being dropped, and only the most recent superseded follow-up

‎docs/how-to/linear-agent-sessions.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ if err := la.UpdateSession(ctx, ev.Thread.ID(), linear.AgentSessionUpdateInput{
9797
{Title: "Reproduce the bug", Status: "pending"},
9898
{Title: "Fix and test", Status: "pending"},
9999
},
100-
ReplacePlan: true,
101100
}); err != nil {
102101
return err
103102
}
@@ -148,9 +147,10 @@ This check only runs when the stop event reaches your handler, and events on
148147
one thread are serialized by the thread lock — a stop arriving while a
149148
handler is still running cannot preempt it (`ConcurrencyDrop` discards it on
150149
conflict; `ConcurrencyQueue` delivers it only after the in-flight handler
151-
returns). The ADR 0012 force/steerability hook that would allow preemption is
152-
staged behind the deferred-dispatch coordination design work, so **Linear's
153-
Stop control cannot cancel in-flight work through this adapter today**. What
150+
returns). The runtime has no preemption: the ADR 0012 force/steerability hook
151+
that would allow it is rejected for v0.x by
152+
[ADR 0015](../adr/0015-runtime-coordination.md), so **Linear's Stop control
153+
cannot cancel in-flight work through this adapter**. What
154154
you can do: structure long
155155
sessions as short handler turns (each turn checks `StopRequested` on the
156156
event that started it before doing more work — `confirmStop` above is exactly

‎docs/linear-agent-capabilities.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ hatch rather than typed helpers.
3333
| Typed activity helpers | Supported | `PostThought`, `PostAction`, `PostElicitation`, `PostError`; `Thread.Post` creates the `response` activity. |
3434
| Agent-to-human signals | Supported | `auth` and `select` signals with metadata pass through `CreateAgentActivity` / `PostElicitation`. |
3535
| Human-to-agent stop signal | Supported | `RawMessageFrom(ev.Message)` exposes `Signal` / `StopRequested()`; see the routing caveat below. |
36-
| Session updates | Supported | `UpdateSession` sets `externalUrls` and replaces the session plan array. |
36+
| Session updates | Supported | `UpdateSession` replaces `externalUrls` or adjusts them with `AddExternalURLs` / `RemoveExternalURLs`, and replaces the session plan array. |
3737
| GraphQL escape hatch | Supported | `GraphQL` (single-install) and `GraphQLForTenant` (multi-tenant) reuse adapter auth and token refresh, surface GraphQL errors, and never expose tokens. |
3838
| Proactive agent session creation | Supported | `CreateSessionOnIssue` / `CreateSessionOnComment` (plus `ForTenant` variants) wrap `agentSessionCreateOnIssue` / `agentSessionCreateOnComment`; the returned `CreatedAgentSession` carries the adapter's opaque `ThreadID` ([#47](https://github.com/coder/chat/issues/47)). |
3939
| Repository suggestions | Supported | `SuggestRepositories` wraps `issueRepositorySuggestions` with typed candidates and confidence-scored results ([#48](https://github.com/coder/chat/issues/48)). |

‎docs/reference.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,11 @@ inbound webhook request context before the platform acknowledgement. For
239239
long-running work, opt in to `DispatchDeferred` (ack-then-work,
240240
[ADR 0002](adr/0002-async-dispatch.md)): the dedupe and lock prelude runs
241241
before the acknowledgement, then the handler runs on a detached work context
242-
with automatic lock lease renewal, bounded by `DetachTimeout`. Under deferred
243-
dispatch `MaxDetached` bounds admitted-but-incomplete deliveries; a delivery
244-
arriving at the bound is rejected with `chat.ErrAdmissionRejected` before
245-
acknowledgement and before dedupe marking ([ADR 0015](adr/0015-runtime-coordination.md)).
242+
with automatic lock lease renewal, bounded by `DetachTimeout` (which must be
243+
positive under deferred dispatch). Under deferred dispatch `MaxDetached`
244+
bounds admitted-but-incomplete deliveries; a delivery arriving at the bound is
245+
rejected with `chat.ErrAdmissionRejected` before acknowledgement and before
246+
dedupe marking ([ADR 0015](adr/0015-runtime-coordination.md)).
246247
The [deferred dispatch guide](how-to/deferred-dispatch.md) covers enabling
247248
it and writing handlers for the detached context.
248249

@@ -325,7 +326,7 @@ The runtime implements all five upstream-aligned concurrency strategies
325326
- `ConcurrencyDebounce`: each new routed event supersedes the previous
326327
waiter; only the final event in a `DebounceInterval` quiet period
327328
dispatches, and superseded events are observable. Requires deferred
328-
dispatch.
329+
dispatch and a `DetachTimeout` longer than `DebounceInterval`.
329330
- `ConcurrencyConcurrent`: no thread lock at all; every event dispatches in
330331
its own execution, bounded by `MaxConcurrent`.
331332
- `ConcurrencyBurst`: routed events for a scope collect for a `BurstWindow`,

‎docs/tutorials/slack-bot.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ discover the bot's own identity. If the token is wrong you find out now, not
9797
on the first message. When the bot is up you should see a log line like:
9898

9999
```text
100-
level=INFO msg=listening addr=:8080
100+
2026/09/23 14:00:00 INFO listening addr=:8080
101101
```
102102

103103
## Step 5: Expose The Bot To Slack
@@ -165,7 +165,7 @@ this, only mentions ever reach it:
165165
2. In **Event Subscriptions**, add the `message.channels` bot event.
166166
3. Reinstall the app from **OAuth & Permissions**.
167167

168-
(If you set up `message.im` in Step 2, you can skip this and test the
168+
(If you subscribed to `message.im` in Step 6, you can skip this and test the
169169
follow-up flow in a direct message instead.)
170170

171171
Then open `examples/slack-hello-world/main.go` and replace the

‎examples/linear-agent-hello-world/README.md‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,25 @@ Expected behavior:
151151
Thinking...
152152
```
153153

154-
5. The app posts the final response:
154+
5. The app posts a `search-codebase` action and adds a **Draft PR** external
155+
link to the session.
156+
6. The app posts the final response, a bold `hello from Linear app actor`
157+
line followed by a note inviting a follow-up prompt.
155158

156-
```text
157-
hello from Linear app actor
158-
```
159+
If the prompt text is exactly `deploy`, the app instead asks a `select`
160+
question ("Which environment should I target?", `staging` or `prod`), and
161+
your next follow-up is read as the answer.
159162

160-
6. Send a follow-up prompt in the same Linear agent session.
161-
7. The example routes it to `OnSubscribedMessage`, posts another ephemeral
162-
thought, and replies with:
163+
7. Send a follow-up prompt in the same Linear agent session.
164+
8. The example routes it to `OnSubscribedMessage`, posts the ephemeral thought
165+
`Reading your follow-up...`, and replies with:
163166

164167
```text
165168
Follow-up received: YOUR_MESSAGE
166169
```
167170

171+
A follow-up carrying Linear's stop signal gets a stop confirmation instead.
172+
168173
## Dogfooding Evidence
169174

170175
Before claiming a live Linear dogfood passed, capture screenshots or video of:
@@ -179,7 +184,8 @@ Before claiming a live Linear dogfood passed, capture screenshots or video of:
179184

180185
- State is in memory, so subscriptions and dedupe data are lost when the process
181186
exits.
182-
- Use Redis or Postgres runtime state for production deployments.
187+
- Use Redis, Postgres, or NATS JetStream runtime state for production
188+
deployments.
183189
- Linear request signatures are verified with `LINEAR_WEBHOOK_SECRET`.
184190
- Client credentials are exchanged during adapter startup and refreshed lazily
185191
before Linear API calls.

‎examples/slack-hello-world/README.md‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ mentioned in a channel, or messaged directly if you enable the DM event, it
55
replies with portable Markdown: `**hello** _world_`.
66

77
This example requires `CHAT_DEMO_IN_MEMORY_STATE=1` because state is lost on
8-
restart. Use `examples/slack-redis-state` or `examples/slack-postgres-state` for
9-
durable Slack apps.
8+
restart. Use `examples/slack-redis-state`, `examples/slack-postgres-state`, or
9+
`examples/slack-nats-state` for durable Slack apps.
1010

1111
## Slack App Setup
1212

@@ -45,12 +45,9 @@ In **Event Subscriptions**:
4545
https://YOUR_PUBLIC_HOST/webhooks/slack
4646
```
4747

48-
3. Subscribe to these **Bot User Events**:
49-
50-
```text
51-
app_mention
52-
message.im
53-
```
48+
3. Subscribe to the `app_mention` **Bot User Event**. Also add `message.im`
49+
if you want direct messages to reach the bot (it needs the `im:history`
50+
scope).
5451

5552
4. Save changes.
5653

0 commit comments

Comments
 (0)