| phase | R |
|---|---|
| title | Beads Formula Composition Integration |
| status | complete |
| branch | integrate/phase-r |
| target | develop |
| related_issue | #551 |
Ship a Beads-aware integration that composes a .formula.toml.j2 or
.formula.json.j2 through the existing renderer, proves the output with the
real bd executable, and exposes the same operation to the CLI and Python.
The library contract is intentionally host-neutral so a future bd compose
implementation can invoke it without a Rust reverse dependency.
sc-composerremains a generic, pure renderer. It gets no formula mode, Beads types, list feature, state model, orbddependency.crates/sc-composer-beadsowns the one render-to-bdintegration seam.- Beads owns formula schema, runtime variable semantics, validation, closure semantics, and persistent state. sc-compose does not infer or redefine any of them.
- Formula composition uses existing structured JSON variables and Jinja
control blocks. The Beads request contract uses triple-brace sc-compose
expressions so ordinary Beads
{{ variable }}placeholders survive. - Python is a separate Maturin adapter over
sc-composer-beads, not a Python subprocess wrapper oversc-compose. - Real persistent pour requires an explicit authorization value. Validation and preview are safe-by-default dry runs.
- Change Beads or implement
bd composehere. - Parse/validate Beads syntax in Rust, convert Markdown into Beads structures, or determine Beads closure criteria.
- Add a formula-specific renderer mode, hidden copy into
.beads/formulas, or a custom list/foreach language. - Publish Python packages or execute a real non-dry-run pour as part of a test, except an isolated, explicitly authorized negative-path pour that is expected to fail validation before any bead can persist.
CLI (`sc-compose bead`) ─┐
Python (`sc_composer_beads`) ─┼─> sc-composer-beads ─> sc-composer
future `bd compose` ─────┘ │
└─> pinned `bd` executable
sc-composer-beads defines the versioned sc-compose/beads/v1 JSON request
and receipt. It owns deterministic argument construction, stage ordering,
authorization, and process-result classification. bd is the only formula
validator and the only state writer.
| Sprint | Scope | Depends on | Unblocks |
|---|---|---|---|
| R.1 | Boundary gate, host-neutral core, and real bd validation/preview engine |
ADR-0021 approval | R.2, R.3 |
| R.2 | sc-compose bead CLI and JSON protocol adapter |
R.1 | CLI users; future bd compose caller |
| R.3 | Maturin/PyO3 Python adapter and cross-surface conformance | R.1; R.2 JSON contract | Python extensions |
R.2 and R.3 may run in parallel after R.1. No sprint may treat a mocked process runner as the sole proof: every operation claimed must also be proven against the pinned Beads release binary on supported platforms.
- Boundary documents and sc-lint rules prohibit reverse, ATM, CLI, and adapter dependencies before core source is added.
- A host-neutral request can render a Beads formula with structured values
while retaining Beads
{{ runtime_var }}placeholders. -
Validateruns realbd cook --dry-run;PreviewPourruns realbd mol pour --dry-runafter validation and active-registry resolution; failure prevents later stages. -
Pourcannot run unless its explicit authorization sentinel is present, and it never runs in CI. - CLI JSON and Python return the same versioned request/receipt semantics as the Rust library.
- The exact pinned Beads binary is verified on Linux, macOS, and Windows;
cargo test --workspace, Python tests, formatting, clippy, and boundary checks pass.
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace
python3 -m pytest -q bindings/sc-composer-beads-python/tests
sc-compose bead validate --request <fixture-request.json> --json
sc-compose bead preview-pour --request <fixture-request.json> --json
git diff --check
The real Beads fixture runs on every supported CI platform using the pinned
release binary. It must cover TOML and JSON formula fixtures, structured list
expansion, multiline/Unicode Markdown values, runtime-placeholder retention,
missing bd, invalid formula output, missing runtime variables, and a
refused unauthorized pour. It must also prove that bd where --json selects
the target formula directory and that same-name TOML/JSON registry entries are
rejected rather than silently shadowed.
- R.1 merged as PR #558
at
b3180ab. Its hosted CI run 32922056920 passed formatting, clippy, manifest validation, and the Linux, macOS, and Windows workspace-test jobs that execute the pinned Beads integration fixture. - R.2 merged as PR #559
at
4d5d1c2. Its hosted CI run 32939409974 passed those same three platform test jobs, including the CLI receipt and authorization coverage. - R.3 merged as PR #560
at
372930e. Its hosted CI run 32947283597 passed all 17 required checks, including the installedsc-composer-beadswheel on Linux, macOS, and Windows. - PR #561 is the explicitly non-blocking R.3 residual fast-follow. It adds sdist marker/stub coverage and direct in-process parity coverage; it is not part of the source commit used to close this phase.
- Phase R merged as PR #562
at
8920f62. - A dedicated post-close production-readiness review found a Blocking
symlink-escape/TOCTOU output-write defect. It is resolved by
PR #563, implementation
commit
74956cf, which rejects final-component symlinks and uses a sibling temporary file plus replacement rename; the same fix also boundsbdoutput and rejects non-UTF-8bdargument paths.