fix(linear-slack): actually create Linear issues (stop fabricating them)#54
Conversation
Asked to "make an issue", the harness hand-wrote a JSON file into the read-only Linear mirror — inventing an AR-NN ref + UUID and copying an existing issue's shape (including read-only fields the writeback rejects). That is not a Linear mutation: nothing was created, yet the reply claimed "Done! Created AR-83". Route board WRITES through the real writeback instead of the harness filesystem: - persona.ts: the harness treats ./linear as READ-ONLY and, when a change is asked for, resolves real ids from the VFS and emits a fenced `linear-actions` block (create_issue / comment). It must not invent ids/refs, must not claim success in prose, and is told a milestone can't be set on create. - agent.ts: the handler parses that block and executes each action through `linearClient()` (draft → issueCreate/comment → receipt), allow-listing IssueCreateInput fields so a stray read-only field can't fail the create. It reports the CONFIRMED Linear url; because relay-helpers `created()` falls back to the draft path on a missing receipt, a non-http url is surfaced as "unconfirmed" rather than claimed done. Malformed/again-missing ids are refused, not guessed. - tests: new tests/linear-slack-agent.test.mjs — confirmed create, unconfirmed (no-receipt) create, missing-id refusal, comment, read-only turn, malformed block. Follow-up to #53 (which fixed delivery). Refs AgentWorkforce/cloud#2029. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI. |
|
Warning Review limit reached
More reviews will be available in 34 minutes and 23 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the linear-slack agent to process board writes via a fenced linear-actions JSON block instead of direct VFS writes. The agent parses these actions, executes them using linearClient(), and appends confirmed URLs or warning messages to the Slack reply. The persona's system prompt has been updated to reflect this new write mechanism, and comprehensive unit tests have been added. The feedback suggests improving action validation to ensure that if any action in a multi-action block is invalid, the entire block is treated as malformed to prevent silent partial failures.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const valid = actions.filter( | ||
| (a): a is LinearAction => Boolean(a) && typeof a === 'object' && typeof a.action === 'string', | ||
| ); | ||
| return { prose, actions: valid, malformed: false }; |
There was a problem hiding this comment.
Currently, any invalid action objects (e.g., missing the action property, or non-object elements like null) are silently filtered out and ignored. This can lead to partial execution of a multi-action block without any warning or error being surfaced to the user.
To prevent silent failures, we should validate that every element in the parsed actions array is a valid LinearAction. If any element is invalid, we should treat the entire block as malformed so that the user/LLM is alerted and no partial state is committed.
const allValid = actions.every(
(a): a is LinearAction => Boolean(a) && typeof a === 'object' && typeof a.action === 'string',
);
if (!allValid) {
return { prose, actions: [], malformed: true };
}
return { prose, actions, malformed: false };|
pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. pr-reviewer could not complete review for #54 in AgentWorkforce/agents. |
…ast ack (#55) Live trace of a real run: the issue WAS created (AR-87 in Launch SDK) but no reply ever posted. Two causes, one of them self-inflicted by #53: - The writeback receipt timeout defaults to 3s — too short for the cloud worker's round-trip. `slack.post()` returned `ts:''` and `createIssue()` returned the draft-path fallback even though both writes landed. - #53 made `postReply` THROW on an empty `ts`. With the scope mounted the draft still flushes at cleanup, so an empty `ts` is a slow receipt, not a drop — but the throw crashed the turn and tore the box down before the reply flushed. Issue created, channel silent. Fixes: - Build slack/linear clients with `writebackTimeoutMs: 12s` so receipts have time to arrive (keeps the box alive to flush) and we get real confirmation. - `postReply` no longer throws on a missing receipt — it logs `linear-slack.reply.no-receipt` and lets the draft flush. The genuine no-scope failure is already caught at build time by tests/persona-integration-scopes, not at runtime. - Reframe an unconfirmed create/comment from a scary "never confirmed" to "submitting now — appears on the board shortly" (creates land via the mirror within ~minutes), still logged for triage. - Add a fast 👀 ack: react on the teammate's message the moment the handler picks up the turn (fire-and-forget, doesn't block the harness), so the channel isn't silent for the minutes-long run. Follow-up to #53/#54. Refs AgentWorkforce/cloud#2029. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The bug (the second half of the investigation)
#53 fixed Slack delivery. This fixes correctness: when asked to make an issue,
linear-slacknever actually created one.The harness "created" the issue by hand-writing a file into the read-only Linear mirror — inventing the
AR-83ref + a UUID and copying an existing issue's full shape (including read-only fields the Linear writeback explicitly rejects). That is not a Linear mutation, so nothing was created — yet the reply said "Done! Created AR-83…". (The adapter's create path isPOST /linear/issues/<draft>.json → issueCreate, requiring a bareIssueCreateInputwithteamId+title, noid/identifier.)The fix: route writes through the real writeback, confirm by receipt
Reads stay in the VFS (the whole point of this persona). Writes no longer touch the filesystem — the harness emits a structured block the handler executes via
linearClient():./linearas read-only; to change the board it resolves real ids from the VFS and emits one fenced block:linearClient().createIssue()/.comment()(draft →issueCreate→ receipt). It allow-listsIssueCreateInputfields so a stray read-only field can't fail the create, and reports the confirmed Linear url. Crucially, relay-helperscreated()falls back to the draft path when no receipt arrives (the same swallow-the-timeout footgun as Slack), so a non-httpurl is surfaced as "unconfirmed — please double-check" instead of claimed done. Missing/again-unresolvable ids are refused, not guessed.Behaviour now
AR-83, false "Done!"issueCreate, reply links the confirmed issueTest
New
tests/linear-slack-agent.test.mjs: confirmed create, unconfirmed (no-receipt) create, missing-id refusal, comment, read-only turn, malformed block.npm test60/60,npm run typecheckclean.Follow-up to #53. Framework-level "make writeback failures loud" is still tracked in AgentWorkforce/cloud#2029.
🤖 Generated with Claude Code
Summary by cubic
Fixes a bug where
linear-slackclaimed it created issues without actually doing so. Writes now go through the real Linear writeback, and replies include confirmed Linear links or a clear unconfirmed warning.linearClient()and report only confirmed URLs; flag unconfirmed writes instead of claiming success../linearas read-only; the persona emits a singlelinear-actionsblock (create_issue,comment) using real IDs only and no success prose.IssueCreateInputfields to avoid read-only rejections; refuse missing IDs and malformed action blocks without changing anything.Written for commit 830d376. Summary will update on new commits.