fix: board_create_feature refuses a same-title open duplicate#63
Merged
Conversation
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>
|
👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow. |
There was a problem hiding this comment.
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=Truebypasses.__init__.pyL82-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.tomlandprotoagent.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 realbrCLI 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
|
Submitted COMMENT review on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found live, in dogfooding
Spun up a fresh team on
portfolio-pluginvia Roxy (portfolio_spinup_team) and dispatched one feature. The team's board ended up with two beads for the same work, twice:The team's own audit log confirms the root cause — its own agent called
board_create_featuretwice, ~7-9s apart, same title each time:board_create_featurehad 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_dispatchalready 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) callingboard_create_featuretwice 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_featureitself — so it protects every caller, not just PM dispatches:force=truecreates a second copy anyway.portfolio_dispatch's own failure posture.Tests
tests/test_board_create_feature_dedup.py— pure_norm_title/_open_duplicateunit tests, plus tool-boundary tests (refuse / force-override / recreate-after-done) via a lightweight fake store (no realbrCLI needed, matching this repo's existing test style for store-adjacent logic).Gate:
ruff check . && ruff format --check . && pytest -q— 244 passed (237 + 7 new).Version bumped 0.26.1 → 0.27.0 (new capability — a
forceparam + refusal behavior on an existing tool).🤖 Generated with Claude Code