This recipe creates an organization-owned process language and reusable audit
trait that compose with One without becoming one.* meaning.
The .one and Rust APIs below are illustrative end-product syntax.
1. Define the domain package
semantic acme.audit
domain one.trait@1
use acme.audit.contracts@1 as audit_contracts
trait Audited@1
target: declaration implements one.semantic#StateTransition@1
retention: CalendarPeriod
contributes: audit_contracts.AuditRequirementContribution
semantic acme.process
domain one.semantic.domain@1
use one.data.SubjectScopeValue@1
use one.contracts.StableKeyValue@1
domain Process@1
extends one.semantic@1
use acme.audit.Audited@1
use one.data@1 only Key, Scope
use one.contracts.Length@1
kind Entity@1
posture: definition
members: named_fields
exposes: one.semantic#FieldContainer@1
kind Transition@1
posture: definition
from: StageRef
to: StageRef
deadline: DurationConstraint?
exposes: one.semantic#StateTransition@1
normalizer ProcessTraitNormalizer@1
impl Key
for Entity@1.field<T>
where T implements StableKeyValue
normalize_with: ProcessTraitNormalizer
impl Scope
for Entity@1.field<T>
where T implements SubjectScopeValue
normalize_with: ProcessTraitNormalizer
impl Audited
for Transition@1
normalize_with: ProcessTraitNormalizer
extends establishes one compatibility lineage. use composes reusable
meaning and exports a closed public name table. The exact trait implementations
make imported traits applicable to Acme's own declaration kinds; import alone
would not.
2. Author with the domain
semantic acme.shipping
domain acme.process@1
type TenantId@1 = Text @Length(1..=64)
type ShipmentId@1 = Text @Length(1..=64)
enum ShipmentStage@1
ready
in_transit
entity Shipment@1
tenant: TenantId @Key @Scope
id: ShipmentId @Key
label: Text @Length(1..=128)
transition Dispatch@1
from: ShipmentStage.ready
to: ShipmentStage.in_transit
deadline: <= 30s
@Audited(retention: 7y)
entity, transition, and their normalization belong to Acme. Text,
@Length, @Key, @Scope, time quantities, and global composition laws keep
their original One owners. The authored submission remains untrusted until
every applicable owner validates its contribution.
3. Normalize at the owner boundary
impl DomainNormalizer<ProcessSubmission> for ProcessNormalizer {
type Output = ProcessDescriptor;
fn normalize(input: ProcessSubmission) -> Result<Self::Output, DomainFault> {
ProcessDescriptor::validate(input)
}
}
Lower systems consume ProcessDescriptor, never parser tokens or authored
field ordering. A custom normalizer is capability-empty and returns only an
untrusted bounded proposal; it cannot mint trusted Knowledge records.
4. Report mapping loss
mapping acme.process#transition_to_history@1
preserved transition identity, subject, and audit retention
defaulted observation independence = same_process
lost presentation label
receipt projection:sha256:31a8…Directional loss is explicit and never implies a reverse mapping.
See domains and traits and semantic domains.