Move an integration from 0.8 to 0.9

The 0.9 development SDK uses C ABI 57, schema 27 and exchange protocol 1. Update the binding and native library together. Python 0.8 bindings reject an ABI 57 library. Existing production identities and stored knowledge retain their meaning; opening older files follows the existing migration rules.

Old form

Use in 0.9

C additive/revision-only transaction constructors

pp_production_begin_edit(production, base_or_NULL, context_or_NULL, ...)

C++ beginTransaction() and context setters

production.edit(context) / production.edit(base, context) / view.edit(context)

Python edit context setter

Pass origin= and message= to edit()

Python production.transaction() / Transaction

production.edit() / Edit

Python revision-ID-only transaction base

production.edit(view.decision_base)

Python FileSource(path)

The path itself

Asset-only Python/C++ import result

ImportedMedia.asset_id and .representation_id

C import’s asset-only outputs

Add nullable out_representation_id before the error output

Python find_known_media_by_locator / find_known_media_by_fingerprint

find_known_media(path_or_evidence, limit=...)

C++ findKnownMediaByLocator / findKnownMediaByFingerprint

findKnownMedia(path_or_evidence, limit)

Python production.assets

view.assets(page_size=100) inside a read session

Python id in production.assets

production.asset_exists(id)

Manual asset/revision page loops in Python or Rust

The retained view’s assets / revisions scans

C file-source creation, staging and release

pp_transaction_import_file / pp_transaction_add_file_representation

C resolution factory plus setters

One pp_resolution_config_t passed to creation

C++ resolution factory plus setters

ResolutionOptions::create(ResolutionConfig)

C++ MediaSource::file(path)

std::filesystem::path or UTF-8 path string

Rust commit_with_receipt() / C++ commitWithReceipt()

commit()

C status-only commit / pp_transaction_commit_with_receipt

pp_transaction_commit(edit, &receipt, &error)

CLI --base-revision ID

--decision-base TOKEN from inspect

Python metadata accepts (vocabulary, property) keys and native str, bool, signed int, bytes, exact Fraction and list/tuple values. Reads return scalars and immutable tuples. Keep explicit unsigned, URI, localized text and other ambiguous variants. Floats, None and dictionaries do not acquire an inferred metadata meaning. See Metadata vocabularies.

Python edit(), edit(base) and view.edit() accept origin= and message=. Uncommitted context exit rolls back. Commit returns this edit’s own receipt, including an absent revision for no change. A failed commit is terminal; Base revisions and semantic conflicts explains explicit retry.

Five Python tasks

These excerpts run together in docs/examples/python/ergonomics.py. Its fixture supplies rushes/A001.mov, an empty moved/ directory and an output directory. The host produces the output bytes before recording them. The historical comparison runs the same workflow against the published 0.8 platform wheel. The qualified ABI-53 comparison preserves the initial 0.9 program; the current excerpts use ABI 57.

Import a file and ordinary metadata with one edit:

with Production.create(work / "tasks.pproj", "Documentary") as production:
    with production.edit(origin="example:editor", message="Import take") as edit:
        imported = edit.import_media(original, display_name="Camera A")
        asset_id = imported.asset_id
        edit.add_metadata(
            AssetRef(asset_id), ("example:editorial", "title"), "Interview"
        )
        receipt = edit.commit()
    assert receipt.revision is not None  # This edit's own revision.

Reuse an existing identity after an explicit adoption decision:

with production.read_session() as view:
    matches = view.find_known_media(original, limit=2)
    if matches.next_cursor is not None or len(matches.items) != 1:
        raise ValueError("Adoption needs an explicit, unique choice")
    known = matches.items[0]
    adopted_asset = known.asset_id  # Store this ID in the host project.
assert adopted_asset == asset_id  # No new identity or revision.
assert known.representation_id == imported.representation_id

After the fixture moves the file, resolve and explicitly confirm its location. The detached base lets the caller close the view before host/UI work:

