Skip to content

feat: board_create_from_plan: batch-create features from a structured decomposition (#92)#94

Merged
mabry1985 merged 1 commit into
mainfrom
feat/bd-2np
Jul 23, 2026
Merged

feat: board_create_from_plan: batch-create features from a structured decomposition (#92)#94
mabry1985 merged 1 commit into
mainfrom
feat/bd-2np

Conversation

@mabry1985

Copy link
Copy Markdown
Member

Summary

board_create_from_plan: batch-create features from a structured decomposition (#92) (bd-2np)

See the diff for details.

@mabry1985 mabry1985 closed this Jul 23, 2026
@mabry1985 mabry1985 reopened this Jul 23, 2026
protoreview[bot]
protoreview Bot previously requested changes Jul 23, 2026

@protoreview protoreview 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 panel review — FAIL

code-review-structural · head 8c21cc19cc7f · formal

[review-synthesizer completed: workflow code-review-structural:report]

This PR introduces a batch feature-creation path (board_create_from_plancreate_from_plan/features/batch) that carries real correctness risk despite its limited surface. The by_title.setdefault bug (finding 1) is the fix-first item: it can silently break valid dependency edges whenever plan items share a normalized title. The panel agreed on all six findings — none were refuted. Verification confirmed every finding against the diff; the only nuance is that finding 4's residual gap is narrower than it first appears (JSON parse handles most quoting). A coverage gap worth noting: create_from_plan (~40 new lines in store.py) has no unit tests in this diff, and all six findings emerge from that method and its wrappers — the batch path was read but not exercised.

[
  {
    "file": "store.py",
    "line": 416,
    "severity": "major",
    "category": "correctness",
    "claim": "Duplicate plan-item titles cause dependency resolution to pick the FIRST item (possibly failed) rather than any successfully-created item with that title, silently breaking valid dependency edges.",
    "evidence": "In Phase 2, `by_title.setdefault(_plan_title_key(it[\"title\"]), it)` keeps only the first item per normalized title. If that first item failed to create (`id` is None), every `depends_on` reference to that title resolves to `None` and reports 'its create failed' — even when a later item with the SAME title was created successfully and has a valid id.",
    "source": "correctness",
    "verdict": "confirmed",
    "note": "setdefault keeps only the first item per key; if item 0 fails (id=None) and item 1 succeeds with same normalized title, by_title maps to the failed item — every dep on that title hits the dead id path, ignoring the successful duplicate."
  },
  {
    "file": "store.py",
    "line": 380,
    "severity": "minor",
    "category": "correctness",
    "claim": "Priority 0 is silently coerced to 2 because `int(section.get(\"priority\", 2) or 2)` treats the integer 0 as falsy.",
    "evidence": "`int(section.get(\"priority\", 2) or 2)` — when `section.get(\"priority\")` returns the integer `0`, `0 or 2` evaluates to `2`, then `int(2)` gives `2`. A user who explicitly sets `\"priority\": 0` in a plan item gets priority 2 instead, with no warning.",
    "source": "correctness",
    "verdict": "confirmed",
    "note": "Python: 0 is falsy, so `0 or 2` → 2, then int(2) → 2. Explicit priority=0 silently maps to 2."
  },
  {
    "file": "store.py",
    "line": 103,
    "severity": "minor",
    "category": "conventions",
    "claim": "`_plan_title_key` is an exact duplicate of `__init__.py`'s `_norm_title` — identical implementation, diverging only in docstring — creating a maintenance hazard where future changes to normalization logic could be applied to only one copy.",
    "evidence": "store.py: `return \" \".join(str(t or \"\").strip().lower().split())` vs __init__.py: `return \" \".join(str(t or \"\").strip().lower().split())` — identical byte-for-byte. Docstring of `_plan_title_key` acknowledges 'the same exact-after-normalize matching the tool-boundary dedup uses.'",
    "source": "conventions",
    "verdict": "confirmed",
    "note": "Both functions: `' '.join(str(t or '').strip().lower().split())` — identical implementation. store.py's docstring even acknowledges it mirrors the tool-boundary version."
  },
  {
    "file": "api.py",
    "line": 224,
    "severity": "minor",
    "category": "correctness",
    "claim": "`bool(body.get(\"mark_ready\"))` coerces the string `\"false\"` to `True`, causing mark_ready to activate when a string false was intended.",
    "evidence": "`bool(body.get(\"mark_ready\"))` — `bool(\"false\")` returns `True` because any non-empty string is truthy. Normal JSON `false` → Python `False` is fine, but a caller that sends the string `\"false\"` (e.g. from a form-encoded body or a mis-serialized payload) gets unexpected promotion.",
    "source": "correctness",
    "verdict": "confirmed",
    "note": "bool('false') → True (any non-empty string is truthy). JSON false → Python False works; the gap is string 'false' from non-JSON callers."
  },
  {
    "file": "__init__.py",
    "line": 239,
    "severity": "minor",
    "category": "cross-file",
    "claim": "The `board_create_from_plan` tool does not perform the `_open_duplicate` dedup check that `board_create_feature` does, despite its docstring claiming 'Every section gets board_create_feature's validation.' Re-calling the batch tool with overlapping plan items would silently stack duplicate features.",
    "evidence": "`board_create_feature` calls `_open_duplicate(existing, title)` and returns a 'Skipped' message if a duplicate exists. `board_create_from_plan` calls `get_store(**store_kw).create_from_plan(parsed, mark_ready=mark_ready)` directly with no dedup check. The store's `create_from_plan` in turn calls `self.create_feature(...)` for each item — and `create_feature` (store.py) has no dedup logic either.",
    "source": "cross-file",
    "verdict": "confirmed",
    "note": "No _open_duplicate call anywhere in the batch path; store.create_feature also lacks dedup. Re-calling with overlapping items would stack duplicates with no guard."
  },
  {
    "file": "__init__.py",
    "line": 239,
    "severity": "minor",
    "category": "cross-file",
    "claim": "The `board_create_from_plan` tool does not apply `_strip_wrapping_quotes` to individual plan-item fields (title, spec, acceptance_criteria, design, etc.), unlike `board_create_feature` which strips every string field. The store's `create_feature` relies on the tool layer for this hygiene, so batch-created features can retain literal wrapping double-quotes that the single-create path removes.",
    "evidence": "In `board_create_from_plan`: `json.loads(_strip_wrapping_quotes(parsed.strip()) or \"[]\")` strips only the outer JSON string, then passes parsed dicts directly to `create_from_plan`. In `board_create_feature`, every string field goes through `_strip_wrapping_quotes`: `title = _strip_wrapping_quotes(title)`, `spec = _strip_wrapping_quotes(spec)`, etc. The store's `create_feature` does NOT strip wrapping quotes — that contract lives entirely at the tool boundary.",
    "source": "cross-file",
    "verdict": "confirmed",
    "note": "Only the outer JSON string is stripped; individual fields are not. JSON parse handles JSON-level quoting, so the residual gap is narrower than single-create (embedded literal \"\" inside JSON string values), but the inconsistency with board_create_feature is real."
  }
]

@mabry1985

Copy link
Copy Markdown
Member Author

Operator note on the FAIL verdict: all six findings ground-truthed against head 8c21cc1 — the major (by_title.setdefault dep-resolution on duplicate titles) verified real. Ruling: fix-all-now. The feature is being requeued with the findings as explicit criteria (including unit tests for create_from_plan, which shipped untested); this branch will be force-pushed with the fix round.

@protoreview protoreview 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 panel review — WARN

code-review-structural · head cdfdbd0f6738 · formal

[review-synthesizer completed: workflow code-review-structural:report]

Prose Brief

Overall risk: Moderate. The batch board_create_from_plan codepath has at least one confirmed crash vector (double-dash dep strings) that bypasses the tool's error guard and terminates the entire batch. The prior round's major finding about duplicate plan-item titles silently resolving to the wrong dependency edge remains unverified — I cannot tell whether it was fixed or still lurks.

Fix first: The _resolve_plan_dep crash (store.py:567). Wrap the int(dep.strip()) call in a try/except, or replace the .lstrip("-") heuristic with removeprefix("-") so only a single dash is stripped before the digit test. Also wrap the call-site in create_from_plan so one malformed dep string doesn't kill the whole batch.

Panel disagreement: None surfaced in this verification pass — only one finding was submitted for verification and it was confirmed unanimously.

What verification changed: The verifier confirmed the crash path by reading head-SHA code: "--5".lstrip("-")"5" (isdigit→True), then int("--5") raises ValueError with no handler anywhere in the call chain. The finding stands as confirmed.

Gaps: Five prior-round findings (including one major at store.py:416 on duplicate-title dependency misrouting) were not re-examined in this verification pass. The structural pass appears to have been skipped or not included. I am carrying the major as open below.


Prior Round Dispositions

[{"prior": "store.py:416", "disposition": "open", "why": "Not addressed in this verification pass — the verifier only examined one finding. Cannot determine from available evidence whether the duplicate-title dependency-routing defect was fixed."}]

Canonical Findings

[
  {
    "file": "store.py",
    "line": 567,
    "severity": "minor",
    "category": "correctness",
    "claim": "_resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the remainder — but int() rejects the original double-dash string.",
    "evidence": "`elif isinstance(dep, str) and dep.strip().lstrip(\"-\").isdigit():\n            idx = int(dep.strip())` — for dep='--5', lstrip('-') → '5' (isdigit() → True), then int('--5') raises ValueError. The exception propagates out of create_from_plan uncaught, bypassing the tool's `except BoardError` guard and crashing the entire batch.",
    "source": "correctness",
    "verdict": "confirmed",
    "note": "Code read at head SHA confirms exact bug: dep.strip().lstrip('-') strips ALL dashes ('--5'→'5'), isdigit()→True, but int(dep.strip()) calls int('--5') which raises ValueError in Python. Call-site in create_from_plan pass 2 has no try/except; tool wrapper's except BoardError won't catch ValueError. Also affects '--0', '---3', etc. — any dep string with ≥2 leading dashes whose remainder is all-digits."
  }
]

1 finding(s) downgraded to uncertain: the code they quote as evidence does not appear in the file at the reviewed head, nor in this PR's patch for it. A finding that cannot be grounded does not gate a merge (issue #25) — it still stands for a human to judge.

  • store.py (minor) — quoted evidence not found at this head: — for dep='--5', lstrip('-') → '5' (isdigit() → True), then int('--5') raises ValueError. The exception propagates out o

@protoreview
protoreview Bot dismissed their stale review July 23, 2026 06:31

Superseded — a later head cleared the QA panel (see the newest verdict).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@mabry1985

Copy link
Copy Markdown
Member Author

Operator adjudication of the round-2 WARN (brain-ruled): fix-now. The residual --5 dep-string crash violates the feature's own per-item failure contract (and the #85 leading-dash hardening history). Requeueing once more: guard _resolve_plan_dep with a proper ^-?\d+$ validation (or catch ValueError → that item fails with a named reason) + the test. Round-1 fixes verified complete — the major and test coverage are done; do not touch them.

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

@protoreview protoreview 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.

Promoting the WARN verdict for head cdfdbd0f6738: all checks terminal-green, zero unresolved review threads. (approve-on-green)

Open findings carried by this approval — non-blocking, but they did not go away:

  • minor store.py:567 — _resolve_plan_dep crashes with uncaught ValueError on dependency strings containing multiple leading dashes (e.g. '--5'), because str.lstrip('-') strips all dashes — allowing isdigit() to pass on the

Approving a WARN does not resolve its findings (issue #22).

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