From 982282c92ceb7215c8b8fa238bb8d4ccf1d1d8c7 Mon Sep 17 00:00:00 2001 From: FreshSoftware4 <99464939+FreshSoftware4@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:59:40 -0700 Subject: [PATCH 1/3] Add non-sovereign native upper projections --- .github/workflows/native-core.yml | 5 +- Cargo.toml | 1 + ...ATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens | 58 ++++ l64-native/src/graph/construction.rs | 5 +- l64-native/src/graph/derived.rs | 2 +- l64-native/src/kernel/equality/validation.rs | 9 +- l64-projection/Cargo.toml | 8 + l64-projection/README.md | 15 + l64-projection/examples/native_projection.rs | 29 ++ l64-projection/src/atlas.rs | 101 +++++++ l64-projection/src/certification.rs | 90 ++++++ l64-projection/src/lib.rs | 23 ++ l64-projection/src/order.rs | 9 + l64-projection/src/replay.rs | 54 ++++ l64-projection/src/report.rs | 76 ++++++ l64-projection/src/research.rs | 87 ++++++ l64-projection/src/set.rs | 109 ++++++++ l64-projection/src/source.rs | 172 ++++++++++++ l64-projection/tests/architecture.rs | 54 ++++ l64-projection/tests/projection.rs | 256 ++++++++++++++++++ 20 files changed, 1155 insertions(+), 8 deletions(-) create mode 100644 LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens create mode 100644 l64-projection/Cargo.toml create mode 100644 l64-projection/README.md create mode 100644 l64-projection/examples/native_projection.rs create mode 100644 l64-projection/src/atlas.rs create mode 100644 l64-projection/src/certification.rs create mode 100644 l64-projection/src/lib.rs create mode 100644 l64-projection/src/order.rs create mode 100644 l64-projection/src/replay.rs create mode 100644 l64-projection/src/report.rs create mode 100644 l64-projection/src/research.rs create mode 100644 l64-projection/src/set.rs create mode 100644 l64-projection/src/source.rs create mode 100644 l64-projection/tests/architecture.rs create mode 100644 l64-projection/tests/projection.rs diff --git a/.github/workflows/native-core.yml b/.github/workflows/native-core.yml index cf66d52..0373960 100644 --- a/.github/workflows/native-core.yml +++ b/.github/workflows/native-core.yml @@ -9,6 +9,7 @@ on: - Cargo.toml - Cargo.lock - l64-native/** + - l64-projection/** - l64-cli/** - LOCUS64_*_RAIL.athens - LOCUS64_*_CHANGE_CHAIN.athens @@ -39,5 +40,5 @@ jobs: - name: Test workspace run: cargo test -q - - name: Lint native core - run: cargo clippy -p l64-native --all-targets -- -D warnings + - name: Lint native projection spine + run: cargo clippy -p l64-native -p l64-projection --all-targets -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index 2f07a79..90cf3c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "l64-native", + "l64-projection", "l64-core", "l64-locus", "l64-research", diff --git a/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens b/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens new file mode 100644 index 0000000..0f44472 --- /dev/null +++ b/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens @@ -0,0 +1,58 @@ +ATHENS_DEVELOPMENT_RAIL v1 +field=key=projection_authority;value=non_authoritative +field=key=rail_version;value=1 +field=key=schema_version;value=1 +gate=id=projection-contract-green +gate=id=read-only-query-green +gate=id=atlas-view-green +gate=id=certification-view-green +gate=id=replay-view-green +gate=id=report-research-view-green +gate=id=deterministic-regeneration-green +gate=id=native-upper-projection-green +stage=id=projection-contract +stage_field=stage=projection-contract;key=required_gates;value=projection-contract-green +stage_field=stage=projection-contract;key=status;value=complete +stage=id=read-only-native-query +stage_field=stage=read-only-native-query;key=depends_on;value=projection-contract +stage_field=stage=read-only-native-query;key=required_gates;value=read-only-query-green +stage_field=stage=read-only-native-query;key=status;value=complete +stage=id=atlas-candidate-view +stage_field=stage=atlas-candidate-view;key=depends_on;value=read-only-native-query +stage_field=stage=atlas-candidate-view;key=required_gates;value=atlas-view-green +stage_field=stage=atlas-candidate-view;key=status;value=complete +stage=id=certification-burden-view +stage_field=stage=certification-burden-view;key=depends_on;value=atlas-candidate-view +stage_field=stage=certification-burden-view;key=required_gates;value=certification-view-green +stage_field=stage=certification-burden-view;key=status;value=complete +stage=id=replay-causal-view +stage_field=stage=replay-causal-view;key=depends_on;value=certification-burden-view +stage_field=stage=replay-causal-view;key=required_gates;value=replay-view-green +stage_field=stage=replay-causal-view;key=status;value=complete +stage=id=report-research-view +stage_field=stage=report-research-view;key=depends_on;value=replay-causal-view +stage_field=stage=report-research-view;key=required_gates;value=report-research-view-green +stage_field=stage=report-research-view;key=status;value=complete +stage=id=deterministic-regeneration +stage_field=stage=deterministic-regeneration;key=depends_on;value=report-research-view +stage_field=stage=deterministic-regeneration;key=required_gates;value=deterministic-regeneration-green +stage_field=stage=deterministic-regeneration;key=status;value=complete +stage=id=native-upper-projection-closure +stage_field=stage=native-upper-projection-closure;key=depends_on;value=deterministic-regeneration +stage_field=stage=native-upper-projection-closure;key=required_gates;value=native-upper-projection-green +stage_field=stage=native-upper-projection-closure;key=status;value=current +history=from_status=current;gates=projection-contract-green;mode=linear_advance;stage_id=projection-contract;to_status=complete +history=evidence=local%3Aprojection-source-binding%2Bnonmutation%2Bno-storage%2Bsingle-native-dependency;kind=dogfood_promotion_receipt;stage_id=projection-contract +history=from_status=current;gates=read-only-query-green;mode=linear_advance;stage_id=read-only-native-query;to_status=complete +history=evidence=local%3Acontext%2Bvisible-node%2Broute%2Bequality-evidence-read-surface;kind=dogfood_promotion_receipt;stage_id=read-only-native-query +history=from_status=current;gates=atlas-view-green;mode=linear_advance;stage_id=atlas-candidate-view;to_status=complete +history=evidence=local%3Atyped-operation-and-equality-candidates%2Bnative-route-resolution;kind=dogfood_promotion_receipt;stage_id=atlas-candidate-view +history=from_status=current;gates=certification-view-green;mode=linear_advance;stage_id=certification-burden-view;to_status=complete +history=evidence=local%3Aclosed-open-invalid%2Bcontext-discharge-without-authority-rewrite;kind=dogfood_promotion_receipt;stage_id=certification-burden-view +history=from_status=current;gates=replay-view-green;mode=linear_advance;stage_id=replay-causal-view;to_status=complete +history=evidence=local%3Ajournal-bound-replay%2Bsource-route-causality%2Bjournal-divergence-rejection;kind=dogfood_promotion_receipt;stage_id=replay-causal-view +history=from_status=current;gates=report-research-view-green;mode=linear_advance;stage_id=report-research-view;to_status=complete +history=evidence=local%3Adeterministic-counts%2Bobligation-and-invalid-routes%2Baffected-set-ranking;kind=dogfood_promotion_receipt;stage_id=report-research-view +history=from_status=current;gates=deterministic-regeneration-green;mode=linear_advance;stage_id=deterministic-regeneration;to_status=complete +history=evidence=local%3A56-tests%2Bstrict-clippy%2Bformat%2Blive-projection-execution%2Bforged-and-stale-rejection;kind=dogfood_promotion_receipt;stage_id=deterministic-regeneration +END diff --git a/l64-native/src/graph/construction.rs b/l64-native/src/graph/construction.rs index e401956..6291a9f 100644 --- a/l64-native/src/graph/construction.rs +++ b/l64-native/src/graph/construction.rs @@ -58,6 +58,10 @@ impl Graph { self.contexts.len() } + pub fn context(&self, id: ContextId) -> Option<&ContextDelta> { + self.contexts.get(id as usize) + } + pub fn journal_len(&self) -> usize { self.journal.len() } @@ -339,5 +343,4 @@ impl Graph { commitment: after, } } - } diff --git a/l64-native/src/graph/derived.rs b/l64-native/src/graph/derived.rs index 560c39b..424b707 100644 --- a/l64-native/src/graph/derived.rs +++ b/l64-native/src/graph/derived.rs @@ -57,7 +57,7 @@ impl Graph { Ok(affected.into_iter().collect()) } - fn visible_nodes(&self, context: ContextId) -> Result, Obstruction> { + pub fn visible_nodes(&self, context: ContextId) -> Result, Obstruction> { self.ensure_context(context)?; let mut contexts = Vec::new(); let mut cursor = context; diff --git a/l64-native/src/kernel/equality/validation.rs b/l64-native/src/kernel/equality/validation.rs index 4f612d2..f894f78 100644 --- a/l64-native/src/kernel/equality/validation.rs +++ b/l64-native/src/kernel/equality/validation.rs @@ -47,7 +47,6 @@ impl Graph { Ok(()) } - pub(crate) fn validate_equality_rule( &self, context: ContextId, @@ -184,12 +183,12 @@ impl Graph { self.equality_parts(premise) } - pub(crate) fn equality_witness_for(&self, judgment: NodeId) -> Option { + pub fn equality_witness_for(&self, judgment: NodeId) -> Option { let route = self.route_for_node(judgment)?; self.resolve(&route.composed(EVIDENCE_LOCUS)) } - fn route_for_node(&self, node: NodeId) -> Option<&Route> { + pub fn route_for_node(&self, node: NodeId) -> Option<&Route> { self.routes_raw() .iter() .find_map(|(route, candidate)| (*candidate == node).then_some(route)) @@ -213,7 +212,9 @@ impl Graph { fn same_equality_sort(&self, left: &crate::Node, right: &crate::Node) -> bool { match (left.ty(), right.ty()) { (Some(left_ty), Some(right_ty)) => left_ty == right_ty, - (None, None) => self.equality_endpoint_allowed(left) && self.equality_endpoint_allowed(right), + (None, None) => { + self.equality_endpoint_allowed(left) && self.equality_endpoint_allowed(right) + } _ => false, } } diff --git a/l64-projection/Cargo.toml b/l64-projection/Cargo.toml new file mode 100644 index 0000000..c3edf3f --- /dev/null +++ b/l64-projection/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "l64-projection" +edition.workspace = true +version.workspace = true +license.workspace = true + +[dependencies] +l64-native = { path = "../l64-native" } diff --git a/l64-projection/README.md b/l64-projection/README.md new file mode 100644 index 0000000..e876655 --- /dev/null +++ b/l64-projection/README.md @@ -0,0 +1,15 @@ +# l64-projection + +`l64-projection` derives read-only upper views from `l64-native::Graph` authority. + +It provides five coordinated views over one native state and context: + +- atlas candidates from executable operation and equality nodes; +- certification burdens from context-relative closure and native evidence; +- replay steps from the native journal; +- deterministic aggregate reporting; +- ranked research candidates from open and invalid reverse-reachable structure. + +Every projection carries its native state commitment, context, structural counts, journal length, and projection version. Verification rebuilds the complete view from the graph and requires exact equality. The crate has no storage, registry, cache, import, promotion, serialization, hashing, or alternate graph dependency. + +Projection records are expendable. Native authority remains canonical. diff --git a/l64-projection/examples/native_projection.rs b/l64-projection/examples/native_projection.rs new file mode 100644 index 0000000..f3c593d --- /dev/null +++ b/l64-projection/examples/native_projection.rs @@ -0,0 +1,29 @@ +use l64_native::{Dimension, Graph, LocusWord, Proposal, ROOT_CONTEXT, Route}; +use l64_projection::ProjectionSet; + +fn route(index: u64) -> Route { + Route::root(LocusWord(0x50524f4a45435431)).composed(LocusWord(index)) +} + +fn main() { + let mut graph = Graph::new(); + let scalar = graph.declare_atom_type(route(1), LocusWord(0x52)).unwrap(); + let quantity = graph + .declare_quantity_type(route(2), scalar, Dimension::new([0, 0, 0, 0, 0, 0, 0])) + .unwrap(); + let value = graph + .insert_value(route(3), ROOT_CONTEXT, quantity) + .unwrap(); + graph + .transact(Proposal::square_root( + route(4), + ROOT_CONTEXT, + value, + quantity, + )) + .unwrap(); + + let projection = ProjectionSet::derive(&graph, ROOT_CONTEXT, 8).unwrap(); + projection.verify(&graph).unwrap(); + print!("{}", projection.render_text()); +} diff --git a/l64-projection/src/atlas.rs b/l64-projection/src/atlas.rs new file mode 100644 index 0000000..040be18 --- /dev/null +++ b/l64-projection/src/atlas.rs @@ -0,0 +1,101 @@ +use crate::{ + EvidenceRef, PortRef, ProjectionError, ProjectionSource, SourceRef, closure_rank, evidence_ref, + source_ref, +}; +use l64_native::{ClosureState, ContextId, Graph, OpCode, Route}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum AtlasCandidateKind { + Operation, + Equality, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AtlasCandidate { + pub kind: AtlasCandidateKind, + pub source: SourceRef, + pub opcode: OpCode, + pub closure: ClosureState, + pub output_type: Option, + pub ports: Vec, + pub direct_dependents: Vec, + pub canonical_route: Option, + pub evidence: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AtlasView { + pub source: ProjectionSource, + pub candidates: Vec, +} + +impl AtlasView { + pub fn derive(graph: &Graph, context: ContextId) -> Result { + let source = ProjectionSource::capture(graph, context)?; + let mut candidates = Vec::new(); + for node in graph.visible_nodes(context)? { + let record = graph.node(node).ok_or(ProjectionError::UnknownNode(node))?; + let kind = match record.opcode() { + OpCode::Compose + | OpCode::MatMul + | OpCode::Add + | OpCode::Multiply + | OpCode::Divide + | OpCode::Sqrt => AtlasCandidateKind::Operation, + OpCode::TypeEquality => AtlasCandidateKind::Equality, + _ => continue, + }; + let source = source_ref(graph, node)?; + let output_type = record.ty().map(|id| source_ref(graph, id)).transpose()?; + let ports = graph + .ports(node) + .ok_or(ProjectionError::UnknownNode(node))? + .iter() + .map(|port| { + Ok(PortRef { + role: port.role(), + ordinal: port.ordinal(), + target: source_ref(graph, port.target())?, + }) + }) + .collect::, ProjectionError>>()?; + let mut direct_dependents = graph + .direct_dependents(node)? + .iter() + .copied() + .map(|id| source_ref(graph, id)) + .collect::, ProjectionError>>()?; + direct_dependents.sort(); + let canonical_route = graph.canonical_representative_route(context, node).ok(); + let evidence = evidence_ref(graph, context, node)?; + candidates.push(AtlasCandidate { + kind, + source, + opcode: record.opcode(), + closure: graph.closure_state(context, node)?, + output_type, + ports, + direct_dependents, + canonical_route, + evidence, + }); + } + candidates.sort_by(|left, right| { + closure_rank(left.closure) + .cmp(&closure_rank(right.closure)) + .then_with(|| left.kind.cmp(&right.kind)) + .then_with(|| left.source.route.cmp(&right.source.route)) + }); + Ok(Self { source, candidates }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } +} diff --git a/l64-projection/src/certification.rs b/l64-projection/src/certification.rs new file mode 100644 index 0000000..d4c31a6 --- /dev/null +++ b/l64-projection/src/certification.rs @@ -0,0 +1,90 @@ +use crate::{AtlasView, ProjectionError, ProjectionSource, SourceRef}; +use l64_native::{ClosureState, ContextId, Graph, OpCode}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum BurdenState { + Discharged, + Open, + Invalid, + MissingEvidence, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CertificationBurden { + pub subject: SourceRef, + pub closure: ClosureState, + pub state: BurdenState, + pub judgment: Option, + pub evidence: Option, + pub evidence_opcode: Option, + pub dependencies: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CertificationView { + pub source: ProjectionSource, + pub burdens: Vec, +} + +impl CertificationView { + pub fn derive(graph: &Graph, context: ContextId) -> Result { + let source = ProjectionSource::capture(graph, context)?; + let atlas = AtlasView::derive(graph, context)?; + let mut burdens = Vec::with_capacity(atlas.candidates.len()); + for candidate in atlas.candidates { + let closure = candidate.closure; + let (judgment, evidence, evidence_opcode) = candidate + .evidence + .as_ref() + .map(|item| { + ( + item.judgment.clone(), + Some(item.evidence.clone()), + Some(item.opcode), + ) + }) + .unwrap_or((None, None, None)); + let state = match (closure, evidence_opcode) { + (ClosureState::Invalid, _) => BurdenState::Invalid, + (ClosureState::Open, _) => BurdenState::Open, + (ClosureState::Closed, Some(_)) => BurdenState::Discharged, + (ClosureState::Closed, None) => BurdenState::MissingEvidence, + }; + let mut dependencies = candidate + .ports + .into_iter() + .map(|port| port.target) + .collect::>(); + if let Some(output_type) = candidate.output_type { + dependencies.push(output_type); + } + dependencies.sort(); + dependencies.dedup(); + burdens.push(CertificationBurden { + subject: candidate.source, + closure, + state, + judgment, + evidence, + evidence_opcode, + dependencies, + }); + } + burdens.sort_by(|left, right| { + left.state + .cmp(&right.state) + .then_with(|| left.subject.route.cmp(&right.subject.route)) + }); + Ok(Self { source, burdens }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } +} diff --git a/l64-projection/src/lib.rs b/l64-projection/src/lib.rs new file mode 100644 index 0000000..076bf51 --- /dev/null +++ b/l64-projection/src/lib.rs @@ -0,0 +1,23 @@ +#![forbid(unsafe_code)] + +mod atlas; +mod certification; +mod order; +mod replay; +mod report; +mod research; +mod set; +mod source; + +pub use atlas::{AtlasCandidate, AtlasCandidateKind, AtlasView}; +pub use certification::{BurdenState, CertificationBurden, CertificationView}; +pub use replay::{ReplayStep, ReplayView}; +pub use report::{OpcodeCount, ReportView}; +pub use research::{ResearchCandidate, ResearchView}; +pub use set::ProjectionSet; +pub use source::{EvidenceRef, PortRef, ProjectionError, ProjectionSource, SourceRef}; + +pub const PROJECTION_VERSION: u16 = 1; + +pub(crate) use order::closure_rank; +pub(crate) use source::{evidence_ref, source_ref}; diff --git a/l64-projection/src/order.rs b/l64-projection/src/order.rs new file mode 100644 index 0000000..15bf0f8 --- /dev/null +++ b/l64-projection/src/order.rs @@ -0,0 +1,9 @@ +use l64_native::ClosureState; + +pub(crate) fn closure_rank(state: ClosureState) -> u8 { + match state { + ClosureState::Invalid => 0, + ClosureState::Open => 1, + ClosureState::Closed => 2, + } +} diff --git a/l64-projection/src/replay.rs b/l64-projection/src/replay.rs new file mode 100644 index 0000000..a0bfc20 --- /dev/null +++ b/l64-projection/src/replay.rs @@ -0,0 +1,54 @@ +use crate::{ProjectionError, ProjectionSource, SourceRef, source_ref}; +use l64_native::{ContextId, Graph, OpCode}; +use std::collections::BTreeSet; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ReplayStep { + pub event: u32, + pub operation: OpCode, + pub subject: SourceRef, + pub before: [u8; 32], + pub after: [u8; 32], + pub parent: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ReplayView { + pub source: ProjectionSource, + pub steps: Vec, +} + +impl ReplayView { + pub fn derive(graph: &Graph, context: ContextId) -> Result { + let source = ProjectionSource::capture(graph, context)?; + let visible = graph + .visible_nodes(context)? + .into_iter() + .collect::>(); + let mut steps = Vec::new(); + for (event, item) in graph.journal().iter().enumerate() { + if !visible.contains(&item.subject()) { + continue; + } + steps.push(ReplayStep { + event: event as u32, + operation: item.operation(), + subject: source_ref(graph, item.subject())?, + before: item.before(), + after: item.after(), + parent: item.parent(), + }); + } + Ok(Self { source, steps }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } +} diff --git a/l64-projection/src/report.rs b/l64-projection/src/report.rs new file mode 100644 index 0000000..b452405 --- /dev/null +++ b/l64-projection/src/report.rs @@ -0,0 +1,76 @@ +use crate::{ProjectionError, ProjectionSource, source_ref}; +use l64_native::{ClosureState, ContextId, Graph, OpCode, Route}; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OpcodeCount { + pub opcode: OpCode, + pub count: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ReportView { + pub source: ProjectionSource, + pub closed: usize, + pub open: usize, + pub invalid: usize, + pub opcode_counts: Vec, + pub obligation_routes: Vec, + pub invalid_routes: Vec, +} + +impl ReportView { + pub fn derive(graph: &Graph, context: ContextId) -> Result { + let source = ProjectionSource::capture(graph, context)?; + let mut closed = 0; + let mut open = 0; + let mut invalid = 0; + let mut counts = BTreeMap::::new(); + let mut obligation_routes = Vec::new(); + let mut invalid_routes = Vec::new(); + for node in graph.visible_nodes(context)? { + let record = graph.node(node).ok_or(ProjectionError::UnknownNode(node))?; + let state = graph.closure_state(context, node)?; + match state { + ClosureState::Closed => closed += 1, + ClosureState::Open => open += 1, + ClosureState::Invalid => { + invalid += 1; + invalid_routes.push(source_ref(graph, node)?.route); + } + } + let entry = counts + .entry(record.opcode() as u16) + .or_insert((record.opcode(), 0)); + entry.1 += 1; + if record.opcode() == OpCode::Obligation { + obligation_routes.push(source_ref(graph, node)?.route); + } + } + obligation_routes.sort(); + invalid_routes.sort(); + let opcode_counts = counts + .into_values() + .map(|(opcode, count)| OpcodeCount { opcode, count }) + .collect(); + Ok(Self { + source, + closed, + open, + invalid, + opcode_counts, + obligation_routes, + invalid_routes, + }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } +} diff --git a/l64-projection/src/research.rs b/l64-projection/src/research.rs new file mode 100644 index 0000000..b22ebad --- /dev/null +++ b/l64-projection/src/research.rs @@ -0,0 +1,87 @@ +use crate::{ProjectionError, ProjectionSource, SourceRef, closure_rank, evidence_ref, source_ref}; +use l64_native::{ClosureState, ContextId, Graph, OpCode, Route}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResearchCandidate { + pub source: SourceRef, + pub opcode: OpCode, + pub closure: ClosureState, + pub direct_dependents: usize, + pub affected_nodes: usize, + pub canonical_route: Option, + pub evidence: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResearchView { + pub source: ProjectionSource, + pub total_candidates: usize, + pub limit: usize, + pub candidates: Vec, +} + +impl ResearchView { + pub fn derive( + graph: &Graph, + context: ContextId, + limit: usize, + ) -> Result { + let source = ProjectionSource::capture(graph, context)?; + let mut candidates = Vec::new(); + for node in graph.visible_nodes(context)? { + let record = graph.node(node).ok_or(ProjectionError::UnknownNode(node))?; + let closure = graph.closure_state(context, node)?; + let affected_nodes = graph.affected_nodes(node)?.len(); + let noteworthy = closure != ClosureState::Closed + || matches!( + record.opcode(), + OpCode::Compose + | OpCode::MatMul + | OpCode::Add + | OpCode::Multiply + | OpCode::Divide + | OpCode::Sqrt + | OpCode::TypeEquality + ) && affected_nodes > 1; + if !noteworthy { + continue; + } + candidates.push(ResearchCandidate { + source: source_ref(graph, node)?, + opcode: record.opcode(), + closure, + direct_dependents: graph.direct_dependents(node)?.len(), + affected_nodes, + canonical_route: graph.canonical_representative_route(context, node).ok(), + evidence: evidence_ref(graph, context, node)?.map(|item| item.evidence), + }); + } + candidates.sort_by(|left, right| { + closure_rank(left.closure) + .cmp(&closure_rank(right.closure)) + .then_with(|| right.affected_nodes.cmp(&left.affected_nodes)) + .then_with(|| right.direct_dependents.cmp(&left.direct_dependents)) + .then_with(|| left.source.route.cmp(&right.source.route)) + }); + let total_candidates = candidates.len(); + if limit < candidates.len() { + candidates.truncate(limit); + } + Ok(Self { + source, + total_candidates, + limit, + candidates, + }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context, self.limit)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } +} diff --git a/l64-projection/src/set.rs b/l64-projection/src/set.rs new file mode 100644 index 0000000..39765c7 --- /dev/null +++ b/l64-projection/src/set.rs @@ -0,0 +1,109 @@ +use crate::{ + AtlasView, CertificationView, ProjectionError, ProjectionSource, ReplayView, ReportView, + ResearchView, +}; +use l64_native::{ContextId, Graph, Route}; +use std::fmt::Write as _; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectionSet { + pub source: ProjectionSource, + pub atlas: AtlasView, + pub certification: CertificationView, + pub replay: ReplayView, + pub report: ReportView, + pub research: ResearchView, +} + +impl ProjectionSet { + pub fn derive( + graph: &Graph, + context: ContextId, + research_limit: usize, + ) -> Result { + let source = ProjectionSource::capture(graph, context)?; + Ok(Self { + source, + atlas: AtlasView::derive(graph, context)?, + certification: CertificationView::derive(graph, context)?, + replay: ReplayView::derive(graph, context)?, + report: ReportView::derive(graph, context)?, + research: ResearchView::derive(graph, context, research_limit)?, + }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + self.source.verify(graph)?; + let rebuilt = Self::derive(graph, self.source.context, self.research.limit)?; + if &rebuilt == self { + Ok(()) + } else { + Err(ProjectionError::ProjectionMismatch) + } + } + + pub fn render_text(&self) -> String { + let mut out = String::new(); + let _ = writeln!(out, "L64 NATIVE PROJECTION v{}", self.source.version); + let _ = writeln!(out, "context={}", self.source.context); + let _ = writeln!(out, "commitment={}", hex(&self.source.commitment)); + let _ = writeln!(out, "nodes={}", self.source.node_count); + let _ = writeln!(out, "contexts={}", self.source.context_count); + let _ = writeln!(out, "journal={}", self.source.journal_len); + let _ = writeln!(out, "atlas_candidates={}", self.atlas.candidates.len()); + let _ = writeln!( + out, + "certification_burdens={}", + self.certification.burdens.len() + ); + let _ = writeln!(out, "replay_steps={}", self.replay.steps.len()); + let _ = writeln!(out, "closed={}", self.report.closed); + let _ = writeln!(out, "open={}", self.report.open); + let _ = writeln!(out, "invalid={}", self.report.invalid); + let _ = writeln!( + out, + "research_candidates={}/{}", + self.research.candidates.len(), + self.research.total_candidates + ); + for candidate in &self.research.candidates { + let _ = writeln!( + out, + "research route={} opcode={:?} closure={:?} affected={} dependents={}", + format_route(&candidate.source.route), + candidate.opcode, + candidate.closure, + candidate.affected_nodes, + candidate.direct_dependents + ); + } + out + } +} +fn format_route(route: &Route) -> String { + let mut out = format!("{:016x}", route.domain().0); + for word in route.tail() { + let _ = write!(out, "/{:016x}", word.0); + } + out +} + +fn hex(bytes: &[u8]) -> String { + let mut out = String::with_capacity(bytes.len() * 2); + for byte in bytes { + let _ = write!(out, "{byte:02x}"); + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + use l64_native::LocusWord; + + #[test] + fn route_format_is_numeric_and_stable() { + let route = Route::root(LocusWord(0x12)).composed(LocusWord(0x34)); + assert_eq!(format_route(&route), "0000000000000012/0000000000000034"); + } +} diff --git a/l64-projection/src/source.rs b/l64-projection/src/source.rs new file mode 100644 index 0000000..ea32704 --- /dev/null +++ b/l64-projection/src/source.rs @@ -0,0 +1,172 @@ +use crate::PROJECTION_VERSION; +use l64_native::{ClosureState, ContextId, Graph, NodeId, Obstruction, OpCode, PortRole, Route}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProjectionError { + UnknownContext(ContextId), + UnknownNode(NodeId), + MissingRoute(NodeId), + Native(Obstruction), + VersionMismatch { expected: u16, actual: u16 }, + SourceMismatch, + ProjectionMismatch, +} + +impl From for ProjectionError { + fn from(value: Obstruction) -> Self { + Self::Native(value) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectionSource { + pub commitment: [u8; 32], + pub context: ContextId, + pub node_count: usize, + pub context_count: usize, + pub journal_len: usize, + pub version: u16, +} + +impl ProjectionSource { + pub fn capture(graph: &Graph, context: ContextId) -> Result { + graph + .context(context) + .ok_or(ProjectionError::UnknownContext(context))?; + Ok(Self { + commitment: graph.state_commitment(), + context, + node_count: graph.node_count(), + context_count: graph.context_count(), + journal_len: graph.journal_len(), + version: PROJECTION_VERSION, + }) + } + + pub fn verify(&self, graph: &Graph) -> Result<(), ProjectionError> { + if self.version != PROJECTION_VERSION { + return Err(ProjectionError::VersionMismatch { + expected: PROJECTION_VERSION, + actual: self.version, + }); + } + graph + .context(self.context) + .ok_or(ProjectionError::UnknownContext(self.context))?; + if self.commitment != graph.state_commitment() + || self.node_count != graph.node_count() + || self.context_count != graph.context_count() + || self.journal_len != graph.journal_len() + { + return Err(ProjectionError::SourceMismatch); + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SourceRef { + pub node: NodeId, + pub route: Route, + pub declared_context: ContextId, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PortRef { + pub role: PortRole, + pub ordinal: u16, + pub target: SourceRef, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EvidenceRef { + pub judgment: Option, + pub evidence: SourceRef, + pub opcode: OpCode, + pub closure: ClosureState, +} + +pub(crate) fn source_ref(graph: &Graph, node: NodeId) -> Result { + let record = graph.node(node).ok_or(ProjectionError::UnknownNode(node))?; + let route = graph + .route_for_node(node) + .cloned() + .ok_or(ProjectionError::MissingRoute(node))?; + Ok(SourceRef { + node, + route, + declared_context: record.context(), + }) +} + +pub(crate) fn evidence_ref( + graph: &Graph, + context: ContextId, + subject: NodeId, +) -> Result, ProjectionError> { + let subject_node = graph + .node(subject) + .ok_or(ProjectionError::UnknownNode(subject))?; + if subject_node.opcode() == OpCode::TypeEquality { + let Some(evidence) = graph.equality_witness_for(subject) else { + return Ok(None); + }; + let evidence_node = graph + .node(evidence) + .ok_or(ProjectionError::UnknownNode(evidence))?; + return Ok(Some(EvidenceRef { + judgment: Some(source_ref(graph, subject)?), + evidence: source_ref(graph, evidence)?, + opcode: evidence_node.opcode(), + closure: graph.closure_state(context, evidence)?, + })); + } + + let mut judgments = graph + .direct_dependents(subject)? + .iter() + .copied() + .filter(|candidate| { + let Some(node) = graph.node(*candidate) else { + return false; + }; + if node.opcode() != OpCode::TypeJudgment { + return false; + } + graph + .ports(*candidate) + .and_then(|ports| ports.first()) + .map(|port| port.role() == PortRole::Subject && port.target() == subject) + .unwrap_or(false) + }) + .collect::>(); + judgments.sort_by_key(|node| graph.route_for_node(*node).cloned()); + let Some(judgment) = judgments.first().copied() else { + return Ok(None); + }; + let mut evidence = graph + .direct_dependents(judgment)? + .iter() + .copied() + .filter(|candidate| { + let Some(node) = graph.node(*candidate) else { + return false; + }; + node.ty() == Some(judgment) + && matches!(node.opcode(), OpCode::KernelWitness | OpCode::Obligation) + }) + .collect::>(); + evidence.sort_by_key(|node| graph.route_for_node(*node).cloned()); + let Some(evidence) = evidence.first().copied() else { + return Ok(None); + }; + let evidence_node = graph + .node(evidence) + .ok_or(ProjectionError::UnknownNode(evidence))?; + Ok(Some(EvidenceRef { + judgment: Some(source_ref(graph, judgment)?), + evidence: source_ref(graph, evidence)?, + opcode: evidence_node.opcode(), + closure: graph.closure_state(context, evidence)?, + })) +} diff --git a/l64-projection/tests/architecture.rs b/l64-projection/tests/architecture.rs new file mode 100644 index 0000000..711fa56 --- /dev/null +++ b/l64-projection/tests/architecture.rs @@ -0,0 +1,54 @@ +use std::{fs, path::Path}; + +#[test] +fn projection_crate_depends_only_on_native_authority() { + let manifest = fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml")) + .expect("read projection manifest"); + let dependency_block = manifest + .split_once("[dependencies]") + .map(|(_, tail)| tail) + .expect("dependency block"); + let dependencies = dependency_block + .lines() + .map(str::trim) + .filter(|line| !line.is_empty() && !line.starts_with('#')) + .collect::>(); + assert_eq!( + dependencies, + vec!["l64-native = { path = \"../l64-native\" }"] + ); +} + +#[test] +fn projection_source_contains_no_parallel_authority_machinery() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); + let mut source = String::new(); + for entry in fs::read_dir(root).expect("read source directory") { + let path = entry.expect("source entry").path(); + if path.extension().and_then(|value| value.to_str()) == Some("rs") { + source.push_str(&fs::read_to_string(path).expect("read source file")); + } + } + for forbidden in [ + "std::fs", + "File::", + "OpenOptions", + "serde", + "bincode", + "blake3", + "HashMap", + "Mutex", + "RwLock", + "thread::", + "spawn(", + "Registry", + "persist_", + "import_", + "promote_", + ] { + assert!( + !source.contains(forbidden), + "projection source contains forbidden authority token {forbidden}" + ); + } +} diff --git a/l64-projection/tests/projection.rs b/l64-projection/tests/projection.rs new file mode 100644 index 0000000..11743d9 --- /dev/null +++ b/l64-projection/tests/projection.rs @@ -0,0 +1,256 @@ +use l64_native::{ + ClosureState, ConstraintKind, Dimension, Graph, LocusWord, OpCode, Proposal, ROOT_CONTEXT, + Route, canonical_bytes, decode_canonical, +}; +use l64_projection::{BurdenState, ProjectionError, ProjectionSet}; + +fn route(index: u64) -> Route { + Route::root(LocusWord(0x50524f4a45435431)).composed(LocusWord(index)) +} + +struct Fixture { + graph: Graph, + subject: u32, + sqrt: u32, + downstream: u32, + independent: u32, +} + +fn fixture() -> Fixture { + let mut graph = Graph::new(); + let scalar = graph.declare_atom_type(route(1), LocusWord(0x52)).unwrap(); + let quantity = graph + .declare_quantity_type(route(2), scalar, Dimension::new([0, 0, 0, 0, 0, 0, 0])) + .unwrap(); + let subject = graph + .insert_value(route(3), ROOT_CONTEXT, quantity) + .unwrap(); + let peer = graph + .insert_value(route(4), ROOT_CONTEXT, quantity) + .unwrap(); + let sqrt = graph + .transact(Proposal::square_root( + route(5), + ROOT_CONTEXT, + subject, + quantity, + )) + .unwrap() + .node; + let downstream = graph + .transact(Proposal::multiply( + route(6), + ROOT_CONTEXT, + sqrt, + peer, + quantity, + )) + .unwrap() + .node; + let left = graph + .insert_value(route(7), ROOT_CONTEXT, quantity) + .unwrap(); + let right = graph + .insert_value(route(8), ROOT_CONTEXT, quantity) + .unwrap(); + let independent = graph + .transact(Proposal::add(route(9), ROOT_CONTEXT, left, right, quantity)) + .unwrap() + .node; + graph + .prove_reflexive_equality(route(10), ROOT_CONTEXT, independent) + .unwrap(); + Fixture { + graph, + subject, + sqrt, + downstream, + independent, + } +} + +#[test] +fn all_upper_views_are_derived_without_mutating_native_authority() { + let fixture = fixture(); + let before = canonical_bytes(&fixture.graph); + let commitment = fixture.graph.state_commitment(); + let counts = ( + fixture.graph.node_count(), + fixture.graph.port_count(), + fixture.graph.context_count(), + fixture.graph.journal_len(), + ); + + let projection = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 16).unwrap(); + projection.verify(&fixture.graph).unwrap(); + + assert_eq!(canonical_bytes(&fixture.graph), before); + assert_eq!(fixture.graph.state_commitment(), commitment); + assert_eq!( + ( + fixture.graph.node_count(), + fixture.graph.port_count(), + fixture.graph.context_count(), + fixture.graph.journal_len(), + ), + counts + ); + assert!( + projection + .atlas + .candidates + .iter() + .any(|item| item.source.node == fixture.sqrt && item.closure == ClosureState::Open) + ); + assert!( + projection + .certification + .burdens + .iter() + .any(|item| item.subject.node == fixture.sqrt && item.state == BurdenState::Open) + ); + assert!(!projection.replay.steps.is_empty()); + assert!(projection.report.open > 0); + assert!( + projection + .research + .candidates + .iter() + .any(|item| item.source.node == fixture.sqrt) + ); +} + +#[test] +fn regeneration_and_rendering_are_deterministic() { + let fixture = fixture(); + let first = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 8).unwrap(); + let second = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 8).unwrap(); + assert_eq!(first, second); + assert_eq!(first.render_text(), second.render_text()); +} + +#[test] +fn every_projection_reference_resolves_to_native_authority() { + let fixture = fixture(); + let projection = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 32).unwrap(); + for candidate in &projection.atlas.candidates { + assert_eq!( + fixture.graph.resolve(&candidate.source.route), + Some(candidate.source.node) + ); + for port in &candidate.ports { + assert_eq!( + fixture.graph.resolve(&port.target.route), + Some(port.target.node) + ); + } + for dependent in &candidate.direct_dependents { + assert_eq!( + fixture.graph.resolve(&dependent.route), + Some(dependent.node) + ); + } + if let Some(evidence) = &candidate.evidence { + assert_eq!( + fixture.graph.resolve(&evidence.evidence.route), + Some(evidence.evidence.node) + ); + if let Some(judgment) = &evidence.judgment { + assert_eq!(fixture.graph.resolve(&judgment.route), Some(judgment.node)); + } + } + } +} + +#[test] +fn stale_and_forged_projections_are_rejected() { + let mut fixture = fixture(); + let projection = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 8).unwrap(); + + let mut forged = projection.clone(); + forged.report.closed = forged.report.closed.saturating_add(1); + assert_eq!( + forged.verify(&fixture.graph), + Err(ProjectionError::ProjectionMismatch) + ); + + fixture + .graph + .declare_atom_type(route(100), LocusWord(0x99)) + .unwrap(); + assert_eq!( + projection.verify(&fixture.graph), + Err(ProjectionError::SourceMismatch) + ); +} + +#[test] +fn context_specific_views_preserve_open_closed_and_invalid_divergence() { + let mut positive = fixture(); + let cause = positive + .graph + .declare_constraint( + route(20), + ROOT_CONTEXT, + positive.subject, + ConstraintKind::NonNegative, + true, + ) + .unwrap(); + let child = positive.graph.extend_context(ROOT_CONTEXT, cause).unwrap(); + let root = ProjectionSet::derive(&positive.graph, ROOT_CONTEXT, 16).unwrap(); + let refined = ProjectionSet::derive(&positive.graph, child, 16).unwrap(); + let root_sqrt = root + .certification + .burdens + .iter() + .find(|item| item.subject.node == positive.sqrt) + .unwrap(); + let child_sqrt = refined + .certification + .burdens + .iter() + .find(|item| item.subject.node == positive.sqrt) + .unwrap(); + assert_eq!(root_sqrt.state, BurdenState::Open); + assert_eq!(child_sqrt.state, BurdenState::Discharged); + assert_eq!(child_sqrt.evidence_opcode, Some(OpCode::Obligation)); + assert_eq!(child_sqrt.closure, ClosureState::Closed); + assert_eq!(root.source.context, ROOT_CONTEXT); + assert_eq!(refined.source.context, child); + + let mut negative = fixture(); + let cause = negative + .graph + .declare_constraint( + route(30), + ROOT_CONTEXT, + negative.subject, + ConstraintKind::NonNegative, + false, + ) + .unwrap(); + let child = negative.graph.extend_context(ROOT_CONTEXT, cause).unwrap(); + let invalid = ProjectionSet::derive(&negative.graph, child, 16).unwrap(); + assert!( + invalid.certification.burdens.iter().any(|item| { + item.subject.node == negative.sqrt && item.state == BurdenState::Invalid + }) + ); + assert!(invalid.certification.burdens.iter().any(|item| { + item.subject.node == negative.downstream && item.state == BurdenState::Invalid + })); + assert!(!invalid.certification.burdens.iter().any(|item| { + item.subject.node == negative.independent && item.state == BurdenState::Invalid + })); +} + +#[test] +fn replay_projection_is_bound_to_the_actual_runtime_journal() { + let fixture = fixture(); + let projection = ProjectionSet::derive(&fixture.graph, ROOT_CONTEXT, 8).unwrap(); + let decoded = decode_canonical(&canonical_bytes(&fixture.graph)).unwrap(); + assert_eq!(decoded.state_commitment(), fixture.graph.state_commitment()); + assert_eq!(decoded.journal_len(), 0); + assert_eq!(projection.verify(&decoded), Err(ProjectionError::SourceMismatch)); +} From 0ad8acc52f35362262c383efd02cc455e6778489 Mon Sep 17 00:00:00 2001 From: FreshSoftware4 <99464939+FreshSoftware4@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:07:55 -0700 Subject: [PATCH 2/3] Restore rustfmt-stable projection test layout --- l64-projection/tests/projection.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/l64-projection/tests/projection.rs b/l64-projection/tests/projection.rs index 11743d9..72ace28 100644 --- a/l64-projection/tests/projection.rs +++ b/l64-projection/tests/projection.rs @@ -252,5 +252,8 @@ fn replay_projection_is_bound_to_the_actual_runtime_journal() { let decoded = decode_canonical(&canonical_bytes(&fixture.graph)).unwrap(); assert_eq!(decoded.state_commitment(), fixture.graph.state_commitment()); assert_eq!(decoded.journal_len(), 0); - assert_eq!(projection.verify(&decoded), Err(ProjectionError::SourceMismatch)); + assert_eq!( + projection.verify(&decoded), + Err(ProjectionError::SourceMismatch) + ); } From 8774e1c60b44756eef8b3fd57ad8dfa519e15e2d Mon Sep 17 00:00:00 2001 From: FreshSoftware4 <99464939+FreshSoftware4@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:12:00 -0700 Subject: [PATCH 3/3] Close native upper projection rail --- LOCUS64_EXECUTION_COHERENCE_RAIL.athens | 12 +++++++----- ...4_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens | 6 ++++-- l64-native/README.md | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/LOCUS64_EXECUTION_COHERENCE_RAIL.athens b/LOCUS64_EXECUTION_COHERENCE_RAIL.athens index c191c09..0f31927 100644 --- a/LOCUS64_EXECUTION_COHERENCE_RAIL.athens +++ b/LOCUS64_EXECUTION_COHERENCE_RAIL.athens @@ -1,8 +1,8 @@ ATHENS_DEVELOPMENT_RAIL v1 -field=key=current_stage;value=native-upper-projection -field=key=next_stage;value=legacy-authority-quarantine +field=key=current_stage;value=legacy-authority-quarantine +field=key=next_stage;value=complete field=key=projection_authority;value=non_authoritative -field=key=rail_version;value=4 +field=key=rail_version;value=5 field=key=schema_version;value=1 gate=id=incremental-closure-green gate=id=legacy-authority-quarantine-green @@ -23,15 +23,17 @@ stage_field=stage=incremental-closure;key=status;value=complete stage=id=native-upper-projection stage_field=stage=native-upper-projection;key=depends_on;value=incremental-closure stage_field=stage=native-upper-projection;key=required_gates;value=native-upper-projection-green -stage_field=stage=native-upper-projection;key=status;value=current +stage_field=stage=native-upper-projection;key=status;value=complete stage=id=legacy-authority-quarantine stage_field=stage=legacy-authority-quarantine;key=depends_on;value=native-upper-projection stage_field=stage=legacy-authority-quarantine;key=required_gates;value=legacy-authority-quarantine-green -stage_field=stage=legacy-authority-quarantine;key=status;value=next +stage_field=stage=legacy-authority-quarantine;key=status;value=current history=from_status=current;gates=native-constraint-core-green;mode=linear_advance;stage_id=native-constraint-core;to_status=complete history=evidence=github-cbcbfe5d9c4f34baf0ca54306c77a85ac970bd26-run-29990484222;kind=dogfood_promotion_receipt;stage_id=native-constraint-core history=from_status=current;gates=proof-producing-congruence-green;mode=linear_advance;stage_id=proof-producing-congruence;to_status=complete history=evidence=github-0a84bdd3dc232a0db1b79009c3bf99cfd4fb40cd-run-29997066639;kind=dogfood_promotion_receipt;stage_id=proof-producing-congruence history=from_status=current;gates=incremental-closure-green;mode=linear_advance;stage_id=incremental-closure;to_status=complete history=evidence=github-fd90966af2191e6d9ca5e14e17f0bcaca431c260-run-30009891592;kind=dogfood_promotion_receipt;stage_id=incremental-closure +history=from_status=current;gates=native-upper-projection-green;mode=linear_advance;stage_id=native-upper-projection;to_status=complete +history=evidence=github-0ad8acc52f35362262c383efd02cc455e6778489-run-30139859138;kind=dogfood_promotion_receipt;stage_id=native-upper-projection END diff --git a/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens b/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens index 0f44472..c5f3344 100644 --- a/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens +++ b/LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens @@ -1,6 +1,6 @@ ATHENS_DEVELOPMENT_RAIL v1 field=key=projection_authority;value=non_authoritative -field=key=rail_version;value=1 +field=key=rail_version;value=2 field=key=schema_version;value=1 gate=id=projection-contract-green gate=id=read-only-query-green @@ -40,7 +40,7 @@ stage_field=stage=deterministic-regeneration;key=status;value=complete stage=id=native-upper-projection-closure stage_field=stage=native-upper-projection-closure;key=depends_on;value=deterministic-regeneration stage_field=stage=native-upper-projection-closure;key=required_gates;value=native-upper-projection-green -stage_field=stage=native-upper-projection-closure;key=status;value=current +stage_field=stage=native-upper-projection-closure;key=status;value=complete history=from_status=current;gates=projection-contract-green;mode=linear_advance;stage_id=projection-contract;to_status=complete history=evidence=local%3Aprojection-source-binding%2Bnonmutation%2Bno-storage%2Bsingle-native-dependency;kind=dogfood_promotion_receipt;stage_id=projection-contract history=from_status=current;gates=read-only-query-green;mode=linear_advance;stage_id=read-only-native-query;to_status=complete @@ -55,4 +55,6 @@ history=from_status=current;gates=report-research-view-green;mode=linear_advance history=evidence=local%3Adeterministic-counts%2Bobligation-and-invalid-routes%2Baffected-set-ranking;kind=dogfood_promotion_receipt;stage_id=report-research-view history=from_status=current;gates=deterministic-regeneration-green;mode=linear_advance;stage_id=deterministic-regeneration;to_status=complete history=evidence=local%3A56-tests%2Bstrict-clippy%2Bformat%2Blive-projection-execution%2Bforged-and-stale-rejection;kind=dogfood_promotion_receipt;stage_id=deterministic-regeneration +history=from_status=current;gates=native-upper-projection-green;mode=linear_advance;stage_id=native-upper-projection-closure;to_status=complete +history=evidence=github-0ad8acc52f35362262c383efd02cc455e6778489-run-30139859138;kind=dogfood_promotion_receipt;stage_id=native-upper-projection-closure END diff --git a/l64-native/README.md b/l64-native/README.md index 5294fa3..318096b 100644 --- a/l64-native/README.md +++ b/l64-native/README.md @@ -57,7 +57,7 @@ State identity is the domain-separated BLAKE3 commitment of canonical native byt The existing `l64-cli` command names now route `L64R1` and `L64D` directly through this native path. Legacy RNA/DNA behavior is classified as compatibility/forensic ingress and is available explicitly through `l64-cli legacy ...`; ambient fallback remains temporarily available with a mandatory deprecation warning. -This remains additive. It does not yet implement native upper-stack projections or replacement of the legacy runtime, registry, certification, and old packet implementation internally. +This remains additive. Native upper projections now exist as read-only derivations in `l64-projection`; replacement and quarantine of the legacy runtime, registry, certification, and old packet implementation remain incomplete. The eighth boundary adds incremental closure without turning invalidation into a second authority database: @@ -70,3 +70,17 @@ The eighth boundary adds incremental closure without turning invalidation into a - closure queries do not alter canonical bytes, commitments, routes, contexts, or journal history. The derived reverse index is an in-memory accelerator only. It is excluded from RNA, DNA, state commitments, and authority identity. + +The ninth boundary derives the first native upper views without turning any view into authority: + +- `l64-projection` depends only on `l64-native`; +- atlas candidates are reconstructed from executable operations, equalities, ports, routes, closure, and native evidence; +- certification burdens distinguish discharged, open, invalid, and missing-evidence states without rewriting obligation nodes; +- replay is a deterministic view of the native journal and rejects a canonical decode that lacks that runtime history; +- reporting counts visible native structure and exposes obligation and invalid routes; +- research ranking follows open, invalid, and high-impact reverse-reachable structure; +- every view binds to the native commitment, context, structural counts, journal length, and projection version; +- verification rebuilds the complete view and requires exact equality; +- the projection crate contains no storage, registry, cache, alternate graph, import, promotion, serialization, or hash authority. + +`LOCUS64_NATIVE_UPPER_PROJECTION_CHANGE_CHAIN.athens` is complete on repository evidence. The parent execution rail has advanced to legacy authority quarantine.