Integrate release recovery, semantic review, coverage and Windows packaging

This commit is contained in:
Codex
2026-09-06 16:31:55 -07:00
parent c091f80e6e
commit 2b2d2bc18b
174 changed files with 27635 additions and 127 deletions
+99 -2
View File
@@ -10,7 +10,7 @@ use iced::{
use oa_core::*;
use std::collections::BTreeSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Action {
Component,
Input,
@@ -83,7 +83,7 @@ pub fn actions(p: &Project, id: Id) -> Vec<Action> {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ContractKind {
Input,
Output,
@@ -128,7 +128,104 @@ pub struct State {
primary_input: bool,
preview: Result<Vec<Command>, String>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct RecoveryState {
version: u32,
base: String,
action: Action,
subject: Id,
id: Id,
name: String,
kind: ContractKind,
orientation: ComponentPortOrientation,
oriented: bool,
boundary: Option<Id>,
source_port: PortChoice,
target_port: PortChoice,
incoming: BTreeSet<Id>,
outgoing: BTreeSet<Id>,
primary_input: bool,
}
#[cfg(test)]
mod recovery_tests {
use super::*;
#[test]
fn port_recovery_retains_authored_contract_and_identity_but_rebuilds_preview() {
let mut p = Project::empty();
let owner = p
.add_element(Kind::Function, Layer::Sa, "Receiver", None)
.unwrap();
let item = p
.add_element(Kind::ExchangeItem, Layer::Sa, "Payload", None)
.unwrap();
let mut state = State::new(&p, owner, Action::Input).unwrap();
state.name = " exact pending port ".into();
state.incoming.insert(item);
state.primary_input = false;
state.preview = Err("An old transient preview must not be restored".into());
let saved = state.recovery_snapshot().unwrap();
assert!(saved.get("preview").is_none());
let restored = State::from_recovery(&p, saved.clone()).unwrap();
assert_eq!(restored.recovery_snapshot().unwrap(), saved);
assert_eq!(restored.id, state.id);
assert!(restored.preview.is_ok());
p.name.push('!');
assert!(State::from_recovery(&p, saved).is_err());
}
}
impl State {
pub fn recovery_snapshot(&self) -> Result<serde_json::Value, String> {
serde_json::to_value(RecoveryState {
version: 1,
base: transitions::project_fingerprint(&self.baseline),
action: self.action,
subject: self.subject,
id: self.id,
name: self.name.clone(),
kind: self.kind,
orientation: self.orientation,
oriented: self.oriented,
boundary: self.boundary.as_ref().map(|c| c.id),
source_port: self.source_port.choice,
target_port: self.target_port.choice,
incoming: self.incoming.clone(),
outgoing: self.outgoing.clone(),
primary_input: self.primary_input,
})
.map_err(|e| e.to_string())
}
pub fn from_recovery(p: &Project, value: serde_json::Value) -> Result<Self, String> {
let saved: RecoveryState = serde_json::from_value(value).map_err(|e| e.to_string())?;
if saved.version != 1 || saved.base != transitions::project_fingerprint(p) {
return Err(
"Port workflow recovery does not match the accepted model identity and revision."
.into(),
);
}
if saved.incoming.len().saturating_add(saved.outgoing.len())
> crate::source_workspace::LIMITS.model.max_records
{
return Err("Recovered port draft exceeds item limits.".into());
}
let mut state = Self::new(p, saved.subject, saved.action)?;
state.id = saved.id;
state.name = saved.name;
state.kind = saved.kind;
state.orientation = saved.orientation;
state.oriented = saved.oriented;
state.boundary = saved.boundary.map(|id| Choice {
id,
label: p.named(id).into(),
});
state.source_port = workflow::recovery::endpoint(p, saved.source_port);
state.target_port = workflow::recovery::endpoint(p, saved.target_port);
state.incoming = saved.incoming;
state.outgoing = saved.outgoing;
state.primary_input = saved.primary_input;
state.preview = state.plan(p);
Ok(state)
}
pub fn new(p: &Project, subject: Id, action: Action) -> Result<Self, String> {
if !actions(p, subject).contains(&action) {
return Err("This port action is unavailable for the selected element.".into());