From 8175049c0854ba847d04320baf1ffed120de8711 Mon Sep 17 00:00:00 2001 From: "melodic-automation-lanes[bot]" <337595936+melodic-automation-lanes[bot]@users.noreply.github.com> Date: Sun, 1 Nov 2026 07:12:29 +0000 Subject: [PATCH 1/8] Resolve issue #11869 Lane-Activity: work-resolve-issue/resolve-issue --- ...work-resolve-issue-issue-11869-5d2c9a17.md | 7 ++ .../case.yaml | 47 +++++++++ .../samples/no-bash-search.json | 98 +++++++++++++++++++ .../samples/no-glob.json | 54 ++++++++++ .../samples/no-grep.json | 57 +++++++++++ .../samples/no-source-read.json | 32 ++++++ .../samples/routes-to-interview.json | 26 +++++ .../scaffold.sh | 39 ++++++++ .../work-items/reference/label-taxonomy.md | 2 +- plugins/work-items/skills/triage/SKILL.md | 21 +++- .../skills/triage/context/apply-outcome.md | 7 +- 11 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 .changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-glob.json create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-grep.json create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-source-read.json create mode 100644 plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/routes-to-interview.json create mode 100755 plugins/work-items/evals/tr-idea-issue-routes-to-interview/scaffold.sh diff --git a/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md new file mode 100644 index 00000000000..f0821be0395 --- /dev/null +++ b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md @@ -0,0 +1,7 @@ +--- +bump: minor +--- + +### Added + +- triage: an idea issue, a feature request that does not say what it is for, now goes to `/planning:interview` and is parked with `needs-spec` before any code search, ledger check, verification or research (#11869). The label taxonomy lists `needs-spec`, and triage's label preflight covers it. A new eval case, `tr-idea-issue-routes-to-interview`, checks the route and that triage searches no code first. diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml new file mode 100644 index 00000000000..56d4e4c2626 --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -0,0 +1,47 @@ +schema_version: "1.1" +name: tr-idea-issue-routes-to-interview +description: 'Idea issue: /work-items:triage named on item #1, a raw feature request ("We should add a plugin system to the ledger.") that states no problem, user or outcome, routes the item to /planning:interview and parks it with needs-spec. It runs no exploration first: no code search, no source read, no research. The prompt authorizes mutations up front, so the run is not held at the direction gate. A run needs --scaffold and --allow-tools Bash,Write,Edit.' +tags: [triage, idea-issue, must-not] +plugins: ["../.."] +expected_outcome: 'The run reads item #1, judges it an idea issue because it says nothing about what the plugin system is for, names /planning:interview as the next step and parks the item with needs-spec. It searches no code and reads nothing under src/ before or after routing.' +context: + scaffold_script: scaffold.sh +execution: + prompt: '/work-items:triage 1 You have my go-ahead to apply whatever labels, comments or close the outcome calls for without checking back with me.' + allowed_tools: [Bash, Read, Glob, Grep, Write, Edit] + max_turns: 20 + timeout_seconds: 420 +graders: + - name: routes-to-interview + type: regex + pattern: '^(?=[\s\S]*/planning:interview)(?=[\s\S]*\bneeds-spec\b)' + target: last_message + arm: both + - name: no-grep + type: tool_used + tool: Grep + input_match: '^(?![\s\S]*/work-items/)' + min: 0 + max: 0 + arm: both + - name: no-glob + type: tool_used + tool: Glob + input_match: '^(?![\s\S]*(?:\.work|/work-items/))' + min: 0 + max: 0 + arm: both + - name: no-source-read + type: tool_used + tool: Read + input_match: '"file_path":"(?:[^"\\]|\\.)*/src/' + min: 0 + max: 0 + arm: both + - name: no-bash-search + type: tool_used + tool: Bash + input_match: '(?:"command":"|[ \t;&(]|\\[nt])(?:(?:grep|rg|ag)[ \t]|find[ \t]+[./~]|git[ \t]+(?:grep|log)\b)|(?:[ \t/"]|\\[nt])src(?![\w-])' + min: 0 + max: 0 + arm: both diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json new file mode 100644 index 00000000000..835f5e7f85d --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json @@ -0,0 +1,98 @@ +{ + "pass": [ + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1'" + } + } + ], + "why": "reads the item through the seam" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh comment 'local-markdown:local/markdown#1' --body 'Run /planning:interview to find out what this is for.'" + } + } + ], + "why": "prose 'find out' in a comment" + }, + { + "answer": [], + "why": "nothing" + } + ], + "fail": [ + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "grep -rn plugin ." + } + } + ], + "why": "greps the checkout" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "git log --oneline -20" + } + } + ], + "why": "reads history" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "find . -name '*.js'" + } + } + ], + "why": "finds files" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "cat src/export.js" + } + } + ], + "why": "reads the source" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "cd /w\ngrep -rn plugin ." + } + } + ], + "why": "greps on a second line" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "ls src" + } + } + ], + "why": "lists the source" + } + ] +} diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-glob.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-glob.json new file mode 100644 index 00000000000..4cf3fae6d8e --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-glob.json @@ -0,0 +1,54 @@ +{ + "pass": [ + { + "answer": [ + { + "tool": "Glob", + "input": { + "pattern": ".work-items/*.md" + } + } + ], + "why": "lists the item store" + }, + { + "answer": [ + { + "tool": "Glob", + "input": { + "pattern": ".work/lanes/*.json" + } + } + ], + "why": "looks for the lane config the scope rule reads" + }, + { + "answer": [], + "why": "nothing" + } + ], + "fail": [ + { + "answer": [ + { + "tool": "Glob", + "input": { + "pattern": "src/**/*.js" + } + } + ], + "why": "lists the source" + }, + { + "answer": [ + { + "tool": "Glob", + "input": { + "pattern": "**/*plugin*" + } + } + ], + "why": "explores the checkout" + } + ] +} diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-grep.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-grep.json new file mode 100644 index 00000000000..812377b8479 --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-grep.json @@ -0,0 +1,57 @@ +{ + "pass": [ + { + "answer": [ + { + "tool": "Grep", + "input": { + "pattern": "needs-spec", + "path": "/cache/plugins/work-items/reference" + } + } + ], + "why": "searches the plugin's own references" + }, + { + "answer": [], + "why": "nothing" + } + ], + "fail": [ + { + "answer": [ + { + "tool": "Grep", + "input": { + "pattern": "plugin" + } + } + ], + "why": "searches the checkout" + }, + { + "answer": [ + { + "tool": "Grep", + "input": { + "pattern": "export", + "path": "/w/src" + } + } + ], + "why": "searches the source" + }, + { + "answer": [ + { + "tool": "Grep", + "input": { + "pattern": "plugin", + "path": "/w/.work-items" + } + } + ], + "why": "cluster search over the item store" + } + ] +} diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-source-read.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-source-read.json new file mode 100644 index 00000000000..7c9623eb955 --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-source-read.json @@ -0,0 +1,32 @@ +{ + "pass": [ + { + "answer": [ + { + "tool": "Read", + "input": { + "file_path": "/w/.work-items/1.md" + } + } + ], + "why": "reads the item" + }, + { + "answer": [], + "why": "nothing" + } + ], + "fail": [ + { + "answer": [ + { + "tool": "Read", + "input": { + "file_path": "/w/src/export.js" + } + } + ], + "why": "reads the source" + } + ] +} diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/routes-to-interview.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/routes-to-interview.json new file mode 100644 index 00000000000..9c11ad1ae98 --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/routes-to-interview.json @@ -0,0 +1,26 @@ +{ + "pass": [ + { + "answer": "#1 \"Add a plugin system\" is an idea issue: it says nothing about what the plugin system is for. Posted a comment naming `/planning:interview` as the next step, applied `needs-spec` and removed `needs-triage`.", + "why": "names the interview and the park label" + }, + { + "answer": "Parked #1 with needs-spec.\n\nNext: /planning:interview, to settle what the plugin system is for before anyone explores it.", + "why": "both on separate lines" + } + ], + "fail": [ + { + "answer": "Triaged #1: enhancement, priority: medium, briefed and routed to needs-human.", + "why": "state-machined, no interview" + }, + { + "answer": "Searched src/ for an existing extension point and found none. Next: /planning:interview.", + "why": "no needs-spec park" + }, + { + "answer": "Applied needs-spec to #1.", + "why": "no interview route" + } + ] +} diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/scaffold.sh b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/scaffold.sh new file mode 100755 index 00000000000..3ba8900c7b6 --- /dev/null +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/scaffold.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# A checkout bound to the offline local-markdown tracker holding one open idea issue: a feature +# request carrying the raw needs-triage marker that names something to build but says nothing +# about what it is for. The checkout also holds source code an exploring triage would search. +set -euo pipefail + +git init -q -b main +git config user.name eval +git config user.email eval@example.invalid + +printf '.work/\n' >.gitignore +printf '# ledger\n\nLedger service.\n' >README.md +mkdir -p .work-items src +cat >src/export.js <<'JS' +export function exportCsv(rows) { + return rows.map((row) => row.join(",")).join("\n"); +} +JS +cat >.work-item-tracker.json <<'JSON' +{"schema_version": "1.0", "provider": "local-markdown", "config": {"lease_ttl_hours": 24, "storage_dir": ".work-items"}} +JSON +cat >.work-items/1.md <<'MD' +--- +id: "local-markdown:local/markdown#1" +number: 1 +title: "Add a plugin system" +state: "open" +assignees: [] +labels: ["needs-triage"] +type: null +parent: null +url: "file://.work-items/1.md" +created_at: "2026-10-01T08:00:00Z" +--- + +We should add a plugin system to the ledger. +MD +git add .gitignore README.md src .work-item-tracker.json .work-items +git commit -q -m "chore: scaffold ledger" diff --git a/plugins/work-items/reference/label-taxonomy.md b/plugins/work-items/reference/label-taxonomy.md index 76f540cbfa6..caf1d02cc30 100644 --- a/plugins/work-items/reference/label-taxonomy.md +++ b/plugins/work-items/reference/label-taxonomy.md @@ -18,7 +18,7 @@ These axes work in any repo and don't change per team. Do not snapshot their mem | Type | native Issue Type (org) · `type:` label (personal/non-org) | The kind of issue: `Bug` (broken vs. intent), `Feature` (new capability), `Task` (any other tracked work: maintenance, refactor, tests, docs, audits, chores). Commit-type granularity (`fix`/`feat`/`chore`/`docs`/`refactor`/`test`/`build`/`perf`) stays at the commit layer, not the issue axis. | | Priority | `priority:` | Urgency. Members from the live set. | | Status | `status:` | Exception and gate flags only (e.g. `needs-info`, `needs-decision`, `ready`). Members from the live set. **Claim is not a status label.** It is assignee + lease (see the seam claim protocol). **Blocked is not a status label.** It is a native `blocked-by` dependency edge. The triage floor is not a status value. | -| Meta | (none) | Tool-owned flat markers the automation sets: `automated`, `good-first-issue`, `migrated`, `stale`, the triage floor `needs-triage` (bare; not a `priority:` or `status:` value), plus the three canonical-role labels (defaults `agent-ready`, `needs-human`, and `recurring`, listed under "Canonical roles" below). | +| Meta | (none) | Tool-owned flat markers the automation sets: `automated`, `good-first-issue`, `migrated`, `stale`, the triage floor `needs-triage` (bare; not a `priority:` or `status:` value), the idea-issue park `needs-spec` (bare; set by triage), plus the three canonical-role labels (defaults `agent-ready`, `needs-human`, and `recurring`, listed under "Canonical roles" below). | | Cadence | `cadence:` | Recurrence period for maintenance items. Members from the live set. | | Work class | `work-class:` | Semantic risk class (C1–C5) stamped at triage for the fail-closed admission gate and merge partition. Canonical members and migration: [`work-class-labels.md`](work-class-labels.md). Classification criteria: the `autonomy` plugin's [`work-classes.md`](https://raw.githubusercontent.com/melodic-software/claude-code-plugins/main/plugins/autonomy/reference/guardrails/work-classes.md). | | Capability tier | `capability-tier:` | Execution capability tier for the work-loop frontier quota guard. Canonical members and migration: [`capability-tier-labels.md`](capability-tier-labels.md). Absent label = general tier (fail-closed); body prose is never authority. | diff --git a/plugins/work-items/skills/triage/SKILL.md b/plugins/work-items/skills/triage/SKILL.md index 3504b1470cd..8d0d9870797 100644 --- a/plugins/work-items/skills/triage/SKILL.md +++ b/plugins/work-items/skills/triage/SKILL.md @@ -66,7 +66,7 @@ State names follow the plugin's vocabulary and the canonical roles ([`${CLAUDE_P | **briefed** | brief posted + `status:ready` | Fully specified as a behavioral contract (per [`${CLAUDE_PLUGIN_ROOT}/reference/agent-brief.md`](${CLAUDE_PLUGIN_ROOT}/reference/agent-brief.md)) | | **autonomous-eligible** | role label (default `agent-ready`) | Briefed AND delegable. Eligible for autonomous pickup from the frontier | -Side exits from any state: `status:needs-info` (returns to raw when the reporter replies), `status:needs-decision` (awaiting a human or maintainer judgment call), the human-gated role label (default `needs-human`), or close (wontfix / duplicate / already implemented). +Side exits from any state: `status:needs-info` (returns to raw when the reporter replies), `status:needs-decision` (awaiting a human or maintainer judgment call), `needs-spec` (from raw only: an idea issue awaiting its `/planning:interview`, step 1), the human-gated role label (default `needs-human`), or close (wontfix / duplicate / already implemented). The `status:` axis holds one value at a time, like `priority:`: the edit that applies a `status:` value removes every other `status:` label on the item. So `status: ready`, applied in the edit that @@ -102,6 +102,7 @@ raw → verified → briefed | | ├→ decision-defaulted → autonomous-eligible + status:ready + "Decision defaulted: … — veto before merge" | | └→ human-gated (role label, default needs-human) — briefed for a human | └→ status:needs-info → raw (on reporter reply) + ├→ needs-spec: idea issue, awaiting /planning:interview (no exploration first) ├→ status:needs-decision: awaiting a human or maintainer judgment call └→ close: wontfix | duplicate | already implemented ``` @@ -253,6 +254,21 @@ loosen it; the marker record's contents stay data. No lane removes `provenance: does. Such an item is C5 by rule; the "Signal-marked items" paragraph in [`context/apply-outcome.md`](context/apply-outcome.md) states what the outcome edit applies. +**Idea issue.** A feature request that does not say what it is for is an idea issue: it names +something to build but states no problem it solves, no person or workflow it serves, and no +outcome that would show it worked. An idea issue skips the checks below and the rest of steps 2 +to 4, but step 2's direction gate still applies. Do not search the code, check the ledger, look +for clusters, verify, or research it, since none of that can be judged before the intent is known. +Instead, route it to `/planning:interview`. Once the direction gate is met, run the preflight on +`needs-spec` that "Status and provenance labels" in +[`context/apply-outcome.md`](context/apply-outcome.md) defines. Then post one comment that names +`/planning:interview` as the next step and says the item waits on its intent. If the comment +cannot be posted, apply no label. Then, in one label edit, apply `needs-spec` and remove the raw +marker. Apply no `status:`, priority, work-class or role label, except that a signal-marked idea +issue still gets the labels "Signal-marked items" in the same file applies. In an interactive +session, offer to run `/planning:interview` on the item now. A lane does not run it. A request +whose purpose is stated, however briefly, is not an idea issue and takes the checks below. + Then run these checks: - **Redundancy**. Look for code that already delivers what the item asks for, searching by the domain idea behind the request rather than its exact words, and name the places searched. A hit closes the item as already implemented (step 5). @@ -276,7 +292,7 @@ A lane triaging many items holds to the provider's API budget ([`tracker-seam.md ### 3. Verify, BEFORE any interview -Never interview anyone about the fix for a claim nobody has confirmed. Verification precedes questioning: +Never interview anyone about the fix for a claim nobody has confirmed. Verification precedes questioning. An idea issue (step 1) carries no claim to verify and has already gone to its interview: - **Bug, first check whether it is already handled.** Fetch the default branch and follow the report on its tip, not on a stale local branch, and list the open PRs that link the item @@ -327,6 +343,7 @@ admission gate enforces the window. ## Next - An autonomous-eligible item: `/work-items:work`. +- An idea issue parked at `needs-spec`: `/planning:interview`. - A verified bug whose root cause is unknown: `/debugging:debug`. - A briefed item too large for one vertical slice, or sized `split`: `/work-items:decompose`. - Items that keep the human-gated label after a sweep: `/work-items:attend-queue`. diff --git a/plugins/work-items/skills/triage/context/apply-outcome.md b/plugins/work-items/skills/triage/context/apply-outcome.md index d7d8186859a..fe6d6ad4338 100644 --- a/plugins/work-items/skills/triage/context/apply-outcome.md +++ b/plugins/work-items/skills/triage/context/apply-outcome.md @@ -92,19 +92,20 @@ Label edits, comments, and closes route through the adapter's write mechanics (a **Closing invariant, no outcome leaves a re-selectable raw item.** The attention view lists *open* items and re-selects anything still carrying the raw marker, so every outcome must leave the item unre-selectable: -- **Every routing outcome that keeps the item open clears the raw-intake marker in the same edit that applies the outcome's labels, no exceptions across the routing space.** `status:ready` (briefed/ready and decision-defaulted), the autonomous-eligible role label, the human-gated role label (default `needs-human`), `status:needs-decision`, and `status:needs-info` each **remove the raw marker**; never leave both the raw marker and a routing label present. A raw marker alongside any routing label is a contradiction, the open-only attention view reads it as still-raw and re-triages it every cycle, so an already-decided item re-enters the needs-triage queue as if it were unrouted intake and wastes a read-and-confirm pass. If an item shows both, the routed state is the truth; clear the stale raw marker. +- **Every routing outcome that keeps the item open clears the raw-intake marker in the same edit that applies the outcome's labels, no exceptions across the routing space.** `status:ready` (briefed/ready and decision-defaulted), the autonomous-eligible role label, the human-gated role label (default `needs-human`), `status:needs-decision`, `status:needs-info`, and an idea issue's `needs-spec` each **remove the raw marker**; never leave both the raw marker and a routing label present. A raw marker alongside any routing label is a contradiction, the open-only attention view reads it as still-raw and re-triages it every cycle, so an already-decided item re-enters the needs-triage queue as if it were unrouted intake and wastes a read-and-confirm pass. If an item shows both, the routed state is the truth; clear the stale raw marker. - **Close** (already implemented / wontfix / duplicate) drops the item from the open-only attention frontier, so the raw marker is moot, a closed item never re-triages. - **`status: confirmed` is not a routing outcome.** Step 3 applies it beside the raw marker. The outcome edit here always clears the raw marker, and replaces `status: confirmed` when it applies another `status:` value; a human-gated outcome, which applies none, keeps `status: confirmed`. ## Status and provenance labels -Two labels this skill writes come from the live label set and are never invented: +Three labels this skill writes come from the live label set and are never invented: - `status: confirmed`, applied by step 3 on a passed verification. - `provenance: signal`, applied by the outcome edit to an item whose body carries the autonomy signal marker (step 1). +- `needs-spec`, applied to an idea issue (step 1). -**Preflight.** Before the edit that would apply either one, check that it exists in the repository +**Preflight.** Before the edit that would apply any of them, check that it exists in the repository (adapter: the label listing step 2 uses for `priority:`). When it is absent, stop without mutating anything, not even the other labels of that edit, and report it as a provisioning gap for the repository's label-as-code owner, or for a person to create in the tracker. Never fall back to a From 5e24718d319314ab7dfbe34766d65ea331769138 Mon Sep 17 00:00:00 2001 From: "melodic-automation-lanes[bot]" <337595936+melodic-automation-lanes[bot]@users.noreply.github.com> Date: Sun, 1 Nov 2026 07:24:25 +0000 Subject: [PATCH 2/8] Address review feedback on work-resolve-issue/issue-11869-41281674150-1 Lane-Activity: pr-unblock/address-feedback Lane-Thread: PRRT_kwDOTCGFQM6yYIKA Lane-Thread: PRRT_kwDOTCGFQM6yYIKF --- .../evals/tr-idea-issue-routes-to-interview/case.yaml | 4 ++-- plugins/work-items/skills/triage/SKILL.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml index 56d4e4c2626..671379e4c74 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -1,9 +1,9 @@ schema_version: "1.1" name: tr-idea-issue-routes-to-interview -description: 'Idea issue: /work-items:triage named on item #1, a raw feature request ("We should add a plugin system to the ledger.") that states no problem, user or outcome, routes the item to /planning:interview and parks it with needs-spec. It runs no exploration first: no code search, no source read, no research. The prompt authorizes mutations up front, so the run is not held at the direction gate. A run needs --scaffold and --allow-tools Bash,Write,Edit.' +description: 'Idea issue: /work-items:triage named on item #1, a raw feature request ("We should add a plugin system to the ledger.") that states no problem, user or outcome, routes the item to /planning:interview and names needs-spec as the label that parks it. It runs no exploration first: no code search, no source read, no research. The prompt authorizes mutations up front, so the run is not held at the direction gate. The case grades the route and the absence of exploration only, not the park itself: the local-markdown tracker has no label listing, so the needs-spec preflight in context/apply-outcome.md may stop the label edit, and no grader reads the final labels of item #1. A run needs --scaffold and --allow-tools Bash,Write,Edit.' tags: [triage, idea-issue, must-not] plugins: ["../.."] -expected_outcome: 'The run reads item #1, judges it an idea issue because it says nothing about what the plugin system is for, names /planning:interview as the next step and parks the item with needs-spec. It searches no code and reads nothing under src/ before or after routing.' +expected_outcome: 'The run reads item #1, judges it an idea issue because it says nothing about what the plugin system is for, names /planning:interview as the next step and names needs-spec as the label that parks it. It searches no code and reads nothing under src/ before or after routing. Whether the park lands is not graded: on this tracker, which has no label listing, the needs-spec preflight may stop the label edit.' context: scaffold_script: scaffold.sh execution: diff --git a/plugins/work-items/skills/triage/SKILL.md b/plugins/work-items/skills/triage/SKILL.md index 8d0d9870797..8869dbd8a6d 100644 --- a/plugins/work-items/skills/triage/SKILL.md +++ b/plugins/work-items/skills/triage/SKILL.md @@ -346,8 +346,6 @@ admission gate enforces the window. - An idea issue parked at `needs-spec`: `/planning:interview`. - A verified bug whose root cause is unknown: `/debugging:debug`. - A briefed item too large for one vertical slice, or sized `split`: `/work-items:decompose`. -- Items that keep the human-gated label after a sweep: `/work-items:attend-queue`. -- Unsure where this leaves the work, or arrived mid-flow: `/session-flow:workflow`. ## AI disclaimer From c1085fb6ab780553a28868ec4c10c44bdafcaa60 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sun, 1 Nov 2026 04:50:26 -0500 Subject: [PATCH 3/8] fix(work-items): keep a needs-spec item off the autonomous frontier Add needs-spec to the tracker's human floor (lib/labels.sh) so list-frontier --autonomous drops a parked idea issue the way it drops status: needs-decision and status: needs-info items. The frontier test's STATUS fixture gains a needs-spec item, and CONTRACT.md's floor sentence names the label. Co-Authored-By: Claude Opus 5.5 --- .../work-resolve-issue-issue-11869-5d2c9a17.md | 2 +- plugins/work-items/tools/work-item-tracker/CONTRACT.md | 4 ++-- .../tools/work-item-tracker/lib/frontier.test.sh | 9 +++++---- plugins/work-items/tools/work-item-tracker/lib/labels.sh | 6 ++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md index f0821be0395..dadd8fefa96 100644 --- a/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md +++ b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md @@ -4,4 +4,4 @@ bump: minor ### Added -- triage: an idea issue, a feature request that does not say what it is for, now goes to `/planning:interview` and is parked with `needs-spec` before any code search, ledger check, verification or research (#11869). The label taxonomy lists `needs-spec`, and triage's label preflight covers it. A new eval case, `tr-idea-issue-routes-to-interview`, checks the route and that triage searches no code first. +- triage: an idea issue, a feature request that does not say what it is for, now goes to `/planning:interview` and is parked with `needs-spec` before any code search, ledger check, verification or research (#11869). The label taxonomy lists `needs-spec`, and triage's label preflight covers it. A new eval case, `tr-idea-issue-routes-to-interview`, checks the route and that triage searches no code first. The tracker's human floor now holds `needs-spec`, so `list-frontier --autonomous` drops a parked idea issue the way it drops `status: needs-decision` and `status: needs-info` items. diff --git a/plugins/work-items/tools/work-item-tracker/CONTRACT.md b/plugins/work-items/tools/work-item-tracker/CONTRACT.md index a44d0f3ee96..62b5e1bacb1 100644 --- a/plugins/work-items/tools/work-item-tracker/CONTRACT.md +++ b/plugins/work-items/tools/work-item-tracker/CONTRACT.md @@ -320,8 +320,8 @@ assignee AND not a container (a `work-map` item is never its own frontier item, `needs-human` are additionally excluded, as are items carrying a **human-floor work class**: `work-class: structural` (C4) or `work-class: untrusted-provenance` (C5), whose admission disposition is human-gated -regardless of any other signal, and items labeled `status: needs-decision` or -`status: needs-info`, which wait on a person. The floor wins over the autonomous-eligible role label: an +regardless of any other signal, and items labeled `status: needs-decision`, +`status: needs-info` or `needs-spec`, which wait on a person. The floor wins over the autonomous-eligible role label: an item carrying both is contradictory, and resolving it against the floor is what keeps a worker from claiming and then escalating it once per lane pass. `work-class: scoped` (C3) is deliberately not floored here: its disposition turns on bug-fix-vs-feature shape and diff --git a/plugins/work-items/tools/work-item-tracker/lib/frontier.test.sh b/plugins/work-items/tools/work-item-tracker/lib/frontier.test.sh index 10460a1fb1a..faffe54367b 100755 --- a/plugins/work-items/tools/work-item-tracker/lib/frontier.test.sh +++ b/plugins/work-items/tools/work-item-tracker/lib/frontier.test.sh @@ -159,13 +159,14 @@ assert_eq "default frontier still surfaces a provenance: signal item" \ STATUS='{"schema_version":"1.0","items":[ {"id":"github:o/r#1","state":"open","assignees":[],"labels":["agent-ready","work-class: scoped"],"blocked_by_count":0}, {"id":"github:o/r#9","state":"open","assignees":[],"labels":["agent-ready","status: needs-decision"],"blocked_by_count":0}, - {"id":"github:o/r#10","state":"open","assignees":[],"labels":["agent-ready","status: needs-info"],"blocked_by_count":0}]}' + {"id":"github:o/r#10","state":"open","assignees":[],"labels":["agent-ready","status: needs-info"],"blocked_by_count":0}, + {"id":"github:o/r#11","state":"open","assignees":[],"labels":["needs-spec"],"blocked_by_count":0}]}' IDS="$(frontier_ids true <<<"$STATUS")" -assert_eq "autonomous frontier drops status: needs-decision and needs-info even when agent-ready" \ +assert_eq "autonomous frontier drops status: needs-decision and needs-info even when agent-ready, and a parked needs-spec item" \ "github:o/r#1" "$IDS" IDS="$(frontier_ids false <<<"$STATUS")" -assert_eq "default frontier still surfaces status: needs-decision and needs-info items" \ - "github:o/r#1,github:o/r#9,github:o/r#10" "$IDS" +assert_eq "default frontier still surfaces status: needs-decision, needs-info and needs-spec items" \ + "github:o/r#1,github:o/r#9,github:o/r#10,github:o/r#11" "$IDS" # C3 scoped is deliberately NOT in the floor (the admission gate owns it), so it # must survive this filter. diff --git a/plugins/work-items/tools/work-item-tracker/lib/labels.sh b/plugins/work-items/tools/work-item-tracker/lib/labels.sh index f1a0af89e48..4fe675b52f7 100755 --- a/plugins/work-items/tools/work-item-tracker/lib/labels.sh +++ b/plugins/work-items/tools/work-item-tracker/lib/labels.sh @@ -39,10 +39,12 @@ readonly WIT_HUMAN_FLOOR_WORK_CLASS_LABELS=( # shellcheck disable=SC2034 readonly WIT_HUMAN_FLOOR_PROVENANCE_LABELS=("provenance: signal") -# An item waiting on an owner decision or on information is not workable by an -# autonomous lane, whatever else it carries. Fixed strings, no binding key. +# An item waiting on an owner decision, on information or on a spec is not +# workable by an autonomous lane, whatever else it carries. Fixed strings, no +# binding key. needs-spec is a bare Meta label, not a status: value. # shellcheck disable=SC2034 readonly WIT_HUMAN_FLOOR_STATUS_LABELS=( "status: needs-decision" "status: needs-info" + "needs-spec" ) From cc2959ca563212ae759a00ab0a07b0a4d657101a Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sun, 1 Nov 2026 06:15:10 -0500 Subject: [PATCH 4/8] fix(work-items): return a needs-spec item to raw and name a signal idea issue's labels Nothing removed needs-spec once an idea issue's interview was done, and the "already triaged, stop" rule refused a re-run on an item without the raw marker, so a specified item stayed off the autonomous frontier for good. Triage step 1, the side-exit line and the state diagram now return the item to raw once an interview records its intent: one label edit removes needs-spec and restores the raw marker, then triage runs from step 1. A signal-marked idea issue's labels are now named in place of a pointer whose paragraph also applies the human-gated role label: it gets provenance: signal and work-class: untrusted-provenance (provenance: signal alone in a lane barred from recording a class), and never the human-gated role label. Co-Authored-By: Claude Opus 5.5 --- .../work-resolve-issue-issue-11869-5d2c9a17.md | 2 +- plugins/work-items/skills/triage/SKILL.md | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md index dadd8fefa96..c2647c8dad1 100644 --- a/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md +++ b/.changes/work-items/work-resolve-issue-issue-11869-5d2c9a17.md @@ -4,4 +4,4 @@ bump: minor ### Added -- triage: an idea issue, a feature request that does not say what it is for, now goes to `/planning:interview` and is parked with `needs-spec` before any code search, ledger check, verification or research (#11869). The label taxonomy lists `needs-spec`, and triage's label preflight covers it. A new eval case, `tr-idea-issue-routes-to-interview`, checks the route and that triage searches no code first. The tracker's human floor now holds `needs-spec`, so `list-frontier --autonomous` drops a parked idea issue the way it drops `status: needs-decision` and `status: needs-info` items. +- triage: an idea issue, a feature request that does not say what it is for, now goes to `/planning:interview` and is parked with `needs-spec` before any code search, ledger check, verification or research (#11869). Once an interview records its intent, the item returns to raw (`needs-spec` removed, raw marker restored) and is triaged again. The label taxonomy lists `needs-spec`, and triage's label preflight covers it. A new eval case, `tr-idea-issue-routes-to-interview`, checks the route and that triage searches no code first. The tracker's human floor now holds `needs-spec`, so `list-frontier --autonomous` drops a parked idea issue the way it drops `status: needs-decision` and `status: needs-info` items. diff --git a/plugins/work-items/skills/triage/SKILL.md b/plugins/work-items/skills/triage/SKILL.md index 8869dbd8a6d..4d12a9c38aa 100644 --- a/plugins/work-items/skills/triage/SKILL.md +++ b/plugins/work-items/skills/triage/SKILL.md @@ -66,7 +66,7 @@ State names follow the plugin's vocabulary and the canonical roles ([`${CLAUDE_P | **briefed** | brief posted + `status:ready` | Fully specified as a behavioral contract (per [`${CLAUDE_PLUGIN_ROOT}/reference/agent-brief.md`](${CLAUDE_PLUGIN_ROOT}/reference/agent-brief.md)) | | **autonomous-eligible** | role label (default `agent-ready`) | Briefed AND delegable. Eligible for autonomous pickup from the frontier | -Side exits from any state: `status:needs-info` (returns to raw when the reporter replies), `status:needs-decision` (awaiting a human or maintainer judgment call), `needs-spec` (from raw only: an idea issue awaiting its `/planning:interview`, step 1), the human-gated role label (default `needs-human`), or close (wontfix / duplicate / already implemented). +Side exits from any state: `status:needs-info` (returns to raw when the reporter replies), `status:needs-decision` (awaiting a human or maintainer judgment call), `needs-spec` (from raw only: an idea issue awaiting its `/planning:interview`, step 1; returns to raw once the interview records its intent), the human-gated role label (default `needs-human`), or close (wontfix / duplicate / already implemented). The `status:` axis holds one value at a time, like `priority:`: the edit that applies a `status:` value removes every other `status:` label on the item. So `status: ready`, applied in the edit that @@ -102,7 +102,7 @@ raw → verified → briefed | | ├→ decision-defaulted → autonomous-eligible + status:ready + "Decision defaulted: … — veto before merge" | | └→ human-gated (role label, default needs-human) — briefed for a human | └→ status:needs-info → raw (on reporter reply) - ├→ needs-spec: idea issue, awaiting /planning:interview (no exploration first) + ├→ needs-spec: idea issue, awaiting /planning:interview (no exploration first) → raw (once the interview records its intent) ├→ status:needs-decision: awaiting a human or maintainer judgment call └→ close: wontfix | duplicate | already implemented ``` @@ -265,9 +265,12 @@ Instead, route it to `/planning:interview`. Once the direction gate is met, run `/planning:interview` as the next step and says the item waits on its intent. If the comment cannot be posted, apply no label. Then, in one label edit, apply `needs-spec` and remove the raw marker. Apply no `status:`, priority, work-class or role label, except that a signal-marked idea -issue still gets the labels "Signal-marked items" in the same file applies. In an interactive -session, offer to run `/planning:interview` on the item now. A lane does not run it. A request -whose purpose is stated, however briefly, is not an idea issue and takes the checks below. +issue also gets `provenance: signal` and `work-class: untrusted-provenance` (`provenance: signal` +alone in a lane barred from recording a class), and never the human-gated role label. In an +interactive session, offer to run `/planning:interview` on the item now. A lane does not run it. +Once an interview records the item's intent, return it to raw in one label edit that removes +`needs-spec` and restores the raw marker, then triage it from step 1. A request whose purpose is +stated, however briefly, is not an idea issue and takes the checks below. Then run these checks: From 61d993e17b0080cab29ab1c77f1825c7c3f613ac Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sun, 1 Nov 2026 06:24:13 -0500 Subject: [PATCH 5/8] test(work-items): let the idea-issue case's Bash grader pass plugin and item-store reads The no-bash-search grader in tr-idea-issue-routes-to-interview failed a run that grepped the plugin's own references, listed the item store, or filtered the tracker seam's output, while no-grep and no-glob pass the same reads. It now carries the same exemption (.work, /work-items/, and the work-item-tracker.sh seam), and each of those three commands is a pass sample. The six fail samples still fail. Co-Authored-By: Claude Opus 5.5 --- .../case.yaml | 2 +- .../samples/no-bash-search.json | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml index 671379e4c74..bfbc534cb9d 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -41,7 +41,7 @@ graders: - name: no-bash-search type: tool_used tool: Bash - input_match: '(?:"command":"|[ \t;&(]|\\[nt])(?:(?:grep|rg|ag)[ \t]|find[ \t]+[./~]|git[ \t]+(?:grep|log)\b)|(?:[ \t/"]|\\[nt])src(?![\w-])' + input_match: '^(?![\s\S]*(?:\.work|/work-items/|work-item-tracker\.sh))[\s\S]*?(?:(?:"command":"|[ \t;&(]|\\[nt])(?:(?:grep|rg|ag)[ \t]|find[ \t]+[./~]|git[ \t]+(?:grep|log)\b)|(?:[ \t/"]|\\[nt])src(?![\w-]))' min: 0 max: 0 arm: both diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json index 835f5e7f85d..61d7897ef10 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json @@ -25,6 +25,39 @@ { "answer": [], "why": "nothing" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "grep -n needs-spec /cache/plugins/work-items/reference/label-taxonomy.md" + } + } + ], + "why": "searches the plugin's own references" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "find .work-items -name '*.md'" + } + } + ], + "why": "lists the item store" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1' | grep labels" + } + } + ], + "why": "filters the seam's read of the item" } ], "fail": [ From a57cfbb60a88994b3847ac3c0d0584c67d1c9559 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sun, 1 Nov 2026 06:30:03 -0500 Subject: [PATCH 6/8] test(work-items): exempt only the item-store segment of a Bash command in the idea-issue grader The no-bash-search exemption applied to the whole Bash input, so a command that also named the item store, the plugin cache or the seam hid a code search chained after it (cat .work-items/1.md && grep -rn plugin src), and the bare .work prefix exempted .workspace. The grader now checks each command segment split on ;, &, ( and newlines, exempts a segment only when it names .work-items (not as an --exclude-dir= value), /work-items/ or work-item-tracker.sh, and keeps a pipe inside its segment so a filtered seam read still passes. Four fail samples pin the boundary. Co-Authored-By: Claude Opus 5.5 --- .../case.yaml | 2 +- .../samples/no-bash-search.json | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml index bfbc534cb9d..d2c11887107 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -41,7 +41,7 @@ graders: - name: no-bash-search type: tool_used tool: Bash - input_match: '^(?![\s\S]*(?:\.work|/work-items/|work-item-tracker\.sh))[\s\S]*?(?:(?:"command":"|[ \t;&(]|\\[nt])(?:(?:grep|rg|ag)[ \t]|find[ \t]+[./~]|git[ \t]+(?:grep|log)\b)|(?:[ \t/"]|\\[nt])src(?![\w-]))' + input_match: '(?:"command":"|[;&(]|\\n)(?!(?:[^;&\\]|\\[^n])*(?:(? Date: Sun, 1 Nov 2026 06:38:47 -0500 Subject: [PATCH 7/8] test(work-items): keep redirect ampersands inside a no-bash-search segment The segment split treated the & of 2>&1, >&2 and &>/dev/null as a command separator, so a seam read piped through 2>&1 | grep failed the grader. A lone & still splits; an & next to > stays in the segment. Add pass samples for the three redirect forms and fail samples for a redirected source listing and a grep after a redirected seam read, and state the grader's scope above it: heredoc bodies and quoting are not parsed. Co-Authored-By: Claude Opus 5.5 --- .../case.yaml | 7 ++- .../samples/no-bash-search.json | 55 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml index d2c11887107..7a50b86ff49 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -38,10 +38,15 @@ graders: min: 0 max: 0 arm: both + # Scope: the no-bash-search pattern splits a command into segments on ;, &&, ||, a lone & + # (not the & of a redirect such as 2>&1, >&2 or &>/dev/null), ( and an escaped newline. It + # does not parse heredoc bodies or quoting, so a search word inside a heredoc --body line + # after a store read can still count; a search in the same segment as a store path stays + # exempt. Check further layout findings against real recorded answers before changing it. - name: no-bash-search type: tool_used tool: Bash - input_match: '(?:"command":"|[;&(]|\\n)(?!(?:[^;&\\]|\\[^n])*(?:(?)&(?!>)|\\n)(?!(?:[^;&\\]|(?<=>)&|&(?=>)|\\[^n])*(?:(?)&|&(?=>)|\\[^n])*?(?:(?:(?<=[ \t"(;&|])|(?<=\\[nt]))(?:(?:grep|rg|ag)[ \t]|find[ \t]+[./~]|git[ \t]+(?:grep|log)\b)|(?:(?<=[ \t/"])|(?<=\\[nt]))src(?![\w-]))' min: 0 max: 0 arm: both diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json index 4b74f9cf84e..f504cd9285e 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/samples/no-bash-search.json @@ -58,6 +58,39 @@ } ], "why": "filters the seam's read of the item" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1' 2>&1 | grep labels" + } + } + ], + "why": "filters the seam read with stderr merged" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1' >&2" + } + } + ], + "why": "sends the seam read to stderr" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1' &>/dev/null" + } + } + ], + "why": "discards the seam read output" } ], "fail": [ @@ -170,6 +203,28 @@ } ], "why": "a .work prefix that is not the item store" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "ls src 2>&1" + } + } + ], + "why": "lists the source with stderr merged" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "/p/tools/work-item-tracker/work-item-tracker.sh get-item 'local-markdown:local/markdown#1' 2>&1; grep -rn plugin src" + } + } + ], + "why": "greps the source after a redirected seam read" } ] } From a7a8476d9008996eec4d90cbec6e773d2e4dc0c4 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Sun, 1 Nov 2026 06:45:50 -0500 Subject: [PATCH 8/8] test(work-items): state that | and || stay inside a no-bash-search segment The scope comment listed || as a separator; the pattern has no || starter and its segment class admits |, so a search after `store-read ||` is exempt. Correct the comment; the pattern is unchanged. Co-Authored-By: Claude Opus 5.5 --- .../evals/tr-idea-issue-routes-to-interview/case.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml index 7a50b86ff49..b6450aeefdc 100644 --- a/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml +++ b/plugins/work-items/evals/tr-idea-issue-routes-to-interview/case.yaml @@ -38,11 +38,12 @@ graders: min: 0 max: 0 arm: both - # Scope: the no-bash-search pattern splits a command into segments on ;, &&, ||, a lone & - # (not the & of a redirect such as 2>&1, >&2 or &>/dev/null), ( and an escaped newline. It - # does not parse heredoc bodies or quoting, so a search word inside a heredoc --body line - # after a store read can still count; a search in the same segment as a store path stays - # exempt. Check further layout findings against real recorded answers before changing it. + # Scope: the no-bash-search pattern splits a command into segments on ;, &&, a lone & (not + # the & of a redirect such as 2>&1, >&2 or &>/dev/null), ( and an escaped newline. | and || + # stay inside a segment, so a search after `store-read || ` is exempt. It does not parse + # heredoc bodies or quoting, so a search word inside a heredoc --body line after a store + # read can still count; a search in the same segment as a store path stays exempt. Check + # further layout findings against real recorded answers before changing it. - name: no-bash-search type: tool_used tool: Bash