docs: enrich CHANGELOG and add Contributors section for 1.4.0

Audit pass against the actual commit messages between v1.3.0 and
v1.4.0 turned up several substantive items the initial CHANGELOG
under-described or omitted entirely.  Fix-forward expansion plus a
Contributors section recognizing external contributors.

Added detail / coverage:

- New "Server compatibility layer for local model servers" entry —
  the vLLM / llama.cpp profiles + admin UI fields shipped in #352
  alongside the capabilities passthrough; previously buried under one
  bullet.
- Per-call plan/task model selection split into three sub-bullets
  (backend split, runtime configurability without restart via
  ConfigStore admin tab, per-call override) — three PRs that build on
  each other deserve to be discoverable independently.
- Opus 4.7 entry expanded with 1M ctx / 128K output, the new
  thinking_display capability field, xhigh effort level, and admin
  dropdown updates.
- Dashboard composer note: tab-bar `+` modal also gained the paperclip
  + chip strip + first-message field.
- Slack adapter: explicit "session recovery via persisted recoverable
  route keys" — ops-relevant promise for restart behaviour.
- pgbouncer swap: helm chart link + ports updates noted.
- Provider capabilities entry: defensive shallow-copy + chat_template
  deep-merge follow-ups.

New Fixed entries:

- Cross-user attachment-fetch hardening (get_attachment_content
  scopes by user_id).
- Attachment-list DoS guard on /v1/api/send.
- Bounded LRU for upload locks.
- 3.12 CI deadlock root-cause writeup (asyncio.Lock vs Starlette
  TestClient loop teardown).

New SDK entry:

- PlanResolvedEvent type + guard, dispatched cross-client when one
  client resolves a plan so others dismiss in sync.

New Operational subsection:

- vendor-js workflow now auto-downloads hls.js for future Renovate
  bumps so they're merge-ready without manual file fetches.

Contributors:

