Skip to main content

infotheory/spec/document/
types.rs

1//! Canonical top-level specification document schema types.
2
3use crate::aixi::common::{ActionAlphabet, MctsStrategy, ObservationKeyMode};
4use crate::api::{BitStreamSemantics, CompressionBackend, RateBackend};
5use crate::spec::core::{
6    AssetRef, CanonicalBytes, CompiledCompressionBackend, CompiledRateBackend,
7    ValidatedCompressionBackend, ValidatedRateBackend,
8};
9use std::path::PathBuf;
10use std::sync::Arc;
11
12/// Stable identifier for an external asset binding.
13pub type AssetId = String;
14
15/// Filesystem binding for a named external asset referenced by a spec document.
16#[derive(Clone, Debug, Eq, PartialEq)]
17#[non_exhaustive]
18pub struct AssetBinding {
19    /// Stable asset identifier used inside canonical specs.
20    pub id: AssetId,
21    /// Filesystem path used to resolve the asset at runtime.
22    pub path: String,
23}
24
25/// Resolved runtime asset binding derived from a canonical asset identifier.
26#[derive(Clone, Debug, Eq, PartialEq)]
27#[non_exhaustive]
28pub struct ResolvedAssetBinding {
29    /// Stable asset identifier used inside canonical specs.
30    pub id: AssetId,
31    /// Resolved asset handle in the current compilation environment.
32    pub asset: AssetRef,
33}
34
35/// Built-in non-VM environment choices available to planner runs.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37#[non_exhaustive]
38pub enum BuiltinEnvironmentSpec {
39    /// Internal planner environment bridge used by tuner controller execution.
40    TunerBridge,
41    /// GameEngine biased coin-flip environment.
42    CoinFlip,
43    /// GameEngine biased rock-paper-scissor environment.
44    BiasedRockPaperScissor,
45    /// GameEngine Kuhn poker environment.
46    KuhnPoker,
47    /// GameEngine extended tiger environment.
48    ExtendedTiger,
49    /// GameEngine tic-tac-toe environment.
50    TicTacToe,
51    /// GameEngine blackjack environment.
52    Blackjack,
53    /// GameEngine platformer environment.
54    Platformer,
55}
56
57/// Shared-memory persistence policy for Nyx VM environments.
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59#[non_exhaustive]
60pub enum SharedMemoryPolicySpec {
61    /// Preserve the region across resets.
62    Preserve,
63    /// Reset from the snapshot baseline each iteration.
64    Snapshot,
65}
66
67/// Reward shaping configuration for VM environments.
68///
69/// Algorithmic configuration for the entropy estimator (such as ROSA's
70/// `max_order`) lives inside the active rate backend's variant; the shaping
71/// spec only carries shaping-policy parameters.
72#[derive(Clone, Debug, PartialEq)]
73#[non_exhaustive]
74pub enum VmRewardShapingSpec {
75    /// Entropy reduction relative to a baseline asset.
76    EntropyReduction {
77        /// Asset identifier providing baseline bytes.
78        baseline_asset: AssetId,
79        /// Linear scale applied to the shaping reward.
80        scale: f64,
81        /// Optional bonus applied on crash exits.
82        crash_bonus: Option<i64>,
83        /// Optional bonus applied on timeout exits.
84        timeout_bonus: Option<i64>,
85    },
86    /// Trace entropy shaping using online trace bytes.
87    TraceEntropy {
88        /// Linear scale applied to the shaping reward.
89        scale: f64,
90        /// Whether to normalize by trace length.
91        normalize: bool,
92    },
93}
94
95/// Reward policy for canonical VM environment specs.
96#[derive(Clone, Debug, Eq, PartialEq)]
97#[non_exhaustive]
98pub enum VmRewardPolicySpec {
99    /// Parse reward directly from the guest protocol.
100    FromGuest,
101    /// Pattern-based reward shaping against guest output.
102    Pattern {
103        /// Pattern searched in guest output.
104        pattern: String,
105        /// Reward applied when the pattern does not match.
106        base_reward: i64,
107        /// Additional reward applied on match.
108        bonus_reward: i64,
109    },
110}
111
112/// Optional information-theoretic action filtering for VM runs.
113///
114/// Algorithmic configuration for the entropy estimator (such as ROSA's
115/// `max_order`) lives inside the active rate backend's variant; the filter
116/// spec only carries filter-policy thresholds.
117#[derive(Clone, Debug, PartialEq)]
118#[non_exhaustive]
119pub struct VmActionFilterSpec {
120    /// Minimum entropy threshold.
121    pub min_entropy: Option<f64>,
122    /// Maximum entropy threshold.
123    pub max_entropy: Option<f64>,
124    /// Minimum intrinsic dependence threshold.
125    pub min_intrinsic_dependence: Option<f64>,
126    /// Minimum novelty threshold.
127    pub min_novelty: Option<f64>,
128    /// Optional prior asset used for novelty scoring.
129    pub novelty_prior_asset: Option<AssetId>,
130    /// Reward assigned when an action is rejected.
131    pub reject_reward: Option<i64>,
132}
133
134/// Optional VM trace collection settings.
135#[derive(Clone, Debug, Eq, PartialEq)]
136#[non_exhaustive]
137pub struct VmTraceSpec {
138    /// Shared-memory region name carrying trace bytes.
139    pub shared_region_name: Option<String>,
140    /// Maximum trace bytes collected per step.
141    pub max_bytes: usize,
142    /// Whether the trace model resets on episode boundaries.
143    pub reset_on_episode: bool,
144}
145
146/// Canonical observation derivation modes for VM environments.
147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
148#[non_exhaustive]
149pub enum VmObservationPolicySpec {
150    /// Parse observations from the guest protocol.
151    FromGuest,
152    /// Hash guest output into the observation stream.
153    OutputHash,
154    /// Use raw guest output bytes directly.
155    RawOutput,
156    /// Read observations from shared memory.
157    SharedMemory,
158}
159
160/// Canonical normalization modes for VM observation streams.
161#[derive(Clone, Copy, Debug, Eq, PartialEq)]
162#[non_exhaustive]
163pub enum VmObservationStreamModeSpec {
164    /// Pad short streams and truncate long streams.
165    PadTruncate,
166    /// Only pad short streams.
167    Pad,
168    /// Only truncate long streams.
169    Truncate,
170}
171
172/// Canonical payload encodings for VM action and protocol payloads.
173#[derive(Clone, Copy, Debug, Eq, PartialEq)]
174#[non_exhaustive]
175pub enum VmPayloadEncodingSpec {
176    /// Interpret payload strings as UTF-8 text.
177    Utf8,
178    /// Interpret payload strings as hexadecimal bytes.
179    Hex,
180}
181
182/// Canonical fuzz mutator choices for VM action generation.
183#[derive(Clone, Copy, Debug, Eq, PartialEq)]
184#[non_exhaustive]
185pub enum VmFuzzMutatorSpec {
186    /// Flip one random bit.
187    FlipBit,
188    /// Flip one random byte.
189    FlipByte,
190    /// Insert one random byte.
191    InsertByte,
192    /// Delete one random byte.
193    DeleteByte,
194    /// Splice in bytes from another seed.
195    SpliceSeed,
196    /// Reset to a seed input.
197    ResetSeed,
198    /// Apply a short random mutation sequence.
199    Havoc,
200}
201
202/// Canonical runtime action source for VM environments.
203#[derive(Clone, Debug, Eq, PartialEq)]
204#[non_exhaustive]
205pub enum VmRuntimeActionSourceSpec {
206    /// Inline literal action payloads.
207    Literal {
208        /// Human-readable action names.
209        names: Vec<Option<String>>,
210        /// Action payloads encoded with the selected payload encoding.
211        payloads: Vec<String>,
212        /// Payload encoding applied to all literals.
213        encoding: VmPayloadEncodingSpec,
214    },
215    /// Mutation-based fuzzing configuration.
216    Fuzz {
217        /// Seed inputs encoded with the selected payload encoding.
218        seeds: Vec<String>,
219        /// Payload encoding applied to seeds and dictionary entries.
220        encoding: VmPayloadEncodingSpec,
221        /// Enabled mutators by canonical name.
222        mutators: Vec<VmFuzzMutatorSpec>,
223        /// Minimum generated payload length.
224        min_len: usize,
225        /// Maximum generated payload length.
226        max_len: usize,
227        /// Optional dictionary entries.
228        dictionary: Vec<String>,
229        /// Deterministic RNG seed for mutation sampling.
230        rng_seed: u64,
231    },
232}
233
234/// Canonical Nyx/Firecracker environment configuration.
235#[derive(Clone)]
236#[non_exhaustive]
237pub struct VmEnvironmentSpec {
238    /// Asset identifier pointing at the Firecracker JSON config.
239    pub firecracker_config_asset: AssetId,
240    /// VM instance identifier.
241    pub instance_id: String,
242    /// Shared-memory region name used for guest communication.
243    pub shared_region_name: String,
244    /// Shared-memory region size in bytes.
245    pub shared_region_size: usize,
246    /// Snapshot-vs-preserve policy for shared memory.
247    pub shared_memory_policy: SharedMemoryPolicySpec,
248    /// Per-step timeout in milliseconds.
249    pub step_timeout_ms: u64,
250    /// Initial boot timeout in milliseconds.
251    pub boot_timeout_ms: u64,
252    /// Episode length in steps.
253    pub episode_steps: usize,
254    /// Per-step cost subtracted from rewards.
255    pub step_cost: i64,
256    /// Observation derivation mode.
257    pub observation_policy: VmObservationPolicySpec,
258    /// Observation bit width.
259    pub observation_bits: usize,
260    /// Observation stream length.
261    pub observation_stream_len: usize,
262    /// Observation stream normalization mode.
263    pub observation_stream_mode: VmObservationStreamModeSpec,
264    /// Padding byte for short observation streams.
265    pub observation_pad_byte: u8,
266    /// Reward bit width.
267    pub reward_bits: usize,
268    /// Reward policy.
269    pub reward_policy: VmRewardPolicySpec,
270    /// Optional reward shaping policy.
271    pub reward_shaping: Option<VmRewardShapingSpec>,
272    /// Runtime action source.
273    pub action_source: VmRuntimeActionSourceSpec,
274    /// Optional information-theoretic filter.
275    pub action_filter: Option<VmActionFilterSpec>,
276    /// Protocol action prefix.
277    pub action_prefix: String,
278    /// Protocol action suffix.
279    pub action_suffix: String,
280    /// Protocol observation prefix.
281    pub obs_prefix: String,
282    /// Protocol reward prefix.
283    pub rew_prefix: String,
284    /// Protocol done prefix.
285    pub done_prefix: String,
286    /// Protocol data prefix.
287    pub data_prefix: String,
288    /// Payload encoding label (`utf8` or `hex`).
289    pub wire_encoding: VmPayloadEncodingSpec,
290    /// Rate backend used for entropy/statistics estimation.
291    pub stats_backend: RateBackend,
292    /// Optional trace configuration.
293    pub trace: Option<VmTraceSpec>,
294    /// Whether to enable verbose VM diagnostics.
295    pub debug_mode: bool,
296    /// Optional crash log path for VM exits.
297    pub crash_log: Option<String>,
298}
299
300/// Canonical planner-visible environment specification.
301#[derive(Clone)]
302#[non_exhaustive]
303pub enum EnvironmentSpec {
304    /// Built-in Rust environment.
305    Builtin {
306        /// Built-in environment kind.
307        builtin: BuiltinEnvironmentSpec,
308    },
309    /// Nyx/Firecracker VM environment.
310    #[cfg(feature = "vm")]
311    NyxVm(VmEnvironmentSpec),
312}
313
314/// Planner observation/reward/action interface contract.
315#[derive(Clone, Debug, PartialEq)]
316#[non_exhaustive]
317pub struct PlannerInterfaceSpec {
318    /// Observation bit width.
319    pub observation_bits: usize,
320    /// Observation stream length.
321    pub observation_stream_len: usize,
322    /// Observation key projection used by MC-AIXI.
323    pub observation_key_mode: ObservationKeyMode,
324    /// Reward bit width.
325    pub reward_bits: usize,
326    /// Action alphabet cardinality.
327    pub agent_actions: ActionAlphabet,
328}
329
330/// Canonical tuning interface contract for planner-visible I/O shape.
331///
332/// Unlike [`PlannerInterfaceSpec`], this tune-document interface is limited to
333/// candidate-facing semantics and intentionally excludes reward encoding
334/// bounds that belong to executor/runtime policy.
335#[cfg(feature = "tuner")]
336#[derive(Clone, Debug, PartialEq)]
337#[non_exhaustive]
338pub struct TunePlannerInterfaceSpec {
339    /// Observation bit width.
340    pub observation_bits: usize,
341    /// Observation stream length.
342    pub observation_stream_len: usize,
343    /// Observation key projection used by MC-AIXI.
344    pub observation_key_mode: ObservationKeyMode,
345    /// Reward bit width.
346    pub reward_bits: usize,
347    /// Action alphabet cardinality.
348    pub agent_actions: ActionAlphabet,
349}
350
351/// MC-AIXI controller configuration using a unified rate-backend predictor.
352#[derive(Clone)]
353#[non_exhaustive]
354pub struct McAixiControllerSpec {
355    /// Predictive backend used by the planner model.
356    pub predictor: RateBackend,
357    /// Bit-stream semantics used to adapt the predictor to AIXI symbols.
358    pub bit_stream_semantics: BitStreamSemantics,
359    /// Planning horizon.
360    pub agent_horizon: usize,
361    /// Number of simulations per planning step.
362    pub num_simulations: usize,
363    /// Explicit MCTS strategy used by the planner.
364    pub mcts_strategy: MctsStrategy,
365    /// UCT exploration constant.
366    pub exploration_exploitation_ratio: f64,
367    /// Reward discount factor.
368    pub discount_gamma: f64,
369}
370
371/// Discounted AIQI controller configuration.
372#[derive(Clone)]
373#[non_exhaustive]
374pub struct AiqiDiscountedControllerSpec {
375    /// Predictive backend used by the return model.
376    pub predictor: RateBackend,
377    /// Bit-stream semantics used to adapt the predictor to AIQI symbols.
378    pub bit_stream_semantics: BitStreamSemantics,
379    /// Discount factor used for return construction.
380    pub discount_gamma: f64,
381    /// Return horizon.
382    pub return_horizon: usize,
383    /// Number of discrete return bins.
384    pub return_bins: usize,
385    /// Label augmentation period.
386    pub augmentation_period: usize,
387    /// Optional bounded-history retention hint.
388    pub history_prune_keep_steps: Option<usize>,
389    /// Baseline exploration probability.
390    pub baseline_exploration: f64,
391}
392
393/// Warm-start exact-\u{1d4a5}_H controller configuration.
394#[cfg(feature = "aixi")]
395#[derive(Clone)]
396#[non_exhaustive]
397pub struct WarmStartExactJhControllerSpec {
398    /// Predictive backend used by the return model.
399    pub predictor: RateBackend,
400    /// Bit-stream semantics used to adapt the predictor to warm-start symbols.
401    pub bit_stream_semantics: BitStreamSemantics,
402    /// Return horizon in planner steps.
403    pub return_horizon: usize,
404    /// Exact return-label alphabet size.
405    ///
406    /// Valid canonical warm-start specs use `return_horizon * max_reward + 1`;
407    /// slack labels are rejected because they do not represent reachable exact
408    /// returns.
409    pub return_bins: usize,
410    /// Delayed-label phase period.
411    pub label_phase_period: usize,
412    /// Asset identifier for the warm-start teacher dataset.
413    pub teacher_dataset_asset: AssetId,
414    /// Canonical direct-evaluator budget marker.
415    ///
416    /// Warm-start exact-\(J_H\) performs deterministic full return-law
417    /// evaluation, not MCTS-style simulation. Valid canonical specs use `1`.
418    pub planner_simulations_per_step: usize,
419}
420
421/// Canonical planner controller selection.
422#[derive(Clone)]
423#[non_exhaustive]
424pub enum ControllerSpec {
425    /// Monte Carlo AIXI.
426    McAixi(McAixiControllerSpec),
427    /// Discounted AIQI.
428    AiqiDiscounted(AiqiDiscountedControllerSpec),
429    /// Warm-start exact-\u{1d4a5}_H AIQI-style controller.
430    #[cfg(feature = "aixi")]
431    AiqiWarmstartExactJh(WarmStartExactJhControllerSpec),
432}
433
434/// Operational planner-run controls that do not change predictor semantics.
435#[derive(Clone, Debug, PartialEq)]
436#[non_exhaustive]
437pub struct PlannerRuntimeSpec {
438    /// Seed used for planner/environment stochasticity.
439    ///
440    /// `None` canonicalizes to `Some(0)` during validation/compilation.
441    pub random_seed: Option<u64>,
442    /// Number of learning cycles.
443    pub learn_cycles: Option<usize>,
444    /// Number of evaluation cycles.
445    pub eval_cycles: Option<usize>,
446    /// Default cycle count when learn/eval are omitted.
447    pub terminate_lifetime: usize,
448    /// Logging interval in steps.
449    pub log_every: usize,
450    /// Whether to print throughput diagnostics.
451    pub perf: bool,
452    /// Whether to run VM perf-only mode.
453    pub vm_perf_only: bool,
454    /// Extra epsilon exploration used during execution.
455    pub explore_epsilon: f64,
456    /// Exponential decay for extra exploration.
457    pub explore_gamma: f64,
458}
459
460/// Canonical planner-run specification.
461#[derive(Clone)]
462#[non_exhaustive]
463pub struct PlannerRunSpec {
464    /// External asset bindings referenced by this run.
465    pub assets: Vec<AssetBinding>,
466    /// Planner-facing environment.
467    pub environment: EnvironmentSpec,
468    /// Planner observation/reward/action contract.
469    pub interface: PlannerInterfaceSpec,
470    /// Controller configuration.
471    pub controller: ControllerSpec,
472    /// Operational run controls.
473    pub runtime: PlannerRuntimeSpec,
474}
475
476/// Tuning controller kind specified by the formal tuner document.
477#[cfg(feature = "tuner")]
478#[derive(Clone, Copy, Debug, Eq, PartialEq)]
479#[non_exhaustive]
480pub enum TuneControllerKind {
481    /// Annealed hill climbing.
482    AnnealedHillClimbing,
483    /// MC-AIXI(FAC-CTW).
484    McAixiFacCtw,
485    /// Discounted AIQI.
486    AiqiDiscounted,
487    /// Warm-start exact-\u{1d4a5}_H controller.
488    #[cfg(feature = "aixi")]
489    AiqiWarmstartExactJh,
490}
491
492/// Annealed hill-climbing controller settings for tuning.
493#[cfg(feature = "tuner")]
494#[derive(Clone, Debug, Eq, PartialEq)]
495#[non_exhaustive]
496pub struct AnnealedHillClimbingTuneControllerSpec {
497    /// Maximum mutation radius applied to a candidate step.
498    pub max_mutation_radius: usize,
499}
500
501/// MC-AIXI(FAC-CTW) controller settings for tuning.
502#[cfg(feature = "tuner")]
503#[derive(Clone, Debug, PartialEq)]
504#[non_exhaustive]
505pub struct McAixiFacCtwTuneControllerSpec {
506    /// Planner/environment observation/reward/action contract.
507    pub interface: TunePlannerInterfaceSpec,
508    /// Simulation budget per planner step.
509    pub planner_simulations_per_step: usize,
510}
511
512/// Discounted AIQI controller settings for tuning.
513#[cfg(feature = "tuner")]
514#[derive(Clone, Debug, PartialEq)]
515#[non_exhaustive]
516pub struct AiqiDiscountedTuneControllerSpec {
517    /// Planner/environment observation/reward/action contract.
518    pub interface: TunePlannerInterfaceSpec,
519    /// Simulation budget per planner step.
520    pub planner_simulations_per_step: usize,
521    /// Return horizon.
522    pub return_horizon: usize,
523    /// Number of return bins.
524    pub return_bins: usize,
525    /// Discount factor used to construct returns.
526    pub discount_factor: f64,
527    /// Minimum clipped improvement value.
528    pub min_improvement: f64,
529    /// Maximum clipped improvement value.
530    pub max_improvement: f64,
531}
532
533/// Warm-start exact-J_H controller settings for tuning.
534#[cfg(feature = "tuner")]
535#[derive(Clone, Debug, PartialEq)]
536#[non_exhaustive]
537pub struct WarmStartExactJhTuneControllerSpec {
538    /// Planner/environment observation/reward/action contract.
539    pub interface: TunePlannerInterfaceSpec,
540    /// Canonical direct-evaluator budget marker.
541    ///
542    /// Warm-start exact-\(J_H\) performs deterministic full return-law
543    /// evaluation, not MCTS-style simulation. Valid canonical specs use `1`.
544    pub planner_simulations_per_step: usize,
545    /// Return horizon.
546    pub return_horizon: usize,
547    /// Teacher dataset asset for warm-start labels.
548    pub warmstart_teacher_dataset_asset: AssetId,
549    /// Label phase period.
550    pub label_phase_period: usize,
551}
552
553/// Runtime-selectable controller configuration for the tuning runtime.
554#[cfg(feature = "tuner")]
555#[derive(Clone, Debug, PartialEq)]
556#[non_exhaustive]
557pub enum TuneControllerSpec {
558    /// Annealed hill climbing.
559    AnnealedHillClimbing(AnnealedHillClimbingTuneControllerSpec),
560    /// MC-AIXI(FAC-CTW).
561    McAixiFacCtw(McAixiFacCtwTuneControllerSpec),
562    /// Discounted AIQI.
563    AiqiDiscounted(AiqiDiscountedTuneControllerSpec),
564    /// Warm-start exact-J_H.
565    #[cfg(feature = "aixi")]
566    AiqiWarmstartExactJh(WarmStartExactJhTuneControllerSpec),
567}
568
569#[cfg(feature = "tuner")]
570impl TuneControllerSpec {
571    /// Controller family tag for this tuning controller configuration.
572    pub fn kind(&self) -> TuneControllerKind {
573        match self {
574            Self::AnnealedHillClimbing(_) => TuneControllerKind::AnnealedHillClimbing,
575            Self::McAixiFacCtw(_) => TuneControllerKind::McAixiFacCtw,
576            Self::AiqiDiscounted(_) => TuneControllerKind::AiqiDiscounted,
577            #[cfg(feature = "aixi")]
578            Self::AiqiWarmstartExactJh(_) => TuneControllerKind::AiqiWarmstartExactJh,
579        }
580    }
581}
582
583#[cfg(all(test, feature = "tuner"))]
584mod tests {
585    use super::*;
586
587    fn action_alphabet(n: usize) -> ActionAlphabet {
588        ActionAlphabet::try_from_usize(n).expect("test action alphabet must be non-zero")
589    }
590
591    fn sample_tune_interface() -> TunePlannerInterfaceSpec {
592        TunePlannerInterfaceSpec {
593            observation_bits: 8,
594            observation_stream_len: 1,
595            observation_key_mode: ObservationKeyMode::FullStream,
596            reward_bits: 8,
597            agent_actions: action_alphabet(2),
598        }
599    }
600
601    #[test]
602    fn tune_controller_kind_matches_each_variant() {
603        let annealed =
604            TuneControllerSpec::AnnealedHillClimbing(AnnealedHillClimbingTuneControllerSpec {
605                max_mutation_radius: 3,
606            });
607        assert_eq!(annealed.kind(), TuneControllerKind::AnnealedHillClimbing);
608
609        let mc_aixi = TuneControllerSpec::McAixiFacCtw(McAixiFacCtwTuneControllerSpec {
610            interface: sample_tune_interface(),
611            planner_simulations_per_step: 8,
612        });
613        assert_eq!(mc_aixi.kind(), TuneControllerKind::McAixiFacCtw);
614
615        let aiqi = TuneControllerSpec::AiqiDiscounted(AiqiDiscountedTuneControllerSpec {
616            interface: sample_tune_interface(),
617            planner_simulations_per_step: 8,
618            return_horizon: 2,
619            return_bins: 8,
620            discount_factor: 0.5,
621            min_improvement: -1.0,
622            max_improvement: 1.0,
623        });
624        assert_eq!(aiqi.kind(), TuneControllerKind::AiqiDiscounted);
625
626        #[cfg(feature = "aixi")]
627        {
628            let warmstart =
629                TuneControllerSpec::AiqiWarmstartExactJh(WarmStartExactJhTuneControllerSpec {
630                    interface: sample_tune_interface(),
631                    planner_simulations_per_step: 1,
632                    return_horizon: 2,
633                    warmstart_teacher_dataset_asset: "teacher".to_string(),
634                    label_phase_period: 3,
635                });
636            assert_eq!(warmstart.kind(), TuneControllerKind::AiqiWarmstartExactJh);
637        }
638    }
639}
640
641/// Bounded numeric range for a named canonical tuning parameter.
642#[cfg(feature = "tuner")]
643#[derive(Clone, Debug, PartialEq)]
644#[non_exhaustive]
645pub struct TuneParameterRangeSpec {
646    /// Canonical parameter path or name.
647    pub parameter: String,
648    /// Inclusive lower bound.
649    pub min: f64,
650    /// Inclusive upper bound.
651    pub max: f64,
652}
653
654/// Canonical bounds specification for the future tuning runtime.
655#[cfg(feature = "tuner")]
656#[derive(Clone, Debug, PartialEq)]
657#[non_exhaustive]
658pub struct TuneBoundsSpec {
659    /// Allowed canonical backend names.
660    pub allowed_backends: Vec<String>,
661    /// Forbidden canonical backend names.
662    pub forbidden_backends: Vec<String>,
663    /// Inclusive ranges for canonical numeric parameters.
664    pub parameter_ranges: Vec<TuneParameterRangeSpec>,
665    /// Maximum experts per mixture node.
666    pub max_experts: usize,
667    /// Maximum recursive mixture nesting depth.
668    pub max_mixture_nesting_depth: usize,
669    /// Optional minimum experts per mixture node.
670    pub min_experts: Option<usize>,
671    /// Whether duplicate experts are permitted.
672    pub allow_duplicate_experts: Option<bool>,
673    /// Expert names that must appear in the candidate set.
674    pub required_experts: Vec<String>,
675    /// Canonical expert-pair combinations that are forbidden together.
676    pub forbidden_expert_pairs: Vec<(String, String)>,
677}
678
679/// Canonical future-facing tune request document.
680#[cfg(feature = "tuner")]
681#[derive(Clone)]
682#[non_exhaustive]
683pub struct TuneSpec {
684    /// External asset bindings referenced by this request.
685    pub assets: Vec<AssetBinding>,
686    /// Asset identifier for the input dataset.
687    pub input_asset: AssetId,
688    /// Baseline candidate configuration.
689    pub baseline_candidate: CompressionBackend,
690    /// Runtime-selectable controller used by the tuning runtime.
691    pub controller: TuneControllerSpec,
692    /// Candidate bounds specification.
693    pub bounds: TuneBoundsSpec,
694    /// Per-candidate evaluation time limit in seconds.
695    pub eval_time_limit_seconds: f64,
696    /// Total search budget in seconds.
697    pub time_budget_seconds: f64,
698    /// Minimum required throughput in bytes/second.
699    pub min_throughput_bytes_per_second: f64,
700    /// Maximum allowed memory use in bytes.
701    pub max_memory_bytes: u64,
702    /// Output path for the best canonical candidate.
703    pub output_config_path: String,
704    /// Deterministic search seed.
705    pub seed: u64,
706    /// Optional report output path.
707    pub report_path: Option<String>,
708}
709
710/// Compiled planner controller with precompiled predictor backends.
711#[derive(Clone)]
712#[non_exhaustive]
713pub enum CompiledPlannerController {
714    /// MC-AIXI controller.
715    McAixi {
716        /// Compiled predictor backend.
717        predictor: CompiledRateBackend,
718        /// Bit-stream semantics used to adapt the predictor to AIXI symbols.
719        bit_stream_semantics: BitStreamSemantics,
720        /// Planning horizon.
721        agent_horizon: usize,
722        /// Number of simulations per planning step.
723        num_simulations: usize,
724        /// Explicit MCTS strategy used by the planner.
725        mcts_strategy: MctsStrategy,
726        /// UCT exploration constant.
727        exploration_exploitation_ratio: f64,
728        /// Reward discount factor.
729        discount_gamma: f64,
730    },
731    /// Discounted AIQI controller.
732    AiqiDiscounted {
733        /// Compiled predictor backend.
734        predictor: CompiledRateBackend,
735        /// Bit-stream semantics used to adapt the predictor to AIQI symbols.
736        bit_stream_semantics: BitStreamSemantics,
737        /// Discount factor used for return construction.
738        discount_gamma: f64,
739        /// Return horizon.
740        return_horizon: usize,
741        /// Number of return bins.
742        return_bins: usize,
743        /// Label augmentation period.
744        augmentation_period: usize,
745        /// Optional bounded-history retention hint.
746        history_prune_keep_steps: Option<usize>,
747        /// Baseline exploration probability.
748        baseline_exploration: f64,
749    },
750    /// Warm-start exact-J_H controller.
751    #[cfg(feature = "aixi")]
752    AiqiWarmstartExactJh {
753        /// Compiled predictor backend.
754        predictor: CompiledRateBackend,
755        /// Bit-stream semantics used to adapt the predictor to warm-start symbols.
756        bit_stream_semantics: BitStreamSemantics,
757        /// Return horizon.
758        return_horizon: usize,
759        /// Number of return bins.
760        return_bins: usize,
761        /// Label phase period.
762        label_phase_period: usize,
763        /// Teacher dataset asset id.
764        teacher_dataset_asset: AssetId,
765        /// Canonical direct-evaluator budget marker.
766        planner_simulations_per_step: usize,
767    },
768}
769
770/// Compiled planner-run specification with resolved assets and compiled backends.
771#[derive(Clone)]
772#[non_exhaustive]
773pub struct CompiledPlannerRunSpec {
774    pub(super) canonical_spec: Arc<PlannerRunSpec>,
775    pub(super) canonical_bytes: CanonicalBytes,
776    pub(super) resolved_assets: Arc<[ResolvedAssetBinding]>,
777    pub(super) interface: PlannerInterfaceSpec,
778    pub(super) runtime: PlannerRuntimeSpec,
779    pub(super) controller: CompiledPlannerController,
780    pub(super) action_bits: usize,
781}
782
783/// Compiled tuning controller configuration.
784#[cfg(feature = "tuner")]
785#[derive(Clone)]
786#[non_exhaustive]
787pub enum CompiledTuneController {
788    /// Annealed hill climbing.
789    AnnealedHillClimbing(AnnealedHillClimbingTuneControllerSpec),
790    /// MC-AIXI(FAC-CTW).
791    McAixiFacCtw(McAixiFacCtwTuneControllerSpec),
792    /// Discounted AIQI.
793    AiqiDiscounted(AiqiDiscountedTuneControllerSpec),
794    /// Warm-start exact-J_H.
795    #[cfg(feature = "aixi")]
796    AiqiWarmstartExactJh(WarmStartExactJhTuneControllerSpec),
797}
798
799/// Compiled tune request with resolved assets and compiled baseline candidate.
800#[cfg(feature = "tuner")]
801#[derive(Clone)]
802#[non_exhaustive]
803pub struct CompiledTuneSpec {
804    pub(super) canonical_spec: Arc<TuneSpec>,
805    pub(super) canonical_bytes: CanonicalBytes,
806    pub(super) base_dir: PathBuf,
807    pub(super) resolved_assets: Arc<[ResolvedAssetBinding]>,
808    pub(super) baseline_candidate: CompiledCompressionBackend,
809    pub(super) controller: CompiledTuneController,
810    pub(super) candidate_canonicalization_version: &'static str,
811}
812
813/// Universal top-level spec document.
814#[derive(Clone)]
815#[non_exhaustive]
816#[allow(clippy::large_enum_variant)]
817pub enum SpecDocument {
818    /// Planner-run configuration document.
819    PlannerRun(PlannerRunSpec),
820    /// Tune request document.
821    #[cfg(feature = "tuner")]
822    Tune(TuneSpec),
823    /// Standalone rate-backend document.
824    RateBackend(RateBackend),
825    /// Standalone compression-backend document.
826    CompressionBackend(CompressionBackend),
827}
828
829/// Parsed top-level spec document paired with its parse base directory.
830///
831/// This is the first stage of the canonical pipeline:
832/// parse -> validate -> compile.
833#[derive(Clone)]
834#[non_exhaustive]
835pub struct ParsedSpecDocument {
836    pub(super) document: SpecDocument,
837    pub(super) base_dir: PathBuf,
838}
839
840/// Canonicalized and validated planner-run document.
841#[derive(Clone)]
842#[non_exhaustive]
843pub struct ValidatedPlannerRunSpec {
844    pub(super) canonical_spec: Arc<PlannerRunSpec>,
845    pub(super) canonical_bytes: CanonicalBytes,
846    pub(super) base_dir: PathBuf,
847}
848
849/// Canonicalized and validated tune request document.
850#[cfg(feature = "tuner")]
851#[derive(Clone)]
852#[non_exhaustive]
853pub struct ValidatedTuneSpec {
854    pub(super) canonical_spec: Arc<TuneSpec>,
855    pub(super) canonical_bytes: CanonicalBytes,
856    #[cfg(feature = "tuner")]
857    pub(super) base_dir: PathBuf,
858}
859
860/// Validated top-level spec document.
861///
862/// This is the second stage of the canonical pipeline and can be compiled into
863/// runtime-ready plans/backends.
864#[derive(Clone)]
865#[non_exhaustive]
866pub enum ValidatedSpecDocument {
867    /// Validated planner-run document.
868    PlannerRun(ValidatedPlannerRunSpec),
869    /// Validated tune document.
870    #[cfg(feature = "tuner")]
871    Tune(ValidatedTuneSpec),
872    /// Validated standalone rate-backend document.
873    RateBackend(ValidatedRateBackend),
874    /// Validated standalone compression-backend document.
875    CompressionBackend(ValidatedCompressionBackend),
876}
877
878/// Compiled top-level spec document.
879///
880/// This is the final stage of the canonical pipeline and is executable by
881/// runtime adapters.
882#[derive(Clone)]
883#[non_exhaustive]
884#[allow(clippy::large_enum_variant)]
885pub enum CompiledSpecDocument {
886    /// Compiled planner-run document.
887    PlannerRun(CompiledPlannerRunSpec),
888    /// Compiled tune document.
889    #[cfg(feature = "tuner")]
890    Tune(CompiledTuneSpec),
891    /// Compiled standalone rate-backend document.
892    RateBackend(CompiledRateBackend),
893    /// Compiled standalone compression-backend document.
894    CompressionBackend(CompiledCompressionBackend),
895}