diff --git a/plugins/work-items/.claude-plugin/plugin.json b/plugins/work-items/.claude-plugin/plugin.json index 98fbf2abc..02efba761 100644 --- a/plugins/work-items/.claude-plugin/plugin.json +++ b/plugins/work-items/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "work-items", - "version": "0.17.2", + "version": "0.18.0", "description": "Manages development work items through a provider-neutral tracker seam that ships with the plugin (bundled dispatcher plus github and local-markdown adapters; seam plugin-dir canonical, adapters consumer-local-first): dashboard, taxonomy-labeled creation, a race-safe assignee-plus-lease claim protocol, recurring-schedule checks, TODO scanning, stale-lease auditing, plan decomposition into vertical-slice items, and raw-intake triage (issues and unsolicited PRs through raw, verified, briefed, autonomous-eligible states). The re-runnable setup skill binds the provider (.work-item-tracker.json), seeds the recurring-schedule seam (.github/recurring-schedule.json), and remaps canonical role labels.", "author": { "name": "Melodic Software", diff --git a/plugins/work-items/CHANGELOG.md b/plugins/work-items/CHANGELOG.md index f237cea64..ce9d1dbe9 100644 --- a/plugins/work-items/CHANGELOG.md +++ b/plugins/work-items/CHANGELOG.md @@ -3,6 +3,31 @@ All notable changes to the `work-items` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.18.0] + +Extract the cross-lane self-observation filing rule ("file what you will not fix: dedupe → categorize +→ fixed shape → `needs-triage`") into one shared reference surface so the lanes reference it instead +of each absorbing a private, drift-prone copy (`#540`). + +### Added + +- **Shared self-observation filing contract — `reference/dogfood-filing.md`.** The rule that an + autonomous lane files a problem it will not fix in-cycle is cross-lane-identical, so the absorption + umbrellas (`#477`/`#478`/`#479`) must not each absorb a private copy. The new reference is the single + in-repo source of truth: it composes the existing mechanics by pointer — the *Search items* dedupe + read and body template `track add` owns, the `create-item` seam write, and the `needs-triage` status + label — and adds only the self-observation policy (when to file vs the `tracker-seam.md` "Default = + fix, not file" posture, the mechanical-vs-model split, autonomous authorization, and the AI + disclaimer). No new script: the mechanical core is already the seam + adapter + `track add` + machinery, so the doc references it rather than forking the template and search mechanics. + +### Changed + +- **`work`, `triage`, and `scan-todos` now reference the shared filing contract at their filing + sites** — the `work` post-green deferred-finding follow-up, the `triage` follow-up-work creation, + and the `scan-todos` "file a work item" branch each point at `reference/dogfood-filing.md` for the + dedupe → categorize → fixed shape → `needs-triage` sequence instead of leaving it implicit. + ## [0.17.2] ### Changed diff --git a/plugins/work-items/reference/dogfood-filing.md b/plugins/work-items/reference/dogfood-filing.md new file mode 100644 index 000000000..118ad5855 --- /dev/null +++ b/plugins/work-items/reference/dogfood-filing.md @@ -0,0 +1,88 @@ +# Self-observation filing — the shared dogfood contract + +When an autonomous lane hits a problem it will **not** fix in the current cycle — a bug, a gap, a +piece of orthogonal drift — it files that problem as a tracker item so the lane's own findings feed +the same queue everyone else works from. That filing rule is **cross-lane-identical**: `work`, +`triage`, `scan-todos`, and the external standing-loop lanes (`source-control:babysit-prs`, +control-tower, enrichment) all file the same way. This document is the single source of truth for +*how*, so each lane references it once instead of restating the rule and letting the copies drift. + +The mechanics it composes already live in this plugin — the seam `create-item` verb, the bound +adapter's *Search items* operation, the `track add` body template, and the label taxonomy. This +contract does not re-implement any of them; it **points** at each and adds only the self-observation +policy that binds them into one sequence. + +## When it applies — file what you will not fix, nothing else + +The default posture is **fix, not file** ([`tracker-seam.md`](tracker-seam.md) "Default = fix, not +file"): Boy-Scout-scope drift discovered while working belongs in the current change, not the +tracker. Self-observation filing is the **narrow exception** — a problem is filed only when it is +genuinely orthogonal to the current item, large enough to need its own planning pass, or needs +research this lane is not positioned to do. `work`'s post-green review already draws this line for a +VALID-but-deferred finding ([`../skills/work/SKILL.md`](../skills/work/SKILL.md) "Post-green review +pass"); the same test governs every lane. + +## The sequence + +Four beats, in order. The two **mechanical** beats reuse existing machinery verbatim; the two +**judgment** beats stay with the model. + +1. **Dedupe first (mechanical search + model sameness).** Before creating anything, run the + search-before-create pre-flight — the bound adapter's *Search items* operation over `--state all`, + the same read `track add` performs ([`../skills/track/actions/add.md`](../skills/track/actions/add.md) + "Duplicate check"). Whether a hit is *the same problem* is a model judgment, not a string match: + compare by underlying cause, not wording. A match against an **open** item means comment on it + instead of opening a second one. A match against a **closed** item is different: closed items are + absent from the triage attention view, so commenting there buries a still-live or regressed + observation where no lane will pick it up — reopen the closed item (or open a fresh active item + that links it) so the problem re-enters the queue. Where the consuming repo keeps a rejected-concept + ledger + (`docs/out-of-scope/`), the same step's ledger check applies — do not re-file a settled rejection. + +2. **Categorize (model judgment).** Classify bug vs enhancement first — it steers everything + downstream — then the type and priority, following triage's classification rule + ([`../skills/triage/SKILL.md`](../skills/triage/SKILL.md) "Recommend category + state") and the + label grammar ([`label-taxonomy.md`](label-taxonomy.md)). A self-filed item is raw intake: the + filer records what it observed, not a verified diagnosis. + +3. **File with the fixed shape (mechanical).** File through the canonical `track add` path, which + owns the body template (Context / Proposed work / Acceptance criteria / References / Metadata) and + the argv-safe `create-item` write ([`../skills/track/actions/add.md`](../skills/track/actions/add.md) + "Build body", "Create the item"). The shape is not restated here — `track add` is its source of + truth, so a change to the template lands in one place. + +4. **Label `needs-triage`, then hand off (mechanical label + policy).** Apply `status:needs-triage` + ([`label-taxonomy.md`](label-taxonomy.md) status axis) so the item lands in the triage attention + view for evaluation ([`../skills/triage/SKILL.md`](../skills/triage/SKILL.md) "Attention view"). + **The filer does not self-triage** — filing surfaces the problem into raw intake; triage verifies, + categorizes definitively, and routes it. Auto-application of `needs-triage` to a fresh item lacking + a priority label is tracked separately (`#506`); until it lands, the filing lane applies the label. + +## Mechanical core is already scripted — reference it, do not duplicate + +The "mechanical core" of this contract (dedupe search, filing-template emission, the `create-item` +write) is not a new script to author — it is the existing seam + bound-adapter + `track add` +machinery above. Duplicating it into a standalone wrapper would fork the very template and search +mechanics this document exists to keep single-sourced. The judgment core (sameness, category) is +model work by nature and is not scriptable. A lane composes the two by following the sequence, not by +calling a new binary. + +## Autonomous authorization and the AI disclaimer + +Model-initiated filing is gated: on the interactive path a lane drafts the item and asks before +creating it ([`../skills/track/actions/add.md`](../skills/track/actions/add.md) "Authorization +gate"). On an **autonomous lane** — a `/loop` or `/schedule` session whose standing rules already +authorize tracker mutations — those standing rules **are** the authorization, the same resolution +triage's direction gate makes ([`../skills/triage/SKILL.md`](../skills/triage/SKILL.md) "Direction +gate"). An autonomous lane prefixes every item and comment it creates with an AI disclaimer — +triage's ([`../skills/triage/SKILL.md`](../skills/triage/SKILL.md) "AI disclaimer") section is the +canonical form, but a non-triage lane names its own lane rather than inheriting triage's literal +`during triage` wording, so the provenance phase stays accurate. A lane-neutral disclaimer SSOT that +collapses this to a single pointer is tracked in `#768`. + +## Reconciliation note + +The authoritative wording of this rule currently also lives in the external v4 standing-loop prompts, +where `source-control:babysit-prs` has not yet absorbed its lane rules (`#477`). This document is the +in-repo surface those prompts and the lane-absorption skills reference; when the remaining absorption +lands, its self-observation wording reconciles against this contract rather than adding a fourth copy. diff --git a/plugins/work-items/skills/scan-todos/SKILL.md b/plugins/work-items/skills/scan-todos/SKILL.md index 87bf193f7..2bf9fa132 100644 --- a/plugins/work-items/skills/scan-todos/SKILL.md +++ b/plugins/work-items/skills/scan-todos/SKILL.md @@ -59,7 +59,7 @@ git -C "${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel)}" grep -nE '\b(TO 1. **For each marker**, read context (10 lines before/after), then classify: - **Resolve now** — small fix; do the work, remove the marker -- **File a work item + remove marker** — significant work; create via `/work-items:track add`, remove the inline marker (do not leave `TODO` as a stand-in for the item) +- **File a work item + remove marker** — significant work; create via `/work-items:track add` following the shared self-observation contract ([`${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md`](${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md): dedupe → categorize → fixed shape → `needs-triage`), remove the inline marker (do not leave `TODO` as a stand-in for the item) - **Remove (already done)** — work completed; delete the comment - **False positive** — structured grammar or external upstream citation misclassified; fix the exclusion if systemic, otherwise note it in the PR diff --git a/plugins/work-items/skills/triage/SKILL.md b/plugins/work-items/skills/triage/SKILL.md index 77a5a517a..7030b8aa2 100644 --- a/plugins/work-items/skills/triage/SKILL.md +++ b/plugins/work-items/skills/triage/SKILL.md @@ -137,7 +137,7 @@ For a PR, the outcome addresses the attached code explicitly: adopt the diff (br The canonical-role labels applied by these outcomes (autonomous-eligible default `agent-ready`, human-gated default `needs-human`) are **resolved from the binding's `config.role_labels` at action entry**, never hardcoded — warn loudly when a role defaults because `.work-item-tracker.json` or the entry is absent rather than applying the default string silently (a repo that remapped roles would otherwise be mislabeled), and stop on a malformed/empty/non-string value ([`${CLAUDE_PLUGIN_ROOT}/reference/label-taxonomy.md`](${CLAUDE_PLUGIN_ROOT}/reference/label-taxonomy.md) "Canonical roles"). -Label edits, comments, and closes route through the adapter's write mechanics (adapter: "Edit labels / assignees", "Comment on item / edit a comment", "Close item"); the gather + attention-view reads are bare. Item creation, when triage spawns follow-up work, goes through the seam `create-item` verb (`/work-items:track add` is the canonical path). +Label edits, comments, and closes route through the adapter's write mechanics (adapter: "Edit labels / assignees", "Comment on item / edit a comment", "Close item"); the gather + attention-view reads are bare. When triage spawns follow-up work — a fresh, orthogonal problem it surfaces but will not fix this pass, distinct from the item under evaluation and from work it has already scoped and routed — item creation goes through the seam `create-item` verb (`/work-items:track add` is the canonical path) and follows the shared self-observation contract ([`${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md`](${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md): dedupe → categorize → fixed shape → `needs-triage`). That new item is genuinely raw intake, so `needs-triage` is correct for it; the item triage is *evaluating* is never sent back to raw intake — its raw marker is cleared by the closing invariant below — and follow-up whose scope triage has already decided is routed through the outcome labels above, not filed as a self-observation. **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: diff --git a/plugins/work-items/skills/work/SKILL.md b/plugins/work-items/skills/work/SKILL.md index 6c434d20c..e8e465716 100644 --- a/plugins/work-items/skills/work/SKILL.md +++ b/plugins/work-items/skills/work/SKILL.md @@ -186,7 +186,7 @@ On user confirmation ("yes"): 1. **High-blast-radius diff gate (pre-PR).** Before a PR is opened, the orchestrator does a **full-diff read** when the diff touches skill frontmatter descriptions or trigger keywords, cross-plugin contracts, or hooks. This complements the worker scope-fence: the scope-fence bounds what a worker *may* touch, this gate is the orchestrator's own read of what the worker *did* touch before the change leaves the lane. -1. **Post-green review pass, then hand off.** After CI is green, run one review pass. The fetch-once → validate → classify → threaded-reply → react → resolve-bot-thread loop is owned by `/source-control:pull-request`; this skill adds only the sequencing and the work-item linkage: fix branch-owned findings via the **owning subagent** (the orchestrator still never edits source), and a **VALID-but-deferred finding requires a filed follow-up issue** — file it via `/work-items:track add`, then cite that issue **both** in the classification reply **and** in the PR's `## Related` section; a deferred finding cannot be resolved without it. Then hand the PR off to `/source-control:babysit-prs` (fleet loop, owned there). +1. **Post-green review pass, then hand off.** After CI is green, run one review pass. The fetch-once → validate → classify → threaded-reply → react → resolve-bot-thread loop is owned by `/source-control:pull-request`; this skill adds only the sequencing and the work-item linkage: fix branch-owned findings via the **owning subagent** (the orchestrator still never edits source), and a **VALID-but-deferred finding requires a filed follow-up issue** — file it via `/work-items:track add` following the shared self-observation contract ([`${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md`](${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md): dedupe → categorize → fixed shape → `needs-triage`), then cite that issue **both** in the classification reply **and** in the PR's `## Related` section; a deferred finding cannot be resolved without it. Then hand the PR off to `/source-control:babysit-prs` (fleet loop, owned there). 1. **Never-merge boundary.** This skill's lane ends at PR creation and the handoff above (review pass, then babysit). **Merging is the babysit lane or a human, never `work`** — consistent with `/source-control:babysit-prs`'s safe default never merging (its opt-in `worker`/`autopilot` tiers merge only behind a deterministic readiness gate) and `/source-control:pull-request` merges being human-gated.