Bind deployment to one witnessed release

This commit is contained in:
Meta Vens 2026-07-16 03:46:31 +01:00
commit 7970347af1
15 changed files with 1000 additions and 141 deletions

View file

@ -23,6 +23,10 @@ name = "epiphany-workspace-coverage-recovery-smoke"
path = "src/bin/epiphany-workspace-coverage-recovery-smoke.rs"
required-features = ["workspace-coverage-recovery-smoke"]
[[bin]]
name = "epiphany-release"
path = "src/bin/epiphany-release.rs"
[dependencies]
anyhow = "1"
bm25 = "2.3.2"

View file

@ -29,6 +29,7 @@ use epiphany_core::load_epiphany_cultmesh_managed_service_policy;
use epiphany_core::load_epiphany_cultmesh_managed_service_policy_with_digest;
use epiphany_core::load_epiphany_cultmesh_status;
use epiphany_core::load_epiphany_cultmesh_swarm_brake;
use epiphany_core::load_epiphany_packaged_release;
use epiphany_core::load_latest_epiphany_cultmesh_daemon_heartbeat;
use epiphany_core::load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt_for_service;
use epiphany_core::migrate_memory_semantic_projection_attempts_v0;
@ -47,6 +48,7 @@ use epiphany_core::write_epiphany_cultmesh_daemon_service_lifecycle_receipt;
use epiphany_core::write_epiphany_cultmesh_managed_service_policy;
use epiphany_core::write_epiphany_cultmesh_semantic_projector_service_policy;
use epiphany_core::write_epiphany_cultmesh_workspace_coverage_projector_service_policy;
use epiphany_core::{EpiphanyPackagedReleaseEntry, authenticate_epiphany_packaged_release};
use epiphany_core::{
WORKSPACE_COVERAGE_PROCESS_LAUNCH_SCHEMA_VERSION, WorkspaceCoverageManagedProcessLaunchEntry,
WorkspaceCoverageProcessBootstrap, authenticate_workspace_coverage_provider_heartbeat,
@ -60,6 +62,9 @@ use epiphany_core::{
write_workspace_coverage_managed_process_launch, write_workspace_coverage_process_bootstrap,
write_workspace_coverage_process_termination_observation,
};
use epiphany_core::{
epiphany_packaged_release_binary_path, epiphany_packaged_release_witness_sha256,
};
use rand_core::{OsRng, RngCore};
use serde_json::Value;
use serde_json::json;
@ -200,9 +205,40 @@ fn managed_service_task_name(args: &Args) -> String {
.unwrap_or_else(|| "Epiphany-Idunn-Managed-Service-Reconciler".to_string())
}
fn pinned_packaged_release(
args: &Args,
require_digest: bool,
) -> Result<(EpiphanyPackagedReleaseEntry, String)> {
let release_id = args
.release_id
.as_deref()
.context("managed-service deployment requires --release-id")?;
let witness = load_epiphany_packaged_release(&args.store, &args.runtime_id, release_id)?
.context("pinned packaged release is absent")?;
let digest = match args.release_witness_sha256.as_deref() {
Some(expected) => expected.to_string(),
None if require_digest => {
anyhow::bail!("managed-service runtime requires --release-witness-sha256")
}
None => epiphany_packaged_release_witness_sha256(&witness)?,
};
let authenticated =
authenticate_epiphany_packaged_release(&args.store, &args.runtime_id, release_id, &digest)?;
Ok((authenticated, digest))
}
fn managed_service_task_action(args: &Args) -> Result<(PathBuf, PathBuf, Vec<String>)> {
let command = fs::canonicalize(service_command_path(args)?)
.context("managed-service task command must be an existing absolute executable")?;
if args.service_command.is_some() {
anyhow::bail!(
"managed-service task command comes from --release-id; --service-command is forbidden"
);
}
let (release, witness_digest) = pinned_packaged_release(args, false)?;
let command = fs::canonicalize(epiphany_packaged_release_binary_path(
&release,
"supervisor",
)?)
.context("witnessed managed-service supervisor is absent")?;
let cwd = fs::canonicalize(
args.cwd
.clone()
@ -220,6 +256,10 @@ fn managed_service_task_action(args: &Args) -> Result<(PathBuf, PathBuf, Vec<Str
args.runtime_id.clone(),
"--loop-interval-seconds".to_string(),
args.loop_interval_seconds.to_string(),
"--release-id".to_string(),
release.release_id.clone(),
"--release-witness-sha256".to_string(),
witness_digest,
"--fatal-log".to_string(),
absolutize_from(
&cwd,
@ -593,8 +633,6 @@ fn semantic_projector_service_status(args: Args) -> Result<()> {
"providerIncarnation": heartbeat.provider_incarnation,
"heartbeatStatus": heartbeat.status,
"startupCorrelationMatches": correlation_matches,
"readinessAuthority": "semantic-query-admission",
"semanticReadiness": "not-asserted-by-this-status",
"privateStateExposed": false,
"authoritative": false,
}))?
@ -736,8 +774,17 @@ fn serve(args: Args) -> Result<()> {
}
fn managed_service_serve(args: Args) -> Result<()> {
let (release, _) = pinned_packaged_release(&args, true)?;
let expected_supervisor = fs::canonicalize(epiphany_packaged_release_binary_path(
&release,
"supervisor",
)?)?;
if fs::canonicalize(env::current_exe()?)? != expected_supervisor {
anyhow::bail!("managed-service reconciler executable is not the pinned release supervisor");
}
let mut iteration = 0_u64;
loop {
pinned_packaged_release(&args, true)?;
iteration = iteration.saturating_add(1);
let policies =
load_epiphany_cultmesh_managed_service_policies(&args.store, args.runtime_id.clone())?;
@ -1260,7 +1307,7 @@ fn semantic_projector_service_policy(mut args: Args) -> Result<()> {
"managed-service-policy-{SEMANTIC_PROJECTOR_SERVICE_ID}"
));
args.restart_mode = "always".to_string();
args.service_command = Some(semantic_projector_command_path()?);
args.service_command = Some(packaged_role_command_path(&args, "semantic-projector")?);
args.service_args = semantic_projector_service_args(
agent_store,
runtime_store,
@ -1300,7 +1347,10 @@ fn workspace_coverage_projector_service_policy(mut args: Args) -> Result<()> {
"managed-service-policy-{WORKSPACE_COVERAGE_PROJECTOR_SERVICE_ID}"
));
args.restart_mode = "always".to_string();
args.service_command = Some(workspace_coverage_projector_command_path()?);
args.service_command = Some(packaged_role_command_path(
&args,
"workspace-coverage-projector",
)?);
args.service_args = workspace_coverage_projector_service_args(
runtime_store,
&args.store,
@ -1318,22 +1368,28 @@ fn workspace_coverage_projector_service_policy(mut args: Args) -> Result<()> {
write_managed_service_policy(args)
}
fn workspace_coverage_projector_command_path() -> Result<PathBuf> {
let binary = if cfg!(windows) {
"epiphany-workspace-coverage-projector.exe"
} else {
"epiphany-workspace-coverage-projector"
};
let path = env::current_exe()
.map(|path| path.with_file_name(binary))
.context("failed to derive packaged workspace coverage projector executable")?;
if !path.is_file() {
anyhow::bail!(
"packaged workspace coverage projector executable is missing at {}; build both supervisor and projector together",
path.display()
);
fn packaged_role_command_path(args: &Args, role: &str) -> Result<PathBuf> {
#[cfg(feature = "workspace-coverage-recovery-smoke")]
if args.release_id.is_none()
&& std::env::var_os("EPIPHANY_WORKSPACE_COVERAGE_SMOKE_DIAGNOSTICS").is_some()
{
let file_name = match role {
"semantic-projector" if cfg!(windows) => "epiphany-memory-semantic-projector.exe",
"semantic-projector" => "epiphany-memory-semantic-projector",
"workspace-coverage-projector" if cfg!(windows) => {
"epiphany-workspace-coverage-projector.exe"
}
"workspace-coverage-projector" => "epiphany-workspace-coverage-projector",
_ => anyhow::bail!("unknown smoke-only packaged role {role}"),
};
let path = env::current_exe()?.with_file_name(file_name);
if !path.is_file() {
anyhow::bail!("smoke-only sibling is absent: {}", path.display());
}
return Ok(path);
}
Ok(path)
let (release, _) = pinned_packaged_release(args, true)?;
epiphany_packaged_release_binary_path(&release, role)
}
fn workspace_coverage_projector_service_args(
@ -1364,24 +1420,6 @@ fn workspace_coverage_projector_service_args(
]
}
fn semantic_projector_command_path() -> Result<PathBuf> {
let binary = if cfg!(windows) {
"epiphany-memory-semantic-projector.exe"
} else {
"epiphany-memory-semantic-projector"
};
let path = env::current_exe()
.map(|path| path.with_file_name(binary))
.context("failed to derive packaged semantic projector executable")?;
if !path.is_file() {
anyhow::bail!(
"packaged semantic projector executable is missing at {}; build both supervisor and projector together",
path.display()
);
}
Ok(path)
}
fn semantic_projector_service_args(
agent_store: &Path,
runtime_store: &Path,
@ -1465,12 +1503,32 @@ fn managed_service_read(args: Args) -> Result<()> {
}
fn managed_service_reconcile(mut args: Args) -> Result<()> {
let pinned_release = if args.release_id.is_some() {
Some(pinned_packaged_release(&args, true)?.0)
} else {
None
};
let policy = load_epiphany_cultmesh_managed_service_policy(
&args.store,
args.runtime_id.clone(),
&args.service_id,
)?
.with_context(|| format!("managed service policy missing for {}", args.service_id))?;
if let Some(release) = &pinned_release {
let expected_role = match policy.service_id.as_str() {
SEMANTIC_PROJECTOR_SERVICE_ID => Some("semantic-projector"),
WORKSPACE_COVERAGE_PROJECTOR_SERVICE_ID => Some("workspace-coverage-projector"),
_ => None,
};
if let Some(role) = expected_role
&& fs::canonicalize(&policy.command)?
!= fs::canonicalize(epiphany_packaged_release_binary_path(release, role)?)?
{
anyhow::bail!(
"reserved managed-service policy command is outside pinned release role {role}"
);
}
}
if policy.service_id == WORKSPACE_COVERAGE_PROJECTOR_SERVICE_ID {
return reconcile_workspace_coverage_projector(args, policy);
}
@ -3315,7 +3373,7 @@ mod semantic_projector_authority_tests {
let specialized_end = specialized_tail.find("\nfn ").unwrap();
let specialized = &specialized_tail[..specialized_end];
assert!(specialized.contains("args.service_command.is_some()"));
assert!(specialized.contains("semantic_projector_command_path()"));
assert!(specialized.contains("packaged_role_command_path(&args, \"semantic-projector\")"));
assert!(specialized.contains("write_managed_service_policy(args)"));
let legacy_cli = include_str!("epiphany-memory-semantic.rs");
@ -3367,6 +3425,9 @@ mod semantic_projector_authority_tests {
let body = &tail[..end];
for required in [
"managed-service-serve",
"--release-id",
"--release-witness-sha256",
"pinned_packaged_release",
"New-ScheduledTaskTrigger -AtLogOn",
"LogonType Interactive",
"RunLevel Limited",
@ -3385,6 +3446,7 @@ mod semantic_projector_authority_tests {
);
}
assert!(!body.contains("sc.exe"));
assert!(body.contains("--service-command is forbidden"));
assert!(source.contains("let mut task_restart_count = 999_u32"));
assert!(source.contains("task_readback_has_drift"));
assert!(source.contains("\"install-drift\""));
@ -3461,6 +3523,8 @@ struct Args {
task_logon_delay_seconds: u64,
task_restart_interval_seconds: u64,
task_restart_count: u32,
release_id: Option<String>,
release_witness_sha256: Option<String>,
}
impl Args {
@ -3509,6 +3573,8 @@ impl Args {
let mut task_restart_interval_seconds = 60_u64;
let mut task_restart_count = 999_u32;
let mut fatal_log = None;
let mut release_id = None;
let mut release_witness_sha256 = None;
while let Some(arg) = values.next() {
match arg.as_str() {
@ -3677,6 +3743,16 @@ impl Args {
values.next().context("missing --fatal-log value")?,
));
}
"--release-id" => {
release_id = Some(values.next().context("missing --release-id value")?);
}
"--release-witness-sha256" => {
release_witness_sha256 = Some(
values
.next()
.context("missing --release-witness-sha256 value")?,
);
}
other => anyhow::bail!("unknown argument {other:?}"),
}
}
@ -3825,6 +3901,8 @@ impl Args {
task_logon_delay_seconds,
task_restart_interval_seconds,
task_restart_count,
release_id,
release_witness_sha256,
})
}
}

View file

@ -123,8 +123,7 @@ fn main() -> Result<()> {
"healthPublicationFaults": health_publication_faults,
"selectedScopeId": outcome.selected_scope_id,
"privateStateExposed": false,
"authoritative": false,
"queryAdmission": false
"authoritative": false
}))?
);
if args.max_iterations != 0 && sequence >= args.max_iterations {

View file

@ -254,7 +254,6 @@ fn print_semantic_health(
"authoritative": health.authoritative,
"nonAuthoritative": !health.authoritative,
"authority": "sight-only",
"queryAdmission": false,
"queryEligibleDisplayOnly": health.query_eligible_display_only,
"privateStateExposed": health.private_state_exposed,
}))

View file

@ -0,0 +1,74 @@
use anyhow::{Context, Result, bail};
use epiphany_core::{
PackageReleaseRequest, epiphany_packaged_release_witness_sha256,
package_and_publish_epiphany_release,
};
use std::env;
use std::path::PathBuf;
use std::process::Command;
fn main() -> Result<()> {
let mut args = env::args().skip(1);
if args.next().as_deref() != Some("package") {
bail!(
"usage: epiphany-release package --repo PATH --destination PATH --store PATH --runtime-id ID [--target-triple TRIPLE]"
);
}
let mut repo = None;
let mut destination = None;
let mut store = None;
let mut runtime = None;
let mut target = None;
while let Some(flag) = args.next() {
let value = args
.next()
.with_context(|| format!("missing value for {flag}"))?;
match flag.as_str() {
"--repo" => repo = Some(PathBuf::from(value)),
"--destination" => destination = Some(PathBuf::from(value)),
"--store" => store = Some(PathBuf::from(value)),
"--runtime-id" => runtime = Some(value),
"--target-triple" => target = Some(value),
_ => bail!("unknown argument {flag}"),
}
}
let repo = repo.context("missing --repo")?;
let destination = destination.context("missing --destination")?;
let store = store.context("missing --store")?;
let runtime = runtime.context("missing --runtime-id")?;
let rustc = command_output("rustc", &["-vV"])?;
let target = target
.or_else(|| {
rustc
.lines()
.find_map(|line| line.strip_prefix("host: ").map(str::to_string))
})
.context("rustc did not report host target")?;
let witness = package_and_publish_epiphany_release(PackageReleaseRequest {
repo: &repo,
destination: &destination,
store: &store,
runtime_id: &runtime,
target_triple: &target,
toolchain_fingerprint: &rustc,
})?;
let witness_sha256 = epiphany_packaged_release_witness_sha256(&witness)?;
println!(
"{}",
serde_json::to_string_pretty(
&serde_json::json!({"schemaVersion":witness.schema_version,"status":"published","releaseId":witness.release_id,"witnessSha256":witness_sha256,"sourceCommit":witness.source_commit_sha,"packageRoot":witness.package_root,"binaryCount":witness.binaries.len(),"privateStateExposed":false})
)?
);
Ok(())
}
fn command_output(command: &str, args: &[&str]) -> Result<String> {
let output = Command::new(command).args(args).output()?;
if !output.status.success() {
bail!(
"{command} failed: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(String::from_utf8(output.stdout)?.trim().to_string())
}

View file

@ -291,7 +291,6 @@ fn run_cli() -> Result<()> {
"privateStateExposed": false,
"authoritative": false,
"authority": "sight-only",
"queryAdmission": false,
"rows": report_rows,
"tuiRows": tui_rows,
}))?
@ -2218,7 +2217,7 @@ fn semantic_projection_health_tui_row(
row: &EpiphanyCultMeshSemanticProjectionHealthEntry,
) -> String {
format!(
"SEMANTIC-HEALTH swarm={} partition={} generation={} status=observed-{} documents={} dimensions={} provider={} incarnation={} authority=sight-only query-admission=false private=false",
"SEMANTIC-HEALTH swarm={} partition={} generation={} status=observed-{} documents={} dimensions={} provider={} incarnation={} authority=sight-only private=false",
row.swarm_id,
row.partition,
row.source_generation,
@ -2253,7 +2252,6 @@ fn semantic_projection_health_report_row(
"authoritative": row.authoritative,
"nonAuthoritative": !row.authoritative,
"authority": "sight-only",
"queryAdmission": false,
"queryEligibleDisplayOnly": row.query_eligible_display_only,
"privateStateExposed": row.private_state_exposed,
})

View file

@ -5,6 +5,7 @@ use crate::default_hands_cultnet_contracts;
use crate::default_mind_cultnet_contracts;
use crate::default_soul_cultnet_contracts;
use crate::default_substrate_gate_cultnet_contracts;
use crate::packaged_release::{EpiphanyPackagedReleaseEntry, EpiphanyPackagedReleaseHead};
use crate::workspace_coverage_process_documents::{
WORKSPACE_COVERAGE_PROCESS_EVIDENCE_HEAD_SCHEMA_VERSION,
WORKSPACE_COVERAGE_PROCESS_LAUNCH_SCHEMA_VERSION,
@ -2650,6 +2651,8 @@ cultmesh_documents!(EpiphanyCultMeshDocuments {
WorkspaceCoverageProcessEvidenceHead => WORKSPACE_COVERAGE_PROCESS_EVIDENCE_HEAD_SCHEMA_VERSION,
WorkspaceCoverageProviderHeartbeatEntry => WORKSPACE_COVERAGE_PROVIDER_HEARTBEAT_SCHEMA_VERSION,
WorkspaceCoverageProcessTerminationObservationEntry => WORKSPACE_COVERAGE_PROCESS_TERMINATION_SCHEMA_VERSION,
EpiphanyPackagedReleaseEntry => crate::packaged_release::EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION,
EpiphanyPackagedReleaseHead => crate::packaged_release::EPIPHANY_PACKAGED_RELEASE_HEAD_SCHEMA_VERSION,
EpiphanyCultMeshIdunnDeploymentReceiptEntry => EPIPHANY_CULTMESH_IDUNN_DEPLOYMENT_RECEIPT_SCHEMA_VERSION,
EpiphanyCultMeshIdunnAftercareAuditReceiptEntry => EPIPHANY_CULTMESH_IDUNN_AFTERCARE_AUDIT_RECEIPT_SCHEMA_VERSION,
EpiphanyCultMeshSwarmBrakeEntry => EPIPHANY_CULTMESH_SWARM_BRAKE_SCHEMA_VERSION,

View file

@ -19,6 +19,7 @@ mod host_identity;
mod memory_graph;
mod mind_gateway;
mod organ_dependencies;
mod packaged_release;
mod persona_turn;
mod process_observation;
mod promotion;
@ -792,6 +793,16 @@ pub use organ_dependencies::evaluate_receipt_proof_profiles;
pub use organ_dependencies::receipt_proof_evaluation_errors;
pub use organ_dependencies::render_organ_dependencies;
pub use organ_dependencies::render_organ_dependency;
pub use packaged_release::{
EPIPHANY_PACKAGED_RELEASE_HEAD_SCHEMA_VERSION, EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION,
EpiphanyPackagedReleaseBinary, EpiphanyPackagedReleaseEntry, EpiphanyPackagedReleaseHead,
PackageReleaseRequest, authenticate_epiphany_packaged_release,
epiphany_packaged_release_binary_path, epiphany_packaged_release_witness_sha256,
load_epiphany_packaged_release, load_epiphany_packaged_release_head,
package_and_publish_epiphany_release, publish_epiphany_packaged_release,
required_packaged_release_binaries, validate_epiphany_packaged_release,
verify_epiphany_packaged_release_files,
};
pub use persona_turn::PERSONA_INTERPRETER_PROMPT_SCHEMA_VERSION;
pub use persona_turn::PERSONA_PROJECTOR_PROMPT_SCHEMA_VERSION;
pub use persona_turn::PERSONA_TURN_PROMPT_SCHEMA_VERSION;

View file

@ -0,0 +1,647 @@
use crate::open_epiphany_cultmesh_node;
use anyhow::{Context, Result, anyhow, bail};
use chrono::{DateTime, Utc};
use cultcache_rs::{DatabaseEntry, SingleFileMessagePackBackingStore};
use sha2::{Digest, Sha256};
use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
use uuid::Uuid;
pub const EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION: &str = "epiphany.packaged_release.v0";
pub const EPIPHANY_PACKAGED_RELEASE_HEAD_SCHEMA_VERSION: &str = "epiphany.packaged_release_head.v0";
const RELEASE_KEY_PREFIX: &str = "epiphany-local/packaged-release/by-id/";
const RELEASE_HEAD_KEY: &str = "epiphany-local/packaged-release/current";
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct EpiphanyPackagedReleaseBinary {
pub role: String,
pub file_name: String,
pub canonical_path: String,
pub sha256: String,
pub byte_len: u64,
}
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
#[cultcache(
type = "epiphany.packaged_release",
schema = "EpiphanyPackagedReleaseEntry"
)]
pub struct EpiphanyPackagedReleaseEntry {
#[cultcache(key = 0)]
pub schema_version: String,
#[cultcache(key = 1)]
pub release_id: String,
#[cultcache(key = 2)]
pub runtime_id: String,
#[cultcache(key = 3)]
pub source_commit_sha: String,
#[cultcache(key = 4)]
pub target_triple: String,
#[cultcache(key = 5)]
pub cargo_profile: String,
#[cultcache(key = 6)]
pub toolchain_fingerprint: String,
#[cultcache(key = 7)]
pub created_at_utc: String,
#[cultcache(key = 8)]
pub package_root: String,
#[cultcache(key = 9)]
pub binaries: Vec<EpiphanyPackagedReleaseBinary>,
#[cultcache(key = 10)]
pub private_state_exposed: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)]
#[cultcache(
type = "epiphany.packaged_release_head",
schema = "EpiphanyPackagedReleaseHead"
)]
pub struct EpiphanyPackagedReleaseHead {
#[cultcache(key = 0)]
pub schema_version: String,
#[cultcache(key = 1)]
pub runtime_id: String,
#[cultcache(key = 2)]
pub release_id: String,
#[cultcache(key = 3)]
pub witness_sha256: String,
#[cultcache(key = 4)]
pub published_at_utc: String,
}
pub struct PackageReleaseRequest<'a> {
pub repo: &'a Path,
pub destination: &'a Path,
pub store: &'a Path,
pub runtime_id: &'a str,
pub target_triple: &'a str,
pub toolchain_fingerprint: &'a str,
}
pub fn required_packaged_release_binaries() -> Vec<(&'static str, &'static str)> {
vec![
(
"supervisor",
if cfg!(windows) {
"epiphany-daemon-supervisor.exe"
} else {
"epiphany-daemon-supervisor"
},
),
(
"semantic-projector",
if cfg!(windows) {
"epiphany-memory-semantic-projector.exe"
} else {
"epiphany-memory-semantic-projector"
},
),
(
"workspace-coverage-projector",
if cfg!(windows) {
"epiphany-workspace-coverage-projector.exe"
} else {
"epiphany-workspace-coverage-projector"
},
),
(
"semantic-query",
if cfg!(windows) {
"epiphany-memory-semantic.exe"
} else {
"epiphany-memory-semantic"
},
),
]
}
pub fn package_and_publish_epiphany_release(
request: PackageReleaseRequest<'_>,
) -> Result<EpiphanyPackagedReleaseEntry> {
let (source_commit_sha, source_commit_time) = clean_source_commit(request.repo)?;
require_nonempty("runtime id", request.runtime_id)?;
require_nonempty("target triple", request.target_triple)?;
require_nonempty("toolchain fingerprint", request.toolchain_fingerprint)?;
fs::create_dir_all(request.destination)?;
let destination = fs::canonicalize(request.destination)?;
let source_root = destination.join(format!(".source-{}", Uuid::new_v4()));
let source_guard = GitWorktreeGuard::create(request.repo, &source_root, &source_commit_sha)?;
let build_root = destination.join(format!(".build-{}", Uuid::new_v4()));
let _build_guard = DirectoryCleanup(build_root.clone());
build_required_release_siblings(&source_guard.path, &build_root, request.target_triple)?;
let built_dir = build_root.join(request.target_triple).join("release");
let staging = destination.join(format!(".staging-{}", Uuid::new_v4()));
fs::create_dir(&staging)?;
let result = (|| {
let mut binaries = Vec::new();
for (role, file_name) in required_packaged_release_binaries() {
let source = built_dir.join(file_name);
if !source.is_file() {
bail!("required packaged sibling is absent: {}", source.display());
}
let target = staging.join(file_name);
fs::copy(&source, &target)?;
binaries.push(binary_record(role, file_name, &target)?);
}
binaries.sort_by(|left, right| left.role.cmp(&right.role));
let release_id = release_id(
request.runtime_id,
&source_commit_sha,
request.target_triple,
request.toolchain_fingerprint,
&binaries,
);
let final_root = destination.join(&source_commit_sha).join(&release_id);
fs::create_dir_all(final_root.parent().expect("release root has parent"))?;
for binary in &mut binaries {
binary.canonical_path = final_root.join(&binary.file_name).display().to_string();
}
let witness = EpiphanyPackagedReleaseEntry {
schema_version: EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION.into(),
release_id,
runtime_id: request.runtime_id.into(),
source_commit_sha,
target_triple: request.target_triple.into(),
cargo_profile: "release".into(),
toolchain_fingerprint: request.toolchain_fingerprint.into(),
created_at_utc: source_commit_time,
package_root: final_root.display().to_string(),
binaries,
private_state_exposed: false,
};
validate_epiphany_packaged_release(&witness)?;
if final_root.exists() {
verify_epiphany_packaged_release_files(&witness)?;
fs::remove_dir_all(&staging)?;
} else {
fs::rename(&staging, &final_root)
.context("failed to atomically publish packaged release directory")?;
verify_epiphany_packaged_release_files(&witness)?;
}
publish_epiphany_packaged_release(request.store, request.runtime_id, witness)
})();
if staging.exists() {
let _ = fs::remove_dir_all(staging);
}
result
}
struct DirectoryCleanup(PathBuf);
impl Drop for DirectoryCleanup {
fn drop(&mut self) {
if self.0.exists() {
let _ = fs::remove_dir_all(&self.0);
}
}
}
struct GitWorktreeGuard {
repo: PathBuf,
path: PathBuf,
}
impl GitWorktreeGuard {
fn create(repo: &Path, path: &Path, commit: &str) -> Result<Self> {
let output = std::process::Command::new("git")
.args(["worktree", "add", "--detach"])
.arg(path)
.arg(commit)
.current_dir(repo)
.output()?;
if !output.status.success() {
bail!(
"failed to create exact-source release worktree: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(Self {
repo: repo.to_path_buf(),
path: path.to_path_buf(),
})
}
}
impl Drop for GitWorktreeGuard {
fn drop(&mut self) {
let removed = std::process::Command::new("git")
.args(["worktree", "remove", "--force"])
.arg(&self.path)
.current_dir(&self.repo)
.status()
.is_ok_and(|status| status.success());
if !removed && self.path.exists() {
let _ = fs::remove_dir_all(&self.path);
}
}
}
fn build_required_release_siblings(repo: &Path, target_dir: &Path, target: &str) -> Result<()> {
let manifest = repo.join("epiphany-core").join("Cargo.toml");
if !manifest.is_file() {
bail!(
"Epiphany release manifest is absent: {}",
manifest.display()
);
}
let mut command = std::process::Command::new("cargo");
command
.arg("build")
.arg("--release")
.arg("--manifest-path")
.arg(&manifest)
.arg("--target-dir")
.arg(target_dir)
.arg("--target")
.arg(target);
command.arg("--locked");
for (role, _) in required_packaged_release_binaries() {
let binary = match role {
"supervisor" => "epiphany-daemon-supervisor",
"semantic-projector" => "epiphany-memory-semantic-projector",
"workspace-coverage-projector" => "epiphany-workspace-coverage-projector",
"semantic-query" => "epiphany-memory-semantic",
_ => bail!("unknown required release role {role}"),
};
command.arg("--bin").arg(binary);
}
let status = command
.status()
.context("failed to start owned release build")?;
if !status.success() {
bail!("owned Epiphany release build failed");
}
Ok(())
}
pub fn validate_epiphany_packaged_release(entry: &EpiphanyPackagedReleaseEntry) -> Result<()> {
if entry.schema_version != EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION {
bail!("unsupported packaged release schema");
}
require_nonempty("runtime id", &entry.runtime_id)?;
validate_commit(&entry.source_commit_sha)?;
require_nonempty("target triple", &entry.target_triple)?;
if entry.cargo_profile != "release" {
bail!("packaged release profile must be release");
}
require_nonempty("toolchain fingerprint", &entry.toolchain_fingerprint)?;
DateTime::parse_from_rfc3339(&entry.created_at_utc)?;
if entry.private_state_exposed {
bail!("packaged release may not expose private state");
}
let root = Path::new(&entry.package_root);
if !root.is_absolute() {
bail!("packaged release root must be absolute");
}
let required = required_packaged_release_binaries();
if entry.binaries.len() != required.len() {
bail!("packaged release sibling set is incomplete");
}
let mut roles = BTreeSet::new();
let mut names = BTreeSet::new();
for binary in &entry.binaries {
if !roles.insert(binary.role.as_str()) || !names.insert(binary.file_name.as_str()) {
bail!("packaged release contains duplicate role or filename");
}
if !required
.iter()
.any(|pair| pair.0 == binary.role && pair.1 == binary.file_name)
{
bail!("packaged release contains an unknown or swapped sibling role");
}
validate_sha256(&binary.sha256)?;
if binary.byte_len == 0 {
bail!("packaged release binary is empty");
}
if Path::new(&binary.canonical_path) != root.join(&binary.file_name) {
bail!("packaged release binary path escapes or disagrees with package root");
}
}
let mut identity_binaries = entry.binaries.clone();
identity_binaries.sort_by(|left, right| left.role.cmp(&right.role));
let expected = release_id(
&entry.runtime_id,
&entry.source_commit_sha,
&entry.target_triple,
&entry.toolchain_fingerprint,
&identity_binaries,
);
if entry.release_id != expected {
bail!("packaged release id does not authenticate sibling set");
}
Ok(())
}
pub fn verify_epiphany_packaged_release_files(entry: &EpiphanyPackagedReleaseEntry) -> Result<()> {
validate_epiphany_packaged_release(entry)?;
let root = fs::canonicalize(&entry.package_root).context("packaged release root is absent")?;
if root != PathBuf::from(&entry.package_root) {
bail!("packaged release root is not canonical");
}
let actual_names = fs::read_dir(&root)?
.map(|item| Ok(item?.file_name().to_string_lossy().into_owned()))
.collect::<Result<BTreeSet<_>>>()?;
let expected_names = entry
.binaries
.iter()
.map(|binary| binary.file_name.clone())
.collect::<BTreeSet<_>>();
if actual_names != expected_names {
bail!("packaged release directory is not the exact witnessed sibling set");
}
for binary in &entry.binaries {
let path = root.join(&binary.file_name);
if fs::canonicalize(&path)? != PathBuf::from(&binary.canonical_path) {
bail!("packaged sibling path aliases another file");
}
let metadata = fs::metadata(&path)?;
if !metadata.is_file()
|| metadata.len() != binary.byte_len
|| file_sha256(&path)? != binary.sha256
{
bail!(
"packaged sibling bytes disagree with witness: {}",
binary.role
);
}
}
Ok(())
}
pub fn publish_epiphany_packaged_release(
store: &Path,
runtime_id: &str,
entry: EpiphanyPackagedReleaseEntry,
) -> Result<EpiphanyPackagedReleaseEntry> {
validate_epiphany_packaged_release(&entry)?;
verify_epiphany_packaged_release_files(&entry)?;
if entry.runtime_id != runtime_id {
bail!("release witness runtime disagrees with target Verse");
}
let node = open_epiphany_cultmesh_node(store, runtime_id.to_string())?;
let identity_key = format!("{RELEASE_KEY_PREFIX}{}", entry.release_id);
let existing = node.get::<EpiphanyPackagedReleaseEntry>(&identity_key)?;
if existing.as_ref().is_some_and(|current| current != &entry) {
bail!("immutable packaged release identity collision");
}
let head = EpiphanyPackagedReleaseHead {
schema_version: EPIPHANY_PACKAGED_RELEASE_HEAD_SCHEMA_VERSION.into(),
runtime_id: runtime_id.into(),
release_id: entry.release_id.clone(),
witness_sha256: witness_sha256(&entry)?,
published_at_utc: Utc::now().to_rfc3339(),
};
let prior_head = node
.cache()
.get_envelope::<EpiphanyPackagedReleaseHead>(RELEASE_HEAD_KEY)?;
let mut expected = Vec::new();
if let Some(envelope) = prior_head {
expected.push(envelope);
}
let mut replacements = Vec::new();
if existing.is_none() {
replacements.push(node.cache().prepare_entry(&identity_key, &entry)?.0);
}
replacements.push(node.cache().prepare_entry(RELEASE_HEAD_KEY, &head)?.0);
if !SingleFileMessagePackBackingStore::new(store)
.compare_and_swap_batch(&expected, replacements)?
{
bail!("packaged release publication lost current-head compare-and-swap");
}
Ok(entry)
}
pub fn load_epiphany_packaged_release(
store: &Path,
runtime_id: &str,
release_id: &str,
) -> Result<Option<EpiphanyPackagedReleaseEntry>> {
open_epiphany_cultmesh_node(store, runtime_id.to_string())?
.get(&format!("{RELEASE_KEY_PREFIX}{release_id}"))
}
pub fn load_epiphany_packaged_release_head(
store: &Path,
runtime_id: &str,
) -> Result<Option<EpiphanyPackagedReleaseHead>> {
open_epiphany_cultmesh_node(store, runtime_id.to_string())?.get(RELEASE_HEAD_KEY)
}
pub fn authenticate_epiphany_packaged_release(
store: &Path,
runtime_id: &str,
release_id: &str,
expected_witness_sha256: &str,
) -> Result<EpiphanyPackagedReleaseEntry> {
validate_sha256(expected_witness_sha256)?;
let entry = load_epiphany_packaged_release(store, runtime_id, release_id)?
.context("packaged release witness is absent")?;
validate_epiphany_packaged_release(&entry)?;
verify_epiphany_packaged_release_files(&entry)?;
if witness_sha256(&entry)? != expected_witness_sha256 {
bail!("packaged release witness digest disagrees with pinned task authority");
}
Ok(entry)
}
pub fn epiphany_packaged_release_witness_sha256(
entry: &EpiphanyPackagedReleaseEntry,
) -> Result<String> {
witness_sha256(entry)
}
pub fn epiphany_packaged_release_binary_path(
entry: &EpiphanyPackagedReleaseEntry,
role: &str,
) -> Result<PathBuf> {
entry
.binaries
.iter()
.find(|binary| binary.role == role)
.map(|binary| PathBuf::from(&binary.canonical_path))
.with_context(|| format!("packaged release lacks required role {role}"))
}
fn clean_source_commit(repo: &Path) -> Result<(String, String)> {
let head = git(repo, &["rev-parse", "HEAD"])?;
validate_commit(&head)?;
if !git(repo, &["status", "--porcelain=v1", "--untracked-files=no"])?.is_empty() {
bail!("packaged release requires a clean tracked source commit");
}
if git(repo, &["submodule", "status", "--recursive"])?
.lines()
.any(|line| line.starts_with(['-', '+', 'U']))
{
bail!("packaged release requires clean initialized submodules");
}
let committed_at = git(repo, &["show", "-s", "--format=%cI", "HEAD"])?;
DateTime::parse_from_rfc3339(&committed_at).context("Git commit time is not RFC3339")?;
Ok((head, committed_at))
}
fn git(repo: &Path, args: &[&str]) -> Result<String> {
let output = std::process::Command::new("git")
.args(args)
.current_dir(repo)
.output()?;
if !output.status.success() {
bail!(
"git {} failed: {}",
args.join(" "),
String::from_utf8_lossy(&output.stderr)
);
}
Ok(String::from_utf8(output.stdout)?.trim().to_string())
}
fn binary_record(
role: &str,
file_name: &str,
path: &Path,
) -> Result<EpiphanyPackagedReleaseBinary> {
let canonical = fs::canonicalize(path)?;
let len = fs::metadata(&canonical)?.len();
if len == 0 {
bail!("required packaged sibling is empty: {file_name}");
}
Ok(EpiphanyPackagedReleaseBinary {
role: role.into(),
file_name: file_name.into(),
canonical_path: canonical.display().to_string(),
sha256: file_sha256(&canonical)?,
byte_len: len,
})
}
fn release_id(
runtime: &str,
commit: &str,
target: &str,
toolchain: &str,
binaries: &[EpiphanyPackagedReleaseBinary],
) -> String {
let mut hash = Sha256::new();
for value in [
"epiphany.packaged-release.identity.v0",
runtime,
commit,
target,
"release",
toolchain,
] {
hash.update((value.len() as u64).to_be_bytes());
hash.update(value.as_bytes());
}
for binary in binaries {
for value in [
&binary.role,
&binary.file_name,
&binary.sha256,
&binary.byte_len.to_string(),
] {
hash.update((value.len() as u64).to_be_bytes());
hash.update(value.as_bytes());
}
}
format!("sha256-{:x}", hash.finalize())
}
fn witness_sha256(entry: &EpiphanyPackagedReleaseEntry) -> Result<String> {
Ok(format!(
"sha256-{:x}",
Sha256::digest(rmp_serde::to_vec(entry)?)
))
}
fn file_sha256(path: &Path) -> Result<String> {
Ok(format!("sha256-{:x}", Sha256::digest(fs::read(path)?)))
}
fn validate_sha256(value: &str) -> Result<()> {
if value.len() != 71
|| !value.starts_with("sha256-")
|| !value[7..]
.bytes()
.all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
{
bail!("invalid SHA-256 digest");
}
Ok(())
}
fn validate_commit(value: &str) -> Result<()> {
if value.len() != 40 || !value.bytes().all(|b| b.is_ascii_hexdigit()) {
bail!("source commit must be a full 40-hex Git object id");
}
Ok(())
}
fn require_nonempty(label: &str, value: &str) -> Result<()> {
if value.trim().is_empty() {
return Err(anyhow!("{label} must not be empty"));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn fixture() -> (TempDir, EpiphanyPackagedReleaseEntry) {
let dir = TempDir::new().unwrap();
let root = dir.path().join("release");
fs::create_dir(&root).unwrap();
let mut binaries = Vec::new();
for (index, (role, name)) in required_packaged_release_binaries().into_iter().enumerate() {
let path = root.join(name);
fs::write(&path, format!("binary-{index}")).unwrap();
binaries.push(binary_record(role, name, &path).unwrap());
}
binaries.sort_by(|a, b| a.role.cmp(&b.role));
let id = release_id("runtime", &"a".repeat(40), "target", "rustc", &binaries);
let entry = EpiphanyPackagedReleaseEntry {
schema_version: EPIPHANY_PACKAGED_RELEASE_SCHEMA_VERSION.into(),
release_id: id,
runtime_id: "runtime".into(),
source_commit_sha: "a".repeat(40),
target_triple: "target".into(),
cargo_profile: "release".into(),
toolchain_fingerprint: "rustc".into(),
created_at_utc: Utc::now().to_rfc3339(),
package_root: fs::canonicalize(root).unwrap().display().to_string(),
binaries,
private_state_exposed: false,
};
(dir, entry)
}
#[test]
fn exact_fixture_verifies() {
let (_d, e) = fixture();
verify_epiphany_packaged_release_files(&e).unwrap();
}
#[test]
fn one_byte_replacement_is_rejected() {
let (_d, e) = fixture();
fs::write(&e.binaries[0].canonical_path, "hostile").unwrap();
assert!(verify_epiphany_packaged_release_files(&e).is_err());
}
#[test]
fn extra_sibling_is_rejected() {
let (_d, e) = fixture();
fs::write(Path::new(&e.package_root).join("stowaway.exe"), "x").unwrap();
assert!(verify_epiphany_packaged_release_files(&e).is_err());
}
#[test]
fn swapped_role_is_rejected() {
let (_d, mut e) = fixture();
let role = e.binaries[0].role.clone();
e.binaries[0].role = e.binaries[1].role.clone();
e.binaries[1].role = role;
assert!(validate_epiphany_packaged_release(&e).is_err());
}
#[test]
fn counterfeit_release_id_is_rejected() {
let (_d, mut e) = fixture();
e.release_id = format!("sha256-{}", "0".repeat(64));
assert!(validate_epiphany_packaged_release(&e).is_err());
}
}

View file

@ -59,23 +59,31 @@ VoidBot's ordinary spine is not the rotten part. Its durable lessons are:
## Current Mechanism
Epiphany currently has the right ingredients, but not yet the final live swarm
shape:
Epiphany owns its state, organ scheduler, workers, process policy, and local
operator surfaces in native Rust and typed CultCache/CultMesh documents. Codex
survives only as the retained OpenAI subscription-auth/model-transport bridge;
it no longer owns Epiphany routes, prompts, scheduling, or state mutation.
- `tools/epiphany_local_run.ps1` can build and smoke the compatibility shell.
- `epiphany-runtime-spine` owns typed runtime identity/session/job/result/event
documents and advertises CultNet contracts.
- heartbeat state owns role dossiers, initiative, heat, active-turn freeze,
sleep, rumination, appraisals, and derived reactions.
- role memory accepts reviewed `selfPatch` petitions into typed Ghostlight-like
dossiers.
- coordinator/CRRC/role/status surfaces exist, but the practical local runner
still goes through Codex app-server JSON-RPC for operator reads/actions.
- Persona, Discord, Rider, Unity, Void memory, and repo birth bridges are typed
surfaces or artifacts, but they are not yet an integrated live swarm loop.
- Runtime spine, Mind gateway, Substrate Gate, Eyes, Hands, Soul, Modeling,
Continuity, heartbeat, and Persona operations have typed intent/review/receipt
families.
- One heartbeat scheduler owns initiative, heat, active-turn freeze,
completion-gated cooldown, stale-turn recovery, sleep, and rumination.
- The local CultMesh swarm brake is typed and enforced at runner/action edges.
- Persona public speech has parent-side repetition/saturation eligibility and
Bifrost/Heimdall crossing gates.
- Memory has reviewed revise/retire/crystallize/prune/merge operations; Qdrant
remains rebuildable semantic sight rather than canonical Mind.
- Idunn owns managed projector lifecycle and exact abandoned-claim recovery.
Task Scheduler owns only current-user after-login Idunn presence.
- Mind and Modeling semantic query admission reauthenticate canonical CultCache
authority at query time against the Yggdrasil-hosted Qdrant/Ollama substrate.
The machine is pointed toward live use. It is not cleared for unattended swarm
operation.
The installed host is operational after login, but its mutable release directory
does not yet prove that supervisor, projectors, and query gate are one source
generation. Whole-repository readiness also lacks its Mind-owned race-bounded
join. Those are the remaining non-permission architecture gates. A real reboot/
logon survival proof remains a separate operator-authorized boundary.
## Invariants
@ -96,66 +104,27 @@ operation.
- No broad live scheduler until the local runner can show the typed surfaces
that explain what it would do and why.
## Readiness Gate
## Readiness Evidence Matrix
Before live fire, Epiphany needs these gates closed:
This matrix is the live gate. A green row names its authority and proof; it is
not permission for another row to borrow the result.
1. **Operator Run Path**
- Keep `tools/epiphany_local_run.ps1` as the human entrypoint for now.
- It must keep printing a concise verdict.
- It must keep artifacts operator-safe by default.
- It must make Codex app-server dependency obvious as compatibility, not
architecture.
| Gate | Owner | Current evidence | State |
| --- | --- | --- | --- |
| Typed brake and initiative physiology | heartbeat/CultMesh | brake, completion cooldown, and stale-turn recovery tests and receipts | closed |
| Reviewed Mind and organ-state mutation | Mind gateway and organ services | typed review/commit/lifecycle families; no whole-state model write path | closed |
| Public speech/crossing eligibility | Persona + Bifrost/Heimdall | speech-audit receipts and fail-closed identity/capability checks | closed |
| Branch-local autonomous action | Substrate Gate + Hands + Soul + Mind | scoped grants and patch/command/commit/verdict/admission chains | closed |
| Semantic projection/query recovery | canonical admission + projector + query gate + Idunn | live Mind/Modeling semantic ranking and GUID-scoped exact process-recovery smokes | closed |
| Atomic installed sibling generation | release packager + Idunn | no immutable commit-addressed package witness yet; mutable sibling directory can mix builds | open |
| Whole-repository observed readiness | Mind | required Body R1/R2 + admitted model + live semantic + live workspace-coverage join does not yet exist | open |
| Current host package | release deployment | installed supervisor/projectors predate current recovery repairs | open |
| After-login recurrence | Task Scheduler + Idunn + ops tunnel | task recurrence and exact child recovery observed on current boot | closed for current boot |
| Reboot/logon survival | Task Scheduler + Idunn + ops tunnel | requires fresh post-boot lineage and query proof | permission-bound |
2. **Typed Swarm Brake**
- Add an Epiphany-owned pause document for swarm/heartbeat/live Persona
operation.
- Runners must fail closed when the document is missing, malformed, or
paused for a teardown reason, according to the surface being protected.
- The brake must be inspectable through Aquarium/status.
3. **Initiative Scheduler Boundary**
- Keep one heartbeat scheduler for lanes/Personas/system organs.
- Heat is a multiplier on recovery pressure, not a time machine.
- Active-turn freeze and completion-gated cooldown are hard invariants.
- Stale active-turn recovery must write typed receipts.
4. **Memory Lifecycle**
- Keep role-local memory writes as reviewed `selfPatch` operations.
- Add explicit lifecycle operations where missing: revise, retire,
crystallize, prune short-term residue, and merge incubation support.
- Sleep/maintenance may propose lifecycle operations; it must not silently
rewrite role identity.
5. **Public Persona Safety**
- Persona speech eligibility must be parent-side state policy, not prompt hope.
- Track recent outputs, repeated topics, repeated openings, target/channel
saturation, and action kind.
- Work-shaped asks route to coordinator/Bifrost-style governance; banter
does not secretly become work dispatch.
6. **CultNet Operator Surface**
- Move the local status/coordinator loop toward native CultNet snapshots,
intents, and receipts.
- Codex JSON-RPC may remain as the compatibility projection while the wall is
being evacuated.
- The next run path should consume typed documents first and only use
app-server JSON-RPC where no native route exists yet.
7. **Audit And Review**
- Every automatic or semi-automatic action must produce a typed receipt.
- Semantic findings remain review-gated.
- Operator artifacts must seal direct thought, raw transcripts, and long
prompt-shaped notes by default.
8. **Autonomous Branch Work**
- A repo swarm must own a git branch/worktree as its sacrificial workbench.
- Hands may edit, run checks, and commit autonomously inside that branch when
Substrate Gate, Hands, Soul, and Mind receipts support the work.
- Bifrost gates PR/upstream publication and credit; it does not gate every
local branch edit.
- Cross-body collaboration must travel through Odin/Eve/CultMesh/Bifrost
contracts instead of direct workspace rummaging.
`providerStatus`, heartbeats, Qdrant collection existence, task state, watcher
silence, legacy retrieval Ready, Eve/Gjallar rows, and empty dirty-path lists are
never substitutes for the Mind readiness join or query admission.
## Ranked Next Cuts
@ -195,16 +164,16 @@ Rebuild:
## Immediate Work Queue
0. Land the repo-swarm MVP contract and make repo initialization create a
branch-local autonomous workbench by default.
1. Add typed swarm pause/brake state and status projection.
2. Add stale active-turn recovery receipts to heartbeat state if the current
heartbeat store cannot prove recovery cleanly.
3. Add a Persona output audit surface before increasing public speech cadence.
4. Add memory lifecycle operations that match VoidBot's useful pattern without
porting its TypeScript state shape wholesale.
5. Move one local-run read path from Codex JSON-RPC compatibility to CultNet
snapshot/receipt consumption.
1. Build and authenticate one immutable, commit-addressed sibling release. Task
installation and Idunn must consume that witness and reject mixed binaries.
2. Implement Mind's race-bounded `RepositoryReadinessProjection`, including a
live workspace-coverage Qdrant evidence reader; stored `Current` is not enough.
3. Deploy the witnessed release, replace the stale running reconciler/projector,
and repeat exact recurrence plus Mind/Modeling query admission proofs.
4. With explicit live operator approval, reboot and prove the fresh Task
Scheduler -> Idunn -> exactly-one-projector lineage plus tunnel and query
admission. Do not silently widen after-login authority into boot service
authority.
The correct posture is patient pressure. We are not rushing to live fire. We are
aiming the barrel, checking the chamber, and making sure the thing does not try

View file

@ -1964,3 +1964,32 @@ projection test passed before the recovery smoke.
Next: audit deployability against the swarm readiness plan and live host state.
Do not conflate code-complete physiology with the permission-bound reboot/logon
proof; no reboot without explicit live operator approval.
## Fresh deployability audit (2026-07-16)
The host is operational after current-user login, not yet deployable as one
proven artifact generation. `GameCult-Yggdrasil-Tunnel` and
`Epiphany-Idunn-Managed-Service-Reconciler` are enabled/running; the Yggdrasil
Qdrant tunnel and Ollama endpoint are live; packaged Mind and Modeling queries
return semantic ranking. Local retired Qdrant remains stopped. This is real
current-boot physiology.
Four non-permission wounds remain. Installed supervisor/projector binaries
predate recovery commit `34802407`; Task Scheduler pins one mutable supervisor
path while Idunn discovers siblings by filename/existence, so mixed generations
are possible; Mind has no race-bounded whole-repository readiness join; and the
old readiness plan described already-landed June work as future work. The plan
now contains the live evidence matrix. Packaging must become an immutable typed
commit-addressed release witness consumed by Task Scheduler and Idunn. Mind
readiness must join fresh Body R1/R2, exact Body-grounded RepoModel admission,
live Modeling semantic eligibility, and live workspace-coverage Qdrant evidence.
Stored coverage `Current` is not sufficient.
The constant `queryAdmission=false` and supervisor fields naming an external
readiness authority are removed from sight-only projector/health/status
projections. Those surfaces never evaluated query admission and therefore have
no opinion. Query-time reauthentication remains the semantic admission owner.
After the witnessed release is deployed, prove exact current-boot recurrence
and Mind/Modeling query ranking. A reboot/logon proof remains the sole
permission-bound host gate; do not reboot without explicit live approval.

View file

@ -21,13 +21,26 @@ embedding work over WireGuard, but moving only the projector to Yggdrasil would
split local store authority. Do not install a second projector on Yggdrasil or
one process per partition.
## Build
## Package one source generation
```powershell
$env:CARGO_TARGET_DIR='C:\Users\Meta\.cargo-target-codex'
cargo build --release --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-memory-semantic-projector --bin epiphany-memory-semantic --bin epiphany-daemon-supervisor
cargo build --release --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-release
& "$env:CARGO_TARGET_DIR\release\epiphany-release.exe" package `
--repo . `
--destination .\.epiphany-run\releases `
--store .\.epiphany-run\cultmesh\local-verse.ccmp `
--runtime-id epiphany-local
```
The packager refuses dirty tracked source and owns a fresh isolated Cargo release
build of the supervisor, semantic projector, workspace-coverage projector, and
semantic query gate. It copies exactly those four siblings into an immutable
commit-addressed directory, verifies every byte, then atomically publishes a
typed CultCache release witness and current head. Keep the returned `releaseId`,
`witnessSha256`, and `packageRoot`; Task Scheduler and Idunn pin them. A mutable
`target\release` directory is build output, not deployment authority.
## Transport configuration
The reserved typed service policy binds the non-secret transport coordinates:
@ -65,10 +78,15 @@ REST/gRPC ports locally as `16333`/`16334`; do not restart the retired node.
## Publish the typed service policy
```powershell
$supervisor = '.\target\release\epiphany-daemon-supervisor.exe'
$releaseRoot = '<packageRoot returned by epiphany-release>'
$releaseId = '<releaseId>'
$releaseDigest = '<witnessSha256>'
$supervisor = Join-Path $releaseRoot 'epiphany-daemon-supervisor.exe'
& $supervisor semantic-projector-service-policy `
--store .\.epiphany-run\cultmesh\local-verse.ccmp `
--runtime-id epiphany-local `
--release-id $releaseId `
--release-witness-sha256 $releaseDigest `
--agent-store .\state\agents.msgpack `
--runtime-store .\state\runtime-spine.msgpack `
--qdrant-url http://127.0.0.1:16333 `
@ -80,8 +98,8 @@ $supervisor = '.\target\release\epiphany-daemon-supervisor.exe'
This command writes the fixed
`epiphany-memory-semantic-projector-service` managed-service policy with
`restartMode=always`. Its generated child command contains both canonical
stores. The projector executable is derived as the packaged sibling of the
running supervisor and must already exist. Caller-supplied executable paths,
stores. The projector executable is resolved from the authenticated
`semantic-projector` role in the pinned release witness. Caller-supplied executable paths,
service IDs, restart modes, arbitrary child arguments, and finite iteration
limits do not own this service shape. The generic managed-service writer
refuses the reserved service id.
@ -101,6 +119,25 @@ publishing a pulse or heartbeat. A launch receipt proves process creation; it
does not claim semantic readiness. The first heartbeat comes later, after the
projector holds the OS singleton and validates both canonical inputs.
## Install the pinned after-login reconciler
```powershell
& $supervisor managed-service-task-install `
--store .\.epiphany-run\cultmesh\local-verse.ccmp `
--runtime-id epiphany-local `
--release-id $releaseId `
--cwd . `
--loop-interval-seconds 60
```
Task installation resolves the supervisor path and witness digest from the
typed release. The scheduled action pins both `--release-id` and
`--release-witness-sha256`; `managed-service-serve` authenticates the complete
four-sibling set and its own path before reconciliation, then revalidates the
set on every pulse. Reserved projector policies must name the exact witnessed
role paths. A task pointing at the right supervisor with a missing/wrong witness
argument, or a release directory with one changed sibling, is drift/failure.
## Recovery
Use `semantic-recover` only with the exact abandoned claim, the immutable launch
@ -142,7 +179,7 @@ curl.exe -fsS http://127.0.0.1:16333/collections
Get-CimInstance Win32_Process |
Where-Object { $_.CommandLine -match 'managed-service-serve|memory-semantic-projector' } |
Select-Object ProcessId, ParentProcessId, CreationDate, CommandLine
C:\Users\Meta\.cargo-target-codex\release\epiphany-daemon-supervisor.exe `
& $supervisor `
semantic-projector-service-status `
--store .\.epiphany-run\cultmesh\local-verse.ccmp `
--runtime-id epiphany-local
@ -181,7 +218,7 @@ BM25, which is a failed deployment proof rather than semantic readiness:
```powershell
$env:EPIPHANY_QDRANT_URL='http://127.0.0.1:16333'
$env:EPIPHANY_OLLAMA_BASE_URL='http://10.77.0.1:11435'
$semantic='C:\Users\Meta\.cargo-target-codex\release\epiphany-memory-semantic.exe'
$semantic=Join-Path $releaseRoot 'epiphany-memory-semantic.exe'
& $semantic context --agent-store .\state\agents.msgpack --partition mind `
--text 'current Epiphany doctrine and memory' --query-id reboot-proof-mind --budget 8
& $semantic context --runtime-store .\state\runtime-spine.msgpack --partition modeling `

Binary file not shown.

View file

@ -732,4 +732,5 @@ current_status:
- Abandoned workspace-coverage authority now has a complete typed transfer. Recovery derives the Body store from the runtime route, reopens the authenticated Body basis, joins obligation/plan/claim to that basis, authenticates current policy plus exact old launch and immutable termination, requires a signed causal termination-to-replacement edge and the replacement's latest ready heartbeat, then performs one Body CAS over Body authority, plan, old claim/attempt, immutable recovery receipt, failed claim history, and epoch+1 successor. The receipt stores exact termination, launch, and heartbeat envelope digests and can reconstruct both CultMesh evidence and current Body authority after restart. Claim/attempt schema and evidence pairs cannot split; impossible completion-before-start timelines refuse. Idunn's reserved reconciler no longer consults generic lifecycle PID receipts for this service. It persists/reuses termination before spawn, uses the specialized bootstrap launch, waits for signed readiness, and then invokes the recovery CAS. One termination admits one replacement slot; concurrent losers are killed when launch persistence loses CAS.
- Workspace-coverage launch, heartbeat, and termination now serialize through one typed per-launch process-evidence head. Launch initializes generation 1 as `launched`; each signed heartbeat exact-CAS advances it to `heartbeat`; host-signed termination exact-CAS seals it as `terminated`. Termination v1 accepts exact launch plus enrolled host/boot/process proof before heartbeat sequence one and records heartbeat id/digest only when the head names current signed heartbeat evidence. A heartbeat cannot advance a terminated head. This removes the pre-readiness death strand without allowing heartbeat/termination split authority.
- The feature-gated native `epiphany-workspace-coverage-recovery-smoke` proves the real packaged supervisor/projector boundary in GUID-scoped repo/runtime/Body/Verse state. It warms local Ollama, derives only its exact fenced Qdrant collection names, publishes reserved policy, launches the initial child through fixed bootstrap, observes epoch 1 claim, kills only after exact native launch-incarnation revalidation, invokes Idunn reconcile, authenticates epoch 2 successor authority plus the immutable recovery receipt through freshly reopened stores, kills the replacement under the same identity fence, and verifies only its owned collections absent before success. Live proof `C:\Users\Meta\AppData\Local\Temp\epiphany-workspace-coverage-a577f820-b0e2-4733-b628-529ee3bdb143\proof.json` passed with receipt `2b44f28f-7c0f-4ad2-aefe-d7c8222e8642` and binds the executed binary, exact embedded smoke source, source head, and tracked-source diff digests. Operator orchestration, not the harness, restored local Qdrant to its prior stopped state. The run also repaired normal-user Windows boot identity and exact recovered-owner claim resumption.
next_action: Perform a fresh deployability audit against `notes/epiphany-swarm-readiness-plan.md`, the live machine map, packaged binaries, Task Scheduler presence, local dependency configuration, and current tests. Separate code-complete physiology from the permission-bound reboot/logon survival proof; do not reboot without explicit live operator approval. Cut any remaining non-permission architectural wound before calling the swarm deployable.
- The 2026-07-16 deployability audit replaced `notes/epiphany-swarm-readiness-plan.md`'s obsolete June queue with the live evidence matrix. Brake, initiative recovery, Persona speech eligibility, memory lifecycle, CultNet-first operation, and branch-local Hands/Soul/Mind gates are landed. The installed after-login host is operational and real Mind/Modeling semantic queries pass against Yggdrasil, but it is not yet deployable as one proven machine: installed binaries predate recovery commit `34802407`; mutable sibling lookup permits mixed build generations; Mind lacks the race-bounded whole-repository readiness join; and reboot/logon lineage remains permission-bound. Health, heartbeat, Task Scheduler state, Qdrant existence, legacy retrieval Ready, and Eve/Gjallar rows cannot substitute for either query admission or repository readiness. Sight surfaces no longer publish the foreign constant `queryAdmission=false`; they report only facts they observe.
next_action: Build a native immutable commit-addressed sibling release witness and make Idunn/Task Scheduler consume it before replacing the stale installed package. Then implement Mind's repository-readiness join with fresh Body R1/R2, exact admitted RepoModel, live Modeling semantic eligibility, and live workspace-coverage Qdrant evidence. Deploy and prove current-boot recurrence/query behavior; do not run the final reboot/logon proof without explicit live operator approval.

View file

@ -186,3 +186,13 @@ replacement could not resume its own recovered running claim, so exact v3 owner
resumption is allowed while all foreign incarnations remain contended.
Next: full deployability audit. Reboot/logon proof still requires permission.
## Deployability audit cut (2026-07-16)
Operational after login, not artifact-clean. Yggdrasil Qdrant/Ollama and both
packaged semantic query gates pass. Installed siblings predate `34802407` and
are mutable/mixable. Build one immutable commit-addressed release witness before
replacement. Mind still needs the R1/model+admission/live-semantic/live-coverage/
R2 repository-readiness join. The readiness plan's June queue was stale and is
now an evidence matrix. Sight surfaces no longer say `queryAdmission=false`;
they never owned that decision. Final reboot/logon proof needs live approval.