wasm4pm-compat 26.6.29

Minimal paper-complete, feature-capped Rust process-evidence crate. Start with compatibility. Graduate to execution.
Documentation
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
//! PDDL8 canonical types for wasm4pm-compat.
//!
//! PDDL8 is the bounded subset of PDDL 3.1 that maps directly onto the
//! Prolog8 + POWL runtime:
//!
//! - Arity ≤ 8, body/precondition conjuncts ≤ 8, parameters ≤ 8
//! - Only STRIPS subset: conjunctive positive preconditions, add/delete effects
//! - Delete effects become epoch seals (immutable history), not silent erasure
//! - Plan depth ≤ 64
//!
//! These types are the canonical cross-crate representation. `bcinr-pddl`
//! parses PDDL 3.1 text (via the `pddl` crate) and lowers it into these
//! types. `wasm4pm-cognition` breeds (STRIPS, POP, HTN) map onto these via
//! `BreedInput` → `Pddl8Problem` conversion.
//!
//! # BRCE position
//! ```text
//! PDDL8Domain + Pddl8Problem  = G_F^B constructor (candidate-future grammar)
//! GroundAction.preconditions   = Prolog8 R ⊢ A query (may_fire)
//! GroundAction.add_effects     = facts loaded after firing
//! GroundAction.del_effects     = epoch advance (sealed, not erased)
//! ExecutionTrace               = OCEL 2.0 (wasm4pm-compat::ocel)
//! ```

use serde::{Deserialize, Serialize};

// ---------------------------------------------------------------------------
// Structural bounds
// ---------------------------------------------------------------------------

/// Maximum predicate arity in PDDL8.
pub const PDDL8_MAX_ARITY: usize = 8;
/// Maximum conjuncts in a precondition or effect list.
pub const PDDL8_MAX_CONJUNCTS: usize = 8;
/// Maximum action parameters (variables per schema).
pub const PDDL8_MAX_PARAMS: usize = 8;
/// Maximum plan depth in forward/BFS search.
pub const PDDL8_MAX_PLAN_DEPTH: usize = 64;
/// Default maximum ground action count (combinatorial explosion guard).
pub const PDDL8_MAX_GROUND: usize = 4096;

// ---------------------------------------------------------------------------
// Schema-level types (ungrounded)
// ---------------------------------------------------------------------------

/// A predicate atom that may contain variables (`?name`) or constants.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Pddl8Atom {
    pub pred: String,
    /// Each entry is `?varname` (variable) or a bare constant.
    pub args: Vec<String>,
}

impl Pddl8Atom {
    pub fn is_variable(arg: &str) -> bool {
        arg.starts_with('?')
    }

    pub fn arity(&self) -> usize {
        self.args.len()
    }
}

/// A precondition that must be satisfied for an action to fire.
/// Maps from praxis_core::Obligation::Precondition.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Precondition {
    /// Identifier for the predicate being checked.
    pub predicate_id: String,
    /// Hash of the parameters passed to the predicate.
    pub params_hash: Option<[u8; 32]>,
}

/// A PDDL8 action schema — bounded to PDDL8 caps.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8ActionSchema {
    pub name: String,
    /// Parameter names (all start with `?`).
    pub params: Vec<String>,
    /// Conjunctive precondition (positive atoms only in STRIPS8).
    pub preconditions: Vec<Pddl8Atom>,
    /// Atoms added to state after action fires.
    pub add_effects: Vec<Pddl8Atom>,
    /// Atoms removed (become epoch-sealed in BRCE execution model).
    pub del_effects: Vec<Pddl8Atom>,
    // PDDL 3.1 extended fields
    #[serde(default)]
    pub typed_params: Vec<(String, String)>,   // (?var, type) pairs
    #[serde(default)]
    pub condition: Option<PddlCondition>,       // full condition algebra
    #[serde(default)]
    pub effects: Vec<PddlEffect>,               // full effect algebra
    #[serde(default)]
    pub numeric_effects: Vec<NumericEffect>,
}

