Skip to content
DocumentationDefine a semantic domain
On this page

Page resources

Open Markdownllms.txtView source

Last updated

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.