@volter/twin-linear
v0.1.0
Published
Local Linear twin — GraphQL (SDL-derived); your real `@linear/sdk` talks to it unmodified. Mirror, simulate, and fork. Built on @volter/twin.
Readme
@volter/twin-linear
The Linear twin — a local replica of Linear's GraphQL API on the shared
@volter/twin kernel. The real @linear/sdk works against it
unmodified. This is the reference twin (every R1–R18 row passes).
Surface
- GraphQL API (
linear-twin.ts→createLinearTwinServer): issues + teams, projects, cycles, users, labels, workflowStates, comments, roadmaps, with issue→team/ project/cycle/comment links and project→roadmap grouping. - Spec-derived schema (
linear-twin-derived.ts→createDerivedLinearTwinServer, the serve default;--subsetselects the hand-written schema): the schema is derived from Linear's real GraphQL SDL so the strict SDK type-checks pass by construction. - Connector (
linear-connector.ts): full-workspace pull from real Linear (issues + teams/projects/cycles/labels/workflowStates) and push of pending local actions, confirmed back into observed events (auth injected). - Webhooks (
linear-webhooks.ts): issueCreate/issueUpdate fire Linear-shaped webhooks on write. - Conformance (
linear-conformance.ts): SDL-subset + recorded-diff vs a real captured Linear response (standing gate). - UI mirror (
linear-mirror-ui.ts): a Linear-style issue board (React) over the twin's GraphQL API.
CLI
world-linear serve [--subset] [--read-only] [--port N] [--root DIR]
world-linear mirror [--port N] [--root DIR]
world-linear conformance [--root DIR]Point the real @linear/sdk LinearClient at it via apiUrl.
Four ways to use it
- Mock SDK — zero edits (preferred):
LINEAR_TWIN_URL=http://127.0.0.1:PORT node --require @volter/twin/inject your-appredirects the real@linear/sdkfromapi.linear.appto the twin. Or override directly:new LinearClient({ apiKey: 'twin', apiUrl: 'http://127.0.0.1:PORT/' }). - API + CLI —
world-linear serve(writable) + drive withvolter-twin status|plan|refs linear, then push. - Read-only —
world-linear serve --read-only: unlimited local reads, no rate limits; writes refuse like Linear (GraphQL error). - UI mirror —
world-linear mirrorrenders a Linear-style view of the twin's state.
(See the repo's GETTING-STARTED → "Four ways to use a twin", and cookbook/zero-edit-inject.)
Stable on the twin rubric: fidelity, read/write/fork, sync, observability, event emission, and conformance are tracked with explicit coverage gaps.
Coverage
Goal: honest, explicitly tracked coverage of Linear's core feature surface. The only accepted carve-outs are explicit out-of-scope items. Anything not done or carved out is a gap to close.
Done — issues (create/update/get/list) with rich IssueFilter (state/title/identifier/
priority/estimate/assignee/labels + and/or compounds) and opaque cursor pagination; workflow
states + transitions (server-assigned stateId, default per-team workflow, name/type stored on move);
comments; teams; projects + depth (state/progress/startDate/targetDate/lead); project milestones;
cycles; estimates; issue relations (blocks/duplicate/related/similar); labels + assignees;
attachments (title/subtitle/url/JSON metadata); notifications (generated from comment/assignment
activity, mark-read + archive); project updates (body/health); favorites (issue/project, create +
idempotent delete); server-side identifier assignment (TEAM-N); roadmaps (create + list/get,
projects linked to a roadmap — modeled on Linear's real Roadmap/RoadmapCreateInput SDL types
and exercised through the real @linear/sdk's createRoadmap/roadmaps/roadmap); documents
(rich-text docs with title/markdown content, create + update + list/get, linked to a project or
initiative — modeled on Linear's real Document/DocumentCreateInput/DocumentUpdateInput/
DocumentPayload SDL types, deterministic doc-N ids, exercised through the real @linear/sdk's
createDocument/updateDocument/documents/document plus Project.documents/Initiative.documents)
and initiatives (workspace themes grouping projects/documents, initiativeCreate/initiatives/
initiative). SDL-derived
schema built from Linear's real published GraphQL SDL so the real @linear/sdk type-checks and runs
unmodified (plus a hand-written subset schema via --subset); webhooks on issue create/update; UI
mirror (rung-5 ✅ — every board/detail surface rendered, plus a Documents view: docs list with
title/content-preview/linked-parent + initiatives); conformance (SDL-subset + recorded-diff +
canonical ops validated against the real SDL); connector — full-workspace pull from real Linear
(issues AND teams/projects/cycles/labels/workflowStates/initiatives/documents, each via its real
collection query, mapped to twin subjects and folded through the same shadow-diff/idempotent
syncPull) + deep push:
issue fields with name→id resolution for state/assignee/labels, comments, relations, attachments,
project updates, favorites, notification update/archive, documents (create/update), initiatives —
each confirmed back into observed events). Niche admin/workflow surfaces:
organization settings (organizationUpdate + workspace policy fields: name/urlKey/
allowMembersToInvite/roadmapEnabled/gitBranchFormat/fiscalYearStartMonth); members + invites
(users/user reading the real User type with role/active/admin/guest, userUpdate/userSuspend/
userUnsuspend, organizationInvites/organizationInvite/organizationInviteCreate/organizationInviteDelete);
issue + project templates (templates/template/templateCreate/templateUpdate/templateDelete,
type-discriminated issue vs project); snooze (Issue.snoozedUntilAt/snoozedBy via issueUpdate);
triage (Team.triageIssueState, untriaged queue via Team.issues, server-stamped Issue.triagedAt
on triage exit); audit log (auditEntries AuditEntry feed, appended on admin mutations); email
intake (emailIntakeAddress/emailIntakeAddressCreate, minted per-team address); git automation
states (Team.gitAutomationStates/gitAutomationStateCreate, branch/PR → workflow-state rules). All
modeled on Linear's real published SDL and exercised through the twin's GraphQL surface (still a clean
subset of the real schema). UI mirror gained four screens: Timeline (project Gantt over a month
axis), Roadmap (initiative columns rolling up projects + progress bars), Settings (Workspace/
Teams/Members tabs), and a Command menu (Cmd-K palette with keyboard nav) — all rung-5 structural-DOM
verified.
Planned (known-missing, will do) — saved views / custom views / filters, SLAs / automations rules (beyond git-automation states), sub-team hierarchy, deeper webhooks (only issueCreate/issueUpdate today), OAuth scopes, insights / analytics, Linear Asks, CSV / external import, and third-party integrations.
Out of scope (deliberately not modeled, with reason):
- GraphQL subscriptions (realtime) — realtime subscriptions need a persistent server-push transport (websocket/SSE) over a live event bus. An offline, deterministic twin has no long-lived connection or wall-clock event stream to reproduce faithfully, and a faked one would not be vendor-true. The same state changes are instead exposed via the twin's webhook delivery surface + pollable queries.
