Generated contract manifest¶
Problem¶
The legacy Webapp template stored contracts/manifest.json as one static file containing both Webapp domain contracts and lifecycle contracts. Under composition, no component may partially own or patch another component's destination. Assigning that static manifest to artifact.webapp-core would also make Webapp the authority for lifecycle registrations reused by other artifacts.
Decision¶
Contract metadata is distributed to its true source authority as component.json contract_registrations. lifecycle.contract-evolution uniquely declares contracts/manifest.json as a generated material using the bounded declarative generator ID contract-manifest-v1.
A generated material names a generator ID rather than executable code or a command. The composer maintains an allowlisted implementation for supported generator IDs; unknown production generator IDs are rejected. This preserves the hook-free execution boundary.
For a resolved component set the contract-manifest-v1 generator:
- collects all
contract_registrations; - rejects duplicate IDs/document paths/schema paths;
- converts source snake_case registration fields to the consumer manifest contract;
- sorts active contracts lexically by contract ID; and
- serializes the manifest deterministically with bootstrap schema version 1.
The component source does not carry a pre-rendered production manifest.
Authority¶
A registered contract document/schema/migration remains owned by the component that registered it. The manifest is only the generated closed registry of those authorities. Editing the generated manifest does not transfer authority away from the component metadata.
Consumer independence¶
After composition, the generated manifest plus managed lifecycle validators are sufficient to validate contract/schema/migration closure without reading component descriptors from the source checkout. The composition lock separately binds generated bytes to the exact source revision and selected component set.
Versioning¶
The composition-era manifest bootstrap starts at schema version 1 because its authority/generation model is new. Individual domain contract histories are independent and may preserve earlier versions; the imported Webapp route and UI-state contract histories preserve their v1→v2 domain evolution.