fix(slack): answer in a thread, and make the manifest readable
Both problems observed on the first live @mention after cloud#3231 deployed.
1. Replies went to the channel, not a thread
postReply only threaded when the incoming message was already in one:
const result = msg.threadTs
? await slack.reply(chanId, msg.threadTs, text)
: await slack.post(chanId, text); // ← top-level mention dumps into the channel
That is worst exactly where agents are useful. Every agent shares one Slack identity, so a single @Agent Relay … wakes every agent watching that channel — the production dispatch for one message matched 8 deployments — and they all replied into the main channel. Now the reply threads under the incoming message, and stays in the existing thread when there is one.
2. capabilities --json was an unreadable wall
~4KB of minified JSON posted as plain text. Now pretty-printed inside a ```json fence and led by one line naming the agent and pointing at plain-language questions — nobody should have to parse JSON to learn they can just ask.
Blast radius, deliberately
The threading change is in shared/slack.ts, so it applies to every agent using that helper (hn-monitor, joke-bot, inbox-buddy, review, …). That is the intent: an agent answering a channel mention should thread. Calling it out because it is broader than the askable-gtm title suggests.
The existing test asserting a channel-level post encoded the old behaviour; it now asserts the thread, with the reasoning in a comment so it is not "fixed" back later.
Validation
npm run typecheck: passtests/askable-gtm.test.mjs: 46 pass, 2 new — fenced/parseable manifest, and an existing thread answered in that thread rather than a new one- full suite: 322 pass / 0 fail
Not fixed here
One @Agent Relay mention still wakes every agent watching the channel, because they share a Slack identity — cloud#3234 gates on the bot being mentioned, which does not disambiguate which agent is being addressed. Threading makes that liveable, not solved. Worth a follow-up.
🤖 Generated with Claude Code
Summary by cubic
Fixes four problems from the first live @mention: replies thread under the message instead of landing in the channel, capabilities --json stays parseable for machines while Slack gets a readable fenced view, cited evidence is an excerpt rather than a whole post, and answers lead with results instead of a preamble.
- Replies thread under the incoming message, and replies to a message already in a thread stay in that thread.
- Threading is opt-in via
startThreadinpostReply/conversationKeyForSlack; only askable-gtm enables it because it keeps no cross-turn Slack context. capabilities --jsonstays standalone parseable JSON over relay; the Slack actor fences and pretty-prints it via a newpresentJsonhook.- Evidence lines are excerpted to 180 characters on a word boundary, and LinkedIn rows are attributed by author handle.
- Answers drop the question restatement and coverage preamble, keeping only a compact source-failure note and the disclosure for metered managed access.
- Unsolicited watch deliveries get a compact header naming the query, cadence, and watch id so the reader knows which one fired and can unwatch it.
- Tests drive both transport halves through the real handlers, so the relay/Slack split can't silently regress.
- One
@Agent Relaymention still wakes every agent watching the channel since they share a Slack identity; threading makes that liveable, not solved.