Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6292d5e
Add comprehensive tracey spec and code annotations
fasterthanlime Jan 8, 2026
d28acdb
Add tracey annotations for macro rules
fasterthanlime Jan 8, 2026
d5071ad
Add tracey annotations for snapshot rules
fasterthanlime Jan 8, 2026
009dc0d
Add tracey annotations for remaining rules
fasterthanlime Jan 8, 2026
cd39ed2
gitignore
fasterthanlime Jan 9, 2026
fb6c4f0
spec: address review feedback and clarify semantics
fasterthanlime Jan 9, 2026
2a4f7ae
spec: rewrite picante spec as semantics-only
fasterthanlime Jan 9, 2026
889d706
spec: treat revisions as opaque tokens
fasterthanlime Jan 9, 2026
6027b14
spec: make key identity semantic
fasterthanlime Jan 9, 2026
3f5185d
spec: explain kinds with Rust examples
fasterthanlime Jan 9, 2026
4997107
spec: clarify key shapes for kinds
fasterthanlime Jan 9, 2026
0cc3507
spec: clarify kind identity; drop stability wording
fasterthanlime Jan 9, 2026
fc0c882
spec: use blockquote for input set/remove
fasterthanlime Jan 9, 2026
1351566
spec: split keys and kinds by ingredient type
fasterthanlime Jan 9, 2026
64655bc
spec: make determinism note non-normative
fasterthanlime Jan 9, 2026
63d74fd
spec: fix tracey blockquote requirement bodies
fasterthanlime Jan 9, 2026
93a5ef9
spec: drop meta commentary from cycle.detect
fasterthanlime Jan 9, 2026
7174aa0
spec: clarify snapshots vs external state
fasterthanlime Jan 9, 2026
a4e2e8e
spec: add non-normative filesystem modeling pattern
fasterthanlime Jan 9, 2026
d8ea8fc
spec: clarify read_file hash validation guidance
fasterthanlime Jan 9, 2026
e4869d1
spec: make determinism guidance non-requirement
fasterthanlime Jan 9, 2026
22f3ac1
spec: define semantics for batch input mutations
fasterthanlime Jan 9, 2026
3e0d520
spec: document input mutation API surface
fasterthanlime Jan 9, 2026
53864b4
spec: expand semantics API direction
fasterthanlime Jan 9, 2026
ab091bf
spec: clarify runtime instance and family
fasterthanlime Jan 9, 2026
d6c6eb8
spec: rename to database and views
fasterthanlime Jan 9, 2026
5ea8a59
spec: define equality, fix revalidation, linearize snapshots
fasterthanlime Jan 9, 2026
b18c7c0
spec: add walkthrough and scope cycle detection
fasterthanlime Jan 9, 2026
64574e8
spec: define concurrency and cancellation semantics
fasterthanlime Jan 9, 2026
addbc89
spec: define changed_at per ingredient
fasterthanlime Jan 9, 2026
304c7d7
spec: add durability semantics (change frequency)
fasterthanlime Jan 9, 2026
94f8d68
spec: specify concurrency edge cases
fasterthanlime Jan 9, 2026
0f63af5
spec: define eviction and memory bounds
fasterthanlime Jan 9, 2026
85354e8
spec: edge cases for absence and interning
fasterthanlime Jan 9, 2026
90a51ae
spec: tighten remaining edge cases
fasterthanlime Jan 10, 2026
ba9dea9
spec: add hard state-byte limits
fasterthanlime Jan 10, 2026
d8a624f
spec: make durability and batching mandatory
fasterthanlime Jan 10, 2026
a8ed02c
spec: add durability diagrams and fix watermark
fasterthanlime Jan 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .config/tracey/config.kdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
spec {
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Tracey configuration file uses KDL syntax correctly. However, consider adding a comment at the top of the file explaining what Tracey is and how this configuration is used, to help other contributors understand its purpose.

Copilot uses AI. Check for mistakes.
name "picante"
prefix "r"
source_url "https://github.com/bearcove/picante"
include "docs/spec/**/*.md"

impl {
name "main"
include "crates/**/*.rs"
exclude "target/**"
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/target
.handoffs
.claude
.cache
.dodeca.db
docs/public
lcov.info
.tracey/
10 changes: 10 additions & 0 deletions crates/picante-macros/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use proc_macro2::{Delimiter, Ident, TokenStream as TokenStream2, TokenTree};
use quote::{format_ident, quote};
use unsynn::{IParse, ToTokenIter, ToTokens};

// r[macro.db.purpose]

#[derive(Default)]
struct DbArgs {
inputs: Vec<ItemPath>,
Expand Down Expand Up @@ -440,6 +442,7 @@ pub(crate) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
});
}

