Commit the previously-untracked standard directories and catalog them in the
root README alongside proof:
- aac/ — communication cargo class: portable boards + a ten-level
offline-first cascade (Apache-2.0 code / CC-BY-4.0 spec).
- map/ — geospatial cargo class: range-requestable signed tilesets +
an F0–F4 fidelity cascade (Apache-2.0 code / CC-BY-4.0 spec).
- sandbox/— isolated, metered, attested execution across the heterogeneous
compute fabric (Apache-2.0 code / CC-BY-4.0 spec).
- jouleclaw-otel-authz, jouleclaw-trust — new jouleclaw reference crates.
Root README now lists aac, map, sandbox, and proof in the catalog, the tree,
and the contribution guide.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
157 lines
6 KiB
Rust
157 lines
6 KiB
Rust
//! The Map fidelity cascade (SPEC §1.2).
|
|
//!
|
|
//! A render request for a [`CellKey`] declares a *requested fidelity* — a
|
|
//! ceiling. The resolver returns the **highest tier at or below that ceiling
|
|
//! that holds data for the cell**, short-circuiting on a cache hit (F0) and
|
|
//! falling back toward the F1 base floor, which the standard requires to be
|
|
//! defined everywhere on the datum. The shared family invariant holds: stop at
|
|
//! the first tier that closes the request, and record it.
|
|
//!
|
|
//! The cascade is pure: it depends only on [`map_schema`] and a [`TileSource`]
|
|
//! the caller supplies. Building the energy receipt for the closing tier is the
|
|
//! caller's job (see `map-receipt`), keeping metering and resolution separate.
|
|
|
|
use map_schema::{CellKey, ContentId, Tier};
|
|
|
|
/// What a resolver can ask of a tile store. Implementations back this with a
|
|
/// tileset directory, an OPFS cache, a live entity feed, etc.
|
|
pub trait TileSource {
|
|
/// Is the cell already resolved in the local store (the F0 short-circuit)?
|
|
/// Returns the cached tile's content id.
|
|
fn cached(&self, cell: &CellKey) -> Option<ContentId>;
|
|
|
|
/// Does `tier` hold data for `cell`? Returns the tile's content id if so.
|
|
///
|
|
/// A conforming source MUST return `Some` for [`Tier::Base`] for any cell on
|
|
/// the datum — the floor that makes graceful fallback a property of the
|
|
/// standard (SPEC §1.2).
|
|
fn tile(&self, cell: &CellKey, tier: Tier) -> Option<ContentId>;
|
|
}
|
|
|
|
/// The outcome of resolving a render request.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub struct Resolution {
|
|
/// The tier that closed the request.
|
|
pub tier: Tier,
|
|
/// The content id of the tile that closed it.
|
|
pub content_id: ContentId,
|
|
/// Whether the resolver fell back below the requested fidelity (true when a
|
|
/// higher tier was requested but absent for this cell).
|
|
pub fell_back: bool,
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum CascadeError {
|
|
/// The source violated SPEC §1.2: not even the F1 base floor was present for
|
|
/// a cell the source was asked to cover.
|
|
#[error("uncovered region: no F1 base tile for the requested cell")]
|
|
UncoveredRegion,
|
|
}
|
|
|
|
/// The fidelity ladder, descending (excludes the F0 cache short-circuit, which
|
|
/// is handled separately). A request walks this filtered to tiers at or below
|
|
/// the ceiling, taking the first that holds data.
|
|
const LADDER_DESC: [Tier; 4] = [Tier::Photoreal, Tier::Feature, Tier::Field, Tier::Base];
|
|
|
|
/// Resolve a render request (SPEC §1.2).
|
|
///
|
|
/// `requested` is the fidelity ceiling. A request below [`Tier::Base`] (i.e.
|
|
/// `Cache`) is clamped to `Base`, since `Cache` is a short-circuit, not a
|
|
/// fidelity a caller targets.
|
|
pub fn resolve(
|
|
source: &dyn TileSource,
|
|
cell: &CellKey,
|
|
requested: Tier,
|
|
) -> Result<Resolution, CascadeError> {
|
|
// F0 — cache short-circuit. Cheapest tier; closes any previously-resolved cell.
|
|
if let Some(content_id) = source.cached(cell) {
|
|
return Ok(Resolution { tier: Tier::Cache, content_id, fell_back: false });
|
|
}
|
|
|
|
let ceiling = requested.rank().max(Tier::Base.rank());
|
|
|
|
// Walk the ladder from the ceiling downward; first tier with data wins.
|
|
for tier in LADDER_DESC {
|
|
if tier.rank() > ceiling {
|
|
continue; // MUST NOT return a tier above the request.
|
|
}
|
|
if let Some(content_id) = source.tile(cell, tier) {
|
|
return Ok(Resolution {
|
|
tier,
|
|
content_id,
|
|
fell_back: tier.rank() < ceiling,
|
|
});
|
|
}
|
|
}
|
|
|
|
// The floor is required to be defined everywhere; absence is a source bug.
|
|
Err(CascadeError::UncoveredRegion)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use map_schema::{AltBand, Lod, S2CellId, TimeBucket};
|
|
use std::collections::HashMap;
|
|
|
|
fn cell(s2: u64) -> CellKey {
|
|
CellKey { s2: S2CellId(s2), alt: AltBand(0), time: TimeBucket::ATEMPORAL, lod: Lod(0) }
|
|
}
|
|
|
|
/// A source described by which tiers each cell holds, plus a cache set.
|
|
#[derive(Default)]
|
|
struct Mock {
|
|
present: HashMap<u64, Vec<Tier>>,
|
|
cache: Vec<u64>,
|
|
}
|
|
impl TileSource for Mock {
|
|
fn cached(&self, c: &CellKey) -> Option<ContentId> {
|
|
self.cache.contains(&c.s2.0).then(|| ContentId::of_bytes(b"cached"))
|
|
}
|
|
fn tile(&self, c: &CellKey, tier: Tier) -> Option<ContentId> {
|
|
self.present
|
|
.get(&c.s2.0)
|
|
.filter(|tiers| tiers.contains(&tier))
|
|
.map(|_| ContentId::of_bytes(&[tier.rank()]))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn returns_highest_available_at_or_below_ceiling() {
|
|
let mut m = Mock::default();
|
|
m.present.insert(1, vec![Tier::Base, Tier::Field, Tier::Photoreal]);
|
|
// ceiling Photoreal -> Photoreal closes, no fallback
|
|
let r = resolve(&m, &cell(1), Tier::Photoreal).unwrap();
|
|
assert_eq!(r.tier, Tier::Photoreal);
|
|
assert!(!r.fell_back);
|
|
// ceiling Field -> must NOT return Photoreal; Field closes
|
|
let r = resolve(&m, &cell(1), Tier::Field).unwrap();
|
|
assert_eq!(r.tier, Tier::Field);
|
|
assert!(!r.fell_back);
|
|
}
|
|
|
|
#[test]
|
|
fn falls_back_to_floor_when_higher_tiers_absent() {
|
|
let mut m = Mock::default();
|
|
m.present.insert(2, vec![Tier::Base]); // only the floor
|
|
let r = resolve(&m, &cell(2), Tier::Photoreal).unwrap();
|
|
assert_eq!(r.tier, Tier::Base);
|
|
assert!(r.fell_back); // requested Photoreal, got Base
|
|
}
|
|
|
|
#[test]
|
|
fn cache_short_circuits() {
|
|
let mut m = Mock::default();
|
|
m.present.insert(3, vec![Tier::Base, Tier::Photoreal]);
|
|
m.cache.push(3);
|
|
let r = resolve(&m, &cell(3), Tier::Photoreal).unwrap();
|
|
assert_eq!(r.tier, Tier::Cache);
|
|
assert!(!r.fell_back);
|
|
}
|
|
|
|
#[test]
|
|
fn uncovered_region_is_an_error() {
|
|
let m = Mock::default(); // nothing present, including the floor
|
|
assert!(matches!(resolve(&m, &cell(9), Tier::Base), Err(CascadeError::UncoveredRegion)));
|
|
}
|
|
}
|