fix(brainstorming): pin brain.py console streams to UTF-8 - #2578
fix(brainstorming): pin brain.py console streams to UTF-8#2578aranellaeth wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThe changes make CLI and resolver output handling explicitly UTF-8-aware. Console streams are reconfigured when supported, JSON output preserves non-ASCII characters, and subprocess stdout decoding uses UTF-8 with replacement errors. ChangesUTF-8 Output Handling
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/core-skills/bmad-forge-idea/scripts/resolve_personas.py (1)
49-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the duplicated
_run_jsonhelper.
_run_jsonis byte-for-byte identical inresolve_personas.py(lines 48-67) andresolve_party.py(lines 46-65). If these scripts share a common package or utility module, extracting this function would prevent the implementations from diverging over time. If they're intentionally standalone per-skill scripts, the duplication is acceptable.The UTF-8 encoding change itself is correct —
encoding="utf-8", errors="replace"withtext=Trueproperly decodes child process output, and the 60s timeout and failure checks are preserved.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/core-skills/bmad-forge-idea/scripts/resolve_personas.py` around lines 49 - 65, Extract the identical _run_json helper shared by resolve_personas.py and resolve_party.py into their common utility or package module, then update both scripts to import and reuse it. Preserve the existing UTF-8 decoding, timeout, failure handling, and JSON parsing behavior; if no shared module is appropriate, leave the standalone implementations unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/core-skills/bmad-forge-idea/scripts/resolve_personas.py`:
- Around line 49-65: Extract the identical _run_json helper shared by
resolve_personas.py and resolve_party.py into their common utility or package
module, then update both scripts to import and reuse it. Preserve the existing
UTF-8 decoding, timeout, failure handling, and JSON parsing behavior; if no
shared module is appropriate, leave the standalone implementations unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a7664864-bac5-4a60-949b-9915a6acacfa
📒 Files selected for processing (4)
src/core-skills/bmad-brainstorming/scripts/brain.pysrc/core-skills/bmad-forge-idea/scripts/resolve_personas.pysrc/core-skills/bmad-party-mode/scripts/resolve_party.pysrc/scripts/resolve_config.py
|
Thanks! _run_json is intentionally duplicated—these are standalone, stdlib-only per-skill scripts with no shared package to import from, and the duplication predates this PR (I only applied the encoding fix to the existing copies). Extracting a shared module would be a larger packaging change, out of scope for this targeted fix. Happy to follow up separately if a shared script utility is ever introduced. |
sanmaxdev
left a comment
There was a problem hiding this comment.
Checked the UTF-8 stream changes against the existing resolver pattern. The focused suite passes with 59 tests, and a cp1252 reproduction now exits cleanly with the emoji preserved. The base branch fails with UnicodeEncodeError. The diff is focused and looks correct.
|
This PR still has the only fix for the producer-side crash, and it has been waiting on review since 2026-07-12 — flagging it because its hunks are being merged piecemeal in a way that risks losing the important one. Of the four files here, the three consumer hunks have now landed separately: Since this PR has been idle a while, I opened #2693 with just the One review note while this is open: 🤖 Generated with Claude Code |
brain.py prints two kinds of arbitrary user text: --extra overlay techniques (customize.toml additional_techniques) and, on stderr, the technique name echoed back by `show NAME` when it is not found. Either can carry a character the platform default cannot encode, and print() then raises UnicodeEncodeError. The shipped catalog is cp1252-safe (its only non-ASCII is U+2014), so this is an overlay/argv path, not a default-catalog crash. pin_utf8() passes errors= through rather than letting it default: reconfigure(encoding=...) alone resets the handler to strict, which would silently downgrade stderr's POSIX default of backslashreplace and turn a diagnostic about an undecodable path into a traceback. Thanks to @armelhbobdad for catching that on bmad-code-org#2693. Four regression tests, all four red against the unpatched script. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5f9ef65 to
3021e86
Compare
Greptile SummaryThe PR prevents Windows console encoding failures in the brainstorming CLI while preserving each stream’s existing error-handling behavior.
Confidence Score: 5/5The PR appears safe to merge, with focused tests covering the changed console-encoding behavior. The normal CLI path uses standard Python process streams that support the guarded UTF-8 reconfiguration, and the new tests exercise both affected streams and the unsupported-stream fallback.
|
| Filename | Overview |
|---|---|
| src/core-skills/bmad-brainstorming/scripts/brain.py | Adds guarded UTF-8 configuration for both console streams while preserving their current error handlers. |
| src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py | Verifies non-cp1252 output, stderr diagnostics, error-handler preservation, and compatibility with streams lacking reconfiguration support. |
Reviews (1): Last reviewed commit: "fix(brainstorming): pin brain.py console..." | Re-trigger Greptile
|
Rescoped and rebased onto current @armelhbobdad — thank you for flagging this. You were right that the piecemeal merges were leaving the important hunk behind, and right about the
Force-pushed 5f9ef65 → 3021e86: two files, +69 lines, four new tests — all four red against the unpatched script. The stderr |
What
Pins
brain.py'ssys.stdout/sys.stderrto UTF-8 — preserving each stream's ownerrors=handler — plus four regression tests.Rescoped 2026-08-07. This PR opened on 2026-07-12 covering four files. Three are now resolved elsewhere:
resolve_party.pyresolve_personas.pyresolve_config.pybrain.pymain; this PR is now only thisRebased onto current
mainand narrowed, so this and #2693 no longer overlap and neither blocks the other.Why
brain.pyprints two kinds of arbitrary user text:--extraoverlay techniques (customize.toml'sadditional_techniques), a documented first-class feature; their category names and descriptions go straight out throughfmt_list/fmt_show.show NAMEwhen nothing matches:print(f"# not found: {m}", file=sys.stderr), wheremis argv.Either can carry a character the platform default cannot encode, and
print()then raisesUnicodeEncodeError.Scoping this honestly: it is not a shipped-catalog crash.
brain-methods.csv's only non-ASCII is U+2014 (34 occurrences), which cp1252 encodes fine, and the--jsonpath isensure_ascii=True. The exposure is the overlay and argv paths —brain.py show 日本語, or an overlay technique namedFikir Fırtınası 🌪.The
errors=detailreconfigure(encoding=...)on its own resets the error handler tostrict:stderr defaults to
backslashreplaceon POSIX, so the naive form would turn a clean diagnostic about a surrogate-escaped path into a traceback — a regression on the exact platform this patch is not even about.pin_utf8()passeserrors=through.Credit to @armelhbobdad for spotting this on #2693: the version of this patch that was up before today had precisely that bug.
Testing
29 existing + 4 new. All four new tests are red against the unpatched script — verified by checking out
main'sbrain.pyand re-running (4 failed, 29 passed), so they are real tripwires and not assertions that were already true.One note for maintainers:
test_brain.pyis wired into no npm script — CI's Python coverage istest:renderer,test:retrospectiveandtest:sprint-planning. Unliketest_context.py, this file is healthy (29/29 green on a cleanmain), so adding it totest:rendererwould be a cheap win with no pre-existing failures to import. Happy to do that here if you want it.Follow-up worth its own PR
#2687 and #2688 took the
encoding="utf-8"half of the consumer fix but not theerrors="replace"andout.stdout or ""half this PR originally carried. On a strict-decode failure the exception is raised on subprocess's reader thread, sosubprocess.runreturnsreturncode=0withstdout=None— which walks straight pastif out.returncode != 0and dies in.strip(), uncaught by the surroundingexcept json.JSONDecodeError. Reproduced on Windows 11 / Python 3.13 against a child emitting invalid UTF-8:Same class as the finding @armelhbobdad fixed in
_installed_resolver_configon #2693, one exception type off because these two call.strip()beforejson.loads. Say the word and I'll open it.🤖 Generated with Claude Code