skills/linear-update/SKILL.md
--- name: linear-update description: Close the loop on a Linear ticket when its work ships - move it to the Interview validation gate (assigned to the collaborator, tagged, with what-we-built + acceptance criteria), or to Done when they've already validated. The transition is GATED: no move to Ready-to-Test/Interview without both an acceptance-criteria checklist and a screenshot in the hand-off comment; mobile/responsive tickets additionally require a real-device or emulation "verified on <devic
npx skillsauth add RonanCodes/ronan-skills skills/linear-updateInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The deploy-time half of the hybrid sync (ronan-skills#61): the collaborator filed the story on Linear, answered the grill, and the build ran on GitHub — this skill brings the outcome back to where THEY live, so the board is self-explanatory without ever opening GitHub.
API mechanics (auth header without Bearer via the RoBot key — never the OAuth MCP — @[displayName](uuid) mentions, per-team states, agent identity as the RoBot user with no label/footer): see /ro:linear-grill § "Linear API mechanics".
--issue <ID> (e.g. NUT-21) — required. Resolve to the issue id + team, then load its full context per /ro:linear-run § "Load full ticket context" and emit the context-load receipt there (mandatory gate — no validation comment without it): the whole description, every comment oldest→newest, and every image/attachment (the most recent comments are most authoritative). The acceptance criteria you validate against come from the complete ask, including anything refined in later comments or shown in a pasted mockup — and review any image the collaborator already attached before you capture your own screenshots.--pr <number> + --repo — fetch title, merge SHA, and the close-with-summary comment from the linked GH issue if one exists (richest source for "what shipped" and the acceptance criteria).--state: default Interview (the human-validation gate — see below). --state Done only when the collaborator has already explicitly confirmed they tested it and are happy (see /ro:linear-grill § "The Done rule"), or In Review for merged-but-not-deployed when there's nothing for them to try yet. Never auto-jump to Done — Done is the collaborator's to set once they've tried it and accepted.--try-url: default = the repo's production URL if known (e.g. from the README or wrangler config); omit the try-it line when unknown — never invent a URL.This skill is the only sanctioned path to the Interview / Ready-to-Test state. The transition is gated: a ticket does not move until the validation comment carries both of:
If you cannot produce both, you cannot transition. Hold the ticket where it is, post a comment saying which half is missing and why (e.g. "no live URL yet to screenshot, lands with the next deploy"), and use In Review instead of Interview. A silent state change with no RoBot hand-off comment is a defect: the collaborator is then asked to verify a build with nothing to verify against, which is exactly the no-AC-for-verification bounce this gate exists to stop (ronan-skills#122).
The gate applies to quick bug fixes too — no small-ticket exemption. The 2026-07-03 board audit found the template holding on feature tickets but slipping to 57% on rapid-fire evening bug fixes (prose hand-offs, no checklist, no screenshot). A one-line fix still gets a one-line AC checklist and one screenshot; that's cheap, and it's what lets a non-technical collaborator verify at a glance.
Wire-in point for the night-shift path: /ro:linear-nightshift Step 4 routes its → Ready to Test transitions through this skill specifically so the gate is enforced on the night-shift path too. That path must never reach the interview state by a bare issueUpdate.
A headless screenshot does not prove a mobile fix. For any ticket that is mobile- or responsive-tagged, OR whose bounce / description text names a phone or mobile symptom (soft-keyboard jump, visualViewport, touch target, viewport overflow, iOS/Android-specific, "on my phone"), the gate tightens: the hand-off comment must also carry explicit real-device or device-emulation verification evidence, a literal verified on <device/viewport> line (e.g. verified on iPhone 15 / 390×844 emulation, or verified on Eoin's iPhone), not just a headless desktop screenshot.
Surface the limitation plainly. A class of mobile bug (the visualViewport soft-keyboard page-jump being the canonical example, nutmeg feedback-mobile-dropdown-jump-visualviewport) cannot be reproduced or confirmed headless. Print "headless cannot reproduce this class; confirming on a real device/emulation before hand-off" rather than re-dispatching a blind headless pass. If no real-device or emulation evidence exists, do NOT transition: hold the ticket and say so. NUT-64 burned 4 blind bounces because the skill let mobile tickets through on an invisible headless "pass"; this gate stops that.
The build path in /ro:linear-run is where the emulation capture is actually run (mobile viewport via Playwright, see Step 2b). Wire-in pointer only, not edited here.
The collaborator owns acceptance. We never mark our own work Done — we hand it to them to validate.
issueUpdate → Interview state (from .ro-linear.json states.interview; create it, type started, if missing) and keep the ticket's people subscribed. (All writes as the RoBot user via LINEAR_API_KEY — no label, see /ro:linear-grill § "Agent identity".) Address the ticket's creator (issue.creator), not always the configured collaborator: a ticket Ronan filed tags Ronan; an Eoin ticket tags Eoin (see /ro:linear-grill § "Address the ticket's creator"). Resolve the creator's displayName + uuid for the mention in step 2.commentCreate with the validation comment — mention-first so they're notified, acceptance criteria they can actually tick through:@[<creator displayName>](<creator uuid>) — **built and ready for you to check** ✅
**What we built:** <1-3 plain-language lines — outcome, not implementation. "Type a team in the fixture picker and pick from their upcoming games" beats "added upcoming_fixtures store">
**Try it:** <try-url, ideally deep-linked to the feature>
**Here's how it looks:**
<embedded screenshot(s) — see Step 2b; before + after when feasible>
**Validate these flows:**
1. <acceptance criterion in plain language — a thing they do and what they should see>
2. <…>
3. <…>
Reply and let me know if it's all good. **If it does what you wanted, move this to Done.** If anything's off, drop it back to **To-do** with a line on what's wrong and I'll pick it up.
**Under the hood:** PR <link> (merged <sha-short>)
<If anything was assumed or descoped from their answers, one honest line about it + what would change it.>
Rules: plain language (the collaborator is not an engineer); calibrated — if it's merged-but-not-deployed, say "lands with the next deploy" and use In Review, don't claim live; if the build made assumptions on their unanswered questions, surface them here, not never. The acceptance criteria come from the ticket's original ask + the grill answers — derive them, don't ask the collaborator to invent them.
Show, don't just tell — a screenshot of the actual result lets the reviewer validate at a glance. After the deploy:
/ro:playwright-check). A throwaway script: navigate to the deep-linked try-url, set the right viewport (use a mobile viewport when the ticket is a mobile issue — e.g. iPhone), do the flow, page.screenshot({ path: '/tmp/NUT-x-after.png' }).main and screenshotting the old state, then "after" on the new build. Don't spend long on it; one "after" beats none.python3 "${CLAUDE_PLUGIN_ROOT:-$HOME/Dev/ronan-skills}/skills/linear-update/scripts/upload-image.py" /tmp/NUT-x-after.png "after" prints . Drop those lines into the comment's "Here's how it looks" block (before above after). The helper does Linear's fileUpload → signed PUT → assetUrl dance.Mobile bugs especially: a screenshot at a real phone viewport is often the fastest way for the reviewer to confirm a fix (and pairs with the diagnostic screenshots we ask them for on repeated rework, see /ro:linear-run § Rework). For mobile/responsive tickets this capture is load-bearing for the gate (see "Mobile / responsive tickets" above): run it at a real device viewport and add the verified on <device/viewport> line to the comment. A desktop-viewport screenshot does not satisfy the gate for this class.
Print: ticket → state, comment posted, and (when called from /ro:linear-nightshift) accumulate into the run's nightsheet line ("Sent for interview: NUT-21, NUT-24 → Interview, assigned to Eoin").
The loop isn't closed when this skill runs — it's handed off. Closure happens when the collaborator moves the ticket to Done. If instead they kick it back to To-do, the next /ro:linear-run treats it as rework and introspects why we missed (see /ro:linear-run § "Rework").
--daily-digest --issue <pinned-ID>)A separate mode (ronan-skills#69) for collaborator-facing release notes. Linear has no first-class release notes, so the fit is a pinned "📰 Release notes" issue: every day that shipped something gets ONE comment, and comments notify subscribers: a push-style changelog they read where they already live.
Invoked at the end of a run, after deploy and after the per-ticket /ro:linear-update hand-offs have run. Wire-in points are /ro:linear-nightshift's close-out and /ro:night-shift's tail. This mode does NOT move any state and does NOT touch the per-ticket tickets; it only appends one comment to the pinned issue.
gh pr list --state merged --search "merged:>=<today>" on --repo. Each item: the user-facing outcome + a try-it link where there is one.commentCreate (RoBot user, no label).**📰 What shipped <weekday D Mon>: <N> updates**
**<Outcome headline in their words>**
<1-2 plain lines: what you can now do, why it helps.>
Try it: <deep-link>
**<Next outcome>**
<…> Try it: <deep-link>
<One honest line if anything shipped partial / behind a flag / lands next deploy.>
Rules: no PR numbers or SHAs in the digest (that's the per-ticket "Under the hood" line's job, not the changelog's); deduplicate against the previous day's digest comment so a slow-merging PR isn't announced twice; if the pinned issue id doesn't resolve, error and ask rather than posting to the wrong place.
Re-running on a ticket already in Interview with an existing validation comment for the same PR: skip with "already sent for interview on PR #N". A NEWER PR for the same ticket appends a fresh comment (e.g. after a rework round).
Daily-digest: re-running for the same day appends nothing new if a digest comment for that date already exists with the same shipped set; a larger shipped set replaces by appending a corrective comment, never edits silently.
| Condition | Behaviour |
|---|---|
| Issue ID not found | List the team's recent identifiers, ask |
| --deployed but no deploy evidence (no live URL probe passes) | Post as In Review with "lands with the next deploy" instead; say why |
| Gate not satisfiable (no screenshot possible, no live URL) | Do NOT transition to Interview; hold, post comment naming the missing half, use In Review |
| Mobile/responsive ticket with no real-device/emulation evidence | Do NOT transition; print "headless cannot reproduce this class", hold for emulation capture |
| --daily-digest with zero merged+deployed work today | Skip silently (no comment), print "no changes today", exit 0 |
| --daily-digest --issue does not resolve to the pinned issue | Error and ask; never post to a guessed issue |
/ro:linear-run (the front door) · /ro:linear-nightshift (calls this at its tail) · /ro:linear-grill (the upstream questions this answers) · ronan-skills#61 (design)
testing
--- name: linear-pipeline description: The Fable orchestrator for a single dispatched Linear ticket. Holds almost no context itself; it receives `--issue <ID> --detached`, decides the stage sequence, and fans out a sub-agent per stage, passing forward only each stage's artifact (never re-derived, never inlined into its own context). Step zero, before any planning or stage routing, is a boundary triage against `canon/security-boundary.md` (#199): a match tags Ronan Connolly and stops the run, no
development
--- name: in-your-face description: Capture a chat-only answer into a durable artifact (markdown + HTML, PDF when cheap) and launch it automatically so the user cannot miss it. Use when user says "in your face", "don't let me lose this", "save that answer", "make that durable", or right after answering a substantive side question (a recipe, comparison, how-to, or generated prompt) that would otherwise die with the context. category: workflow argument-hint: [--no-open] [--vault <short>] [hint of
tools
One-shot headless OpenAI Codex CLI calls for background/admin AI tasks — summaries, classification, extraction, admin glue. The default engine for anything that runs AI constantly in the background (daemon-driven, per-event), because it bills the flat ChatGPT subscription instead of Claude usage or per-token API spend, and it keeps working while Claude is rate-limited. NEVER for coding — coding stays Claude. Use when a skill or daemon needs a cheap always-on AI call, when the user says "use codex", "ask codex", "codex as backup", or when building a background summarizer/classifier into a listener or loop. Reads auth from ~/.codex/auth.json (ChatGPT account, no API key).
research
Turn a warranty rejection, repair quote, or RMA email into a cited decision brief — legal read (NL/EU consumer law), is the part user-serviceable, live part and new-unit prices, repair-vs-DIY-vs-new economics, before-you-send-it checklist, deadlines. Use when the user pastes or screenshots a repair quote, warranty rejection, "not covered" email, onderzoekskosten fee, or asks "should I repair or replace this".