- Recognise @daoxley (Slack adapter, #355) and @pizzaandcheese
  (pgbouncer swap, #353) — the two external contributors with
  meaningful net-new work in this release — plus the Renovate bot.
- Pointer to channel-attachment ingest as the headline 1.4.1 feature
  for would-be contributors.
This commit is contained in:
Patrick Buckley
2026-04-16 15:16:19 -07:00
parent 86d981eb73
commit 879be89bbd
+123 -18
View File
@@ -52,11 +52,13 @@ existing 1.3.x database. Both are additive; no data loss. See
SDKs auto-generate `ws_id` client-side so cluster-routed callers can
bind the body to the owning node before it lands. (#362)
- **Slack channel adapter** (Socket Mode) — mirrors the Discord adapter:
per-user channel sessions via configurable slash command, DM routing,
SSE event consumption, tool approval buttons (with per-user owner
enforcement), plan-review approve / request-changes modal,
notification reply routing back into the workstream, session recovery
after restart. Install with `pip install 'turnstone[slack]'`. (#355)
per-user channel sessions via configurable slash command, DM routing
without slash command, SSE event consumption, tool approval buttons
with per-user owner enforcement, plan-review approve / request-changes
modal, notification reply routing back into the workstream, and
session recovery after restart via persisted recoverable route keys
(the bot re-subscribes to existing Slack-routed workstreams when it
comes back). Install with `pip install 'turnstone[slack]'`. (#355)
- **Console admin UX support for Slack** — channel-link modal offers
Slack alongside Discord; skill notify-on-complete forms expose a
per-row channel-type dropdown (and no longer hardcode `discord`);
@@ -64,16 +66,59 @@ existing 1.3.x database. Both are additive; no data loss. See
theme-aware tokens (`--discord` / `--slack`) so light theme passes
WCAG AA. (#365)
- **Per-call plan/task model selection** — `plan_model` and `task_model`
are now distinct from the conversation model and from each other, with
configurable reasoning effort per agent. `ConfigStore` admin tab in
the console UI lets operators set defaults; per-call overrides
available via the `plan_agent` / `task_agent` tools. (#360, #361)
are now distinct from the conversation model and from each other,
with configurable reasoning effort per agent. Three layers:
- **Backend split** (`#54dd557`) — `ModelRegistry` gains `plan_model`,
`task_model`, `plan_effort`, `task_effort`; per-kind overrides win
over the legacy `agent_model`, which still works as the single-knob
fallback. `resolve_agent_alias(kind)` and `resolve_agent_effort(kind)`
centralise resolution. Loader validates effort against
`{none, minimal, low, medium, high, xhigh, max}` with warn+drop on
typos.
- **Runtime configurability** (`#360`) — `ConfigStore` admin tab in
the console UI lets operators switch alias and reasoning effort per
agent **without restarting**. `INHERIT_EMPTY_LABEL_KEYS` shows
`(inherit)` for empty effort selections — distinct from the literal
`none` choice which actually disables reasoning. Routing overrides
apply on `/v1/api/_internal/config-reload` (admin saves), and
`model-reload` short-circuits when nothing changed so no in-flight
clients churn.
- **Per-call override** (`#361`) — the calling LLM can pass
`model="<alias>"` to `plan_agent` or `task_agent` to override the
operator-configured per-kind model for that one invocation. Tool
descriptions list the live registered aliases (refreshed when the
operator hits "sync to nodes"), so the LLM always sees current
options. Bad aliases return a corrective error dict listing the
available choices. No whitelist — cost control is intentionally
ceded to the model. Plan-retry path reuses the alias so coaching
reflects real model behaviour. (#360, #361)
- **Provider capability passthrough** — resolved per-model capabilities
(vision support, reasoning support, native web search, etc.) flow
through to provider clients so feature gating no longer relies on
string matching. Server companion published in the same change. (#352)
- **Claude Opus 4.7 support** — provider capabilities, tokenizer
awareness, and adaptive thinking semantics. (#357 — also in 1.3.1)
(vision, reasoning, native web search, thinking_mode, token_param,
etc.) flow through to provider clients via a new `capabilities`
parameter on `create_streaming` / `create_completion`, so feature
gating no longer relies on string matching and admin-UI / config.toml
overrides actually reach the provider. Defensive shallow-copy in
`_finalize_extra_body` so callers reusing the same dict across models
are safe; deep-merge of `chat_template_kwargs` so operators can
extend instead of silently overwriting. (#352)
- **Server compatibility layer for local model servers** — vLLM and
llama.cpp profiles suggest the right thinking mode and per-server
workarounds (`skip_special_tokens` for vLLM, `reasoning_format` for
llama.cpp) during model detection. Admin UI gains structured fields
for server type, thinking mode, and extra body params, hidden for
non-local providers (openai/anthropic/google). New `thinking_param`
text field surfaces the alias name (default `enable_thinking`;
Granite/DeepSeek use `thinking`). Verified end-to-end against real
vLLM (Gemma 4 31B) and llama.cpp (Gemma 4 E4B) servers. (#352)
- **Claude Opus 4.7 support** — `claude-opus-4-7` capability entry
(1M ctx, 128K output, adaptive thinking, `supports_temperature=False`,
`thinking_display=summarized`). New `ModelCapabilities.thinking_display`
field — Opus 4.7 omits thinking by default but always sends summarized
blocks back through the provider boundary. Adds `xhigh` effort level
to the global mapping and to Opus 4.7's `effort_levels`; admin-console
skill-template dropdowns gained `xhigh` and `max` options. Reasoning
effort label capitalization aligned across all console dropdowns.
(#357 — also in 1.3.1)
- **Dashboard composer refactor** — unified single-flow create from the
per-node dashboard. Multi-line textarea + collapsible Options panel
(model / judge / skill) + paperclip + drag-drop / paste-image + chip
@@ -84,7 +129,9 @@ existing 1.3.x database. Both are additive; no data loss. See
separate modal. Options panel state persists in `localStorage`;
active non-default selections render as an inline summary chip beside
the Options button; drag-over shows an explicit "Drop to attach"
overlay. (#362, #366)
overlay. The tab-bar `+` new-workstream modal also gained a paperclip
+ chip strip + first-message field so the same flow is reachable from
both entry points. (#362, #366)
- **Workstream attachments — orphan reservation sweep** — periodic
background sweep clears `reserved_for_msg_id` on rows whose
`reserved_at` exceeds a 1-hour threshold, self-healing reservations
@@ -116,9 +163,11 @@ existing 1.3.x database. Both are additive; no data loss. See
show a `…and N more (preview truncated)` suffix. (#365)
- **PostgreSQL deployment image** swapped from `bitnami/pgbouncer` to
`edoburu/pgbouncer` to track upstream releases and reduce image size.
No config changes required for typical deployments; review your helm
values if you depend on `bitnami`-specific environment variable
conventions. (#353)
Environment variables remapped to the edoburu naming, ports updated
to match documented expectations, and the Kubernetes Helm Chart link
in the deployment docs now points at the same container. Review
your helm values if you depend on `bitnami`-specific environment
variable conventions. (#353)
### Fixed
@@ -149,6 +198,28 @@ existing 1.3.x database. Both are additive; no data loss. See
`.scope-slack` first shipped with raw hex that failed WCAG AA on
light theme (1.8:1 / 2.4:1). Theme-aware `--discord` / `--slack`
tokens with proper light variants now pass. (#365)
- **Cross-user attachment fetch hardening** — `get_attachment_content`
now scopes the row by `user_id` in addition to `ws_id`, so an
unowned workstream can't be a vector for cross-user blob fetches via
attachment-id guessing. (#356)
- **Attachment-list DoS guard** — `/v1/api/send` rejects
`attachment_ids` lists longer than the per-(ws, user) pending cap
with a 400, preventing hostile clients from blowing up the storage
`IN (...)` clause. (#356)
- **Bounded LRU for upload locks** — the per-(ws, user) attachment
upload-lock map now evicts the oldest unlocked entries past a soft
cap, so the in-process map can't grow unbounded on long-running
nodes. (#356)
- **3.12 CI deadlock on attachment uploads** — the upload-lock was
initially an `asyncio.Lock`, but Starlette's `TestClient` runs each
request on a fresh anyio task / event loop, so the cached lock's
`_waiters` bound to the first loop and a later request would block
on a Future from a closed loop (silent deadlock). Switched to
`threading.Lock` — loop-agnostic, and the critical section is one
COUNT + one INSERT. Same root cause is reproducible against any
Starlette TestClient harness on Python ≥ 3.10; 3.12 surfaces it
more often. Production users on a single event loop weren't
affected, but the test environment was. (#356)
### Security
@@ -194,6 +265,40 @@ Python + TypeScript clients gained:
- Refusal of `attachments + target_node` combination at the SDK
boundary (the multipart routing layer doesn't honor `target_node`,
so silently picking the wrong node is now an explicit error)
- `PlanResolvedEvent` SSE event with type guard, dispatched when one
client (e.g. mobile) resolves a plan so other connected clients can
dismiss their plan-approval modal in sync. Available in both the
Python and TypeScript SDKs. (#87a9af1)
### Operational
- **CI vendor-asset auto-download covers `hls.js`** — the
`vendor-js.yml` workflow previously only iterated katex/hljs/mermaid,
so Renovate bumps for `hls.js` failed the wheel-completeness check
and required manual file downloads. Detection loop now includes
`hls`, so future Renovate bumps are merge-ready without intervention.
(#354)
### Contributors
Thanks to the people who made this release happen — especially the
external contributors who picked up substantial pieces of work:
- **[@daoxley](https://github.com/daoxley)** — designed and shipped
the Slack channel adapter (Socket Mode bot, per-user sessions,
approvals, plan-review, notification routing). Major new feature
surface in #355.
- **[@pizzaandcheese](https://github.com/pizzaandcheese)** — replaced
the deprecated bitnami pgbouncer image with the edoburu image,
remapped environment variables, ports, and helm chart references.
Operationally important for anyone running our reference Postgres
deployment (#353).
- Renovate kept dependencies and the JS vendor tree current via
several automated bumps.
If you're interested in contributing, channel-attachment ingest from
Discord + Slack is the headline 1.4.1 feature and a solid place to
start — see the open issues on GitHub or open one to scope a piece.
## [1.3.1]