- docs
- Concepts
- Launcher frames in records
Launcher frames in records
This page explains the extra frames that appear in execution records when an entrypoint launcher, a deferred package or a patch companion is involved. It extends Two records per launcher invocation in Execution Records.
Forwarded names and degraded frames
Section titled “Forwarded names and degraded frames”The inner frame can name its package only when the parent forwarded the name in OCX_LAUNCH_IDENTITIES. A launcher finds its package by directory. Every name that resolves to the same digest shares that directory, so only the parent knows which name it used.
OCX sets the variable, never you, and only while a [patches] tier is in effect. The value is a JSON object mapping each content digest a composition resolved to the registry/repository[:tag] names it was resolved under. no_patches marks a package the project opted out.
These commands write the variable for the process they spawn:
ocx exec,ocx package exec,ocx package test,- a lazy tool’s shim.
These commands export it, keeping an inherited value’s entries for packages they did not compose:
ocx env,ocx package env,ocx direnv export,- the per-prompt shell hook, which also restores the replaced value when you leave a project.
A malformed value is a hard startup error for the launcher (exit 78) while a patch tier is in effect. Like every OCX_* key, it cannot be set from ocx.toml or through --env. With a forwarded name the frame is complete. Its package descriptor carries the real name and a purl without a tag qualifier, as a shim frame does.
Without a forwarded name, the identifier is a synthetic content digest minted at the launcher. Package directories are content-shared and never persist their own registry or repository. The frame is then degraded, even when an outer exec or package exec frame pairs with it a few milliseconds earlier.
On a host without a patch tier, a consumer whose policy assumes “degraded means no ocx parent” fires on every ordinary entrypoint invocation.
Every degraded frame states the limitation in-band, so a consumer reading one record in isolation still learns why the name is missing. The frame carries frame.identityNote. The package descriptor carries sh.ocx.identity: "synthetic" and no uri at all, since a purl cannot be built without a repository.
{ "frame": { "command": "launcher exec", "identity": "degraded", "identityNote": "package directories are content-shared and carry no registry/repository, so logical identity is not recoverable in this frame and no purl can be emitted" }, "packages": [ { "name": "file-url-mode/3f7a2b9c5d1e8f04a6b3c7d2e9f1a5b8c4d6e0f2a3b7c9d1e5f8a0b2c4d6e8f0", "digest": { "sha256": "3f7a2b9c5d1e8f04a6b3c7d2e9f1a5b8c4d6e0f2a3b7c9d1e5f8a0b2c4d6e8f0" }, "annotations": { "sh.ocx.role": "root", "sh.ocx.identity": "synthetic" } } ]}A launcher run from a PATH that no ocx command composed produces this shape too. Only the content digest identifies what ran.
A launcher run from a PATH that ocx env, ocx direnv export or the shell hook composed names its package. Those exports carry the same variable while a patch tier is in effect.
Previews write no record
Section titled “Previews write no record”ocx package test and ocx patch test write no record, entrypoint or not. They are maintainer previews over locally materialised, unpublished artifacts, and a record from one would describe something that was never published.
The exclusion covers the launcher re-entry too. A launcher built into a package under one of those commands’ scratch roots carries it. No forwarded sink makes that frame record, and neither does a sink the child re-reads from its own config chain.
The exemption stops at required = true. Under a fail-closed posture the two contradict each other. The policy says this invocation must be recorded, and the exemption says it will not be. OCX resolves the contradiction in the operator’s favour. It grants no exemption and refuses the launch with exit 74, naming the [records] policy that refused it.
The reason is that the exemption is claimed from a path. ocx launcher exec grants it when the pkg-root it was handed sits under one of those scratch roots. Both roots are inside the invoking user’s own $OCX_HOME. Anyone who can place a directory there can claim the exemption. No capability handshake fixes that when the parent and the caller run as the same user.
On a host with a fail-closed policy, run a preview command with the policy relaxed, or accept that previews do not run there.
The launcher shim frame
Section titled “The launcher shim frame”Deferred packages add a fourth launching frame beside exec, package exec and launcher exec. It is the hidden ocx launcher shim subcommand. A package composed with lazy-mode = always puts a generated shim on PATH instead of an eager install. Nothing about it exists on disk until that shim runs for the first time.
The shim frame is therefore the only place a deferred package’s content is downloaded. Its first invocation produces two records, for a different reason than the entrypoint split.
The join key is the same content digest used everywhere else. Unlike an ocx launcher exec frame with no forwarded name, a shim’s generated launcher carries the pinned identifier it was built for as an argument. ocx launcher shim reads that identifier directly, so it needs no parent to know its own logical name.
A later invocation of the same binary resolves straight to its real entrypoints/, which outranks the shim on PATH. It produces exactly one record, the same shape as any package that was never deferred.
Patch companions in the record
Section titled “Patch companions in the record”A patch companion composes as part of the packages it patches rather than replacing them. The record treats it as one more package and not as a side channel. Any companion admitted for the invocation appears in packages[] as an ordinary entry. Scanning packages[] in order gives the sequence of roots, package dependencies, companions and the companions’ dependencies.
A companion that contributes only integrations is recorded too, because OCX loaded it into the environment like any other package.
The patch tier’s freeze is independent of the package tier’s. resolution.patchSnapshot can be present while resolution.frozen is false, and the reverse. Each fact is carried by its own field.