// r[snapshot.interned]
// Interned: share the Arc (append-only, stable)
for interned in &args.interned {
let entity = &interned.name;
Expand Down Expand Up @@ -521,6 +524,10 @@ pub(crate) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
});
}

// r[macro.db.snapshot]
// r[snapshot.frozen]
// r[snapshot.independent]
// r[snapshot.multiple]
let snapshot_def = quote! {
/// A point-in-time snapshot of the database.
///
Expand All @@ -538,6 +545,8 @@ pub(crate) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
}

impl #snapshot_name {
// r[snapshot.creation]
// r[snapshot.async]
/// Create a snapshot from a database.
///
/// This captures the current state of all inputs and cached query results.
Expand Down Expand Up @@ -622,6 +631,7 @@ pub(crate) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
all_ingredient_fields.push(quote! { &*self.#field });
}

// r[macro.db.output]
let expanded = quote! {
#(#struct_attrs)*
#vis struct #db_name {
Expand Down
4 changes: 4 additions & 0 deletions crates/picante-macros/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::{format_ident, quote};

// r[macro.input.purpose]
pub(crate) fn expand(item: TokenStream) -> TokenStream {
let item: TokenStream2 = item.into();
let parsed = match StructItem::parse(item) {
Expand All @@ -26,6 +27,7 @@ pub(crate) fn expand(item: TokenStream) -> TokenStream {
}
}

// r[macro.input.keyed]
/// Expand a keyed input (has exactly one #[key] field)
fn expand_keyed(
parsed: &StructItem,
Expand Down Expand Up @@ -93,6 +95,7 @@ fn expand_keyed(
quote! { let _ = __picante_assert_field_traits::<#ty>; }
});

// r[macro.input.kind-id]
let expanded = quote! {
/// Stable kind id for the key interner of `#name`.
#vis const #keys_kind: picante::QueryKindId = picante::QueryKindId::from_str(concat!(
Expand Down Expand Up @@ -245,6 +248,7 @@ fn split_key_field(parsed: &StructItem) -> KeyFieldResult<'_> {
}
}

// r[macro.input.singleton]
/// Expand a singleton input (no #[key] field)
fn expand_singleton(
parsed: &StructItem,
Expand Down
2 changes: 2 additions & 0 deletions crates/picante-macros/src/interned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::{format_ident, quote};

// r[macro.interned.purpose]
pub(crate) fn expand(item: TokenStream) -> TokenStream {
let item: TokenStream2 = item.into();
let parsed = match StructItem::parse(item) {
Expand Down Expand Up @@ -68,6 +69,7 @@ pub(crate) fn expand(item: TokenStream) -> TokenStream {
quote! { let _ = __picante_assert_field_traits::<#ty>; }
});

// r[macro.interned.output]
let expanded = quote! {
/// Stable kind id for interned `#name` values.
#vis const #kind_const: picante::QueryKindId =
Expand Down
4 changes: 4 additions & 0 deletions crates/picante-macros/src/tracked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::{format_ident, quote};

// r[macro.tracked.purpose]
pub(crate) fn expand(item: TokenStream) -> TokenStream {
let item: TokenStream2 = item.into();
let parsed = match FnItem::parse(item) {
Expand Down Expand Up @@ -53,9 +54,11 @@ pub(crate) fn expand(item: TokenStream) -> TokenStream {
Err(e) => return compile_error(&e),
};

// r[macro.tracked.key-tuple]
let (key_ty, key_expr, unpack_key) = build_key(&parsed.params[1..]);
let call_impl = build_call_impl(&impl_name, parsed.is_async, &parsed.params[1..]);

// r[macro.tracked.return-wrap]
let compute = if returns_picante_result {
quote! { #call_impl }
} else {
Expand Down Expand Up @@ -95,6 +98,7 @@ pub(crate) fn expand(item: TokenStream) -> TokenStream {
quote! { where #db_ident: #db_bounds }
};

// r[macro.tracked.output]
let expanded = quote! {
/// Stable kind id for this query.
#vis const #kind_const: picante::QueryKindId =
Expand Down
4 changes: 4 additions & 0 deletions crates/picante/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Mutex;

// r[debug.graph]
/// A snapshot of the dependency graph for visualization and analysis.
#[derive(Debug, Clone)]
pub struct DependencyGraph {
Expand Down Expand Up @@ -208,6 +209,7 @@ impl DependencyGraph {
}
}

// r[debug.cache-stats]
/// Statistics about cache usage and performance.
#[derive(Debug, Clone)]
pub struct CacheStats {
Expand Down Expand Up @@ -332,6 +334,7 @@ pub enum TraceEvent {
},
}

// r[debug.trace-collector]
/// A collector that records runtime events for analysis.
///
/// This subscribes to the runtime's event stream and records
Expand Down Expand Up @@ -467,6 +470,7 @@ impl TraceCollector {
}
}

// r[debug.trace-analysis]
/// Analysis of a collected trace.
#[derive(Debug, Clone)]
pub struct TraceAnalysis {
Expand Down
3 changes: 3 additions & 0 deletions crates/picante/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ use crate::key::{DynKey, QueryKindId};
use std::fmt;
use std::sync::Arc;

// r[error.result]
/// Result type used by Picante APIs.
pub type PicanteResult<T> = std::result::Result<T, Arc<PicanteError>>;

// r[error.type]
// r[error.variants]
Comment on lines +7 to +12
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirement anchors reference non-existent spec requirements. The error.rs file contains anchors (e.g., "r[error.result]", "r[error.type]", "r[error.variants]") that don't exist in the spec document. These are implementation details. Consider using a different prefix for implementation notes.

Suggested change
// r[error.result]
/// Result type used by Picante APIs.
pub type PicanteResult<T> = std::result::Result<T, Arc<PicanteError>>;
// r[error.type]
// r[error.variants]
// impl[error.result]
/// Result type used by Picante APIs.
pub type PicanteResult<T> = std::result::Result<T, Arc<PicanteError>>;
// impl[error.type]
// impl[error.variants]

Copilot uses AI. Check for mistakes.
/// A Picante runtime / persistence error.
#[derive(Debug)]
pub enum PicanteError {
Expand Down
8 changes: 8 additions & 0 deletions crates/picante/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ use std::future::Future;
use std::sync::Arc;
use tracing::trace;

// r[frame.task-local]
// r[frame.cycle-stack]
tokio::task_local! {
static ACTIVE_STACK: RefCell<Vec<ActiveFrameHandle>>;
}

// r[frame.purpose]
/// A cheap, clonable handle for the currently-running query frame.
#[derive(Clone)]
pub struct ActiveFrameHandle(Arc<ActiveFrameInner>);

// r[frame.no-lock-await]
struct ActiveFrameInner {
dyn_key: DynKey,
started_at: Revision,
Expand Down Expand Up @@ -87,6 +91,8 @@ pub fn has_active_frame() -> bool {
.unwrap_or(false)
}

// r[frame.record-dep]
// r[dep.recording]
/// Record a dependency on the current top-of-stack frame, if any.
pub fn record_dep(dep: Dep) {
let _ = ACTIVE_STACK.try_with(|stack| {
Expand All @@ -96,6 +102,8 @@ pub fn record_dep(dep: Dep) {
});
}

// r[frame.cycle-detect]
// r[frame.cycle-per-task]
/// If `requested` already exists in the task-local stack, returns the full stack of `DynKey`s.
pub fn find_cycle(requested: &DynKey) -> Option<Vec<DynKey>> {
ACTIVE_STACK
Expand Down
13 changes: 13 additions & 0 deletions crates/picante/src/inflight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use tracing::trace;
/// Type-erased result value from a computation.
pub(crate) type ArcAny = Arc<dyn std::any::Any + Send + Sync>;

// r[inflight.registry]
// r[inflight.purpose]
/// Global registry for in-flight computations.
///
/// This allows concurrent queries from different database snapshots to share
Expand All @@ -29,6 +31,7 @@ static IN_FLIGHT_REGISTRY: std::sync::LazyLock<DashMap<InFlightKey, Arc<InFlight
// Shared completed-result cache (cross-snapshot memoization)
// ============================================================================

// r[inflight.shared-cache]
/// A completed derived-query result that can be adopted by other runtimes/snapshots.
#[derive(Clone)]
pub(crate) struct SharedCacheRecord {
Expand All @@ -39,6 +42,8 @@ pub(crate) struct SharedCacheRecord {
pub(crate) insert_id: u64,
}

// r[inflight.shared-cache-key]
/// Key for shared-cache entries; intentionally does not include a revision.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct SharedCacheKey {
runtime_id: RuntimeId,
Expand All @@ -53,6 +58,7 @@ static SHARED_CACHE_ORDER: std::sync::LazyLock<
parking_lot::Mutex<VecDeque<(SharedCacheKey, u64)>>,
> = std::sync::LazyLock::new(|| parking_lot::Mutex::new(VecDeque::new()));

// r[inflight.shared-cache-size]
static SHARED_CACHE_MAX_ENTRIES: AtomicUsize = AtomicUsize::new(20_000);
static SHARED_CACHE_MAX_ENTRIES_OVERRIDDEN: AtomicBool = AtomicBool::new(false);
static SHARED_CACHE_INSERT_ID: AtomicU64 = AtomicU64::new(1);
Expand Down Expand Up @@ -132,6 +138,8 @@ pub fn __test_shared_cache_set_max_entries(max_entries: usize) {
SHARED_CACHE_MAX_ENTRIES_OVERRIDDEN.store(true, Ordering::Relaxed);
}

// r[inflight.key]
// r[inflight.scope]
/// Key identifying an in-flight computation.
///
/// Two queries are considered the same if they have the same:
Expand Down Expand Up @@ -221,6 +229,7 @@ impl InFlightEntry {
pub(crate) enum TryLeadResult {
/// We became the leader. The guard MUST be used to complete/fail/cancel.
Leader(InFlightGuard),
// r[inflight.follower]
/// Someone else is already computing. Wait on the entry.
Follower(Arc<InFlightEntry>),
}
Expand All @@ -235,6 +244,7 @@ pub(crate) struct InFlightGuard {
}

impl InFlightGuard {
// r[inflight.complete]
/// Mark the computation as successfully completed.
pub(crate) fn complete(mut self, value: ArcAny, deps: Arc<[Dep]>, changed_at: Revision) {
self.entry.complete(value, deps, changed_at);
Expand All @@ -244,6 +254,7 @@ impl InFlightGuard {
IN_FLIGHT_REGISTRY.remove(&self.key);
}

// r[inflight.fail]
/// Mark the computation as failed.
pub(crate) fn fail(mut self, error: Arc<PicanteError>) {
self.entry.fail(error);
Expand All @@ -252,6 +263,7 @@ impl InFlightGuard {
}
}

// r[inflight.cancel]
impl Drop for InFlightGuard {
fn drop(&mut self) {
if !self.completed {
Expand All @@ -263,6 +275,7 @@ impl Drop for InFlightGuard {
}
}

// r[inflight.try-lead]
/// Try to become the leader for a computation, or get the existing entry if
/// someone else is already computing.
pub(crate) fn try_lead(key: InFlightKey) -> TryLeadResult {
Expand Down
Loading