with production.read_session() as view:
    resolutions = view.resolve(
        asset_id,
        search_directories=[moved.parent],
        verification=VerificationMode.CONTENT,
    )
    selected = next(
        item
        for item in resolutions
        if item.representation_id == known.representation_id
    )
    selection = selected.select_verified()
# The copied decision survives the view; host/UI work can happen here.
with production.edit(base=selection.base, message="Confirm moved take") as edit:
    edit.confirm_selection(selection)
    relink_receipt = edit.commit()
assert relink_receipt.revision is not None

Record a produced representation with its actual activity and parameters:

with production.read_session() as view:
    input_id = known.representation_id  # Retained from explicit adoption.
    with view.edit(message="Record host output") as edit:
        registered = edit.register_outputs(
            ((asset_id, RepresentationKind.DERIVED, output),),
            ActivitySpec(
                "example:preview",
                inputs=(input_id,),
                tool=ToolIdentity("Example file copy", "1.0"),
                parameters={("example:preview", "mode"): "copy"},
            ),
        )
        output_id = registered.outputs[0].representation_id
        output_receipt = edit.commit()  # Output and knowledge are atomic.
assert output_receipt.revision is not None

Activity inputs describe provenance. They do not imply authored references inside the output; record dependency observations only when the host has inspected such references.

Scan a pinned asset set and finite history with bounded pages:

with production.read_session() as view:
    assets = list(view.assets(page_size=100))
    revisions = list(view.revisions(after=0, page_size=100))
assert len(assets) == 1
assert [revision.sequence for revision in revisions] == [1, 2, 3]

For the equivalent C, C++17, Rust and CLI paths, use Create a production and import media, Adopt media already known to a production, Media roots and resolution, Provenance integration and Consuming the revision feed. The CLI JSON format remains 1; command-line decisions now use the scoped token. See CLI output.

Produced outputs

Use Edit.register_outputs, C++ Transaction::registerOutputs, native pp_transaction_register_outputs or CLI artifact register for an already-produced file or compound source and its actual activity/parameters. Rust uses prepare_outputs followed by owned stage; failed staging rolls back the owned edit. Commit remains explicit.

ActivitySpec writes accept representation IDs or (ID, role) pairs. Copied read edges retain snapshots and no longer serve as write builders. Python parameters accept a mapping or repeated key/value pairs; C++ accepts typed parameter tuples. Native activity creation now takes pp_activity_recipe_t and a separate output-edge array. Update headers, library and bindings together.

Verified relinking (development ABI 57)

Retain the import’s representation ID and select it explicitly from resolution results. Python select_verified, C++ selectVerified and native pp_resolution_set_select_verified copy/lend complete verified locations with the original decision base. Confirm them together using a read-bound edit; Rust uses stage_relink. CLI media relink REPRESENTATION --confirm captures its own coherent decision and requires explicit confirmation.

A single candidate is insufficient when verification is partial or search is incomplete. Unverified optional members remain in diagnostics. Existing locations are guarded no-ops. Explicit locator confirmation remains available for independent human/host decisions. Deferred intent uses locator.confirm.v1; older protocol decoders reject that command as unsupported.

Native Python metadata values

Read and write strings, signed integers, booleans, binary values and exact rationals as str, int, bool, bytes and Fraction. Lists and tuples write one list value; reads return immutable tuples. The corresponding MetadataString, MetadataI64, MetadataBool, MetadataBytes, MetadataRational and MetadataList classes are removed.

Unsigned values keep MetadataU64; URI, language, timestamp, decimal, reference and ordered structure values remain explicit. Inspect Python types when reading: ordinary Python equality equates True and 1, while metadata assertion/field equality preserves their distinct stored kinds. Repeated assertions and ordered fields retain their meaning. Cycles and unsupported dictionaries are rejected.

Advanced Python imports

Import job requests, states, leases and job events from postproject.jobs. Import proposal commands/builders, request IDs, durable outcomes and replication positions from postproject.exchange. Their package-root exports are removed; ordinary production/media/metadata recipes keep the root imports.

Edit.prepare_proposal captures an already prepared native edit. The command builder expresses deferred intent. Both remain available. Notification progress and replication positions retain their distinct scopes.