Skip to main content
Version: 0.1.0-alpha.4 (alpha)

Owned selected test processes

The selected OCI registry and Angular renderer/node runners share a Python ownership contract. This is test tooling, not a runtime service, a cross-language daemon, or a requirement to install Docker for LSF. Only the explicitly selected OCI fixture needs a prepared Docker image. Ordinary in-process correctness tests do not run provider, browser, filesystem, or process-accounting preflight.

Existing owners and migration boundary​

LayerExisting ownerBoundary after this change
Python prepared Rust teststools/ci_rust_artifacts.pyPreserves exact Cargo identity/list/result validation; delegates subprocess ownership to owned_test_process.py.
Python registrytools/run_oci_registry_tests.pyMigrated CLI: shared preflight, private root, total deadline, identity-bound TLS readiness, immutable-ID removal, structured diagnostics. Imported fixture helpers remain compatible with unmigrated callers.
Python renderer/nodetools/run_angular_renderer_tests.pyMigrated: explicit prepared components and Cargo inventory; exact registered ignored cases; descendant retirement on listing and execution.
Python other providersrun_s3_blob_tests.py, run_vault_secret_tests.py, run_nats_event_tests.py, nats_test_support.pyTheir existing provider/startup owners remain. They are not silently claimed as migrated.
Python operator/resource workflowsphase2_operator_process.py (Process, Client), phase2_gate_resource_os.py (Probe), phase2_gate_resource_run.py (BoundedClient, stop)Retain workflow-specific evidence/accounting and shutdown semantics; no measurement or security-policy relaxation.
Rustcrates/latent-testkit/src/process.rs, process/owned.rs, process/tests.rsPreserve ProcessHarness, Tokio OwnedProcess, bounded capture and wait/terminate tests. These own a direct child; a retained stdout line or Drop is not proof of current readiness or reaped descendants. Rust suite callers needing the stronger descendant boundary can run under the Python owner.

owned_process_worker.py is an isolated, per-command Linux subreaper. Its anonymous, close-on-exec status pipe carries a fresh nonce and is separate from untrusted stdout/stderr. It reports the target PID, observed exit/signal, timing, and completion of descendant retirement. Output EOF alone never ends ownership. The worker reads only its own /proc/self/task/<self>/children list, kills direct unreaped children, and adopts/reaps orphaned descendants (including setsid and double-fork escapes). ECHILD, not an empty host process survey, proves retirement. The parent keeps its worker unreaped until the final process-group kill decision, so a reused PID cannot select an unrelated process.

The declared platform is native linux-x86_64. Restricted Linux without the owned-child accounting interface is unavailable, not a passing substitute. No host-wide /proc enumeration, fabricated zero I/O, privilege escalation, security-policy edits, system-wide subreaper, or blanket container cleanup is used. This is lifecycle supervision for trusted tests, not a security sandbox against malicious fork bombs or an unresponsive kernel. Unconfirmed cleanup fails the run.

Inventory and explicit preparation​

processContracts in the existing tools/ci/suites.json extends #427's inventory; its suite IDs, registered ignored cases, package/target/source identity, platforms and recipe names remain the authority. test_run.contract() uses #427's loader; it does not implement a competing suite classifier. The dependency files are the #427 snapshots, with only the process-contract extension to the JSON catalogue.

Each selected contract declares tools, prepared service image identities, filesystem operations, owned accounting access, artifact roles and supported version scopes. Python's exact baseline comparison reuses tools/check_tool_versions.py / tools/toolchain.toml (#338); no second version comparator is introduced. Unknown version scopes fail rather than guessing an unimplemented #338 command-line interface. Rust/toolchain setup remains pinned by the build workflow.

The prepared boundary composes ci_rust_artifacts.read_inventory() and cargo_environment(): it consumes explicit successful Cargo JSON, never scans target, picks the newest cached executable, calls Cargo, or builds on demand. This is the existing boundary being extended by #428, not a new artifact service. The renderer preserves explicitly configured CARGO_TARGET_DIR. Required target kind, source, manifest and executable ownership are validated; duplicate, foreign, malformed or unsuccessful build inventories fail. Each executable and fixture is bounded and hashed, and an executable replacement between discovery and execution is rejected. Source revision and dirty state are observed locally; these diagnostics are not a provenance attestation or proof that a cached binary was built from a claimed revision. Build inventories must be generated by the current job on its checked-out source, as in CI, not reused across checkouts.

For the registry, prepare the pinned image explicitly (its digest is in the inventory), then run environment-only preflight before Cargo compilation:

