feat: PR opener injects Fixes #N line from feature metadata (#93)#95
Conversation
There was a problem hiding this comment.
QA panel review — WARN
code-review-structural · head aad75dd95fe9 · formal
[review-synthesizer completed: workflow code-review-structural:report]
Verdict
Both findings confirmed. No refutations — the verifier reproduced the substring bug from the diff and re-inspected store.py signatures to confirm the API gap. The array stays at two findings with the verifier's annotations preserved.
Brief
Overall risk: Low. Both findings are minor — a correctness edge case in URL dedup and a cross-file API gap that's harmless in practice thanks to a spec-text fallback.
Fix-first: Finding #1 (loop.py:259) — the substring URL check can silently skip injection when a body mentions a sibling issue with a shared digit prefix (e.g. #12 vs #123). The regex-based already guard uses \b correctly; the plain in operator just needs to follow suit.
Panel agreement: No disagreement — the verifier confirmed both findings with concrete reproduction evidence. The only nuance was the verifier's note that Finding #2 is "harmless in practice" because the text-scanning fallback covers the realistic case, which the original claim already acknowledged was low-impact.
Verification changed nothing: Both findings arrived at the same severity and substance they had before verification; the verifier only strengthened the evidence.
Gaps: None reported. The structural pass (protopatch) completed without output beyond these two items. loop.py is the only production file touched, and two findings on a focused ~40-line change is proportionate coverage.
[
{
"file": "loop.py",
"line": 259,
"severity": "minor",
"category": "correctness",
"claim": "_inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. body mentions issue #123, source is #12 — 'https://github.com/acme/widgets/issues/12' is a substring of '.../issues/123').",
"evidence": "The check `issue_slug and url in (body or \"\")` on line 259 does a naive substring test: `url = f\"https://github.com/{issue_slug}/issues/{n}\"`. For n=12, the URL `https://github.com/acme/widgets/issues/12` is a substring of any body containing a reference to issue #120–#129, #1200–#1299, etc. in the same repo. A word-boundary-aware check (e.g. `re.search(rf\"https://github\\.com/{re.escape(issue_slug)}/issues/{n}\\b\", body)`) would prevent this.",
"verdict": "confirmed",
"note": "Reproduced from diff: url='https://github.com/acme/widgets/issues/12' IS a substring of '.../issues/123' (characters 0..N-1 of url match). The regex-based `already` check uses \\b correctly, but the plain `in` bypasses it. The test `test_inject_issue_ref_skips` covers `Fixes #123` (no URL substring) but NOT a URL reference to #123, which would trigger the false positive."
},
{
"file": "loop.py",
"line": 227,
"severity": "minor",
"category": "cross-file",
"claim": "_source_issue() reads `feature.get(\"source_issue\")` as its primary source of truth, but `BeadsBoard.create_feature()` and `update_feature()` in `store.py` have no `source_issue` parameter and cannot set this field — the explicit field the PR describes as the 'source of truth' is unreachable through the board's own API.",
"evidence": "`store.py` `create_feature()` signature: `def create_feature(self, title, *, spec=\"\", acceptance_criteria=\"\", design=\"\", files_to_modify=(), parent=\"\", priority=2, difficulty=\"\", depends_on=(), foundation=False)` — no `source_issue`. `update_feature()` similarly omits it. Meanwhile `_source_issue()` in the diff reads: `explicit = str(feature.get(\"source_issue\") or \"\").strip()` as the first checked field.",
"verdict": "confirmed",
"note": "Re-read store.py: both signatures confirmed to lack `source_issue`. The field IS read by `_source_issue()` (diff line ~218). However the spec/design text-scanning fallback covers the realistic case (URL in body), and the field could still be set via direct `br update` outside the board API, so the practical impact is low — the brief's assessment that it is 'harmless in practice' is reasonable."
}
]There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
|
Operator adjudication of the WARN verdict (brain-ruled, per the standing review flow):
Do not merge until the fix round lands and re-reviews. |
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head aad75dd95fe9: 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
loop.py:259— _inject_issue_ref's URL dedup check uses a plain substring match that produces false positives when the body references a sibling issue whose number shares a digit prefix with the source issue (e.g. b - minor
loop.py:227— _source_issue() readsfeature.get("source_issue")as its primary source of truth, butBeadsBoard.create_feature()andupdate_feature()instore.pyhave nosource_issueparameter and cannot s
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
QA panel review — WARN
code-review-structural · head c081e6264983 · formal
[review-synthesizer completed: workflow code-review-structural:report]
Prose Brief
Overall risk: Low. No blockers survive verification; all three findings are minor. The PR's core mechanism (injecting source-issue references into PR bodies) works correctly in the common case. The defects are edge cases — a cross-repo dedup false positive, an unreachable API field with a working fallback, and a misleading docstring on a function whose callers already catch the exception.
Fix first: loop.py:242 — the cross-repo dedup false positive in _inject_source_issue. It's a confirmed correctness bug: when a cross-repo source issue shares its number with a target-repo issue closed in the PR body, the Refs line is silently suppressed. Add a same_repo guard to the already regex check.
Panel disagreement: Finding 1 (worktree.py:492) was originally flagged as major for an uncaught WorktreeError. Verification showed both callers catch it — _drive via its existing WorktreeError handler, _salvage_verified_candidate via a broad except Exception. Downgraded to minor; the real defect is the docstring lie, not a crash hazard.
What verification changed: Finding 1 downgraded major → minor after confirming both call sites wrap the call. Finding 2 and 3 confirmed as-is (minor).
Gaps: None. The structural pass (protopatch) contributed Finding 3.
[
{
"file": "loop.py",
"line": 242,
"severity": "minor",
"category": "correctness",
"claim": "_inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the cross-repo Refs <url> line to be erroneously suppressed.",
"evidence": "The regex rf\"\\b(?:Fixes|Closes|Resolves)\\b\\s+#{n}\\b\" on line ~242 is applied unconditionally regardless of whether the source issue lives in the same repo. For a cross-repo source issue (e.g. other/lib#7), if the PR body contains Fixes #7 (closing target-repo issue #7), already matches and the function returns the body unchanged — suppressing the Refs https://github.com/other/lib/issues/7 line. The already check should be gated on same_repo being true.",
"verdict": "confirmed",
"note": "Reproduced by stepping through the code: source=('other/lib',7), slug='other/lib', same_repo=False. Body='Fixes #7' → already regex matches (unconditional, no same_repo guard), url_present=False (URL regex uses other/lib slug, body doesn't contain it) → returns body unchanged, suppressing 'Refs https://github.com/other/lib/issues/7'. Narrow edge case (requires same issue number in source+target repos AND a closing keyword in body), but real."
},
{
"file": "loop.py",
"line": 207,
"severity": "minor",
"category": "cross-file",
"claim": "_source_issue() reads feature.get(\"source_issue\") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this field — the explicit field the PR describes as the 'source of truth' is unreachable through the board's own API.",
"evidence": "store.py create_feature() signature: def create_feature(self, title, *, spec=\"\", acceptance_criteria=\"\", design=\"\", files_to_modify=(), parent=\"\", priority=2, difficulty=\"\", depends_on=(), foundation=False) — no source_issue. update_feature() similarly omits it. _source_issue() in the diff reads: raw = str(feature.get(\"source_issue\") or \"\").strip() as the first checked field. The text-scanning fallback covers realistic cases, so practical impact is low.",
"source": "protopatch",
"verdict": "confirmed",
"note": "Read store.py at PR head: create_feature signature confirmed — no source_issue param. update_feature signature confirmed — no source_issue param (params: fid, spec, acceptance_criteria, design, files_to_modify, difficulty, depends_on, foundation). _source_issue() reads feature.get('source_issue') as primary check (loop.py line ~207). Fallback scans spec/design/acceptance_criteria for URLs, so realistic cases are covered. The explicit field is unreachable through these two methods, but could still be set via br CLI directly or a future API addition."
},
{
"file": "worktree.py",
"line": 492,
"severity": "minor",
"category": "correctness",
"claim": "repo_slug's docstring promises it returns \"\" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning \"\"), contradicting its documented contract.",
"evidence": "repo_slug calls _gh(\"repo\", \"view\", ...) with no try/except, but _gh raises WorktreeError on asyncio.TimeoutError (worktree.py line ~375: raise WorktreeError(f\"gh {' '.join(args)} timed out after {timeout}s\")). The docstring explicitly says it returns \"\" on failure. Both callers (_drive and _salvage_verified_candidate) catch the exception at their level, so no crash — but the docstring is misleading and the fail-closed design contradicts the PR's intent that the issue line is advisory.",
"verdict": "uncertain",
"note": "_gh raises WorktreeError on timeout (confirmed); repo_slug has no try/except (confirmed); docstring contradicted (confirmed). But caller-impact overstated: in _drive, repo_slug IS inside the try that catches WorktreeError (diff shows same indent as open_pr, above except); in _salvage_verified_candidate, the full function has a broad except Exception wrapping the whole try block — WorktreeError does NOT propagate uncaught in either caller. Severity is lower than 'major' since both sites catch it. Core defect (misleading docstring + fail-closed instead of fail-open) stands."
}
]|
Operator adjudication of the round-2 WARN (brain-ruled): round-1 fix verified in. Finding 2 (source_issue API) = already-filed follow-up #97. The two new findings both ruled fix-now (each a one-liner + test): (A) cross-repo sources — the |
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
There was a problem hiding this comment.
Promoting the WARN verdict for head c081e6264983: 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
loop.py:242— _inject_source_issue's already dedup regex matches Fixes/Closes/Resolves #n even for cross-repo source issues, where #n in the body refers to the TARGET repo's issue (a different issue), causing the c - minor
loop.py:207— _source_issue() reads feature.get("source_issue") as its primary source of truth, but BeadsBoard.create_feature() and update_feature() in store.py have no source_issue parameter and cannot set this fi - minor
worktree.py:492— repo_slug's docstring promises it returns "" when gh can't resolve the repo, but _gh raises WorktreeError on timeout — the function fails closed (raises) instead of failing open (returning ""), contra
Approving a WARN does not resolve its findings (issue #22).
Summary
The loop now stamps the PR↔issue link itself at PR-open (#93) — pure metadata the coder never needed to be in the loop for.
_source_issue(feature)resolves the originating GitHub issue: an explicitsource_issuefield wins (full URL, or bare#42/42meaning the target repo), falling through to the firstgithub.com/<owner>/<repo>/issues/<n>URL in the feature's spec/design text._origin_slug(wt)reads the worktree'soriginremote to get the repo the PR opens against (https/ssh forms both parsed); it never raises — the issue line is advisory, not worth failing a PR over._inject_issue_ref(...)appendsFixes #<n>for a same-repo issue, orRefs <full-url>when the issue lives in a different repo (or the target repo can't be determined — a bareFixes #<n>there could close the wrong repo's issue<n>). It skips entirely when the body already references the issue (Fixes/Closes/Resolves #n,owner/repo#n, or the full URL), and features with no issue link leave the body untouched._pr_body's 4000-char cap so a max-length summary can't truncate it.Tests cover all four contract cases (same-repo, cross-repo, dedupe, no-link) at both the pure-helper level and through
_drive, plus remote-slug parsing and the fail-open git-error path.