Releases: ashbrener/spec-kit-linear-sync
v0.5.0 — author attribution, ADR mirroring, identity hardening
[0.5.0] — 2026-06-12 — Author attribution, ADR mirroring, identity hardening
Three additive, spec-driven features (specs 008/010 + the #69 hardening) plus
Dependabot. extension.id stays linear; the command surface is unchanged;
every existing install behaves identically until it opts in. All safety
guarantees (idempotency, drift-awareness, fail-closed writes) hold.
Author-based attribution — spec 010
- The board shows who authored each spec. Opt-in (
linear.attribution.*,
default OFF). When enabled, each spec Issue gets an account-independent
author:<handle>label and — for authors who are Linear members — is assigned
to that author on creation. A non-member or unresolved author is labelled but
left unassigned (never the operator). Disabled/absent ⇒ byte-for-byte today's
behaviour (operator assignee per FR-034, no author label). - Author resolution is filesystem-derived. A
**Owner:**/**Author:**
line inspec.mdwins; otherwise the first git author to add the spec
directory; otherwise unknown (a graceful no-op — no label, no assignee, no
failure). - Members match automatically; the override map is optional. Linear's member
roster is read at runtime, so a git email that matches a Linear email resolves
with no configuration. An optional, gitignoredlinear-authors.local.yml
(only a.sampleis committed) aliases git≠Linear emails and pins handles for
non-members — no real email or user id ever lands in a tracked file (the
identity-leak guard covers it). - Idempotent + never-clobber. The author label is strip-and-set (zero churn
on re-run; one swap on author change); the author assignee is written on create
only, so a manual reassignment in Linear always survives. Sub-issues optionally
inherit the author label (subissue_label, default off) but never the author
assignee. Parity-locked with the spec-kit-jira author-attribution feature at the
user-visible level. No new command;extension.idstayslinear.
ADR / decision-record mirroring — spec 008
- Your decisions now reach the tracker. The
Decision / Rationale / Alternativesblocks in each spec'sresearch.mdare mirrored as ADR comments
on that spec's Linear Issue — one comment per decision, in an ADR layout (id,
title, status, decision, rationale, alternatives, source). It rides the
existingafter_*hooks; no new command, no config. Works for any spec that
has aresearch.md; a spec without one is a graceful no-op. - Idempotent + update-in-place. Each ADR is keyed by a hidden
<!-- spec-kit-linear: adr <NNN>-<key> -->marker (heading idD<N>/R<N>,
else a title slug); an unchanged corpus is zero-churn, a revised decision
updates its one comment in place, a new decision adds one comment. (Update-in-
place is Principle I — the filesystem is canonical; it is the sole behavioural
delta from the clarify-comment path, and the one new mutation,commentUpdate.) - Parity with the spec-kit-jira ADR feature: the user-visible comment shape
matches across both sinks. No config/schema change;extension.idstays
linear.
Consumer identity-leak hardening (#69)
- Install refuses to let your identity reach a tracked file. A new install-
time guard (install::assert_no_identity_leak) scans the consumer's tracked
tree for operator-identity leaks —operator.*keys, email-shaped strings, and
a force-tracked operator-local / authors-override file — and surfaces a loud,
named warning with remediation (Principle VIII). Export
SPECKIT_LINEAR_STRICT_IDENTITY=1to make a detected leak fail the install.
Reinforces the spec-004 config/identity split: identity stays in the gitignored
operator-local file, never in committed config.
Tooling — Dependabot (#64–#67)
v0.4.0 — Configurable artifact mapping (#17) + lifecycle fix (#61)
Highlights
Configurable artifact mapping (spec 007 — resolves #17). The spec→Linear mapping is now operator-configurable via an optional mapping: block in linear-config.yml, while the spec-001 default (repo→Project, spec→Issue, phase→sub-issue, task→checklist) stays the frozen zero-config default — existing installs are byte-for-byte unchanged.
- #17 spec-as-Project shape:
Initiative > Project > Issue > sub-issue— idempotent (zero-churn re-runs), drift-aware on the spec level. - Optional L0 narrative super-level → a Linear Initiative above the repo (narrative from the spec
**Input**:only), degrading gracefully where Initiatives aren't on the plan. - Fail-closed validation at config-load rejects mappings Linear can't build (Project-under-Project, dependency-links-as-nesting, etc.).
- Constitution v2.1.0 (MINOR) records the bounded configurability.
- Validated end-to-end against a real Linear workspace (created the full hierarchy, re-ran zero-churn). The three fixes that validation surfaced are included.
Lifecycle fix (#61). Linear issues no longer stick at "in-progress": the reconciler reads the spec's real branch from plan.md for the PR-state lookup instead of guessing from the directory name.
extension.id stays linear; the command surface is unchanged. See the CHANGELOG for full detail.
v0.3.0 — config/identity split, team-scoped seeding, faithful projection
Three spec-driven features (specs 004/005/006). extension.id stays linear — commands are unchanged, and existing installs migrate automatically.
Config / identity split (#38, #20)
linear-config.yml now holds only the shareable team/project binding (safe to commit); your operator identity moves to a gitignored operator-local file. Identity + API key resolve via env → local file → prompt (no more per-worktree .env copies). Legacy single-file configs auto-migrate with a one-time notice; the docs/.gitignore contradiction is resolved.
Team-scoped / non-admin seeding (#41)
Seed without workspace-admin: team-scoped label creation, an adopt-existing path (capture UUIDs of states/labels that already exist), graceful permission-error handling, and --scope workspace|team (default team).
Faithful projection (#34, #42)
push now accepts ## Phase N headers with :, —, -, or whitespace separators (was colon-only → silent zero sub-issues). The Linear issue description now inlines the spec's own content (Input + Overview + body, capped with clean-boundary truncation + a full-spec link).
All existing safety guarantees (idempotency, drift-awareness, fail-closed) hold. Upgrade is automatic for existing users; catalog users get this once the listing's download_url is bumped.
v0.2.2 — community bug-fix round
Fixes from the first wave of external issue reports (thanks @davieshq, @rcollette). No behavior changes beyond the fixes — extension.id stays linear, so commands are unchanged.
- #33 / #39 / #40 — seed no longer writes a duplicate
default_state_uuidskey intolinear-config.yml; the managed UUID blocks update in place, so the file stays valid single-key YAML and formats cleanly. - #36 — the reconcile summary now counts created issues/sub-issues correctly (they were being lost to a subshell →
Created: 0). - #35 —
pullandstatusnow surface the "malformed config" diagnostic instead of exiting with a silent code 2. - #42 — removed the hardcoded README-anchor links from generated issue descriptions (dead links in any consumer repo).
- #34 —
pushnow warns on## Phase Nheadings not in the## Phase N:form (previously skipped silently → 0 sub-issues).
Install / upgrade: nothing required for existing users. Catalog users — the listing is being bumped to this version.
v0.2.1 — spec-kit-linear-sync (rename + pull alignment fix)
Branding/metadata release. The repo + extension display name become spec-kit-linear-sync, joining the spec-kit-<tracker>-sync family. No behavior change — extension.id stays linear, so /speckit.linear.* commands are unchanged, and GitHub redirects the old repo URL.
Fixed
- #31 —
speckit.linear.pull --humanmisaligned every column when a cell was empty (nophase:*label,nullestimate):IFS=$'\t' readcollapsed empty fields (tab is IFS-whitespace), shifting later columns left. Now splits on a non-whitespace unit separator so empty fields are preserved.
Changed
- Repository renamed
spec-kit-linear→spec-kit-linear-sync;extension.ymlname/repository/homepageupdated. - Functional identifiers intentionally unchanged: command ids, FR-033 hook markers,
specs/001-*paths, the GitHub Action filename.
Upgrade: nothing required. Installed users keep /speckit.linear.*. The old GitHub URL redirects; new canonical URL is https://github.com/ashbrener/spec-kit-linear-sync
v0.2.0 — drift-aware write-authority
Drift-aware write-authority (spec 003). Write-authority now follows the filesystem, not the branch — any worktree may reconcile a spec to Linear. The bridge detects backward-drift (Linear ahead of the worktree you're reconciling from), surfaces it as a warning, and lets you decide (--on-drift=abort|proceed) — it never silently regresses Linear and never blocks the write. Implements the v2.0.0 constitution's amended Principle IV.
Highlights
- Write from any worktree — a merged spec (feature branch deleted) reconciles cleanly from
main; the old branch-gate is gone. - Backward-drift surfaced, never blocked — phase-ordering is the signal; commit-recency only corroborates it. A no-op re-run stays silent (idempotent, SC-017).
--on-drift=abort|proceedfor non-interactive control; interactive runs prompt via/dev/tty.--retroactiveis now a deprecated no-op alias (write-from-any-branch is the default).
Hardening (cross-model review, #26)
- Recency idempotency (SC-017); fail-closed reads under
--on-drift=abort; portable_timeout; skew-value validation; deterministic worktree tie-break;dogfood.shrequires--team; end-to-end drift test coverage (SC-014/015/017/018/019).
Also
- Catalog tag aligned
issue-tracker→issue-tracking(#27).
Full notes in CHANGELOG.md.
v0.1.2 — worktree hooks + merge detection
Two dogfood-surfaced bug fixes, plus the governance groundwork for the upcoming drift-aware authority work.
Fixed
- Worktree-safe git hooks path (FR-033, #14) —
install.shhardcoded.git/hooks, wrong in a git worktree (where.gitis a file and hooks live elsewhere). Now resolves viagit rev-parse --git-path hooks(honorscore.hooksPath). Worktree-based operators' local hooks now install correctly. - Detect merged specs from any branch (FR-013/FR-030, #15) — a merged spec's Linear Issue stayed stuck at its pre-merge lifecycle state when reconciled from a non-feature branch. Root cause:
gh pr view --json mergedused a non-existent JSON field, so merge detection always failed and fell through to a git probe that can't resolve a deleted/non-local branch. Now usesgh pr list --head <branch> --state all; lifecycle resolves to Merged from any worktree.
Changed — Governance
- Constitution amended to v2.0.0 (#13) — Principle IV redefined to "Write-Authority Follows The Filesystem (Drift-Aware)": any worktree may write; the bridge surfaces backward-drift but does not block. Governance-only this release — the drift-aware reconcile behavior ships when spec 003 is implemented. The extension version line (0.1.2) and the constitution version line (2.0.0) are independent.
Notes
The worktree-hooks fix unblocks a clean re-vendor for operators who installed via --dev from a worktree. Pin to the release tag: specify extension add linear --from https://github.com/ashbrener/spec-kit-linear/archive/refs/tags/v0.1.2.zip.
Full detail in CHANGELOG.md under [0.1.2].
v0.1.1 — install ergonomics
Install ergonomics redesign (spec 002) plus three dogfood-surfaced reconcile hotfixes.
Headline
The Linear API key is the only thing an operator brings to install. Team and project are discovered interactively — numbered pickers, "Create new project" inline, zero UUIDs surfaced. /speckit.linear.install resolves the key from .env/env/prompt, verifies via the viewer query, and writes the resolved binding to linear-config.yml.
Added — install ergonomics (spec 002)
- Viewer-driven discovery flow (FR-037..FR-043) — API-key-first, interactive team + project pickers, projectCreate inline
- Backwards-compat preserved (FR-044/045) —
--team/--projectflags + tightened--non-interactive - Self-install safety guard (FR-046) — refuses
--devfrom inside the bridge's own checkout - Vendored
.git/detection (FR-049) - README install commands corrected (FR-047) — archive-ZIP
--fromURL,--devpath
Fixed — reconcile hotfixes
--retroactiveactually bypasses the FR-025 write-authority gate (#3)- Lazy-create
task-phase:Nlabels for specs with 10+ phases (#4) - Guard null
relations/labelsin the blocks-lookup path (#6)
Validation
- Constitution v1.0.0 re-check: 8 Conform / 0 Drift
- Dogfooded live to a Linear workspace during development
Full detail in CHANGELOG.md under [0.1.1].
v0.1.0 — spec-kit ↔ Linear bridge
Mirror every spec on disk into a Linear Issue — the filesystem stays the single source of truth, Linear is the read-only mirror.
The bridge installs via specify extension add linear and attaches to spec-kit's own after_* hooks plus local git hooks plus a GitHub Actions webhook. Every /speckit.* lifecycle command keeps Linear in sync without re-running anything.
Highlights
- 🔁 Reconcile-based, never event-push. Every reconcile rebuilds Linear from disk. Re-running on unchanged state produces zero churn (SC-002).
- 🏷 5 commands:
/speckit.linear.install,.seed,.push,.status,.pull. Push fires automatically on every spec-kit hook; the other four are operator escape hatches. - 📋 Filesystem ↔ Linear mapping: repo → Project, spec → Issue, task phase → sub-issue, tasks → checklist mirror, clarify sessions → comments, lifecycle phase → workflow state +
phase:*label. - 🧠 Memory block auto-managed on every spec Issue: current phase / branch / worktree(s) / last-touched / GitHub link, rewritten every reconcile.
- 🔢 Fibonacci
[N]story-point markers on task lines roll up to Linear'sestimatefield (per-phase + spec rollup). - 🤖 Agent identity stamping —
agent:claude/agent:codexworkspace labels let you filter the kanban by which AI agent did the work. Sticky — once applied, never removed. - 👤 Operator captured at install as Linear
assigneeIdon everyissueCreate(single-write-on-create — manual reassignment in Linear's UI persists). - 🚧 Write-authority gate: only the worktree on a spec's feature branch may mutate that spec's Linear Issue. Other worktrees' syncs are read-only for that spec.
- 🚀 Layer D + Layer E: local reconciler + GitHub Action webhook are independently idempotent. Either alone keeps Linear converging.
Architectural posture
- No daemons, no databases, no filesystem watchers, no crons. Bash + curl + jq + gh + git only.
- Filesystem wins every conflict. Unidirectional sync — bridge never writes back to disk.
- Constitution v1.0.0 ratified — 8 principles. Pre-release audit: 7 Conform / 1 caveat / 0 Drift.
Performance
| Workload | Measured | Target | Headroom |
|---|---|---|---|
| N=10 specs cold reconcile | 0.992s | ≤30s (SC-007) | ~30× |
| N=10 specs hot reconcile | 0.840s | ≤5s (SC-008) | ~6× |
Install
specify extension add linearThen /speckit.linear.install (interactive ceremony) → /speckit.linear.seed (one-shot workspace setup) → use spec-kit as normal.
See README.md for the full adopt-in-3-steps walkthrough.
CI matrix
Bats unit + integration suites pass across:
- ubuntu-latest × bash 4.4
- ubuntu-latest × bash 5.2
- macos-latest × bash 5.2
(macOS × bash 4.4 excluded — bash 4.4 source doesn't compile against Xcode 16.4 SDK; documented inline in .github/workflows/ci.yml.)
Acknowledgements
Designed and dogfooded against the OSH-INFRA Linear workspace, alongside the sibling spec-kit-red-team extension.