← Community
bugfixed

Builder composes linear.create_issue from a read-only prompt ("List my Linear teams")

Muhammad Akif JanjuaMuhammad Akif Janjua#417d ago · 153 views
affected: station-v0.65fixed in: station-v0.88

Reproduction steps:

  1. Studio → Builder. Type exactly: List my Linear teams
  2. Click Build. Read the generated spec.

Expected: a spec calling linear.list_teams — a read.

Actual: the spec's only step is command_id: "linear.create_issue" — a write.

Reproduced twice. Second build (workflow_id list_linear_teams_v2, different integrity_root) produced the same command_id from the identical prompt. Outputs differed between builds (["team_id","team_name"] vs ["teams"]) but command selection did not.

Everything else in the spec describes a read:

workflow_id = list_linear_teams / list_linear_teams_v2
title = "List Linear Teams"
step label = "Get Linear Teams" / "Fetch Linear Teams"
outputs = ["team_id","team_name"] / ["teams"]

command_id is the single field that disagrees. inputs_template is {team_id}, {title}, {description} — create_issue's shape, not a list call.

linear.list_teams is registered in the same module (10 reads, 6 writes, 16 commands loaded, signature verified). The correct command was available and wasn't selected.

Banner printed both times: UNVERIFIED · Composed by a non-primary model (UNKNOWN_DEFAULTED_FALLBACK). Re-verify or rebuild before trusting the output.

Receipt panel showed result = UNVERIFIED, signature = ed25519 · signed, audited = 1 piece(s).

I did not click Run dry-run, so this is a report about composition, not execution. Header showed DRY-RUN · 0 external sends.

Concern: the intent mismatch is visible only in command_id. Title, label and outputs all read as safe. A user skimming the preview approves a write.

Questions: is UNKNOWN_DEFAULTED_FALLBACK expected here, and is command selection meant to be constrained by prompt intent?

Station version ('railcall version'): station-v0.65
Module slug + version: muhammad-akif-janjua/linear-guard v1.5.5
127.0.0.1_8799_v2.png

3 pts

2 replies

Reproduced, and thank you — this is a real composition-quality gap, though not a governance bypass. "List my Linear teams" composing linear.create_issue is the Builder's model picking the wrong command; the correct linear.list_teams was loaded and available. Two things matter here: (1) it happened on a fallback model — the UNVERIFIED · Composed by a non-primary model (UNKNOWN_DEFAULTED_FALLBACK) banner fired both times, which is exactly the signal not to trust the spec, and nothing executed (DRY-RUN · 0 sends); (2) your point stands that the intent mismatch is visible only in command_id while title/label/outputs all read safe — a skimming user could approve a write.

Actions: UNKNOWN_DEFAULTED_FALLBACK means the primary composer wasn't reachable, so the Builder shouldn't have produced a "trustworthy-looking" spec at all — we're tightening that path, and constraining command selection by prompt read/write intent is a Builder improvement we're tracking. Not a v0.70 code fix (no security boundary was crossed), but a valid catch — +1, and it directly informs the fix.

Fixed in station-v0.88. Your two-build repro pinned it perfectly, @akif.janjua.123 — and the root cause is sharper than it looks: the Builder→airlock bridge's command menu is writes-only by design (reads need no approval staging), so for "List my Linear teams" the composer had NO read command available to bind — linear.list_teams was available to the airlock, never to the Builder. The fallback model (UNKNOWN_DEFAULTED_FALLBACK — expected marker for a non-primary composer, but not a license to guess) grabbed the nearest same-provider write, and validation only checked the id existed.

Fix, two layers: (1) every ALLOWED_COMMAND now carries a [read]/[write] marker plus an explicit intent rule — a read request binds no write command, ever; (2) a deterministic write-intent guard in spec validation — a write stays bound only when the step's own text evidences write intent (leading verb of the imperative label wins, so "List open issues" can't false-keep on noun noise). Mismatches drop the binding and record command_dropped_reason on the step, so the UI shows the guard fired instead of silently un-binding. To your question: yes, command selection is now constrained by prompt intent — deterministically, not just by prompting. Credited.

Sign in to reply.