pointlock_runner/lib.rs
1//! # pointlock-runner
2//!
3//! The execution face of Pointlock: the step state machine over the
4//! control-flow vocabulary, verdict folding, checkpoint-driven resume with
5//! repair alignment, and (next wave) the handler engine. Concrete
6//! providers are injected by the assembly layer (`pointlock-cli`); the
7//! entry points accept only [`FlowIR`], never strings (principles 1/2).
8//!
9//! Authoritative design documents:
10//! - `docs/design/00-architecture-spine.md` §5 (error taxonomy and default
11//! dispositions), §6 (RunLog vocabulary, step state machine, verdict
12//! folding, checkpoint model, resume semantics)
13//! - `docs/design/07-subflow-checkpoint-resume-repair.md` §1 (subflow
14//! call-by-value contract and scope isolation), §4 (resume — including
15//! the frame-precise fallback of §4.6) and §5 (alignment / offline
16//! re-judge / the `requiresConfirmation` gate)
17//!
18//! ## M2 scope (iron rule: shapes final, content narrowed, nothing silent)
19//!
20//! Executing since M2: `call` steps (call-by-value inbound/outbound schema
21//! gates, `callFramePushed`/`Popped` frame events, call-step verdict =
22//! callee flow verdict), `if` (strict-boolean cond; unselected branches
23//! leave skipped span pairs), `foreach` (positional iteration frames,
24//! `iter.<as>` scope), `let` (SSA `vars.*`), `assert` steps
25//! (`observe: "fresh"` / `{ fromStep, which }`), and `preflight` probes
26//! (probing phase before acting; failure → the step's `onResumeDrift`
27//! ladder — repair subflow then re-probe, or escalate to a `repairWorld`
28//! human — and only an exhausted ladder blocks
29//! ([`RunOutcome::Blocked`])). The subflow registry travels through
30//! [`RunOptions::subflows`] / [`Runner::resume_with_subflows`] and
31//! self-verifies at load (`maxCallDepth = 8`, 07 §1.3).
32//!
33//! Since the human wave (M2-W2a): `human` steps execute with the unified
34//! wait-as-suspension semantics — `humanRequested` (fsynced) →
35//! `runSuspended` → [`RunOutcome::AwaitingHuman`]; the runner never blocks
36//! waiting (attached-TTY inline collection is the CLI layer). Responses
37//! are arbitrated by the store single writer
38//! (`Store::submit_human_response`, first response wins) and settled on
39//! resume through the four-mode verdict/output mapping (06 §2.2); expired
40//! deadlines settle lazily to `unknown`, a pure function of `deadlineAtMs`
41//! and response absence. R13 supervision
42//! ([`RunOptions::supervise`]/[`ResumeOptions::supervise`], per segment,
43//! never inherited) gates action-step dispatch strictly before the
44//! `actionIntent` WAL: `proceed` dispatches, `abort` ends the run without
45//! consulting any handler, `suspend` keeps the request pending across
46//! segments.
47//!
48//! Narrowed content, each refusal typed (never skipped silently):
49//! - Handlers execute (retry / continue / escalate / abort / repair, flow-
50//! and step-level, trigger-budgeted); the RE-INVOCATION dispositions on
51//! call/human hosts — 07 §1's attempt-framed full re-call and the
52//! fresh-request re-ask — remain typed refusals.
53//! - Cross-IR resume classifies the whole 07 §5.2 nested vocabulary
54//! (`if` branch bodies, `foreach` rounds, the call down-drill of case
55//! (a), the case (b) frame teardown, order consistency); what remains
56//! refused is a resume across a LIVE hook frame or a pending handler
57//! escalation — hook-aware frame re-entry. Same-IR resume is
58//! frame-precise at any depth (07 §4.6).
59//! - Evidence-localization failures during `observing` (fetch unsupported,
60//! stream rupture, integrity mismatch, `ui.snapshot.get` errors) never
61//! abort the run: the observation record keeps the field absent and the
62//! dependent verify channel receives a typed gap → honest `unknown`
63//! (principle 4).
64//!
65//! ## Threading
66//!
67//! The store is a synchronous single writer (`Connection` is `!Sync`);
68//! the runner keeps all store use on the calling task. Async exists only
69//! because the Provider SPI is async — a current-thread runtime (or
70//! `block_on`) is sufficient.
71
72mod align;
73mod engine;
74mod error;
75mod judge;
76mod load;
77mod observe_eval;
78mod runner;
79mod scope;
80
81pub use engine::RunOutcome;
82pub use error::{BlockedReason, RunnerError};
83pub use runner::{ResumeOptions, RunOptions, Runner};
84
85// Re-exported for signature convenience (the entry points consume these).
86pub use pointlock_ir::FlowIR;
87pub use pointlock_provider_kit::CancellationToken;