1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//! # agentplane
//!
//! A durable, replayable, policy-governed runtime for agents whose steps invoke
//! non-deterministic models and mutate real systems.
//!
//! One sentence carries the rest:
//!
//! > **The journal is the plan of record.** Orchestration is deterministic and
//! > replayable; every non-deterministic act — inference, tool call, clock, RNG,
//! > deadline resolution — is an [`Effect`](core::Effect) performed *at most
//! > once*, journaled, and read back on replay.
//!
//! ## The determinism boundary
//!
//! ```text
//! ┌──────────────── DETERMINISTIC ZONE ────────────────┐
//! │ plan traversal · guards · retry decisions · budget │
//! │ policy evaluation · label joins · record upcasting │
//! │ │
//! │ Replay re-executes this and MUST reproduce the │
//! │ identical sequence of effect keys. │
//! └───────────────────────┬─────────────────────────────┘
//! │ cx.effect(…)
//! ┌───────────────────────▼─────────────────────────────┐
//! │ NON-DETERMINISTIC ZONE │
//! │ inference · tools · clock · RNG · network · humans │
//! │ │
//! │ Executed at most once. Journaled. Replay reads. │
//! └──────────────────────────────────────────────────────┘
//! ```
//!
//! Three layers enforce it, because convention is not enforcement:
//!
//! 1. **Lint gating** — `clippy.toml` denies `SystemTime::now`, `rand::random`,
//! `Ulid::new` and friends crate-wide.
//! 2. **Effect-key verification** — on replay, a recomputed key that differs
//! from the journaled one quarantines the run rather than diverging silently
//! ([`core::StepError::NonDeterminism`]).
//! 3. **Storage constraints** — the journal's unique index makes "an effect is
//! started at most once per run" a database invariant, not a code path.
//!
//! ## Example
//!
//! ```no_run
//! use agentplane::core::{Outcome, Skill, SkillDescriptor, Tainted};
//! use agentplane::journal::JournalStore;
//! use agentplane::runtime::{Mode, Runtime, StepCtx};
//! use std::sync::Arc;
//!
//! #[derive(Debug)]
//! struct Greet;
//!
//! #[async_trait::async_trait]
//! impl Skill for Greet {
//! fn descriptor(&self) -> SkillDescriptor {
//! SkillDescriptor::new("greet").provides("demo.greet")
//! }
//!
//! async fn invoke(
//! &self,
//! cx: &mut StepCtx<'_>,
//! input: Tainted<serde_json::Value>,
//! ) -> Result<Outcome, agentplane::core::SkillError> {
//! // `now()` is a journaled effect: on replay it returns the recorded
//! // instant rather than reading the clock again.
//! let at = cx.now().await?;
//! Ok(Outcome::done(input.map(|v| serde_json::json!({
//! "greeted": v, "at": at.to_string(),
//! }))))
//! }
//! }
//!
//! # async fn run(store: Arc<dyn JournalStore>) -> Result<(), Box<dyn std::error::Error>> {
//! // With the default features, `agentplane::store::RedbStore` is one.
//! let runtime = Runtime::builder(store).skill(Greet).build();
//! let outcome = runtime.run("greet", Tainted::trusted(serde_json::json!({"name": "world"}))).await?;
//!
//! // Replaying re-executes the deterministic zone and reads every effect back
//! // from the journal. No clock is read; no tool is called twice. `Strict`
//! // additionally fails if this build wants an effect the journal lacks.
//! runtime.replay(outcome.run_id, Mode::Strict).await?;
//! # Ok(())
//! # }
//! ```
/// The random-number traits [`StepCtx::rng`] hands back.
///
/// Re-exported for the reason [`prelude::async_trait`] is: the generator is
/// unusable without its traits in scope, and reaching them through a `rand` of
/// one's own means matching this crate's version in a second manifest — where
/// getting it wrong is a type error naming two identical-looking traits.
///
/// [`StepCtx::rng`]: crate::runtime::StepCtx::rng
pub use rand;
// A backend feature must deliver its backend.
//
// `store` was declared under `redb` alone while holding both backends, so
// `--no-default-features --features postgres` compiled cleanly, pulled in
// `tokio-postgres` and `deadpool-postgres`, and exposed *no store module at
// all* — the Postgres deployment paid for three dependency crates and could not
// name `PostgresStore`. `just features` reported success throughout, because
// building a feature and reaching what it names are different questions and it
// only ever asked the first.
//
// These make the compiler ask the second. Naming the type is what a consumer
// does, so a gate that configures it out fails here rather than in their editor.
const _: fn = ;
const _: fn = ;
// The same question, asked of the embedding drivers, because they failed it the
// same way. `model::embeddings` was gated on `providers` while holding
// `BedrockEmbedder` — so `--features bedrock` bought the AWS SDK, documented
// Titan and Cohere embeddings, and exposed no embedder at all. Semantic
// retrieval was unavailable to exactly the deployments that chose Bedrock
// because their data may not leave one account, which is the population the
// driver exists for.
const _: fn = ;
const _: fn = ;
/// Embed a directory of single-agent manifests, keyed by declared name.
///
/// One `include_str!` per path, handed to [`Manifest::parse_each`] with the path
/// literal as the origin for diagnostics. The result is
/// `Result<BTreeMap<String, Manifest>, ManifestError>` keyed by each document's
/// own `metadata.name`.
///
/// ```ignore
/// let agents = agentplane::manifests![
/// "agents/obligation-watch.yaml",
/// "agents/clearing-triage.yaml",
/// ]?;
/// let watch = &agents["obligation-watch"];
/// ```
///
/// # Why this exists rather than a hand-written table
///
/// The obvious form is `&[(&str, &str)]` with a name typed beside each path.
/// The name is **already in the document**, so that table is one fact written
/// twice with nothing checking that the two agree — and a file included under
/// two constants, which is what happens while adding the next agent, builds and
/// runs with one agent registered twice and another silently absent. Here the
/// key comes from the document and a duplicate name is a compile-time-embedded,
/// run-time-refused error naming both paths.
///
/// Paths are relative to the invoking file, exactly as `include_str!` resolves
/// them, and each is recorded in the diagnostic for the document it failed on.
/// There is no glob: a macro that expanded a directory listing would make the
/// set of agents a plane runs depend on what is on disk at build time rather
/// than on what is in the source a reviewer reads.
///
/// [`Manifest::parse_each`]: crate::manifest::Manifest::parse_each
pub use crate;
pub use crate;
pub use crate;
/// The names every program needs, so the first one is one `use` line.
///
/// ```
/// use agentplane::prelude::*;
/// ```
///
/// # What is in here, and the rule
///
/// A prelude earns its place by being *predictable*, so this one is chosen by a
/// stated rule rather than by taste: **a name belongs here if a program that
/// does nothing unusual needs it.** Measured, not guessed — every name below
/// appears in a third or more of the crate's own examples, and the set is
/// exactly what the getting-started program imports, which is why that program
/// now opens with one line instead of five.
///
/// The four groups are the four things any program touches: the skill you write
/// (`Skill`, `SkillDescriptor`, `SkillError`, `Outcome`), the context it is
/// handed (`StepCtx`), the labels its data carries (`Tainted`, `Trust`,
/// `Sensitivity`), and the plane that runs it (`Runtime`, `RunStatus`, `Mode`,
/// `JournalStore`, and the default store).
///
/// # What is deliberately left out
///
/// Names that are common in this crate but collision-prone in somebody else's:
/// `Record`, `Digest`, `Label`, `Capability`, `Seq`. A prelude that shadows a
/// user's own `Record` costs more than the import it saved, and each of those is
/// one explicit `use` away. Anything feature-gated beyond the default backend is
/// out for the same reason a glob would be: what a prelude imports must not
/// depend on which features happen to be on, or the same `use` line means
/// different things in two crates.
///
/// Everything here is also reachable by its full path; the prelude adds no API.