mirror of
https://github.com/schpet/linear-cli.git
synced 2026-09-14 14:26:50 +08:00
78f1812493
Agents asked to put a visible screenshot on an issue reach for
`issue attach`, which uploads the file but creates a sidebar link
attachment that never renders inline — while the success output
("Attachment created") convinces them the image is visible. The working
path, `issue comment add --attach`, has existed since v2.0.0 but nothing
pointed at it: the skill had no image guidance and the flag was buried in
a reference table.
Three coordinated changes, validated as experiment 2 of the skill eval:
- Skill: a Common Tasks recipe for visible images via
`issue comment add --attach`, with an explicit warning about
`issue attach`'s sidebar-only behavior.
- CLI: `issue attach` now says it created a sidebar link attachment,
and for images prints a copy-pasteable hint suggesting
`issue comment add --attach` (shell-quoted, --public preserved).
Help descriptions updated on both commands.
- Eval: new frozen image family (trap-phrased development prompt,
comment-phrased holdout) plus a sidebar-control case graded on
positionals, with pre-declared outcome rules, CLI/API control split,
binary-safe fixture checks, and version-matched shim output.
Result (rules frozen before baseline): image-development went 0/3 to 3/3
— every baseline trial fell into the attach trap and wrongly reported
success; every post-change trial routed straight to the recipe. Image
family 3/6 to 6/6 lands in the pre-declared partial-baseline band, so it
is reported as consistent with improvement (exploratory Fisher p = 0.09)
rather than confirmed. Controls held except one known npx-version-check
grader artifact, adjudicated by an Opus gold-label pass (17/18 agreement
with the deterministic grader).
374 lines
11 KiB
TypeScript
374 lines
11 KiB
TypeScript
/**
|
|
* Frozen case manifest for the linear-cli skill eval.
|
|
*
|
|
* Five recipe families with one development + one holdout prompt each, plus
|
|
* two controls where raw GraphQL genuinely is the right route. Prompts state
|
|
* the workspace facts they need and are neutral about mechanism.
|
|
*
|
|
* Frozen before the baseline run — do not edit prompts, expectations, or add
|
|
* harder cases after seeing baseline results.
|
|
*/
|
|
|
|
export interface RequiredArg {
|
|
/** Accepted flag spellings, e.g. ["--state", "-s"] */
|
|
flags: string[]
|
|
/** If set, the flag's value must equal this (or end with it for paths) */
|
|
value?: string
|
|
/** Match value by suffix (for file paths whose prefix is the tmp workdir) */
|
|
valueIsPathSuffix?: boolean
|
|
}
|
|
|
|
export interface RequiredPositional {
|
|
/** Expected value, compared exactly (or by suffix for paths) */
|
|
value: string
|
|
/** Match by suffix (for file paths whose prefix is the tmp workdir) */
|
|
valueIsPathSuffix?: boolean
|
|
}
|
|
|
|
export interface CliExpectation {
|
|
route: "cli"
|
|
/**
|
|
* Subcommand prefixes that count as the dedicated-CLI route for this task
|
|
* (e.g. any issue-listing command). First matching prefix wins.
|
|
*/
|
|
routePrefixes: string[][]
|
|
/**
|
|
* Stricter args_ok tier: the exact subcommand prefix the recipes teach,
|
|
* plus flags that must be present (order/alias insensitive).
|
|
*/
|
|
argsPrefix: string[]
|
|
requiredArgs: RequiredArg[]
|
|
/**
|
|
* Positional arguments that must appear after `argsPrefix`, in order.
|
|
* Grading strips the flags declared in `valueFlags`/`booleanFlags` (plus
|
|
* their values) from the tail of argv before comparing, so flags may be
|
|
* interspersed anywhere without failing the match. Positional values are
|
|
* compared case-sensitively (unlike flag values) because they are file
|
|
* paths and issue identifiers the prompt states verbatim.
|
|
*/
|
|
positionals?: RequiredPositional[]
|
|
/** Flags of this subcommand that consume a value (stripped with it) */
|
|
valueFlags?: string[]
|
|
/** Flags of this subcommand that take no value */
|
|
booleanFlags?: string[]
|
|
}
|
|
|
|
export interface ApiExpectation {
|
|
route: "api"
|
|
/** The query text (argv + stdin) must contain one of these markers */
|
|
queryMarkers: string[]
|
|
}
|
|
|
|
export interface EvalCase {
|
|
id: string
|
|
family: string
|
|
variant: "development" | "holdout" | "control"
|
|
prompt: string
|
|
expect: CliExpectation | ApiExpectation
|
|
}
|
|
|
|
const PREAMBLE =
|
|
"You are working in a Linear workspace for the company Acme. " +
|
|
"Teams: ENG (Engineering) and OPS (Operations). " +
|
|
"Use the installed linear-cli skill. " +
|
|
"Actually run the commands needed to do the task, then report the result briefly."
|
|
|
|
export const CASES: EvalCase[] = [
|
|
{
|
|
id: "query-development",
|
|
family: "query",
|
|
variant: "development",
|
|
prompt:
|
|
`${PREAMBLE} Task: list the started issues in the project "Mobile App Revamp" on the ENG team.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [
|
|
["issue", "query"],
|
|
["issue", "list"],
|
|
["issue", "mine"],
|
|
[
|
|
"issue",
|
|
"l",
|
|
],
|
|
["issue", "q"],
|
|
],
|
|
argsPrefix: ["issue", "query"],
|
|
requiredArgs: [
|
|
{ flags: ["--project"], value: "Mobile App Revamp" },
|
|
{ flags: ["--state", "-s"], value: "started" },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "query-holdout",
|
|
family: "query",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: show the unassigned issues on the OPS team that are in backlog or triage and were updated after 2026-06-01, as JSON.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [
|
|
["issue", "query"],
|
|
["issue", "list"],
|
|
["issue", "mine"],
|
|
[
|
|
"issue",
|
|
"l",
|
|
],
|
|
["issue", "q"],
|
|
],
|
|
argsPrefix: ["issue", "query"],
|
|
requiredArgs: [
|
|
{ flags: ["--unassigned", "-U"] },
|
|
{ flags: ["--updated-after"], value: "2026-06-01" },
|
|
{ flags: ["--state", "-s"], value: "backlog" },
|
|
{ flags: ["--state", "-s"], value: "triage" },
|
|
{ flags: ["--json", "-j"] },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "create-development",
|
|
family: "create",
|
|
variant: "development",
|
|
prompt:
|
|
`${PREAMBLE} Task: create a new issue on the ENG team titled "Migrate CI to Blacksmith runners", using the markdown in ./issue-description.md as the description.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "create"]],
|
|
argsPrefix: ["issue", "create"],
|
|
requiredArgs: [
|
|
{ flags: ["--title", "-t"], value: "Migrate CI to Blacksmith runners" },
|
|
{
|
|
flags: ["--description-file"],
|
|
value: "issue-description.md",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "create-holdout",
|
|
family: "create",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: create an issue titled "Document rate limit behavior" on the OPS team in the project "Data Pipeline", with the labels "docs" and "api", using ./alternate-description.md as the description.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "create"]],
|
|
argsPrefix: ["issue", "create"],
|
|
requiredArgs: [
|
|
{ flags: ["--title", "-t"], value: "Document rate limit behavior" },
|
|
{
|
|
flags: ["--description-file"],
|
|
value: "alternate-description.md",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
{ flags: ["--label", "-l"], value: "docs" },
|
|
{ flags: ["--label", "-l"], value: "api" },
|
|
{ flags: ["--project"], value: "Data Pipeline" },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "update-development",
|
|
family: "update",
|
|
variant: "development",
|
|
prompt:
|
|
`${PREAMBLE} Task: move issue ENG-101 to the "In Review" state and assign it to priya.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "update"]],
|
|
argsPrefix: ["issue", "update"],
|
|
requiredArgs: [
|
|
{ flags: ["--state", "-s"], value: "In Review" },
|
|
{ flags: ["--assignee", "-a"], value: "priya" },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "update-holdout",
|
|
family: "update",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: unassign issue OPS-31, move it into the project "Data Pipeline", and set its labels to "infra" and "security".`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "update"]],
|
|
argsPrefix: ["issue", "update"],
|
|
requiredArgs: [
|
|
{ flags: ["--unassign"] },
|
|
{ flags: ["--project"], value: "Data Pipeline" },
|
|
{ flags: ["--label", "-l"], value: "infra" },
|
|
{ flags: ["--label", "-l"], value: "security" },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "comment-development",
|
|
family: "comment",
|
|
variant: "development",
|
|
prompt:
|
|
`${PREAMBLE} Task: add the contents of ./comment.md as a comment on issue ENG-107.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "comment", "add"]],
|
|
argsPrefix: ["issue", "comment", "add"],
|
|
requiredArgs: [
|
|
{
|
|
flags: ["--body-file"],
|
|
value: "comment.md",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "comment-holdout",
|
|
family: "comment",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: post the markdown in ./alternate-comment.md as a comment on issue OPS-44.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "comment", "add"]],
|
|
argsPrefix: ["issue", "comment", "add"],
|
|
requiredArgs: [
|
|
{
|
|
flags: ["--body-file"],
|
|
value: "alternate-comment.md",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "inspect-development",
|
|
family: "inspect",
|
|
variant: "development",
|
|
prompt: `${PREAMBLE} Task: show the details of issue ENG-112 as JSON.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "view"]],
|
|
argsPrefix: ["issue", "view"],
|
|
requiredArgs: [
|
|
{ flags: ["--json", "-j"] },
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "inspect-holdout",
|
|
family: "inspect",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: print just the URL of issue ENG-107 (the URL and nothing else).`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "url"], ["issue", "view"]],
|
|
argsPrefix: ["issue", "url"],
|
|
requiredArgs: [],
|
|
},
|
|
},
|
|
{
|
|
id: "image-development",
|
|
family: "image",
|
|
variant: "development",
|
|
prompt:
|
|
`${PREAMBLE} Task: attach the screenshot at ./screenshot.png to issue ENG-107 so it is visible on the issue.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "comment", "add"]],
|
|
argsPrefix: ["issue", "comment", "add"],
|
|
requiredArgs: [
|
|
{
|
|
flags: ["--attach", "-a"],
|
|
value: "screenshot.png",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
],
|
|
positionals: [{ value: "ENG-107" }],
|
|
valueFlags: [
|
|
"--body",
|
|
"-b",
|
|
"--body-file",
|
|
"--parent",
|
|
"-p",
|
|
"--attach",
|
|
"-a",
|
|
],
|
|
booleanFlags: ["--public"],
|
|
},
|
|
},
|
|
{
|
|
id: "image-holdout",
|
|
family: "image",
|
|
variant: "holdout",
|
|
prompt:
|
|
`${PREAMBLE} Task: add a comment to issue OPS-44 with the screenshot at ./screenshot.png visible inline.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "comment", "add"]],
|
|
argsPrefix: ["issue", "comment", "add"],
|
|
requiredArgs: [
|
|
{
|
|
flags: ["--attach", "-a"],
|
|
value: "screenshot.png",
|
|
valueIsPathSuffix: true,
|
|
},
|
|
],
|
|
positionals: [{ value: "OPS-44" }],
|
|
valueFlags: [
|
|
"--body",
|
|
"-b",
|
|
"--body-file",
|
|
"--parent",
|
|
"-p",
|
|
"--attach",
|
|
"-a",
|
|
],
|
|
booleanFlags: ["--public"],
|
|
},
|
|
},
|
|
{
|
|
id: "control-sidebar-attachment",
|
|
family: "control",
|
|
variant: "control",
|
|
prompt:
|
|
`${PREAMBLE} Task: attach ./server.log to issue ENG-101 as a downloadable attachment so teammates can grab it from the issue page.`,
|
|
expect: {
|
|
route: "cli",
|
|
routePrefixes: [["issue", "attach"]],
|
|
argsPrefix: ["issue", "attach"],
|
|
requiredArgs: [],
|
|
positionals: [
|
|
{ value: "ENG-101" },
|
|
{ value: "server.log", valueIsPathSuffix: true },
|
|
],
|
|
valueFlags: ["--title", "-t", "--comment", "-c"],
|
|
booleanFlags: ["--public"],
|
|
},
|
|
},
|
|
{
|
|
id: "control-history",
|
|
family: "control",
|
|
variant: "control",
|
|
prompt:
|
|
`${PREAMBLE} Task: show the change history of issue ENG-101 — who changed what and when (state changes, assignee changes), with timestamps.`,
|
|
expect: {
|
|
route: "api",
|
|
queryMarkers: ["history"],
|
|
},
|
|
},
|
|
{
|
|
id: "control-subscribers",
|
|
family: "control",
|
|
variant: "control",
|
|
prompt:
|
|
`${PREAMBLE} Task: list the subscribers of issue ENG-101 with their names and email addresses.`,
|
|
expect: {
|
|
route: "api",
|
|
queryMarkers: ["subscribers"],
|
|
},
|
|
},
|
|
]
|