Lifecycle of a run¶
This page traces a single ldtc run --config configs/profile_r0.yml
invocation from process startup to the last chmod on the manifest.
If you understand this page, you can read any audit log produced by
the harness without surprises.
Phase 0: process startup¶
| Step | Code |
|---|---|
Parse --config. |
_load_yaml |
Seed Python random and NumPy from seed, seed_py, seed_np. |
_set_seeds |
Resolve dt, window_sec, method, Mmin_db, partition hysteresis knobs, etc. |
run_baseline body |
Create artifacts/{audits,indicators,figures,keys} if absent. |
_ensure_dirs |
Open the audit log (artifacts/audits/audit.jsonl, append-only). |
AuditLog |
Append baseline_start and a run_header record (config path, seeds, thresholds, Δt, Ω kind). |
audit.append |
| Construct the plant adapter. | _make_adapter_from_profile |
Allocate SlidingWindow, PartitionManager, LREG, RefusalArbiter, ControllerPolicy, IndicatorExporter, IndicatorConfig. |
(constructors) |
Load or generate Ed25519 keys under artifacts/keys/. |
ensure_keys |
The run_header is the single most useful audit record for
reviewers: it captures every knob that affects the outcome.
Phase 1: the steady-state tick¶
The scheduler runs the tick closure in a daemon thread every Δt
seconds. One tick is roughly:
def tick(_now: float) -> None:
state = adapter.read_state()
predicted = lreg.latest().M_db if lreg.latest() else 0.0
action = policy.compute(state, predicted_M_db=predicted, risky_cmd=risky_cmd)
adapter.write_actuators(action)
sw.append(adapter.read_state())
if sw.ready():
X = np.asarray(sw.get_matrix())
part = pm.get()
res = estimate_L(X, part.C, part.Ex, method=..., p=..., n_boot=...)
diag = stationarity_checks(X); vratio = var_nt_ratio(...)
audit.append("window_diagnostics", diag)
M = m_db(res.L_loop, res.L_ex)
nc1 = M >= Mmin
if invalid_by_ci(res.ci_loop, res.ci_ex, cfg_smell):
lreg.invalidate("ci_inflation"); audit.append("run_invalidated", ...)
lreg.write(LEntry(L_loop=res.L_loop, L_ex=res.L_ex, M_db=M, nc1_pass=nc1, ...))
audit.append("window_measured", {"M_db": M, "nc1": nc1, ...})
if window_idx % part_growth_cadence_windows == 0:
cand = greedy_suggest_C(X, part.C, lambda_=..., theta=..., kappa=...)
pm.maybe_regrow(cand, delta_M_db=...,
delta_M_min_db=part_delta_M_min_db,
consecutive_required=part_consecutive_required)
derived = lreg.derive()
exporter.maybe_export(priv, audit, derived, icfg,
last_sc1_pass=...) # rate-limited to 2 Hz
Important properties:
lreg.latest()andlreg.derive()are the only exits from the enclave. Code paths that need raw𝓛for an internal decision (for example the trough tracker inomega_power_sag) read it inside the same tick and never persist it.- Every smell-test invalidation appends both a
run_invalidatedrecord and sets a flag insideLREG, so the next exported indicator carriesinvalidated = true. The audit log and the signed indicator agree by construction. - The
IndicatorExporteris rate-limited (default 2 Hz) so the signed-indicator stream stays bounded regardless ofΔt.
Phase 2: optional Ω window¶
For the omega-* subcommands the loop above runs first as a
"baseline" phase, then transitions through three phases tracked by
a phase variable:
While in the omega phase:
- The CLI calls
adapter.apply_omega(name, **kwargs), which delegates to the relevant module inldtc.omega(for exampleomega.power_sag.apply). - An
omega_eventrecord is appended to the audit log with the kind, parameters, and start/stop timestamps. - The partition manager is frozen so SC1 cannot be gamed by
reshuffling
(C, Ex). - Estimators continue to fire each window. The minimum
𝓛_loopobserved during the window becomesL_loop_trough.
When the Ω window ends:
- The partition is unfrozen.
- The CLI watches for the first window that satisfies the SC1
recovery gate (
𝓛_loop ≥ baseline · (1 − ε)), holding forsustained_required_windowsconsecutive windows. The elapsed time becomesτ_rec. sc1_evaluateis called with(L_loop_baseline, L_loop_trough, L_loop_recovered, M_post, ε, τ_rec, Mmin, τ_max); the boolean decision becomes the nextSC1indicator bit.- An
sc1_evaluatedaudit record is appended with the stats dict (delta,tau_rec,M_post,passed).
Command-conflict trials add a fifth ingredient: the CLI issues a
risky command (typically hard_shutdown), the
RefusalArbiter refuses
when M < Mmin, and T_refuse is measured from the audit
timestamps.
Phase 3: scheduler stop and post-run checks¶
The CLI sleeps for the requested run length (baseline_sec,
default 10 s) and then enters the finally block:
| Step | Code |
|---|---|
| Stop the scheduler and capture jitter stats. | sch.stop() |
Append baseline_stop (or omega_*_stop) with ticks. |
audit.append |
If p95(|jitter|) / Δt > jitter_p95_rel_max, append dt_jitter_excess. |
SmellConfig |
| Re-validate the audit chain on disk. | audit_chain_broken |
Re-scan the audit for raw 𝓛 leakage. |
audit_contains_raw_lreg_values |
| Print the invalidation footer. | _print_invalidation_footer |
Either smell test failing here will append a final
run_invalidated record. Subsequent exporters and reporters
respect that flag.
Phase 4: artifact bundle¶
Once the audit is closed, the CLI calls
reporting.artifacts.bundle,
which:
- Reads the audit log start to end.
- Renders the timeline figure
(
render_paper_timeline) in PNG and SVG: normalized𝓛_loopand𝓛_ex,M (dB)with anMminrule, anΩshaded band, and a tick rug for audit events. The footer carries the profile and the audit hash head. - Renders the SC1 summary table
(
write_sc1_table) if at least onesc1_evaluatedrecord exists. - Writes the manifest JSON (profile id, thresholds, audit head, public-key SHA-256, list of artifacts).
- Sets POSIX read-only permissions on every produced file (the
chmodis performed inline bybundle). - Appends a
report_generatedaudit record with the basenames.
Finally the CLI prints a one-line summary:
That is the entire lifecycle. Re-running the same command with the
same seeds (and make clean-artifacts) produces a byte-identical
audit log up to wall-clock timestamps, and bit-identical signed
indicators conditional on those timestamps.
What you can rely on¶
- Audit precedes signing. Every fact in a signed indicator appeared in the audit first. A mismatch is a smell test failure.
- Indicators are rate-limited. A 1 kHz
Δtdoes not produce a 1 kHz indicator stream; the exporter is2 Hzby default. - Files become read-only. The bundle's
chmodstep makes it hard to silently mutate a manifest after the fact. (On Windows this is a best-effort no-op.) - The audit chain is verified twice: once in process and once by re-reading the file from disk. A torn write or a partial flush still gets caught.
Next steps¶
- Architecture: the static module map.
- Indicators: the wire format.
- Guardrails: the per-test thresholds.
ldtc.cliAPI: every CLI subcommand by name.