/// A PDDL8 domain: predicates + action schemas.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8Domain {
    pub name: String,
    /// `(predicate_name, arity)` pairs for all declared predicates.
    pub predicates: Vec<(String, u8)>,
    pub actions: Vec<Pddl8ActionSchema>,
    // PDDL 3.1 extended fields (additive, default to empty)
    #[serde(default)]
    pub types: Vec<PddlType>,
    #[serde(default)]
    pub functions: Vec<PddlFunction>,
    #[serde(default)]
    pub durative_actions: Vec<DurativeAction>,
    #[serde(default)]
    pub derived: Vec<DerivedPredicate>,
    #[serde(default)]
    pub constraints: Vec<PddlConstraint>,
    #[serde(default)]
    pub processes: Vec<PddlProcess>,
    #[serde(default)]
    pub events: Vec<PddlEvent>,
}

/// A PDDL8 problem: objects + initial state + goal.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8Problem {
    pub name: String,
    pub domain: String,
    pub objects: Vec<String>,
    /// Ground atoms true in S₀.
    pub init: Vec<Pddl8Atom>,
    /// Conjunctive goal — all must hold in Sₙ.
    pub goal: Vec<Pddl8Atom>,
    // PDDL 3.1 extended fields (additive, default to empty/None)
    /// `(object_name, type)` pairs — sibling of `objects`, used to restrict
    /// grounding to type-compatible bindings. Empty for untyped domains.
    #[serde(default)]
    pub object_types: Vec<(String, String)>,
    #[serde(default)]
    pub fn_values: Vec<(PddlFunction, f64)>,
    #[serde(default)]
    pub timed_inits: Vec<TimedLiteral>,
    #[serde(default)]
    pub preferences: Vec<PddlPreference>,
    #[serde(default)]
    pub metric: Option<Metric>,
}

// ---------------------------------------------------------------------------
// Ground-level types (after grounding = variable substitution)
// ---------------------------------------------------------------------------

/// A fully ground atom — all arguments are concrete constants.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct Pddl8GroundAtom {
    pub pred: String,
    pub args: Vec<String>,
}

impl Pddl8GroundAtom {
    pub fn label(&self) -> String {
        if self.args.is_empty() {
            self.pred.clone()
        } else {
            format!("{}({})", self.pred, self.args.join(","))
        }
    }
}

/// A fully ground action — ready for applicability testing and execution.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8GroundAction {
    pub schema_name: String,
    /// Human-readable: `schema_name(arg0,arg1,...)`
    pub label: String,
    pub preconditions: Vec<Pddl8GroundAtom>,
    pub add_effects: Vec<Pddl8GroundAtom>,
    /// Delete effects — become epoch seals in the BRCE execution model.
    pub del_effects: Vec<Pddl8GroundAtom>,
}

impl Pddl8GroundAction {
    /// Test applicability: all preconditions must be in the current state set.
    pub fn is_applicable<S>(&self, state: &S) -> bool
    where
        S: Contains<Pddl8GroundAtom>,
    {
        self.preconditions.iter().all(|p| state.contains_atom(p))
    }
}

/// Trait for testing atom membership — allows callers to use BTreeSet, HashSet, etc.
pub trait Contains<T> {
    fn contains_atom(&self, item: &T) -> bool;
}

impl Contains<Pddl8GroundAtom> for std::collections::BTreeSet<Pddl8GroundAtom> {
    fn contains_atom(&self, item: &Pddl8GroundAtom) -> bool {
        self.contains(item)
    }
}

impl Contains<Pddl8GroundAtom> for std::collections::HashSet<Pddl8GroundAtom> {
    fn contains_atom(&self, item: &Pddl8GroundAtom) -> bool {
        self.contains(item)
    }
}

// ---------------------------------------------------------------------------
// Tape projection (POWL geometry)
// ---------------------------------------------------------------------------

/// One slot on the PDDL8 execution tape.
///
/// Sequential ordering: `pred_mask = 1 << (index - 1)`.
/// Op 0 has no predecessors (`pred_mask = 0`).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8TapeOp {
    pub index: u8,
    pub label: String,
    /// Bitmask of ops that must complete before this one is eligible.
    pub pred_mask: u64,
    pub action: Pddl8GroundAction,
}

/// A PDDL8 tape — the POWL geometry for one candidate plan.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8Tape {
    pub ops: Vec<Pddl8TapeOp>,
}

