Executables

An executable is one root program together with the closure of modules it uses. It has two levels: a manifest that is its logical identity, and one or more realizations that make it startable for a target. The byte-level schemas are in Artifact Formats; this page is the model.

Source units

The compiler recognizes .cm modules and .ce programs, and the shop recognizes native extension source as a logical requirement:

KindExtensionCompiled form
Module.cmmcode that returns a value
Program.cemcode that starts an actor and returns nothing
Extension.c, .cc, .cppa logical native requirement, satisfied by the shop

The extension decides, and the compiler checks the text against it. A unit is compiled as a module or as a program, because the two lower the same text differently — which is why the kind is a component of the mcode derivation key (see Artifact Formats, “Portable mcode unit”). A kind recovered from the text afterwards could not be a key component at all. So .cm is a module and .ce is a program; a .cm with no top-level value return is module.missing_return and a .ce with one is actor.top_level_return, reported at the offending line. Content with no file of its own — an evaluated program, a script given by absolute path — is whatever the request asked it to be: a start root is a program.

Native extensions are not mcode; they are requirements an executable lists and a runtime satisfies.

Claims

Each unit’s mcode records its claims — the static interface between compiler, builder, policy, and shop:

ClaimMeaning
modulesthe literal use() requests found in the unit
endowmentsthe $name capabilities it references
globalsthe runtime globals it requires
extensionsthe logical native requirements it declares

Claims record the request as written. Resolving a request to a package is the builder’s job, not the unit’s.

Resolution produces identities

Every resolved unit binding carries the package identity it resolved to. The locator text is an input to resolution and is never re-read afterwards to work out where a unit came from.

That is what the one-copy rule is enforceable against: one executable cannot contain the same file from the same package at two different versions, and the closure builder decides between the candidates or refuses. A component that recovered a unit’s origin by splitting its locator on / would classify two candidates from one package as two packages, and the collision would become undetectable rather than reported.

The refusal happens where the manifest’s package table is built, and it names what it found — both identities, and a root-to-unit path to each — because “these two disagree” is not actionable without knowing which branches brought them in:

executable manifest: package 'lib' resolved to two identities in one executable
  workspace snapshot blake2:aaaaaaaa (version 2.0.0)
    reached by: game::main.ce -> game::left.cm -> lib::json.cm
  workspace snapshot blake2:bbbbbbbb (version 3.0.0)
    reached by: game::main.ce -> game::right.cm -> lib::text.cm
one executable cannot contain the same file from the same package
at two different versions; decide between them or drop one branch

Two identities are the same selection exactly when their canonical bytes agree, so a version bump over an unchanged snapshot is still one selection, and an unchanged version over a changed snapshot is two.

The executable manifest

The executable manifest is the logical identity of the program: one root, the resolved closure of unit bindings, the deterministic initialization order, and the claims aggregated across the whole closure. It is content-addressed.

That content hash is also the executable’s source-derivation identity. Each unit row names an mcode hash, and an mcode hash already folds the source bytes, compiler identity, selected pipeline stages, target-neutral flags, unit kind, and static bindings. Comparing two manifest hashes therefore compares the complete selected source closure under the compiler semantics that produced it; no separate boot-only source hash is introduced.

A boot resident carries that hash in:

{
  schema: "pit.resident.derivation@1",
  source_closure: "blake2:..."
}

This is distinct from a target-realization identity. Two realizations can share one source_closure while differing in target profile, pool layout, policy decision, PGO, or payload form. Resident staleness compares source_closure; realization caches and artifact addressing continue to use the target-realization key.

Crucially, the manifest names its parts by hash rather than carrying their code. The compiled images are separate artifacts. That separation is what lets one logical executable — one manifest hash — have several realizations: a desktop realization and a console realization of the same program share a manifest and differ in target profile and image set. A realization catalog maps an actor locator to the realization that fits the running target; the manifest itself carries no per-target arms.

A realization is an ordered array

A manifest is a logical identity; a realization is a list of things to do, in order, to start an actor. Each entry produces one value into one slot, and there are three kinds and no more:

EntryProduces
{value: <any>}a constant the shop already resolved — 4, "debug", or a token the runtime injects at start
{unit: <image>, entry: <n>}run this bytecode; the value is what it returns
{native: <key>}load this compiled-in provider

The runner allocates one slot per entry in the new actor’s heap and fills them in order. It does not decide which kind it is looking at beyond dispatching on it — the ordering, the slot indices and the bindings were all computed when the executable was built. See Actors and Memory.

An endowment is not a fourth kind. It is an entry whose result is bound to a $name instead of a def name — same instantiation, same array, same slots. That is why $delay and $clock need no special case in the runner and no table of known names: they are units in the realization like any other. See Endowments.

Slot resolution is what lets images be shared. A unit’s bytecode names only what is inside its own image, relatively; a reference that crosses an image boundary is a binding row the realization fills in. So the same image can be instantiated more than once, in one actor or in many, without its code knowing.

Policy decides over the closure

Policy applies to the exact closure as a whole, once, at build time. It is not a per-unit decision cached and combined — a program of twenty modules is judged as those twenty modules together, because authority is a property of the combination.

It decides one thing: may this be built. Nothing is checked when an actor starts; a start executes a plan that was already decided. The closure is knowable because use() takes a string literal — an executable that calls $start on a computed name is flagged as allowing guest code, since the actors it may start are not knowable at build time. See Capabilities.

Extension requirements

An executable lists logical extension requirements only — never dylib paths or static symbols. At start, the shop confirms the running runtime can supply each one: a development shop may load a dylib; a sealed build satisfies the same requirement from a static table linked into the binary. See Extensions.

Startup

Actor startup is Pit-owned and realization-driven: the runtime opens and pins the realization’s images through the image provider, installs each unit’s declared bindings, and invokes the units in the recorded order. It re-derives nothing — the shop constructed the realization and it is the only caller. Cold boot is the same shape, with C doing only enough to reach the boot image. The mechanism is described in Actors and Memory.