<!-- source: openspec/changes/linear-as-agent-control-plane-openspec/proposal.md -->

---
title: Linear as agent control plane — OpenSpec ↔ issue body sync + Agent Guidance
audience:
  - founder
  - architect
  - executor
---

# Proposal — Linear as agent control plane — OpenSpec ↔ issue body sync + Agent Guidance

> **Linear epic:** [RAP-1116](https://linear.app/rapoport-studio-slr/issue/RAP-1116). Closeout sub-issue: [RAP-1233](https://linear.app/rapoport-studio-slr/issue/RAP-1233).

## Why

OpenSpec change-folders and Linear issues carry the same intent in two disconnected places. External agents (Linear delegate, Cursor, Codex, Warp/Oz) read the issue body; they never see `openspec/changes/<id>/proposal.md`, `design.md`, `tasks.md`, or `verification.md`. The result is clarification loops, hallucinated scope, and duplicated triage work on every agent handoff.

Two targeted interventions close the gap without bidirectional complexity:

1. **Workspace Agent Guidance** — Linear auto-injects workspace-scoped text into every agent session. Mirroring `AGENTS.md` durable conventions there costs zero runtime and benefits every future agent unconditionally.
2. **OpenSpec → issue body sync** — A CI job renders a structured, marker-fenced block into the linked issue description whenever a PR touches `openspec/changes/**`. Agents that read the issue now see live links, spec deltas, and falsifiable acceptance criteria without any manual copy-paste.

The binding constraint is issue hygiene, not model quality. Closing it once compounds across all current and future agent integrations.

## What Changes

- **`docs/linear-agent-guidance.md` (new)** — Canonical source for workspace Agent Guidance text: RAP naming convention, branch pattern `pavel/rap-<id>-…`, OpenSpec change-folder location, falsifier-required acceptance criteria, `verification.md` as Definition of Done.
- **`tools/openspec-to-linear-sync/` (new)** — Node script that resolves a RAP-id from PR title or branch name, renders a Handlebars template, and upserts a marker-fenced block into the linked Linear issue body via the GraphQL `issueUpdate` mutation. Idempotent: re-push replaces the block in-place; content above the markers is untouched.
- **`.github/workflows/openspec-linear-sync.yml` (new)** — GitHub Actions workflow triggered on `pull_request` events for paths `openspec/changes/**`. Missing RAP-id → hard CI failure with a descriptive error; no silent skip.
- **`tools/openspec-to-linear-sync/template.md.hbs` (new)** — Handlebars template producing the `<!-- openspec:start … -->` / `<!-- openspec:end -->` block: file links, task completion ratio, spec delta summary, acceptance criteria checkboxes.
- **CI guidance-sync step (new)** — On merge to `main` of any change to `docs/linear-agent-guidance.md`, a CI step pushes the file content verbatim to the Linear workspace Agent Guidance field via the Linear API.
- **`apps/studio/e2e/openspec-linear-sync.spec.ts` (new)** — Fixture-driven tests covering: template rendering correctness, idempotent block replacement, duplicate-block prevention, missing-RAP-id failure path.

## Impact

- **Spec files touched:** `openspec/specs/ci-cd.md` (new workflow contract), `openspec/specs/tooling.md` (new sync tool), `openspec/specs/agent-guidance.md` (new — formalises Agent Guidance as a managed artifact)
- **Packages affected:** `tools/openspec-to-linear-sync` (new), `.github/workflows` (new workflow), `docs/` (new guidance doc), `apps/studio/e2e` (new test file)
- **Downstream surfaces:** Linear workspace Agent Guidance field (all agent sessions), Linear issue bodies for any PR touching `openspec/changes/**`, Cursor "Open in Linear" view, Linear delegate flow

## Out of scope

- Bidirectional sync (Linear issue edits do not write back to OpenSpec files)
- Layer 3 CLI scaffold (`muse linear scaffold RAP-<id>`) — deferred to v2
- Multi-issue PRs (single RAP-id per PR enforced; multi-id support is a future extension)
- Slack / Telegram fanout of sync events
- Automated Linear sub-task creation or status transitions