impl Pddl8Tape {
    /// Project a ground plan (ordered list) into a sequential POWL tape.
    pub fn from_plan(plan: Vec<Pddl8GroundAction>) -> Self {
        let ops = plan
            .into_iter()
            .enumerate()
            .map(|(i, action)| Pddl8TapeOp {
                index: i as u8,
                label: action.label.clone(),
                pred_mask: if i == 0 { 0 } else { 1u64 << (i - 1) },
                action,
            })
            .collect();
        Self { ops }
    }

    pub fn len(&self) -> usize {
        self.ops.len()
    }

    pub fn is_empty(&self) -> bool {
        self.ops.is_empty()
    }
}

// ---------------------------------------------------------------------------
// Execution result types
// ---------------------------------------------------------------------------

/// Outcome of a single tape op execution with Prolog8 admission.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8StepResult {
    pub op_index: u8,
    pub label: String,
    pub admitted: bool,
    pub epoch_after: u64,
    /// BLAKE3 hex of the cumulative receipt chain up to this step.
    pub receipt_hash: String,
}

/// Full execution log for a PDDL8 plan run.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8ExecutionLog {
    pub steps: Vec<Pddl8StepResult>,
    pub goal_reached: bool,
    /// Cumulative BLAKE3 chain hash over all step receipts + goal outcome.
    pub chain_hash: String,
}

/// Replayable BLAKE3 receipt for one PDDL8 plan execution.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl8ExecutionReceipt {
    /// BLAKE3 over ordered op labels (plan identity).
    pub plan_root: String,
    /// BLAKE3 over initial state atoms (S₀ identity).
    pub state_root: String,
    /// BLAKE3 over goal atoms (goal identity).
    pub goal_root: String,
    /// Cumulative chain hash from `Pddl8ExecutionLog`.
    pub chain_hash: String,
    pub goal_reached: bool,
    pub step_count: usize,
}

// ---------------------------------------------------------------------------
// Conversion from wasm4pm-cognition BreedInput shapes
// ---------------------------------------------------------------------------

/// Convert a wasm4pm-cognition `Rule` (premise/conclusion/id) into a
/// `Pddl8ActionSchema`. This is the bridge from the Old-AI breed inputs
/// (STRIPS, HTN, POP) into canonical PDDL8 types.
///
/// Convention (from strips.rs / htn_planning.rs):
/// - `rule.id`        → `schema.name`
/// - `rule.premise`   → `schema.preconditions` (each as `pred=val` split)
/// - `rule.conclusion` → semicolon-list of `add1;add2;!del1` effects
pub fn schema_from_rule(
    rule_id: &str,
    premises: &[String],
    conclusion: &str,
) -> Pddl8ActionSchema {
    let preconditions = premises
        .iter()
        .map(|p| atom_from_pred_eq(p))
        .collect();

    let mut add_effects = Vec::new();
    let mut del_effects = Vec::new();
    for tok in conclusion.split(';').map(str::trim).filter(|s| !s.is_empty()) {
        if let Some(rest) = tok.strip_prefix('!') {
            del_effects.push(atom_from_pred_eq(rest));
        } else {
            add_effects.push(atom_from_pred_eq(tok));
        }
    }

    Pddl8ActionSchema {
        name: rule_id.to_string(),
        params: vec![],
        preconditions,
        add_effects,
        del_effects,
        typed_params: vec![],
        condition: None,
        effects: vec![],
        numeric_effects: vec![],
    }
}

fn atom_from_pred_eq(s: &str) -> Pddl8Atom {
    // `predicate=value` → Pddl8Atom { pred: "predicate", args: ["value"] }
    if let Some((p, v)) = s.split_once('=') {
        Pddl8Atom { pred: p.to_string(), args: vec![v.to_string()] }
    } else {
        Pddl8Atom { pred: s.to_string(), args: vec![] }
    }
}

// ─────────────────────────────────────────────────────────────────────────────
// PDDL 3.1 Extended Types
// These are additive — Pddl8* types above are preserved for backwards compat.
// ─────────────────────────────────────────────────────────────────────────────

// --- Type hierarchy -----------------------------------------------------------

/// A PDDL type declaration: (vehicle) or (truck - vehicle)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct PddlType {
    pub name: String,
    pub parent: Option<String>,
}

// --- Condition algebra --------------------------------------------------------

