Skip to content

fix: board_create_feature refuses a same-title open duplicate#63

Merged
mabry1985 merged 1 commit into
mainfrom
fix/board-create-feature-dedup
Jul 3, 2026
Merged

fix: board_create_feature refuses a same-title open duplicate#63
mabry1985 merged 1 commit into
mainfrom
fix/board-create-feature-dedup

Conversation

@mabry1985

Copy link
Copy Markdown
Contributor

Found live, in dogfooding

Spun up a fresh team on portfolio-plugin via Roxy (portfolio_spinup_team) and dispatched one feature. The team's board ended up with two beads for the same work, twice:

bd-2kx / bd-8or   "Create PROTO.md grounding doc..."     (the team's own onboarding task)
bd-1bf / bd-2h6   "Rollup surfacing: priority + stuck"   (my dispatched task)

The team's own audit log confirms the root cause — its own agent called board_create_feature twice, ~7-9s apart, same title each time:

20:02:13  board_create_feature(title="Create PROTO.md grounding doc...")
20:02:20  board_create_feature(title="Create PROTO.md grounding doc...")   <- same title, 7s later
20:02:51  board_create_feature(title="Rollup surfacing...")
20:03:00  board_create_feature(title="Rollup surfacing...")               <- same title, 9s later

board_create_feature had zero dedup — every call just creates a new bead, no matter how many times it's called with the same title.

Why this isn't portfolio-plugin's existing dedup

portfolio_dispatch already refuses a same-title re-dispatch (portfolio-plugin#25/v0.14.6) — but that guard lives on the PM's dispatch call, one tier up. It can't catch a board's own agent (its onboarding pass, or its own reasoning about a task it just received) calling board_create_feature twice within a single turn — which is exactly what happened here, on both the onboarding task and the dispatched task.

Fix

Add the same class of guard, one tier down, at board_create_feature itself — so it protects every caller, not just PM dispatches:

  • Refuse when a feature with the same (normalized) title is already open (backlog/ready/in_progress/in_review/blocked).
  • force=true creates a second copy anyway.
  • A store read failure never blocks creation (better a possible dup than a stuck board) — matches portfolio_dispatch's own failure posture.

Tests

tests/test_board_create_feature_dedup.py — pure _norm_title/_open_duplicate unit tests, plus tool-boundary tests (refuse / force-override / recreate-after-done) via a lightweight fake store (no real br CLI needed, matching this repo's existing test style for store-adjacent logic).

Gate: ruff check . && ruff format --check . && pytest -q244 passed (237 + 7 new).

Version bumped 0.26.1 → 0.27.0 (new capability — a force param + refusal behavior on an existing tool).

🤖 Generated with Claude Code

Live-caught in dogfooding: a freshly spun-up team's own agent called
board_create_feature TWICE, ~9s apart, for the same title (both its onboarding
task and a dispatched task) -- no guard stopped it, so the board ended up with
two beads for one piece of work.

portfolio-plugin's portfolio_dispatch got this exact dedup in v0.14.6 (#25), but
only guards the PM's re-dispatch -- it can't catch a board's OWN agent (onboarding,
or its own reasoning about a dispatched task) calling this tool twice within one
turn. Add the same guard one tier down, at the tool board_create_feature itself,
so it protects EVERY caller: refuse when a same-title feature is already open
(backlog/ready/in_progress/in_review/blocked); `force=true` creates a second copy
anyway; a store read failure never blocks creation (better a possible dup than a
stuck board).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@protoquinn

protoquinn Bot commented Jul 3, 2026

Copy link
Copy Markdown

👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow.

@mabry1985 mabry1985 merged commit 5dc7139 into main Jul 3, 2026
1 check passed
@mabry1985 mabry1985 deleted the fix/board-create-feature-dedup branch July 3, 2026 20:11

@protoquinn protoquinn Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Audit — PR #63 | fix: board_create_feature refuses a same-title open duplicate

VERDICT: WARN (preliminary — CI is queued; re-evaluating on terminal-green)


CI Status

  • test: queued ⏳

Diff Review

  • Adds dedup guard to board_create_feature: same-normalized-title open feature → refuse with helpful message, force=True bypasses. __init__.py L82-180.
  • Three clean helpers: _TERMINAL_STATES, _norm_title, _open_duplicate — scoped, documented, correct.
  • Store read failure never blocks creation (except BoardError → existing=[]) — matches stated fail-safe posture.
  • Version bump 0.26.1 → 0.27.0 consistent across pyproject.toml and protoagent.plugin.yaml.

Observations

  • LOW: clawpatch structural review unavailable for this repo (checkout cache miss) — reviewed from diff directly; diff is self-contained.
  • INFO: 7 new tests (tests/test_board_create_feature_dedup.py) cover normalization, dup detection, terminal skip, refusal, force-override, recreate-after-done. No real br CLI dependency.
  • INFO: Exact-match-only dedup is the right trade-off — false-positive silently drops real work; PR description makes this explicit and intentional.

— Quinn, QA Engineer

@protoquinn

protoquinn Bot commented Jul 3, 2026

Copy link
Copy Markdown

Submitted COMMENT review on protoLabsAI/projectBoard-plugin#63.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant