Compiled entirely from public activity on meta.discourse.org, X, and GitHub.
💬 meta.discourse.org
This week, Sam was mostly in maintenance and support mode: acknowledging and linking fixes for reported issues such as the MCP subfolder-install bug, checking whether a Gemini Pro “thinking budget” error was still reproducible, and responding to security concerns around an Nginx CVE. He also helped close out resolved or inactive bug/support topics, reflecting routine triage across Discourse functionality and infrastructure.
🐦 On social
No X activity captured this week.
🛠️ GitHub — Sam’s Commits
samsaffron/term-llm
Sam focused on making term-llm’s chat and serve experiences more durable, faster, and easier to navigate: persistent response sessions, session search/stats, widget sidebars/indicators, and better recovery from interrupted or stale streams. A major theme was streaming reliability and performance, with backoff/fallback handling, reduced hot-path overhead, cached skill listings, and smoother rendered text/progress updates. He also expanded multimodal support by adding and then centralizing terminal image/artifact rendering, while tightening container workspace startup and widget authentication behavior.
Key commits:
a088da9— refactor(image): centralize terminal image renderinge099498— fix(session): persist visible message countsb13261d— fix(ui): rerender subagent progress updates41ea766— remove irrelevent textd998dab— fix(contain): start existing compose workspaces
discourse/discourse
Over the last 7 days, Sam focused heavily on privacy and permission hardening across Discourse, fixing leaks around hidden posts/tags, real names, IP addresses, private-message publishing, AI persona group enforcement, and AI spam handling. He also advanced Discourse AI UX and research tooling, adding starred AI bot conversations, improving the docked composer, and expanding researcher category filters. Alongside that, he shipped smaller product/admin improvements like allowing dots in tag names and renaming the staff logins report to admin logins.
Key commits:
05e03ea— FIX: Bookmarked hidden post excerpts leak to unauthorized bookmark owners (#39873)008fce7— FIX: Tag settings leaks hidden tag metadata to non-staff tag editors (#39874)d4725b5— FIX: Public post revision JSON exposes editor real names when names are disabled (#40021)82e463c— FEATURE: Star AI bot conversations in sidebar (#39869)246f9a3— FEATURE: Allow dots in the middle of tag names (#39679)
discourse/dv
Sam focused on improving dv’s CLI/runtime reliability around Docker command execution. The main change ensures docker exec operations are cancelled when the request context is cancelled, reducing the chance of orphaned or runaway processes during interrupted CLI/server interactions.
Key commits:
5348d2a— fix(cli): cancel docker exec on request context
SamSaffron/discourse-game-compendium
Sam Saffron spent the last 7 days turning discourse-game-compendium into a functional Discourse plugin foundation: adding the MIT license, persistence models/migrations for assets and asset groups, markdown/rich-editor integration, routes, UI components, styling, settings, and localization. The work looks focused on establishing an end-to-end game compendium experience inside Discourse, with substantial test coverage across rendering, requests, and system flows.
Key commits:
af69149— Add MIT license
discourse/discourse-doc-categories
Sam worked on a new “simple mode” for Discourse docs, aimed at making documentation categories cleaner and less visually noisy. The change strips topic lists down to essentials, collapses replies so docs emphasize the original post, and adds a toggle plus state handling so users can reveal comments when needed. He also hardened the implementation around live updates and refreshes, with tests covering the new behavior.
Key commits:
6f91f75— FEATURE: simple mode for Discourse docs (#83)
discourse/discourse-mcp
Sam focused on improving compatibility for Discourse sites hosted under subfolder paths, such as https://example.com/forum, rather than only domain-root installs. The work preserved configured base paths when normalizing site URLs and routing API calls, while adding tests to ensure root-hosted sites still behave correctly. This appears to be a targeted reliability fix shipped as version 0.2.8.
Key commits:
98fe202— fix: support Discourse installations under subfolders (#46)
🤖 Jarvis — Public Repo Work
Agent-authored public commits, typically guided by Sam during implementation work.
SamSaffron/term-llm
Over the last 7 days, Sam-directed Jarvis work in SamSaffron/term-llm focused heavily on web UI reliability and memory/performance improvements, especially around image uploads, attachment handling, session preservation, response continuation IDs, math rendering, and sidebar/session behavior. A second major theme was agent/container polish and operational efficiency: adding the widgets skill to contained agents, reducing duplicate skill scans, improving jobs summary indexing, strengthening retry/websocket tests, and making file/image serving less wasteful. The work also kept model support current with GPT-5.5 and added targeted regression coverage throughout, suggesting an intent to harden real-world agent workflows rather than ship isolated features.
Key commits:
8309559— feat: add GPT-5.5 to OpenAI models (#662)9203d91— fix: preserve explicit math delimiters in web UI (#664)e508372— feat: show agent name in web sidebar (#661)f830aa0— fix: escape memory search FTS queries (#660)c4d4620— perf: add global jobs run summary index
⤴️ GitHub — Pull Requests
52 PRs this week:
- ✅ SamSaffron/term-llm#659 (diff) — build: exclude UI JS fixtures from embed closed
- Replace the broad
//go:embed static/*serve UI embed with a production-asset allowlist plusstatic/vendor. - Add a regression test that embedded static assets contain every non-test production file, but no*_test.jsJS fixtures. The previous …
- Replace the broad
- ✅ SamSaffron/term-llm#658 (diff) — perf: stop runaway shell output early closed
Stop runaway shell commands that exceed a raw stdout/stderr drain budget instead of continuing to stream discarded bytes until the timeout fires. - Shares a hard raw-output budget across stdout and stderr for
ShellTool. - Cancels the command contex… - ✅ SamSaffron/term-llm#657 (diff) — fix: Every follow-up message blocks on a full server sync before streaming can b closed
Stop blocking normal follow-up sends on an unconditional preflight session sync. This narrows the pre-send refresh to the cases that actually need it: when a session has prior context but is missing a local
lastResponseId. That refresh now also ski… - ✅ SamSaffron/term-llm#656 (diff) — fix: Non-append transcript updates rebuild the entire chat DOM and reparse all m closed
- keep the append fast path for pure tail growth, but reconcile same-session non-append transcript changes by message id instead of clearing
elements.messages- reuse existing message nodes when their render-relevant data is unchanged, only update/…
- keep the append fast path for pure tail growth, but reconcile same-session non-append transcript changes by message id instead of clearing
- ✅ SamSaffron/term-llm#655 (diff) — fix: Sidebar/session listing does a per-session COUNT(*) subquery on every List closed
Persist visible
message_counton thesessionsrow and switchList()to read it directly instead of running a correlatedCOUNT(*)subquery per returned session. This adds a schema migration that backfills counts for existing databases, install… - ✅ SamSaffron/term-llm#654 (diff) — fix: Session history loading fetches every page serially before rendering anythi closed
- stop blocking session switches on a full serial history walk for server-only sessions - render the first fetched page as soon as it arrives while the rest of the history continues loading - prefetch remaining history pages in small parallel batches…
- ✅ SamSaffron/term-llm#653 (diff) — fix: Every follow-up send blocks on a full server resync before the POST stream closed
- stop
sendMessagefrom waiting on a full session message-history reload before posting a normal follow-up - keep the preflight continuation refresh, but callsyncActiveSessionFromServer(..., { skipMessagesFetch: true })so it only refreshes ligh…
- stop
- ✅ SamSaffron/term-llm#652 (diff) — perf: speed up global jobs run summaries closed
Add a global covering index for Jobs V2 run-summary listing: - keeps the existing job-scoped covering index for
/v2/runs?job_id=...- addsidx_job_runs_v2_summary_createdfor all-job summaries ordered bycreated_at DESC- adds an EXPLAIN test t… - ✅ SamSaffron/term-llm#651 (diff) — perf: stream read_file long lines in chunks closed
Stream
read_fileline-numbered output withbufio.Reader.ReadSlicefragments instead ofReadString, so a single oversized line can be capped without allocating or scanning the whole logical line. This keeps the existing line-numbered formatting … - ✅ SamSaffron/term-llm#650 (diff) — fix: Built-in agents always extract embedded resources to disk during prompt set closed
- skip builtin resource extraction during prompt setup unless the builtin prompt actually needs extracted files - continue extracting when the prompt uses
{{resource_dir}}or builtin-relative{{file:...}}includes - add regression coverage for th…
- skip builtin resource extraction during prompt setup unless the builtin prompt actually needs extracted files - continue extracting when the prompt uses
- ✅ SamSaffron/term-llm#649 (diff) — fix: MCP tool completion cold-starts external servers on TAB closed
Stop
term-llm mcp runshell completion from starting MCP servers on a cache miss.MCPRunArgCompletionnow returns cached tool completions only, and otherwise exits immediately without spawning external processes. Added tests to verify completion … - ✅ SamSaffron/term-llm#648 (diff) — fix: Skills startup scans the entire skill catalog twice before first prompt closed
- replace the eager
HasAnySkill()startup probe ininternal/skills/setup.gowith a singleRegistry.List()call - keep returningnilwhen no valid skills are available, but retain the discovered skill list onSetup- reuse that preloaded cat…
- replace the eager
- ✅ SamSaffron/term-llm#646 (diff) — fix: use exponential retry backoff closed
- Change provider retry backoff from linear+jitter to exponential+jitter. - Preserve
Retry-Afternumeric handling and add HTTP-date parsing for wrapped retry header text. - Strengthen Responses WebSocket fallback coverage: after WebSocket setup exh…
- Change provider retry backoff from linear+jitter to exponential+jitter. - Preserve
- ✅ SamSaffron/term-llm#645 (diff) — fix: open search-only sidebar sessions closed
Fix sidebar search results that exist only in
sidebarSearchResultsbut not yet in localstate.sessions. When opening one of those results, copy the search result into local session state before continuing with normal server-only hydration. Recent… - ✅ SamSaffron/term-llm#642 (diff) — feat: enumerate contain default skills closed
- Add a contain helper that enumerates the managed agent image’s bundled bootstrap skills. - Print the default skills in
term-llm contain newnext steps for agent workspaces. - Print the default skills fromterm-llm contain image sync. - Document…
- Add a contain helper that enumerates the managed agent image’s bundled bootstrap skills. - Print the default skills in
- ✅ SamSaffron/term-llm#631 (diff) — fix: Hybrid memory search does a full embedding-table scan and in-memory rerank closed
- change
VectorSearchto keep only the current top-k matches in memory instead of collecting and sorting every scored embedding row - stop loading full fragment payloads during the initial embedding scan; only fetch fragment bodies/metadata for the…
- change
- ✅ SamSaffron/term-llm#632 (diff) — perf: avoid double decoding response websocket events closed
- Add a fast path for extracting known top-level Responses WebSocket
typefields without a preliminary fulljson.Unmarshal. - Reuse the same event type extractor for SSE payloads that omit an explicitevent:line. - Fall back to `json.Unmarshal…
- Add a fast path for extracting known top-level Responses WebSocket
- ✅ SamSaffron/term-llm#633 (diff) — perf: reuse message insert statements for snapshots closed
- Prepare the repeated
messagesINSERT once per transaction inReplaceMessagesandCompactMessagesinstead of reparsing the same SQL for every message row. - Add an end-to-endReplaceMessagesbenchmark for a 120-message snapshot. `ReplaceMess…
- Prepare the repeated
- ✅ SamSaffron/term-llm#636 (diff) — fix: Streamed prose can disappear until a newline/tool boundary because incomple closed
- add a
PreviewUnflushedpath for streaming text segments that keeps already-rendered markdown blocks and appends a raw preview of the current incomplete prose block - switchRenderSegmentsWithLeadingAndImageRendererto use that preview instead o…
- add a
- ✅ SamSaffron/term-llm#637 (diff) — fix: In-progress assistant text is dropped from the render path until a newline closed
- switch the live
SegmentTextrender path to use a view-specific streaming preview instead of onlyRenderedUnflushed()- teach the streaming renderer to build a safe full-document preview for the current incomplete block without changing scrollba…
- switch the live
- ✅ SamSaffron/term-llm#638 (diff) — fix: Live streamed text is buffered until newline/tool boundary instead of appea closed
- enable partial streaming rendering for
TextSegmentRendererso in-progress prose appears in the live TUI before a newline or tool boundary - teach the streaming renderer to support flowing, resettable snapshot updates for partial text while keepin…
- enable partial streaming rendering for
- ✅ SamSaffron/term-llm#639 (diff) — fix: Pending-tool wave animation forces a full alt-screen viewport content reset closed
- stop treating pending-tool wave animation ticks as viewport content changes in alt-screen mode - cache split history lines and synthesize wave-only frames directly for the visible viewport instead of calling
viewport.SetContent(...)- add regress…
- stop treating pending-tool wave animation ticks as viewport content changes in alt-screen mode - cache split history lines and synthesize wave-only frames directly for the visible viewport instead of calling
- ✅ SamSaffron/term-llm#640 (diff) — fix: Subagent progress uses one goroutine per UI event, removing backpressure an closed
- stop wrapping each subagent progress
Program.Sendcall in its own goroutine - sendspawn_agentprogress updates synchronously in both the chat TUI and ask renderer paths - keep the fix narrowly scoped to the subagent progress callback wiring `s…
- stop wrapping each subagent progress
- ✅ SamSaffron/term-llm#641 (diff) — perf: reduce memory vector search row loading closed
Optimize memory vector search so it no longer loads full fragment rows for every matching embedding before ranking. VectorSearch now: - scans only fragment id, updated_at, and embedding vector while scoring; - keeps a bounded top-k heap instead of re…
- ✅ SamSaffron/term-llm#629 (diff) — feat: improve image transcription handling closed
Improve image transcription/OCR handling in three places: - Persist web-uploaded images to the uploads directory even when they are small enough to send inline. - Mark user-supplied images and
view_imageoutputs with Responses APIdetail: "high"… - ✅ discourse/discourse#39873 (diff) — FIX: Bookmarked hidden post excerpts leak to unauthorized bookmark owners merged
In rare occasions bookmarks can be made on spam posts that become hidden. In that case go ahead and hide the excerpts from the bookmark page.
- ✅ discourse/discourse#39874 (diff) — FIX: Tag settings leaks hidden tag metadata to non-staff tag editors merged
Under very rare conditions a user may have edit rights on a tag, but no rights on a tag group that includes the tag. This will omit said tag groups (and synonyms) so they are not visible.
- ✅ discourse/discourse#40021 (diff) — FIX: Public post revision JSON exposes editor real names when names are disabled merged
Hide user name from revisions if full name is disabled
- ✅ discourse/discourse#39869 (diff) — FEATURE: Star AI bot conversations in sidebar merged
Adds an experimental “Starred” section to the AI bot conversation sidebar. Users can star up to 200 conversations they own; starred items appear in a dedicated section above the time-bucketed list and are skipped from the regular pagination. Intr…
- 🟢 discourse/discourse#40019 (diff) — FIX: Enforce can_see_ip checks across admin IP features opened
Same-IP user lookups now identify the target by user_id and ip_type rather than accepting a raw IP in params, so the IP is resolved server-side and never round-trips through clients that lack permission to see it. Additionally: - Hide the `suspicious…
- ✅ SamSaffron/term-llm#634 (diff) — feat: use durable response ids for session continuations closed
Replaces the first-party
/v1/sessions/:id/messagesappend endpoint with durable message-backed/v1/responsescontinuations. - Adds durableprevious_response_idtokens backed by persisted message rows (resp_msg_<messages.id>). - Keeps live/ran… - ✅ discourse/discourse-doc-categories#83 (diff) — FEATURE: simple mode for Discourse docs merged
Adds a “simple mode” for doc categories that strips visual noise: hides replies behind a toggle on doc topic pages, simplifies the topic list (drops posters/replies/views, renames “Activity” to “Updated”), and removes the suggested-topics tab from “m…
- ✅ discourse/discourse#39679 (diff) — FEATURE: Allow dots in the middle of tag names merged
Amends internals so a tag called
5.0-releaseis supported. Specifically dots are allowed in the middle of tag names. - ✅ SamSaffron/term-llm#625 (diff) — fix: Every shell/custom tool teardown scans the entire /proc tree closed
Avoid the host-wide
/procscan on every shell/custom tool teardown. This change keeps the existing process-group kill, but only falls back tokillTaggedDescendantswhen it is actually needed: - the command was cancelled/timed out, or - a lightwei… - ✅ SamSaffron/term-llm#627 (diff) — perf: avoid redundant markdown normalization closed
- Avoid full input copies in the ANSI markdown renderer when tab normalization is configured but the input contains no tabs. - Skip regex newline normalization and inline escape/entity decoding when the rendered/text fragments do not contain the rele…
- ✅ SamSaffron/term-llm#626 (diff) — fix: make stream adapter cancellation responsive closed
Make
ui.StreamAdapter.ProcessStreamuse context-aware sends for every translated stream event. If the downstream event channel is full and the caller cancels the stream context, the adapter now exits and closes the event channel instead of staying … - ✅ SamSaffron/term-llm#624 (diff) — fix: Serve mode rewrites the full session message history after every run closed
- stop serve mode from doing an unconditional full
ReplaceMessagesrewrite after every successful run - keep the existing incrementalAddMessage/UpdateMessagepersistence path as the normal success path - only fall back to a final full snapshot…
- stop serve mode from doing an unconditional full
- ✅ SamSaffron/term-llm#623 (diff) — fix: Glob traversal re-Lstats every ancestor path on every filesystem access—B closed
- make
EventToolExecEndemission inexecuteSingleToolCallbest-effort/non-blocking by switching those sends toTrySend- apply the same non-blocking behavior to the panic-recovery path inexecuteSingleToolCallSafe- add a regression test cove…
- make
- 🟢 discourse/discourse#39948 (diff) — FEATURE: Exclude AI bot PMs from regular notifications and inbox opened
AI bot conversations are now identified by a new
ai_bottopic subtype rather than theis_ai_bot_pmcustom field. Personal message lists, notification counters, the PM tracking state, and theUserActionstream now filter out non-normal PM subtyp… - ✅ discourse/discourse#39872 (diff) — FIX: TL4 PM participants can schedule system publication of private messages merged
Tighted permission checks around scheduled publishing to resolve edge cases with tl4 publishing topics
- ✅ SamSaffron/term-llm#622 (diff) — perf: avoid duplicate skill registry scan closed
- Build the skill registry
List()cache fingerprint during the first discovery scan instead of doing a separate pre-scan when the list cache is empty. - Reuse the already-stattedSKILL.md/skill.mdmanifest path when loading skill metadata durin…
- Build the skill registry
- ✅ SamSaffron/term-llm#621 (diff) — perf: reduce smooth buffer drain copying closed
Reduce
SmoothBuffer.NextWordsbacklog drain work in the terminal streaming path. The old implementation converted the entire pending buffer to a string, converted that whole string to[]rune, extracted a few words, then rebuilt the buffer with th… - ✅ SamSaffron/term-llm#620 (diff) — fix: Image serving re-copies the full source file before checking whether the ha closed
- hash the approved source image first so
ensureImageServeablecan check the deterministic cache path before creating a temp file or copying bytes into the output directory - only rewind and materialize a temp copy when the hashed destination does …
- hash the approved source image first so
- ✅ SamSaffron/term-llm#619 (diff) — fix: filesDir serving duplicates generated media under a new random filename on closed
Use a deterministic content-derived filename when
ensureFileServeablehas to materialize an approved file intofilesDir, and reuse the existing copy when that content is already present. Also add a regression test covering repeated calls for the … - ✅ SamSaffron/term-llm#618 (diff) — fix: Web UI eagerly base64-encodes and retains every attachment before upload closed
- stop mutating pending Web UI attachments into persistent base64
data:URLs before send - materialize attachment payloads only into transient request parts used for/v1/responses- keep sent message attachments lightweight by preserving their ex…
- stop mutating pending Web UI attachments into persistent base64
- ✅ SamSaffron/term-llm#617 (diff) — fix: Gemini edit requests build giant base64+JSON copies in memory closed
Stream Gemini image-edit request bodies instead of prebuilding a giant in-memory base64+JSON payload. - keep Gemini edit parts as raw image bytes until upload time - write request JSON directly to the HTTP body, base64-encoding inline images on the f…
- ✅ SamSaffron/term-llm#616 (diff) — fix: Server decodes full uploaded image payloads even when the original base64 c closed
- preflight uploaded base64 payload sizes from their encoded length before allocating decoded bytes - keep small LLM-native inline images in their original base64 form instead of eagerly decoding and re-encoding them - add regression coverage for the…
- ✅ discourse/discourse#39877 (diff) — FEATURE: Support subcategories and IDs in researcher category filter merged
Expand the AI researcher’s
category:filter so it can target category hierarchies more precisely: - Category filters now include subcategories by default (e.g.category:supportmatches posts insupportand its children) - Prefix with=to e… - ✅ discourse/discourse#39842 (diff) — FIX: Wiki edits can silence the original author through AI spam scans merged
under rare conditions spam scanning can be mis-attributed and apply to author when an editor edited it.
- ✅ discourse/discourse-mcp#46 (diff) — fix: support Discourse installations under subfolders merged
Preserve the path component when normalizing
--siteandauth_pairssite URLs so installations served from a subfolder (e.g.https://example.com/forum) route correctly. - Route leading-slash API paths like/about.jsonand/search.jsonunde… - ✅ discourse/discourse#39875 (diff) — FIX: Screened email list exposes IP addresses to moderators without IP-view perm merged
Edge case where in an outlier case moderators can see ip addresses and should not be allowed to
- ✅ discourse/discourse#39840 (diff) — DEV: rename staff_logins report to admin_logins merged
The report was already titled “Admin Logins” and only displayed admin login data, but was named
staff_loginsand accessible to all staff. Rename it to match its actual contents and restrict access to admins only by adding it toADMIN_ONLY_REPORTS…
🐛 GitHub — Issues
No issue activity this week.
👀 GitHub — Reviews
7 reviews this week:
- discourse/discourse#40025 — FIX: prevent invite links from deleting unrelated email invites approved
- discourse/discourse#39959 — FEATURE: Enable high context topic cards in Horizon by default approved
- discourse/discourse#39869 — FEATURE: Star AI bot conversations in sidebar commented
- discourse/discourse#39956 — DEV: Do not cache chat onebox templates in development approved
- discourse/discourse#39987 — UX: show group ownership and status to all users approved
- discourse/discourse#39952 — FIX: Use search blurbs in
DiscourseAi::Utils::Searchapproved - discourse/discourse#37941 — DEV: Add writing-rspec-tests agent skill approved