/// Full PDDL 3.1 condition (superset of Pddl8Atom conjunction).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PddlCondition {
    Atom(Pddl8Atom),
    Not(Box<PddlCondition>),
    And(Vec<PddlCondition>),
    Or(Vec<PddlCondition>),
    Forall { vars: Vec<(String, String)>, body: Box<PddlCondition> },
    Exists { vars: Vec<(String, String)>, body: Box<PddlCondition> },
    Imply(Box<PddlCondition>, Box<PddlCondition>),
    /// (at start cond) | (over all cond) | (at end cond) inside durative conditions
    Timed(TimeSpecifier, Box<PddlCondition>),
    /// Numeric fluent comparison, e.g. `(>= (available-workers) 1)`.
    Compare(NumericExpr, CompareOp, NumericExpr),
}

/// Comparison operator for a numeric fluent precondition.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CompareOp { Ge, Le, Gt, Lt, Eq }

impl Default for PddlCondition {
    fn default() -> Self { PddlCondition::And(vec![]) }
}

// --- Numeric fluents ----------------------------------------------------------

/// A function symbol (fuel-level ?v) or (total-cost)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PddlFunction {
    pub name: String,
    pub params: Vec<String>,
}

/// Arithmetic expression over numeric fluents.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum NumericExpr {
    Number(f64),
    FunctionTerm(String, Vec<String>),   // (fuel-level ?v)
    BinOp { op: NumericOp, lhs: Box<NumericExpr>, rhs: Box<NumericExpr> },
    Neg(Box<NumericExpr>),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NumericOp { Add, Sub, Mul, Div }

/// Effect that modifies a numeric fluent.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum NumericEffect {
    Assign(PddlFunction, NumericExpr),
    Increase(PddlFunction, NumericExpr),
    Decrease(PddlFunction, NumericExpr),
    ScaleUp(PddlFunction, NumericExpr),
    ScaleDown(PddlFunction, NumericExpr),
}

// --- Effect algebra -----------------------------------------------------------

/// A general PDDL effect (propositional, numeric, conditional, or timed).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PddlEffect {
    Add(Pddl8Atom),
    Del(Pddl8Atom),
    Numeric(NumericEffect),
    /// at start / at end wrapper
    Timed(TimeSpecifier, Box<PddlEffect>),
    Forall { vars: Vec<(String, String)>, effects: Vec<PddlEffect> },
    When { condition: PddlCondition, effects: Vec<PddlEffect> },
}

// --- Temporal PDDL 2.1 -------------------------------------------------------

/// Temporal qualifier for durative action conditions/effects.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TimeSpecifier { AtStart, AtEnd, OverAll }

/// Duration constraint: (= ?duration X), (<= ...), (>= ...), (and ...)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum DurationConstraint {
    Eq(NumericExpr),
    Lte(NumericExpr),
    Gte(NumericExpr),
    And(Vec<DurationConstraint>),
}

/// A PDDL 2.1 durative action.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DurativeAction {
    pub name: String,
    /// (var_name, type_name) pairs
    pub params: Vec<(String, String)>,
    pub duration: DurationConstraint,
    /// Conditions tagged with at-start / over-all / at-end
    pub conditions: Vec<PddlCondition>,
    /// Effects tagged with at-start / at-end (using PddlEffect::Timed)
    pub effects: Vec<PddlEffect>,
}

/// A timed initial literal: (at <time> <literal>) in the problem :init.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimedLiteral {
    pub time: f64,
    pub atom: Pddl8Atom,
    pub negated: bool,
}

// --- Metric ------------------------------------------------------------------

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum MetricExpr {
    Number(f64),
    FunctionTerm(String, Vec<String>),
    TotalTime,
    IsViolated(String),
    BinOp { op: NumericOp, lhs: Box<MetricExpr>, rhs: Box<MetricExpr> },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MetricDir { Minimize, Maximize }

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Metric { pub dir: MetricDir, pub expr: MetricExpr }

// --- PDDL 3.x constraints and preferences ------------------------------------

/// PDDL 3.x trajectory constraint (modal operator over plan trajectory).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TrajectoryConstraint {
    Always(Box<PddlCondition>),
    Sometime(Box<PddlCondition>),
    Within(f64, Box<PddlCondition>),
    AtMostOnce(Box<PddlCondition>),
    SometimeBefore(Box<PddlCondition>, Box<PddlCondition>),
    SometimeAfter(Box<PddlCondition>, Box<PddlCondition>),
    AlwaysWithin(f64, Box<PddlCondition>, Box<PddlCondition>),
    HoldDuring(f64, f64, Box<PddlCondition>),
    HoldAfter(f64, Box<PddlCondition>),
    And(Vec<TrajectoryConstraint>),
}

