Skip to content

Build the Linear transport so a Linear workspace can actually be worked #6432

Description

@macanderson

Linear ships as a manifest now (#1281,
crates/stella-cli/src/issue_provider/linear.toml). Stella can read Linear's
words. It still cannot talk to Linear.

The problem

This build has one issue adapter and it runs gh
(crates/stella-cli/src/issue_provider.rs, GhIssueProvider). So a workspace
that sets [issues] provider = "linear" in stella.toml gets Linear's
vocabulary and the GitHub adapter. The loader prints a line saying so, which is
honest but is not a working Linear setup.

Half of doc:agent-native-delivery §4.1 is therefore not defined anywhere,
because nothing would read it: [connection], [states.write],
[capabilities], [branch] and [fields.write]. Writing that schema without
the transport would be scaffolding, which is why #1281 left it out.

What to build

  • A Linear transport behind the IssueProvider trait
    (crates/stella-protocol/src/issue.rs), sitting beside GhIssueProvider
    rather than branching inside it. The manifest's kind picks which one runs;
    that choice is one table lookup today.
  • [connection]: api_url, api_key_env, team_key. The manifest holds the
    name of an env var, never a secret. A stella connect linear OAuth token
    stays in ~/.stella/integrations.json and keeps winning over the env var.
  • [states.write]: which state Stella sets when it drives a transition.
  • [capabilities]: sub_issues, blocked_state, close_reason, comments,
    search, declared rather than probed (§4.2).
  • [branch] source = "provider": ask Linear for its canonical branch name,
    fall back to a derived one. Nothing in the tree derives a branch name from an
    issue yet, so that consumer has to be built too.
  • Load-time validation (§4.3): an enabled provider whose api_key_env is
    unset, whose [classes] names a label that does not exist, or whose
    [states] leaves a reachable state type unmapped is a startup error naming
    the file and the key. ProviderManifest::resolve is infallible today and
    every caller treats it that way, so this changes its signature.
  • The trust boundary (§4.3), and it is why [connection] cannot land
    alone.
    A provider manifest with a real transport behind it is an egress
    destination
    : a project-scope .stella/issues/*.toml could send diffs, file
    paths and error text to a host the user never approved. It sits on the same
    boundary as context_providers and [integrations], so an untrusted project
    scope must contribute no provider — project_code_execution_trusted()
    (crates/stella-cli/src/plugin_cmd.rs) is the gate that already does this
    for project_custom_tools. This does not bite today because the only
    transport is gh, which reaches the repository the user is already in and
    holds no token Stella can leak. It bites the moment a manifest can name an
    arbitrary api_url. Invariant 3 makes a leak here a privacy incident rather
    than a bug, so land the trust gate in the same change as [connection],
    never after it.

Blocked on a kernel change first

IssueState (crates/stella-protocol/src/issue.rs) is Open / InProgress /
Closed. It has no Blocked. So [states] blocked and
[states.blocked_from] — the label-or-relation degradation the spec describes
— cannot be represented at all until that variant exists and something branches
on it. Decide that before writing the manifest half.

Why this could not ride #1281

It needs a credential and a rig. A LINEAR_API_KEY and a real Linear
workspace are required to write the GraphQL client, and required again to
witness it — a wiremock test can pin the shapes but cannot establish that they
are Linear's actual shapes. AGENTS.md § "Fix over file" names this case.

Files

  • crates/stella-cli/src/issue_provider.rs — the trait impl and the gh
    adapter to sit beside
  • crates/stella-cli/src/issue_provider/manifest.rsBUILT_IN, resolve,
    RUNNING_TRANSPORT, and announce_transport, which is the line that would
    stop being printed
  • crates/stella-cli/src/issue_provider/linear.toml — the shipped manifest to
    extend
  • docs/spec/agent-native-delivery/provider.linear.toml — the full target,
    every block rendered
  • docs/spec/agent-native-delivery.md §4.1, §4.2, §4.3

Which pillar

Innovation, and vendor lock. A customer on Linear cannot use the self-driving
loop at all today. It is also the second half of the proof #1281 started: the
format carries Linear's vocabulary, and this is whether it carries Linear's
behaviour.

Done looks like

stella.toml with [issues] provider = "linear" reads, ranks and closes real
Linear issues; announce_transport prints nothing for a Linear binding; and
a manifest with an unset api_key_env fails at startup naming the file and
the key, with a test for each.


Definition of done (triage addendum)

  • IssueState::Blocked kernel change lands in stella-protocol first, as its own reviewable step, per this issue's own "Blocked on a kernel change first" section.
  • Witness test for each item in "Done looks like": a test that fails before the change and passes after, for (a) stella.toml with provider = "linear" reading/ranking/closing a real Linear issue, (b) announce_transport staying silent for a bound Linear provider, and (c) an unset api_key_env failing at startup naming the file and key.
  • The trust-boundary gate (project_code_execution_trusted()) lands in the same change as [connection], never after — per this issue's own explicit ordering requirement.
  • New code lives in crates/stella-cli/src/issue_provider.rs and its issue_provider/ submodule beside GhIssueProvider — no new crate, and no growth to a grandfathered god file (command_deck.rs etc.).
  • docs/spec/agent-native-delivery.md §4.1–4.3 and docs/spec/agent-native-delivery/provider.linear.toml updated to match what actually ships; make prose and make line-citations stay green on the touched docs.
  • make gate (or the SCR-001-narrowed stella-cli/stella-protocol slice while iterating) passes, including doc-warnings and clippy on the new module.

Parent epic: #1280.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Polish — worth doing, not urgentarea:clistella-cli — commands, flags, wiringbuild-time:4hrsuse-model:proHigh tier: cross-crate design, tricky invariants (e.g. Opus-class)

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions