Skip to content

v0.11: assemble a contract bundle from digest-locked artifact references #106

Description

@flyingrobots

First v0.11 slice. Design record: docs/design/contract-bundle-assembly-v0.11.md.

Move bundle digest recomputation from out-of-scope to implemented:

  • assemble_contract_bundle(...) computes semanticBundleDigest and releaseBundleDigest per the exact spec preimages (CONTINUUM-BUNDLE-SUBJECT-001), reusing the edict.digest/v1 framing.
  • coreIrDigest is computed from a real compiled CoreModule; targetIrDigest and all other layer hashes are supplied digest-locked references (typed so computed-vs-supplied is unrepresentable as a mistake).
  • Assembled manifest round-trips through validate_contract_bundle_manifest.
  • Mutation-sensitivity test matrix honoring the semantic/release split; frozen bundle-digest goldens with an xtask check/regenerate path.

Target-IR byte identity is explicitly not in scope (tracked in #105).

Acceptance criteria

  • Assembly computes both bundle digests; Core digest computed from real Core; other refs supplied (typed provenance).
  • Round-trip validation + full mutation matrix tests; bundle goldens + xtask path.
  • Planned test-plan rows land first (RED), README updated only after GREEN.

Activity

  1. added this to the v0.11.0-alpha.1 milestone on Jun 29, 2026
  2. coderabbitai commented on Jun 29, 2026

    @coderabbitai
    🔗 Related PRs

    #30 - feat: expose surface validation stage [merged]
    #32 - docs: add workflow standards [merged]
    #37 - Add v1 lowerability and target-profile conformance checks [merged]
    #41 - Add Gate C admission boundary checks [merged]
    #67 - Add Echo Target IR lowering [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. flyingrobots commented on Jun 30, 2026

    @flyingrobots
    OwnerAuthor

    🤝 Handoff — v0.11 bundle assembly (foundation done, assembler next)

    Branch bundles/assembly-v0.11 (pushed, cargo xtask verify green, 288+ tests). Clean stopping point: the freeze-critical preimage helper is done; the assembler + golden freeze is the next focused pass.

    Done on the branch

    • docs/design/contract-bundle-assembly-v0.11.md — scope/non-claim/test-matrix (option b; target-IR digest is a supplied reference; Canonical Target IR bytes + digest (reviewed goldens), then integrate into bundle assembly #105 owns canonical target-IR bytes).
    • Planned RED rows BUNDLE-REQ-008 / BUNDLE-TP-010..014 in docs/topics/contract-bundles/test-plan.md.
    • canonical::digest_bundle_layer(domain: BundleDigestDomain, &[BundlePreimageComponent]) -> CoreDigest — the byte-shape foundation, with 8 byte-shape tests. Structured, not digest-only: components are Digest / DigestList / Resource (binds coordinate+digest, reuses {id,digest} encoding which rejects a missing digest) / ResourceList / SourceArtifacts (binds logical_path + artifact). Proven: a coordinate or logical-path change moves the digest even with identical digest bytes.

    Next pass — assemble_contract_bundle (RED→GREEN)

    1. Typed, lowercase-strict inputs. Make computed-vs-supplied unrepresentable and reject uppercase at construction (don't lean on the validator after hashing): SuppliedDigest::new / SuppliedTargetIrDigest::new / DigestLockedResource::new validate ^sha256:[0-9a-f]{64}$. The Core digest is computed inside assembly from a real CoreModule via digest_core_module — the caller cannot supply it.

    2. Exact preimage mapping (this is the whole spec — CONTINUUM-BUNDLE-SUBJECT-001). Build with digest_bundle_layer in this exact order:

    semanticBundleDigest = BundleDigestDomain::Semantic, components in order:

    # spec component manifest field component type
    1 coreIrDigest (computed from CoreModule) Digest
    2 targetProfileDigest target_profile.digest Digest
    3 targetIrDigest supplied Digest
    4 lawpackDigests lawpacks[].digest DigestList
    5 sourceProfileSemanticFactsDigest source_profile_semantic_facts.digest Digest
    6 generatedArtifactDigests generated_artifacts[].digest DigestList
    7 canonicalizationProfileDigest canonicalization_profile.digest Digest
    8 semanticCompileOptionsDigest semantic_compile_options.digest Digest
    9 conformanceFixtureCorpusDigests conformance_fixture_corpora[].digest DigestList
    10 verifierReportDigest verifier_report.digest Digest

    releaseBundleDigest = BundleDigestDomain::Release, components in order:

    # spec component manifest field component type
    1 semanticBundleDigest (just computed) Digest
    2 rawSourceArtifactDescriptors source_artifacts[] (logical_path + artifact) SourceArtifacts
    3 compilerIdentityAndDigest compiler Resource
    4 lowererIdentityAndDigest lowerer Resource
    5 verifierIdentityAndDigest verifier Resource
    6 nonSemanticCompileOptionsDigest non_semantic_compile_options.digest Digest
    7 buildProvenance build_provenance.digest Digest
    8 compileExplanationDigest compile_explanation.digest Digest

    Note the split: semantic binds digests; release binds structured identities (Resource = coordinate+digest) and source descriptors (path+artifact). That is what makes the semantic/release mutation matrix hold.

    3. Assemble + validate. Fill ContractBundleManifest (set semantic_bundle_digest/release_bundle_digest to the computed .to_review_string()), then round-trip through validate_contract_bundle_manifest → Valid.

    4. Mutation tests (the design-doc matrix; crates/edict-syntax/tests/contract_bundle.rs): semantic-layer ⇒ both; release-only (source path, toolchain coordinate-with-same-digest, nonsemantic options, build provenance, compile explanation) ⇒ release only; optional assurance evidence ⇒ no top-level digest change; admission inserted ⇒ rejected; uppercase supplied digest ⇒ rejected.

    5. Freeze. Check in bundle-digest goldens + an xtask check/regenerate path (mirror core-goldens). This is the deliberate freeze. Then flip BUNDLE-REQ-008/BUNDLE-TP-010..014 to implemented with evidence, update the contract-bundles README (only now), CHANGELOG, open the PR (Closes #106).

    Gotchas

    • Test modules must be at EOF (clippy items_after_test_module).
    • ContractBundleManifest has ~20 ResourceRef fields → the input struct is large but mechanical; reuse SourceArtifactRef/ResourceRef types.
    • digest_value accepts uppercase hex; enforce lowercase in the input constructors (the existing bundle validator already rejects uppercase refs at validation).
    • Resource encoding requires a present digest (errors on None) — good, that's the digest-locked invariant.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrelease:bundlesContract bundle assembly and identity release work

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions