Linear GraphQL at https://api.linear.app/graphql. This catalog is name-centric (issue identifiers like ENG-42, team keys like ENG, state/label names in search) — aligned with Linear #1035 task-shaped agent surfaces, not a mirror of every GraphQL operation.
Vendor schema: run introspection — do not hand-author SDL.
export LINEAR_API_TOKEN='lin_api_…'
apis/linear/scripts/refresh_schema.shPersonal API keys use a raw Authorization header (no Bearer prefix).
export LINEAR_API_TOKEN='lin_api_…'NL eval cases under eval/cases.yaml use fixture names like ENG and ENG-42 for synthesis coverage. Do not assume those ids exist in your workspace. Before live plasm_run tests:
discover_capabilitiesorIssue.searchfor a real team key and issue identifier.- Substitute discovered values in get/comment/workflow programs.
- Treat
Entity not foundor zero-row search as workspace data mismatch unless auth fails.
Linear workspaces can define issue custom fields, but the public GraphQL schema exposed in Linear's SDK does not yet include a stable customFields (or equivalent) query for field definitions and issue value shapes. When Linear publishes that surface, add custom_field_query + schema_overlay: with projection.mode: augment_base on Issue (same pattern as ClickUp). Until then, use static Issue fields and issue_update for standard properties only.
cargo run --bin plasm -- \
--schema apis/linear \
--backend https://api.linear.app \
--replSearch / filter
plasm> Issue.search(team_key=ENG, label_name=bug, q=auth)
plasm> Issue.search(assignee_name=Jane Doe, state_name=In Progress)
plasm> Project.search(q=Mobile)
plasm> Document.search(q=spec)
Get by human identifier
plasm> Issue(ENG-42)
plasm> Team(ENG)
Composed reads (#1035-style)
plasm> IssueContext(ENG-42)
plasm> MyWorkSnapshot
plasm> ProjectContext(<project-uuid>)
plasm> IssueNavigationLink(ENG-42)
plasm> CycleBoardSnapshot(<cycle-uuid>)
Writes
plasm> Issue.create(team=Team(ENG), title=New bug from Plasm)
plasm> Issue(ENG-42).update(title=Renamed, state_name=In Progress)
plasm> Issue(ENG-42).delete
plasm> Comment.create(issue=Issue(ENG-42), body=LGTM)
plasm> Issue(ENG-42).comments
Pagination: list/search capabilities paginate via page(pg#) in execute sessions after the first wave.
Cross-catalog goals that end in a Linear write should be one coherent pipeline, not parallel reads from each catalog. Shape (substitute session e#/m#/p# from the teaching table):
team = Team.limit(1) # discover a live anchor — never hardcode workspace ids
src = Pokemon(pikachu) # one upstream GET (simple-id entity, positional identity)
ticket = src => Issue.create(team=Team(ENG), title=Pokedex #025, description=<<MD
…markdown body…
MD)
ticket[identifier,title,description] # single created-row return
Discipline:
- Single return = the created row; do not return
read_a, read_bparallel roots. - Call budget: one GET per catalog; avoid relation fanout unless you genuinely need N rows.
- A provably-singleton source (
Pokemon(pikachu))=>runs the write once (not a fanout). - Discover before id: resolve the team key from
Team/Team.limit(1)/ search — eval ids likeENG/ENG-42are not your workspace. issue_createnow projectsdescription, soticket[description]confirms the markdown body was applied.
See COVERAGE.md. Eval goals: eval/cases.yaml.
cargo run -p plasm-eval -- coverage --schema apis/linear --cases apis/linear/eval/cases.yamlcargo test -p plasm-e2e --test linear_smoke
cargo test -p plasm-e2e --test linear_live -- --ignored # needs LINEAR_API_TOKEN