Skip to content

refactor(documents): make the document builders public (#212)#216

Merged
pbean merged 1 commit into
mainfrom
refactor/documents-public-builders-212
Jul 20, 2026
Merged

refactor(documents): make the document builders public (#212)#216
pbean merged 1 commit into
mainfrom
refactor/documents-public-builders-212

Conversation

@pbean

@pbean pbean commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Closes #212.

documents.py is the library-level read-model projection layer, and its docstring says outright that a non-CLI frontend (the planned web backend) imports these builders directly. Every builder nonetheless kept the leading underscore it had as a private function inside cli.py — which says the opposite. The six *_SCHEMA_VERSION constants beside them were already public; the split had no rationale, it was an artifact of the origin.

Deferred out of #211 because that PR's deliverable was a provable verbatim relocation (AST-identical bodies, untouched tests as the proof) — renaming in the same commit would have voided both.

The one judgement call

run_token_totals goes public too. The issue framed this as open, on the grounds that it is a shared helper rather than document API and could stay "a private sibling within its own module." It cannot: cmd_status imports it across the module boundary (cli.py:53) and calls it on the text path (cli.py:1581), after the --json early return. A private name imported by another module is the exact contradiction being removed.

The run_ prefix stays — it marks run-level vs per-task aggregation, which is the distinction the function exists to get right.

Decisions

  • No back-compat cli._x_document aliases. Nothing in-tree needs them and they would re-create the private surface this removes. Same call the Unreleased notes already record for runs.tmux_sessionsmux_sessions ("internal, no deprecation aliases").
  • No __all__. No leaf module in the package has one, including probe.py and diagnostics.py — the modules this one is modelled on. Adding one here alone would make the absence there newly ambiguous.
  • No new CHANGELOG entry (matches refactor(cli): extract the --json document builders into documents.py #211 — nothing user-visible changes: CLI surface, all six schema versions, and every emitted document are byte-identical). The Unreleased entry naming _run_token_totals is updated, since it is unshipped notes rather than history.

New: the library path's first coverage

Every other --json test reaches the builders through cli.main, so nothing pinned the docstring's central promise — that the library path and the CLI path agree. Two parity tests drive one fixture down both and assert the same dict comes back.

Equality is asserted against the raw builder return, not a json round-trip: a consumer holds this dict before serializing it, so a tuple where a list belongs is a real defect a round-trip would hide. Verified by injecting exactly that ("tasks": tuple(tasks)) and confirming the test fails, then reverting.

Scoped to status/list, which have clean deterministic fixtures. clean/cleanup need mutation fixtures and validate needs a driven ValidationReport — those would balloon this past a rename.

Notes for review

  • tests/conftest.py was the surprise: make_validate_document, new in feat(tui): render validate --json in a findings modal (#210) #215, already calls the builder directly — so an in-tree consumer was already reaching into a private name across a module boundary, which strengthens the issue's case. It needed the rename too.
  • make_validate_document contains _validate_document as a substring. The rename is word-boundary anchored, so it and its 13 call sites in test_tui_app.py are untouched — verified by dry-run diff before applying.
  • tests/test_cli.py has one docstring-only cross-ref fix; zero assertion changes to any pre-existing test, so refactor(cli): extract the --json document builders into documents.py #211's "the untouched tests are the proof" story survives.
  • cli.py:36-47 is deliberately byte-identical: all six noqa: F401 are load-bearing (ruff's autofix silently deletes a re-export), and the comment above them stays accurate. Checked with trunk check --no-fix.

Verification

  • 2628 passed, 1 skipped
  • trunk check --no-fix — no issues
  • Library surface imports with no CLI/argparse involved; cli.*_SCHEMA_VERSION re-exports still resolve; noqa: F401 count still 6

Summary by CodeRabbit

  • New Features
    • Exposed reusable document-generation helpers for validation, decisions, status, listing, cleanup, and token totals.
  • Changed
    • Standardized CLI JSON output generation across supported commands without changing the documented output format.
    • Clarified the machine-readable JSON contract and token-total calculation terminology in the changelog.
  • Tests
    • Added checks confirming library-generated documents match the corresponding CLI --json output.

documents.py is the library-level read-model projection layer, and its
docstring says outright that a non-CLI frontend (the planned web backend)
imports these builders directly. Every builder nonetheless kept the leading
underscore it had as a private function inside cli.py, which says the
opposite. The six *_SCHEMA_VERSION constants beside them were already
public; the split had no rationale, it was an artifact of the origin.

Deferred out of #211 because that PR's deliverable was a provable verbatim
relocation (AST-identical bodies, untouched tests as the proof) — renaming
in the same commit would have voided both.

run_token_totals goes public too. The issue framed it as an open question,
on the grounds that it is a shared helper rather than document API and
could stay a private sibling. It cannot: cmd_status imports it across the
module boundary and calls it on the text path, and a private name imported
by another module is the exact contradiction being removed. The run_ prefix
stays — it marks run-level vs per-task aggregation, which is what the
function exists to get right.

No back-compat cli._x_document aliases: nothing in-tree needs them and they
would re-create the private surface this removes (same call the Unreleased
notes already record for runs.tmux_sessions -> mux_sessions). No __all__:
no leaf module in the package has one, including probe.py and diagnostics.py,
which this module is modelled on. No new CHANGELOG entry — nothing
user-visible changes — but the Unreleased entry naming _run_token_totals is
updated, since it is unshipped notes rather than history.

Also adds the library path's first coverage. Every other --json test reaches
the builders through cli.main, so nothing pinned the docstring's central
promise that the two paths agree. Two parity tests drive one fixture down
both and assert the same dict comes back. Equality is against the raw
builder return, not a json round-trip: a consumer holds this dict before
serializing, so a tuple where a list belongs is a real defect a round-trip
would hide — verified by injecting exactly that and watching the test fail.

conftest.make_validate_document (new in #215) was already calling the
builder directly, so it needed the rename too. Note make_validate_document
contains _validate_document as a substring — the rename is word-boundary
anchored so it and its 13 call sites are untouched.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78cfd74a-d5a9-4002-812b-886c6199f0f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9771650 and 4897da7.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/bmad_loop/cli.py
  • src/bmad_loop/documents.py
  • tests/conftest.py
  • tests/test_cli.py

Walkthrough

The JSON document builders in documents.py are renamed as public functions. CLI JSON handlers, test fixtures, and parity tests are updated to use them, with no changes to document shapes or emitted arguments.

Changes

Public document API

Layer / File(s) Summary
Expose public document builders
src/bmad_loop/documents.py
Renames the seven underscore-prefixed builders to public names and updates module documentation and references.
Route CLI JSON output through public builders
src/bmad_loop/cli.py
Updates imports and JSON handlers for validation, decisions, status, list, cleanup, and clean output.
Align fixtures, parity tests, and changelog
tests/conftest.py, tests/test_cli.py, CHANGELOG.md
Uses public builders in fixtures, verifies status/list parity with CLI output, and corrects the token-total helper name.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: dracic

Poem

I’m a rabbit with builders, now public and bright,
Routing JSON through names that feel right.
Status and lists match, tests softly agree,
No hidden underscore is hiding from me.
Hop, hop—the document paths flow free!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: making the document builders public.
Linked Issues check ✅ Passed The PR satisfies #212 by renaming the seven builders public, updating call sites, and handling run_token_totals consistently.
Out of Scope Changes check ✅ Passed The changes stay within the requested refactor, with only related tests, docstrings, and changelog updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/documents-public-builders-212

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pbean

pbean commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pbean
pbean merged commit 555584a into main Jul 20, 2026
9 checks passed
@pbean
pbean deleted the refactor/documents-public-builders-212 branch July 20, 2026 20:56
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.

documents.py: drop the leading underscore from the document builders before a non-CLI consumer depends on them

1 participant