/// A named or anonymous PDDL 3.x preference.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PddlPreference {
    pub name: Option<String>,
    pub constraint: TrajectoryConstraint,
}

/// A domain-level constraint (possibly named).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PddlConstraint {
    pub name: Option<String>,
    pub constraint: TrajectoryConstraint,
}

// --- Derived predicates ------------------------------------------------------

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DerivedPredicate {
    pub head: Pddl8Atom,
    pub body: PddlCondition,
}

// --- PDDL+ processes and events ----------------------------------------------

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PddlProcess {
    pub name: String,
    pub params: Vec<(String, String)>,
    pub precondition: PddlCondition,
    pub effects: Vec<NumericEffect>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PddlEvent {
    pub name: String,
    pub params: Vec<(String, String)>,
    pub precondition: PddlCondition,
    pub effects: Vec<PddlEffect>,
}

// --- Extended domain and problem types ---------------------------------------

/// Extended PDDL 3.1 domain (includes all PDDL 3.1 features).
/// Pddl8Domain is retained for STRIPS-only compatibility.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Pddl31Domain {
    pub name: String,
    /// (:requirements ...) flags
    pub requirements: Vec<String>,
    /// (:types ...)
    pub types: Vec<PddlType>,
    /// (:predicates ...) — name + typed param list
    pub predicates: Vec<(String, Vec<(String, String)>)>,
    /// (:functions ...)
    pub functions: Vec<PddlFunction>,
    /// (:action ...) — classical PDDL actions (with full condition algebra)
    pub actions: Vec<Pddl31Action>,
    /// (:durative-action ...)
    pub durative_actions: Vec<DurativeAction>,
    /// (:derived ...)
    pub derived: Vec<DerivedPredicate>,
    /// (:constraints ...)
    pub constraints: Vec<PddlConstraint>,
    /// (:process ...) — PDDL+
    pub processes: Vec<PddlProcess>,
    /// (:event ...) — PDDL+
    pub events: Vec<PddlEvent>,
}

/// A full PDDL 3.1 action schema (richer than Pddl8ActionSchema).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pddl31Action {
    pub name: String,
    /// (var_name, type_name) pairs
    pub params: Vec<(String, String)>,
    pub precondition: PddlCondition,
    pub effect: Vec<PddlEffect>,
}

/// Extended PDDL 3.1 problem.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Pddl31Problem {
    pub name: String,
    pub domain: String,
    /// (:objects ...) — (name, type) pairs
    pub objects: Vec<(String, String)>,
    /// (:init ...) — propositional atoms
    pub init_atoms: Vec<Pddl8Atom>,
    /// (:init ...) — numeric fluent initial values
    pub init_fn_values: Vec<(PddlFunction, f64)>,
    /// (:init (at T lit) ...) — timed initial literals
    pub timed_inits: Vec<TimedLiteral>,
    /// (:goal ...)
    pub goal: PddlCondition,
    /// (:constraints ...) with preferences
    pub preferences: Vec<PddlPreference>,
    /// (:metric ...)
    pub metric: Option<Metric>,
}

// --- Temporal planning runtime types -----------------------------------------

/// Ground action with temporal timing information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalPlanStep {
    pub start_time: f64,
    pub duration: f64,
    pub action_name: String,
    pub args: Vec<String>,
}

/// A PDDL temporal plan (OPTIC/POPF-compatible output format).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TemporalPlan {
    pub steps: Vec<TemporalPlanStep>,
    pub makespan: f64,
    pub metric_value: Option<f64>,
}

/// BLAKE3-chained receipt for temporal plan execution.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalExecutionReceipt {
    pub plan_root: String,
    pub state_root: String,
    pub goal_root: String,
    pub makespan: f64,
    pub step_count: usize,
    pub requirements: Vec<String>,
    pub goal_reached: bool,
    pub chain_hash: String,
}