All notable changes to this project will be documented in this file.
- Added the generated
sc-sha-goGo module, exposing typed UniFFI bindings for the canonical file and composition hash operations. - Added a reproducible native-library release layout, target matrix, and
conformance-vector handoff for Go consumers. See
docs/integrations/sc-sha-go.mdfor the module path, release tags, and supported targets. - Added Hermes as a first-class
RuntimeKind, with its own.hermes/agents,.hermes/commands, and.hermes/skillsresolver search chains (falling back to.agents/then.claude/), a--runtime hermesCLI value, and matching Python bindings (RuntimeKind.HERMES). Closes #146. - Added fixed-shape TOML array-of-table extraction support, matching the
existing JSON/YAML
ArrayIndexstructural-recursion approach: repeated[[table]]blocks now extract per-element values with a dedicated cardinality diagnostic on length mismatch. Flat scalar TOML extraction is unchanged; Jinja-loop-driven (variable-cardinality) array-of-table generation remains unsupported by design. Closes #511.
- Bumped the Rust workspace and Python distributions to
1.5.0for the Phase P Go-binding release.
- Added the public
sc-composer::inspect_template_directivesAPI, which validates UTF-8 template bytes and returns classified include, import, and from-import statement spans without exposing MiniJinja parser internals or resolving filesystem targets. - Added fail-closed include-chain validation for JSON escape modes across
validate,validate --lint, andrender. An included template that explicitly declares a mode different from the root's effective mode now receives the stableERR_JSON_MODE_INCLUDE_CONFLICTdiagnostic with the participating template paths and modes; matching or undeclared includes continue to inherit the root mode. - Added one shared Jinja variable-expression scanner in
sc-composerfor template lint and validation. It returns namedJinjaVariableExpressionSpanoffsets, keeping comment, raw-block, and expression boundary handling consistent across both callers. - Added Python diagnostic-code bindings for
ERR_JSON_MODE_INCLUDE_CONFLICTandWARN_LINT_REDUNDANT_FILTER_CHAINso the native and Python surfaces expose the same stable codes.
- Made
RenderCheckMetaandCheckedOutputmetadata private after validation, with read-only accessors so callers cannot mutate the recorded render contract or provenance after a body has been checked. This closes a public API mutation path and is a breaking-surface change for callers that constructed or modified these fields directly. - Clarified ADR-0019's scope: checked output is enforced at the
sc-composeCLI emitter, while directsc-composerlibrary consumers must follow the named Checked-Emission Caller Contract and runcheck_rendered_outputonComposeResult::rendered_textbefore emission or caching. A bundledcompose_checked()helper is deferred to a future Checked Library Composition API sprint. - Migrated the six in-repository JSON assignment templates to explicit
json_escape_mode: auto, with semantic hostile-value fixtures and a documented legacy compatibility fixture. Seedocs/migration/json-escape-mode.mdfor the source-shape matrix. This is repository-local migration evidence; cross-repository release readiness remains a Phase O.5 responsibility. - Added the Phase O.5 pinned release-corpus inventory and parser-backed fuzz
gate. The campaign records actual consumer-root counts, rejects malformed
JSON before emission, preserves the auto/legacy compatibility probe, and
reports external migration owners without editing their repositories. The
1.4.1 release ships under the documented waiver in
docs/phase-O/evidence/o5-release-corpus.md, with legacy-mode fallback and diagnostics (WARN_JSON_LEGACY_ESCAPE_MODE,ERR_JSON_MODE_INCLUDE_CONFLICT) covering downstream consumers until they migrate. The 28 external templates across six downstream roots (atm-core, cpo, raptor, sc-lint, synaptic-canvas, and roslyn-lint) remain migration work for their owners. - Closed the Phase O CI lint-gate gap: the CI-authoritative lint profile now
enforces the production
template-contractstarget throughjust lint-ci-consumerwith an explicit structured pass assertion, excludes intentional negative and non-production test fixtures, and keeps validation and repository lint aligned when scanning Jinja comments and raw blocks.
- Preserved the underlying
serde_yamlparse error as the source cause for malformed YAML frontmatter, so callers can inspect the original parser failure through theConfigErrorchain instead of receiving only the normalized configuration diagnostic.
- Added the standalone
sc-shacrate to the release pipeline, together with thesc-shaPython distribution.sc-shaprovides the portable, LF-normalized content and composition hashing contract consumed bysc-composerand other projects without bringing in renderer or CLI code.
- Bumped the Rust workspace and both PyPI packages from
1.3.1to1.4.0. This is a semver-minor release because the publicComposePolicy,ComposeResult, andExpandedTemplatestructures gained fields and the rendering/validation surface grew; a patch release would not be appropriate for literal construction by downstream users. - Corrected the dependency-aware publish order to
sc-sha→sc-composer→sc-compose.sc-composerhas a real path dependency onsc-sha, sosc-shamust be available in the crates.io index before Cargo can resolve the publishedsc-composerpackage. The missing manifest entry previously madecargo publish --dry-run -p sc-composerfail. - Added release metadata checks that keep workspace-inherited crate versions, explicit Cargo path-dependency pins, and the hard-coded PyPI package versions in lockstep, preventing Rust/Python release-version skew.
- Upgraded
anyhowfrom1.0.102to1.0.103andquick-xmlfrom0.38.4to0.41.0to resolve RUSTSEC-2026-0190, RUSTSEC-2026-0194, and RUSTSEC-2026-0195; removed the deprecatedcargo-deny0.19.4 keys fromdeny.toml. - XML attribute extraction now follows
quick-xml0.41's XML 1.0 AVNormalize behavior, collapsing embedded tab, carriage-return, and line-feed characters to spaces after entity decoding.
- Fix issue #238 where an adjacent rendered-document frontmatter block
containing Jinja syntax was incorrectly parsed as a second YAML config block,
causing
ERR_CONFIG_PARSEduring validation and rendering.
This release covers all work landed on develop since 1.2.0: Phases D
through J. 1.3.0 was bumped in Cargo.toml when Phase D landed but was
never tagged or published, so this release folds every phase merged in the
interim into the single 1.3.0 line rather than burning additional version
numbers on never-published intermediate states — none of Phases E through J
introduce a breaking or consumer-facing incompatible change.
- Phase D (D.1 through D.4, plus D.1-py through D.4-py): first-class nested
template support with stacked frontmatter passes, brace-count-aware variable
discovery, multi-pass composition,
render --all, pass-scoped CLI variable groups,verify, multi-passtemplate-init, and tandem Python bindings for the library-owned Phase D surface. - ADR-0010: a narrowly-scoped stability-policy exception for
Renderer::with_delimiters, documenting why the constructor's move from a panic path toResult<Self, RenderError>ships in the1.3.0line without a major-version bump. - Phase E (E.1 through E.3): recursive structured-input support for
--var-fileand frontmatter/template.jsondefaults — arrays of objects, jagged scalar arrays, and other finite nested JSON/YAML value trees (closes issue #157) — plus the reusableadversarial-fuzzingskill (coordinator and bounded background probe agents) that classifies confirmed rendering-boundary bugs and promotes them into permanent regression tests. - Phase G (G.1 through G.7): the first
sc-compose extractfeature — deterministic recovery of scalar string variable bindings from a known.xml.j2template plus its rendered XML output, exposed through the CLI and matchingsc_composerlibrary/Python API. Extraction uses structural occurrence matching and fails closed (an explicit unsupported/ambiguous result) instead of silently returning a wrong value for repeated sibling tags or unsupported Jinja constructs. Includes a corpus of realistic and adversarial extraction fixtures promoted into permanent Rust/Python/CLI regression coverage. - Phase H (H.1 through H.8):
sc-compose extractand the underlying library/Python API now support JSON, YAML, and TOML rendered-output extraction alongside XML, using the same fail-closed, string-value report model and structural provenance (closes issue #193's JSON/YAML/TOML gaps). - Phase I (I.1 through I.6): a first-class
rawextraction mode for Markdown and other plain-text documents, reused by XML block/mixed-content extraction so a placeholder occupying an XML element's content can recover a full text-plus-markup block (closes issue #193 Gap 1), plus narrow, observable normalization of non-XML preamble text before the document root during rendered-XML extraction (closes issue #193 Gap 5). - Phase J (J.1 through J.4): internal decomposition of the CLI argument/ JSON-capability surface, the validation-state assembly and diagnostic-policy layers, and the frontmatter parser/normalizer, reducing hot-spot risk (issue #212) with zero public-API or behavior change.
Renderer::with_delimitersno longer panics on invalid delimiters; it now returns a typedRenderError, and the CLI/Python surfaces document the same fail-closed behavior.- Multi-pass validation now discovers undeclared tokens per pass and direct
render_all()calls correctly apply frontmatter defaults beneath caller values. - Phase D documentation now reflects the landed delimiter-hardening state, verify/template-init Python scope, and the point-in-time nature of the final consolidated review artifact snapshot.
- The artificial nested-array validation restriction that previously rejected
valid recursive structured input is removed, while top-level var-file and
YAML string-key boundaries are preserved.
ERR_VAL_NESTED_ARRAY_UNSUPPORTEDremains a reserved compatibility code but is no longer emitted for supported recursive input values. - Dotted expressions (e.g.
{{ user.name }}) passed toextractare now rejected as unsupported object-field access instead of being misread as a literal variable name"user.name". - YAML alias/anchor expansion and JSON/YAML/XML input depth are now bounded during extraction parsing, closing a resource-exhaustion path where a malicious rendered document with recursive aliases or excessive nesting could exhaust the process before extraction could fail closed.
- Jinja loop-context built-ins (
loop.last, etc.) are no longer misreported as undeclared variables inside aforscope, while a user variable literally namedloopoutside a loop still validates normally (closes issue #167). - YAML merge keys (
<<: *defaults) in var-files no longer silently discard inherited fields; merge-key handling is now explicit and diagnostic (closes issue #166).
- Workspace version bump:
1.2.0->1.3.0. - Internal restructuring of
crates/sc-compose/src/cli.rs,main.rs,var_file.rs, andobserver_impl.rs(Phase F) into smaller, independently testable modules; the CLI contract, JSON output shape, and thesc-composerpure-library boundary are unchanged.
- Phase C (C.1 through C.3): Python bindings for sc-compose composition APIs,
published as the
sc-composepackage on PyPI/TestPyPI.- Sprint C.1: maturin-based
bindings/pythoncrate foundation, PyO3 extension module scaffolding, and Python packaging shape. - Sprint C.2: the Python API surface —
Renderer,ComposeRequest,ComposeResult,Frontmatter/ParsedTemplate, resolver and validation types, and thecompose/render_template/resolve_profile/validatemodule-level functions. - Sprint C.3: the release train and packaging hardening —
workflow_dispatchrelease workflow with staged TestPyPI rehearsal and production PyPI publish targets, release-gate enforcement, and GitHub Release attachment handling.
- Sprint C.1: maturin-based
- Issue #117:
__repr__on PyO3 pyclass types (e.g.ComposeRequest) now routes correctly and produces an informative repr instead of the default opaque object repr.
- Workspace version bump:
1.1.0->1.2.0.
- Phase HTML-Report (FR-12 through FR-15): map/object variable inputs, arrays
of objects, HTML template output, and the bundled
sprint-report-htmlexample. - Sprint S7:
sc-compose examplesandsc-compose templatescommands, including bundled examples and template-pack workflows. - Sprint S8: release engineering and distribution support, including release gate automation, Homebrew tap automation, winget manifests, and idempotent publish handling for already-published crate versions.
- Phase A (A1 through A9): the SC-Reporting contract foundation, covering the report artifact catalog, producer recipe surface, source-collection and render-many contract, semantic diagram spec, template families, shared panel chrome, latest/archive policy, and publish-manifest handoff.
- Phase B (B1 through B15, integrated via PR #87): the public reporting CLI
surface with
reports init,reports smoke,reports finalize,reports render-spec,reports index,reports verify, andreports publish-manifest, plus the shared report scaffold, semantic-spec rendering path, latest/archive materialization, and publish-manifest CI handoff.
observer_impl.rs: removed the.expect()panic risk inhealth();shutdown()now surfaces flush errors cleanly.- Added text-mode
observability-healthtest coverage alongside the JSON checks. - Consolidated the shared reporting boundary rule in
docs/requirements.md. - Documentation follow-ups: corrected the
requirements.mdHTML-report section header and added the missing H4 row to the FR coverage matrix. - Homebrew release automation and formula rendering fixes that removed the
manual tap-push requirement after
1.0.1. - PR #95: removed the stale
.atm.tomlpane-hook wiring so repo-local ATM configuration no longer depends on tmux pane metadata or post-send hooks.
- Workspace version bump:
1.0.2->1.1.0. - Sprint B9 / PR #85:
sc-composenow adoptssc-observability1.2.0directly for CLI logging, including retained-log maintenance defaults,Logger::log(...)queue-admission semantics, and the shutdown-to-stopped typestate path used byobservability-health.
See git history prior to v1.1.0 for earlier changes.