python3 tools/run_oci_registry_tests.py --preflight
cargo test -p latent-oci --test registry --locked --no-run \
--message-format=json,json-render-diagnostics > /tmp/lsf-oci-tests.jsonl
python3 tools/run_oci_registry_tests.py --test-manifest /tmp/lsf-oci-tests.jsonl

Web admission uses its own latent-policy --lib Cargo inventory and --web-admission-component; observed capsule provenance retains the explicit --provenance-input directory. These are separate selections; missing preparation is never replaced with compilation or a skipped successful test.

The renderer uses the already prepared workspace inventory and public/private composed components:

python3 tools/run_angular_renderer_tests.py --preflight
# Prepare the workspace harnesses and the existing Angular fixture build.
python3 tools/run_angular_renderer_tests.py \
--test-manifest /tmp/lsf-workspace-tests.jsonl \
--component examples/renderer-profile/dist/runtime/application.wasm

Before execution, listing must match the full registered ignored set for the selected target. Each requested case then runs once with --exact --ignored and one test thread. A missing case, duplicate target or zero-test success is failure. The recorded reproduction selection includes exact case names, recipe, source identity and fixture/binary digests. No shell commands, credentials or private artifact paths are captured in that selection.

Deadline, readiness and cleanup​

Each TestRun gets a private mode-0700 root and one monotonic total budget, including source/prerequisite checks, startup, discovery, execution and teardown. A main-thread watchdog also bounds Python/HTTP stages, not just child waits. Cleanup reserves budget inside the original deadline; it does not receive a fresh full test timeout. Combined stdout/stderr is bounded while streaming, not only after process exit. Cancelled async waiters join retirement before returning.

OCI retains dynamic IPv4-loopback ports, pinned images, resource limits, read-only fixture mounts and ephemeral credentials. --pull=never prevents an implicit fetch during execution. Readiness checks the current immutable container ID, this run's label, running state, exact mapped endpoint, TLS CA and distribution v2 response. It checks liveness again after the response. Only finite read-only polling is allowed: no mutation retries or scenario reruns. Removal uses the verified immutable ID and confirms absence before deleting its protected recovery state. Replaced or foreign state is preserved and reported, never overwritten.

Diagnostics, outcomes and retained examples​

--diagnostic-root controls where exclusive mode-0600 JSON records are written; the default is target/test-diagnostics. Each record has schema latent.test-run.v1, suite/stage/run identity, observed source and fixture IDs, child exit/signal and cleanup acknowledgement, elapsed/stage/startup/teardown times, safe exact reproduction selection, cleanup failures and a redacted bounded tail. Redaction runs before tail truncation and includes known secret environment values, credentials, authorization/cookies, private keys and private paths. Diagnostics never include full environments, raw command arguments or fixture private keys. The compact records compose with #426's timing collection.

Unavailable environment produces outcome: not-run, invalid fixture/assertion/ timeout produces outcome: failed, and either exits nonzero for selected CI. Environment-only success is preflight-passed, not test coverage. Records are explicitly runner-diagnostic-not-qualification; synthetic process demonstrations are marked synthetic-process-contract, never Wasmtime/provider qualification.

CI retains attempted renderer/provider diagnostics, including fault examples, for seven days. Outcome guards prevent pretending an unselected job attempted to produce evidence. Actual renderer gates remain real Wasmtime/Angular tests; an additional injected after-discovery failure is independently checked by check_owned_diagnostics.py for the expected reason and confirmed retirement. It never turns a failing normal scenario green. Live OCI normal/fault fixture checks verify TLS readiness and removal; they are fixture checks, not OCI package qualification. Normal OCI package and web round trips remain separate real tests.

LSF_REQUIRE_NATIVE_PROCESS_TESTS=1 python3 -m unittest \
tools.tests.test_owned_test_process tools.tests.test_owned_runners \
tools.tests.test_ci_rust_artifacts tools.tests.test_oci_registry_runner
# After explicitly preparing the pinned image:
LSF_REQUIRE_NATIVE_PROCESS_TESTS=1 LSF_LIVE_OCI_DEMO=1 python3 -m unittest \
tools.tests.test_owned_runners.NativeRunnerDemonstrations

Deterministic tests cover missing prerequisites, denied owned accounting, protected files, false/stale/dead readiness, stalled readiness, crashes, inherited writers, session and double-fork escape, closed stdout with a live child, timeout, overflow, synchronous and async cancellation, cleanup failures, foreign recovery state, exact listings, zero-test rejection and diagnostic redaction. Native renderer demonstrations run the actual migrated Python runner over explicitly labelled synthetic libtests and verify orphaned writer PIDs are gone. Native coverage is required on CI; unsupported developer hosts skip it explicitly and are not counted as having exercised those guarantees.