Skip to content

fix(work-items): guard work Step 5 against loop-prompts that dispatch without claiming (#581) - #689

Merged
kyle-sexton merged 2 commits into
mainfrom
fix/581-work-items-loop-prompt-claim-gap
Jul 20, 2026
Merged

fix(work-items): guard work Step 5 against loop-prompts that dispatch without claiming (#581)#689
kyle-sexton merged 2 commits into
mainfrom
fix/581-work-items-loop-prompt-claim-gap

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

/work-items:work Step 5 already sequences the seam claim (assignee + lease) first, before branch/dispatch — the skill's own contract is correct. The gap #581 documents sits one layer above: a hand-authored loop-prompt standing-rule that restates "dispatch every picked issue to a subagent in its own out-of-tree worktree" reads as a complete execution contract and never mentions claiming, so an orchestrator following that loop-prompt literally did the worktree isolation and skipped the seam's race-safe claim entirely — observed twice on live loop-lane sessions (issues actively worked and shipped as PRs while sitting unassigned with no lease). Documentation/guidance gap, not a skill-code defect (per triage). Patch bump 0.14.30.14.4.

Fix

  • Prominent guard note at the head of Step 5 (plugins/work-items/skills/work/SKILL.md) stating the claim-before-dispatch invariant this skill enforces regardless of loop-prompt wording: an external loop-prompt/standing-rule restating "dispatch ... in its own out-of-tree worktree" describes only the execute sub-step, is not a complete execution contract, and is not a substitute for the claim; worktree isolation is not the race-safe collision signal between concurrent lanes, the seam claim is; dispatching a subagent before the claim is held is a defect even when the loop-prompt never named the claim step. Self-contained and placed before the numbered sub-steps (no renumbering).
  • Eval 1 gains a matching expectation (plugins/work-items/skills/work/evals/evals.json) so the fixture asserts the claim-is-a-non-optional-prerequisite invariant, not just claim-before-execute ordering.
  • Version bump + CHANGELOG.md entry.

Scope (per the issue and triage note). #480 — the future loop-prompt-authoring skill that would host this guidance for hand-authored lane prompts — does not exist yet; not blocked on or created here. A repo-wide search for other in-repo surfaces that restate "dispatch ... out-of-tree worktree" as a full contract found none to patch: the babysit-prs loop is a different lane (PR checkout, not dispatch-to-worktree), and the autonomy runner design pack already models a mandatory leased (race-safe claim) state before executing/dispatch — it reinforces the invariant rather than dropping it. No new enforcement mechanism was invented: there is no execution seam between "agent reads SKILL.md" and "agent dispatches" that could gate on the claim, so the guidance layer is the only place the invariant can live — a code-level guard was considered and correctly declined.

Verification

  • bash scripts/validate-plugins.sh — green (all plugin manifests + catalog).
  • node scripts/validate-plugin-contracts.mjs — green (33 setup skills, 1807 plugin files checked).
  • bash scripts/check-changed-skills.sh origin/mainCHECK-SKILL work: PASS — 0 errors, 1 warning. The lone WARN ("no Gotchas surface") is pre-existing and not introduced here.
  • markdownlint-cli2 plugins/work-items/skills/work/SKILL.md — 0 errors.
  • evals.json and plugin.json parse as valid JSON; all four changed files carry no trailing whitespace or tabs.

Closes #581

Related

🤖 Generated with a Claude Code implementation subagent (issue #581)


Co-authored-by: Claude Opus 4.8 noreply@anthropic.com

… without claiming (#581)

## Summary

`/work-items:work` Step 5 already sequences the seam `claim` (assignee + lease) first, before branch/dispatch — the skill's own contract is correct. The gap #581 documents sits one layer above: a hand-authored loop-prompt standing-rule that restates "dispatch every picked issue to a subagent in its own out-of-tree worktree" reads as a complete execution contract and never mentions claiming, so an orchestrator following that loop-prompt literally did the worktree isolation and skipped the seam's race-safe claim entirely — observed twice on live loop-lane sessions (issues actively worked and shipped as PRs while sitting unassigned with no lease). Documentation/guidance gap, not a skill-code defect (per triage). Patch bump `0.14.3` → `0.14.4`.

## Fix

- **Prominent guard note at the head of Step 5** (`plugins/work-items/skills/work/SKILL.md`) stating the claim-before-dispatch invariant this skill enforces regardless of loop-prompt wording: an external loop-prompt/standing-rule restating "dispatch ... in its own out-of-tree worktree" describes only the execute sub-step, is not a complete execution contract, and is not a substitute for the claim; worktree isolation is not the race-safe collision signal between concurrent lanes, the seam claim is; dispatching a subagent before the claim is held is a defect even when the loop-prompt never named the claim step. Self-contained and placed before the numbered sub-steps (no renumbering).
- **Eval 1 gains a matching expectation** (`plugins/work-items/skills/work/evals/evals.json`) so the fixture asserts the claim-is-a-non-optional-prerequisite invariant, not just claim-before-execute ordering.
- Version bump + `CHANGELOG.md` entry.

**Scope (per the issue and triage note).** #480 — the future loop-prompt-authoring skill that would host this guidance for hand-authored lane prompts — does not exist yet; not blocked on or created here. A repo-wide search for other in-repo surfaces that restate "dispatch ... out-of-tree worktree" as a full contract found none to patch: the `babysit-prs` loop is a different lane (PR checkout, not dispatch-to-worktree), and the `autonomy` runner design pack already models a mandatory `leased` (race-safe claim) state before `executing`/dispatch — it reinforces the invariant rather than dropping it. No new enforcement mechanism was invented: there is no execution seam between "agent reads SKILL.md" and "agent dispatches" that could gate on the claim, so the guidance layer is the only place the invariant can live — a code-level guard was considered and correctly declined.

## Verification

- `bash scripts/validate-plugins.sh` — green (all plugin manifests + catalog).
- `node scripts/validate-plugin-contracts.mjs` — green (33 setup skills, 1807 plugin files checked).
- `bash scripts/check-changed-skills.sh origin/main` — `CHECK-SKILL work: PASS — 0 errors, 1 warning`. The lone WARN ("no Gotchas surface") is pre-existing and not introduced here.
- `markdownlint-cli2 plugins/work-items/skills/work/SKILL.md` — 0 errors.
- `evals.json` and `plugin.json` parse as valid JSON; all four changed files carry no trailing whitespace or tabs.

Closes #581

## Related

- #480 — loop-prompt-authoring guidance (the natural home for calling this out to hand-authored lane prompts). Does not exist yet; related-but-out-of-scope, not created here.
- #513 — role-separated mini-SDLC composition; adjacent (role separation during execution) but not this gap (the claim step being skippable at pick time).

🤖 Generated with a Claude Code implementation subagent (issue #581)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@kyle-sexton kyle-sexton added the automated Opened by automation. label Jul 20, 2026
@claude

claude Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 17s —— View job


PR Review

  • Gather context and understand the PR
  • Review changed files
  • Assess correctness, security, and guideline alignment
  • Post findings

Summary

This is a documentation/guidance patch to plugins/work-items/skills/work/SKILL.md that guards Step 5 against loop-prompts that restate the dispatch step without the claim. No behavioral code changed. Four files touched: SKILL.md, evals.json, CHANGELOG.md, plugin.json. The approach is sound and the changes are internally consistent.


SKILL.md — guard note

The blockquote added at SKILL.md#L130 is well-positioned immediately under the ### Step 5: Claim and execute heading, before any numbered sub-steps. The language is unambiguous on all three points that matter:

  • Claim is non-optional (not just "first in the list")
  • A loop-prompt describing dispatch is not a complete contract
  • Dispatching before the claim is held is named a defect, not a warning

No issues. The note is dense but intentionally so — any LLM reader picking up only the Step 5 header will hit this before the sub-steps.

evals/evals.json — new expectation

The added expectation in eval 1 correctly codifies the invariant as a discrete, testable behavior:

"Treats the seam claim (assignee + lease) as a non-optional prerequisite of dispatch — an external loop-prompt restating \"dispatch in an out-of-tree worktree\" does not substitute for it, and dispatching a subagent before the claim is held is a defect even when the loop-prompt never named the claim step"

One observation (non-blocking): the expected_output field for eval 1 wasn't updated to reference the new invariant. The expectations array is the right place for discrete behaviors, so the split is fine — just noting it in case the team's eval runner scores against expected_output narratively.

CHANGELOG.md

Entry is clear, accurate, and correctly scoped as a documentation-only change. The "observed twice on live loop-lane sessions" incident note is useful signal for readers auditing fix motivation.

plugin.json — version bump

0.14.30.14.4 is a patch bump. Appropriate: guidance-only change, no behavioral or schema modification. Consistent with the CHANGELOG entry and versioning policy.


Correctness

The invariant being documented already existed in the sub-step ordering; this patch promotes it from an implied consequence to an explicit, named contract. The PR description correctly explains why a code-level guard isn't possible (no execution seam between "agent reads SKILL.md" and "agent dispatches") — the guidance layer is the only available surface, and this is the right use of it.

Security

No egress, secrets, or code execution introduced. No PII. Plugin-form safe — all references use ${CLAUDE_PLUGIN_ROOT} or ${CLAUDE_PROJECT_DIR}.

Guideline alignment

  • Repo-agnostic: ✓
  • Configurable without editing: ✓ (no new userConfig keys needed)
  • Versioned with explicit semver: ✓
  • No hardcoded paths: ✓

Verdict: LGTM. The patch is minimal, correctly targeted, and the only meaningful alternative (a code-level enforcement gate) was correctly ruled out. No blockers.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Post-green review pass — 1 round, LGTM, no blockers.

One non-blocking observation (eval expected_output narrative field not updated alongside the new expectations entry) — accepted as-is per the reviewer's own note that the expectations array is the correct place for discrete behaviors.

No inline review threads. Flipping to ready.

@kyle-sexton
kyle-sexton marked this pull request as ready for review July 20, 2026 13:46
…site invariant (#581)

Addresses the review-bot's non-blocking observation: eval 1's narrative
expected_output now names the seam-claim-is-a-non-optional-prerequisite
invariant already added to the expectations array, keeping the fixture's
two descriptions coherent.

🤖 Generated with a Claude Code implementation subagent (issue #581)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 31s —— View job


PR Review

  • Gather context and understand the PR
  • Read changed files and diff
  • Assess correctness, security, and guideline alignment
  • Post findings

Summary

Minimal documentation/guidance patch across four files. The behavioral claim-before-dispatch sequence already existed in SKILL.md's sub-step ordering; this PR promotes it from an implied consequence to an explicit, named invariant with a companion eval expectation. No code or seam behavior changes.


SKILL.md — guard note

Line 130 — the blockquote is well-positioned (directly under the ### Step 5: Claim and execute heading, before numbered sub-steps) and covers the three critical points:

  • The seam claim is non-optional, not just "first in the list"
  • A loop-prompt restating dispatch is not a complete contract
  • Dispatching before the claim is held is named a defect, not a guidance hint

The density is intentional and correct — a model picking up only the Step 5 header hits this before any sub-step instructions.

evals/evals.json — new expectation

Line 16 — placed correctly in the expectations array between the lost-race and dispatch-mechanics expectations, making it a discrete testable behavior rather than part of the narrative expected_output. The wording closely mirrors the guard note, which is the right choice for traceability.

One observation (non-blocking): the expected_output field (line 8) says "then acquires it through the seam claim verb…before executing it" but doesn't name the loop-prompt substitution failure. This asymmetry is fine — expectations is the right granularity for discrete behaviors — but worth noting if the eval runner narratively scores against expected_output alone.

CHANGELOG.md

Lines 6–20 — accurate, consistent with the repo's dateless-version-header pattern, and correctly scoped as documentation-only. The "observed twice on live loop-lane sessions" incident note gives future readers the fix motivation.

One minor stylistic note (non-blocking): Keep-a-Changelog's ### Fixed category conventionally applies to code bug fixes. This is a guidance-only change, so ### Changed or ### Added (for a new guard) would be technically more precise. That said, the change does fix an observed production defect pattern, and using ### Fixed for it is defensible. Pre-existing 0.14.3 entries use ### Fixed for code-level bugs — the two are now slightly category-mixed. Not a blocker given the repo doesn't appear to enforce strict category semantics.

plugin.json

0.14.30.14.4 patch bump is appropriate for a documentation-only change with no behavioral or schema modification.


Correctness

The invariant documented here was already enforced by sub-step ordering; the patch surfaces it as an explicit contract and makes it resilient to external loop-prompt restatements that drop the claim. The PR description correctly rules out a code-level guard (no seam exists between "agent reads SKILL.md" and "agent dispatches") — the guidance layer is the only available surface. That reasoning is sound.

Security

No egress, no secrets, no code execution. Plugin-form safe — no ../ reach-outs, no hardcoded paths.

Guideline alignment

  • Repo-agnostic: ✓
  • No hardcoded paths: ✓
  • Explicit semver bump: ✓ (0.14.4)
  • No new userConfig keys needed: ✓

Verdict: LGTM. Two non-blocking observations noted above (eval expected_output narrative gap, and ### Fixed vs ### Changed categorization) — both acceptable as-is per the reviewer note from the prior pass. No blockers.

@claude

claude Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 27s —— View job


PR Review

  • Gather context and understand the PR
  • Review changed files (SKILL.md, evals.json, CHANGELOG.md, plugin.json)
  • Assess correctness, security, and guideline alignment
  • Post findings

This is a two-commit documentation/guidance patch. The commits are cleanly separated: the first (fix) adds the SKILL.md guard, updates eval expectations, and bumps the version; the second (docs) syncs the expected_output narrative field — a direct follow-up to the previous review's non-blocking observation. All four changed files are internally consistent with each other and the PR description.

SKILL.md — guard note

SKILL.md L130: The blockquote is correctly placed — immediately under the ### Step 5: Claim and execute heading, before any numbered sub-steps, so an orchestrator reading only the step header hits the invariant before the dispatch sub-step. The three-point contract is unambiguous:

  • Claim is non-optional, not merely "first in the sub-step list"
  • An external loop-prompt describing dispatch is not a complete execution contract
  • Dispatching before the claim is held is named a defect, not a caution

No issues.

evals/evals.json — expected_output + new expectation

evals.json L8: The expected_output field now carries a shortened paraphrase of the invariant ("The seam claim is a non-optional prerequisite of dispatch: an external loop-prompt restating dispatch-in-a-worktree never substitutes for it, and dispatching before the claim is held is a defect") — appropriate prose discipline for a narrative summary.

evals.json L16: The new discrete expectation ("Treats the seam claim (assignee + lease) as a non-optional prerequisite of dispatch...") is correctly positioned after the claim exit 7 behavior and before the dispatch-path expectations, matching the ordering of sub-steps in SKILL.md. The two levels of specificity (expected_output narrative + expectations discrete item) are complementary and don't contradict each other.

CHANGELOG.md

CHANGELOG.md L6: Entry is accurate, scoped as documentation-only, and consistent with all prior entries (no-date ## [VERSION] format is the established convention here). The incident note ("observed twice on live loop-lane sessions") is useful signal for readers auditing fix motivation.

plugin.json — version bump

0.14.30.14.4: correct patch-level bump for a guidance-only change with no behavioral or schema modification.


Correctness

The invariant being documented already existed in the sub-step ordering; this patch promotes it from an implied ordering consequence to an explicit, named contract. The PR correctly notes that a code-level enforcement gate isn't available (no execution seam between "agent reads SKILL.md" and "agent dispatches"), so the guidance layer is the only enforceable surface — this is the right use of it.

Security

No code execution, egress, secrets, or PII introduced. Plugin-form safe — all references use standard plugin path variables.

Guideline alignment

Rule Status
Repo-agnostic
Configurable without editing ✓ (no new userConfig keys)
Versioned with explicit semver
No hardcoded paths
Conventional Commits on both commits

Verdict: LGTM. No blockers. No changes needed.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Follow-up commit (d7ee1b4) addressed the eval expected_output observation from round 1. Round 3 (post-push) re-confirms LGTM, no blockers, no unresolved threads. No further action needed — PR remains ready for babysit-lane merge.

@kyle-sexton
kyle-sexton merged commit 4b5fffd into main Jul 20, 2026
17 checks passed
@kyle-sexton
kyle-sexton deleted the fix/581-work-items-loop-prompt-claim-gap branch July 20, 2026 13:56
kyle-sexton added a commit that referenced this pull request Jul 20, 2026
…t warning (#449) (#690)

## Summary

The `work-items` triage/work lanes assumed a resolvable, bound project
context. When
`.work-item-tracker.json` was absent, canonical role labels fell to
their defaults **silently** (a
correctness risk for any repo that remapped `config.role_labels`) and
seam coordination verbs
surfaced a raw mid-flow `exit 3` instead of an actionable message. This
ships the operator-ratified
fix — binding presence as a third loud entry invariant, and the silent
role-default fallback as a
loud warning — reconciled so it does not regress this repo's own
deliberately gh-native
`/work-items:work` lane.

## Fix

- **Binding presence is a third loud entry invariant**
(`reference/tracker-seam.md` "Shared tracker
context"). It sits alongside the existing `jq` and seam-script checks
but is discharged
**distinctly**: the first two have no recovery path and stop; a missing
binding is loud and
routable, **never a silent default and never a raw `exit 3`**, without
halting unconditionally —
- **Seam coordination verbs** (`claim`, `reclaim`, `renew-lease`,
`create-item`, `list-frontier`,
`capabilities`) cannot run unbound (the seam hard-errors `exit 3`,
`CONTRACT.md` "Exit codes"), so
before the first one the skill surfaces a message distinguishing **(1)
setup was never run** →
run `/work-items:setup`, from **(2) a deliberate gh-native operating
mode** → proceed for
provider-mechanic operations only, accepting **no race-safe
claim/lease**.
- **Provider-mechanic operations** (list/search/close,
label/assignee/comment edits) run as raw
`gh` per the bound adapter reference, never read the binding, and
proceed unbound.
- **Caveat recorded:** the gh-native path presumes a `gh`-backed
provider — a `local-markdown`
target with no binding has no `config.storage_dir` and stays a hard
stop.
- **Silent role-label default becomes a loud warning.** When a canonical
role resolves to its default
because the binding or its `config.role_labels` entry is absent, the
skills now warn loudly instead
of substituting silently. Applied at every action-entry resolution site
that inlines it
(`skills/work/SKILL.md`, `skills/track/actions/{due,recheck,audit}.md`,
`skills/decompose/SKILL.md`)
and in the shared invariants (`reference/tracker-seam.md`,
`reference/label-taxonomy.md`). A
present-but-malformed / empty / non-string value remains a hard stop,
unchanged.
- **`skills/work/evals/evals.json`** gains eval 4 asserting the
unbound-binding behavior (loud,
routable, distinguishes setup-vs-gh-native, warns on role default, never
forges a binding).
- Version bump `0.14.4` → `0.15.0` + `CHANGELOG.md` entry.

### The reconciliation (why not an unconditional hard stop)

The ratified decision reads "stop-and-remediate," but the last comment
on #449 (filed *after*
ratification) reports that this very repo runs `/work-items:work`
**deliberately gh-native** (plain
`gh issue`/`gh pr`, `status: ready`, no seam claim/reclaim) because it
has no binding and never ran
`/work-items:setup` — so a literal hard stop would regress a working,
intentional mode every cycle
(session-start reclaim already hits `exit 3` here today). The
coordination-vs-provider-mechanic split
is not invented for this fix — it is the pre-existing "Operation
routing" seam (coordination verbs go
through the dispatcher and need the binding; provider mechanics run as
raw `gh` and never read it).
Scoping the halt to coordination verbs, and making everything else
warn-and-proceed, satisfies the
ratified loud-surfacing intent (#455) **and** keeps the gh-native
read/comment/label path working.

### Parked (see PR comment)

One residual judgment call is **parked with `status: needs-decision`**,
not silently resolved:
whether to formally document a first-class gh-native **no-lease claim**
path for
coordination-*dependent* lanes (`/work-items:work`) so they too run
unbound, versus keeping them
stopped at the coordination check. RECOMMENDED: defer with the same
trigger as the full remote mode.
It does not block this PR — the loud invariant and role-default warning
ship regardless.

## Verification

- `bash scripts/check-changed-skills.sh origin/main` — `work`, `track`,
`decompose` all
**PASS — 0 errors** (the lone per-skill "no Gotchas surface" WARN is
pre-existing).
- `bash scripts/check-skill-portability.sh origin/main` — **No unexcused
coupling tokens** in 5 skill
  files.
- `check-jsonschema` against
`plugins/skill-quality/reference/evals.schema.json` — `work/evals.json`
  **validation done (ok)**.
- `markdownlint-cli2` over all 14 changed/sibling markdown files — **0
errors**.
- `jq empty` on `plugin.json` and `work/evals.json` — valid JSON. `git
diff --check` — no trailing
  whitespace.

## Related

- #513 — convention-gap protocol (surface, never invent-and-proceed);
its dispatched subagents also
  need a resolvable project context.
- #455 — denials / context degradations must surface loudly to the
human.
- #415 — discover conventions via setup, never pre-prescribe.
- #581 / #689 — most recent `work` skill change (claim-before-dispatch
guard), the sibling of this
  entry-invariant work.

Closes #449

🤖 Generated with a Claude Code implementation subagent (issue #449)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated Opened by automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

work-items(work): loop-lane orchestrator dispatched without the seam claim (assignee+lease) — worktree isolation alone isn't enough

1 participant