Delete caller-authored publication authority
This commit is contained in:
parent
6e995a7b4d
commit
57e73cb774
7 changed files with 29 additions and 1301 deletions
|
|
@ -1,628 +0,0 @@
|
|||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use chrono::Utc;
|
||||
use serde_json::Value;
|
||||
use serde_json::json;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse()?;
|
||||
let result = run_smoke(args)?;
|
||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Args {
|
||||
root: PathBuf,
|
||||
smoke_root: PathBuf,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn parse() -> Result<Self> {
|
||||
let mut root = env::current_dir().context("failed to resolve current directory")?;
|
||||
let mut smoke_root = root.join(".epiphany-smoke");
|
||||
let mut args = env::args().skip(1).peekable();
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--root" => root = take_path(&mut args, "--root")?,
|
||||
"--smoke-root" => smoke_root = take_path(&mut args, "--smoke-root")?,
|
||||
other => return Err(anyhow!("unexpected argument {other:?}")),
|
||||
}
|
||||
}
|
||||
Ok(Self { root, smoke_root })
|
||||
}
|
||||
}
|
||||
|
||||
fn run_smoke(args: Args) -> Result<Value> {
|
||||
let root = args
|
||||
.root
|
||||
.canonicalize()
|
||||
.with_context(|| format!("failed to resolve {}", args.root.display()))?;
|
||||
let manifest = root.join("epiphany-core").join("Cargo.toml");
|
||||
if !manifest.exists() {
|
||||
return Err(anyhow!(
|
||||
"could not find epiphany-core manifest at {}",
|
||||
manifest.display()
|
||||
));
|
||||
}
|
||||
fs::create_dir_all(&args.smoke_root)
|
||||
.with_context(|| format!("failed to create {}", args.smoke_root.display()))?;
|
||||
let stamp = Utc::now().format("%Y%m%d-%H%M%S").to_string();
|
||||
let smoke_dir = args
|
||||
.smoke_root
|
||||
.join(format!("swarm-stop-classification-{stamp}"));
|
||||
if smoke_dir.exists() {
|
||||
fs::remove_dir_all(&smoke_dir)
|
||||
.with_context(|| format!("failed to clear {}", smoke_dir.display()))?;
|
||||
}
|
||||
fs::create_dir_all(&smoke_dir)
|
||||
.with_context(|| format!("failed to create {}", smoke_dir.display()))?;
|
||||
|
||||
let origin = smoke_dir.join("origin.git");
|
||||
git(["init", "--bare", path_str(&origin)?], &smoke_dir)?;
|
||||
|
||||
let repo = smoke_dir.join("repo-body");
|
||||
fs::create_dir_all(&repo).with_context(|| format!("failed to create {}", repo.display()))?;
|
||||
git(["init"], &repo)?;
|
||||
git(
|
||||
["config", "user.email", "epiphany-smoke@example.invalid"],
|
||||
&repo,
|
||||
)?;
|
||||
git(["config", "user.name", "Epiphany Smoke"], &repo)?;
|
||||
fs::write(
|
||||
repo.join("README.md"),
|
||||
"# Swarm Stop Classification Smoke\n\nThis repository proves typed repo-swarm run stop classification.\n",
|
||||
)
|
||||
.with_context(|| format!("failed to seed {}", repo.join("README.md").display()))?;
|
||||
git(["add", "README.md"], &repo)?;
|
||||
git(
|
||||
["commit", "-m", "Seed swarm stop classification smoke"],
|
||||
&repo,
|
||||
)?;
|
||||
git(["branch", "-M", "main"], &repo)?;
|
||||
git(["remote", "add", "origin", path_str(&origin)?], &repo)?;
|
||||
git(["push", "-u", "origin", "main"], &repo)?;
|
||||
|
||||
let runtime_id = "swarm-stop-classification-smoke";
|
||||
let local_verse = repo.join(".epiphany").join("local-verse.ccmp");
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-repo",
|
||||
&[
|
||||
"init",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--swarm-id",
|
||||
runtime_id,
|
||||
"--topic",
|
||||
"swarm-stop-classification",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"online",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
|
||||
let empty_run = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"run",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--max-iterations",
|
||||
"1",
|
||||
"--max-items",
|
||||
"1",
|
||||
"--cooldown-seconds",
|
||||
"0",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(
|
||||
&empty_run,
|
||||
&["stopClassification", "schemaVersion"],
|
||||
"epiphany.repo_swarm_run_stop_classification.v0",
|
||||
)?;
|
||||
require_eq(
|
||||
&empty_run,
|
||||
&["stopClassification", "category"],
|
||||
"queue-empty",
|
||||
)?;
|
||||
require_eq(&empty_run, &["stopClassification", "owner"], "Self")?;
|
||||
require_eq(
|
||||
&empty_run,
|
||||
&["stopClassification", "authorityGate"],
|
||||
"repo.work.overview",
|
||||
)?;
|
||||
require_eq(
|
||||
&empty_run,
|
||||
&["stopClassification", "blocker"],
|
||||
"no-repo-work-rows",
|
||||
)?;
|
||||
require_bool(&empty_run, &["stopClassification", "mutatesState"], false)?;
|
||||
require_bool(
|
||||
&empty_run,
|
||||
&["stopClassification", "requiresElevatedAuthority"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&empty_run,
|
||||
&["stopClassification", "privateStateExposed"],
|
||||
false,
|
||||
)?;
|
||||
|
||||
let item = "gate-proof";
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&[
|
||||
"persona-intake",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--item",
|
||||
item,
|
||||
"--message",
|
||||
"Prove a dry-run swarm pulse can classify a ready-to-run queue row without mutation.",
|
||||
"--topic",
|
||||
"swarm-stop-classification",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&[
|
||||
"derive-plan",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--item",
|
||||
item,
|
||||
"--action-family",
|
||||
"repo-status-section",
|
||||
"--model-ref",
|
||||
"swarm-stop-classification-smoke-imagination-v1",
|
||||
"--model-authored",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&["overview", "--workspace", path_str(&repo)?, "--item", item],
|
||||
&root,
|
||||
)?;
|
||||
let dry_run = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"run",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--max-iterations",
|
||||
"1",
|
||||
"--max-items",
|
||||
"1",
|
||||
"--cooldown-seconds",
|
||||
"0",
|
||||
"--dry-run",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(
|
||||
&dry_run,
|
||||
&["stopClassification", "schemaVersion"],
|
||||
"epiphany.repo_swarm_run_stop_classification.v0",
|
||||
)?;
|
||||
require_eq(
|
||||
&dry_run,
|
||||
&["stopClassification", "category"],
|
||||
"dry-run-preview",
|
||||
)?;
|
||||
require_eq(&dry_run, &["stopClassification", "owner"], "Self")?;
|
||||
require_eq(
|
||||
&dry_run,
|
||||
&["stopClassification", "authorityGate"],
|
||||
"ready-to-run",
|
||||
)?;
|
||||
require_eq(&dry_run, &["stopClassification", "selectedItem"], item)?;
|
||||
require_bool(&dry_run, &["stopClassification", "mutatesState"], false)?;
|
||||
require_bool(
|
||||
&dry_run,
|
||||
&["stopClassification", "requiresElevatedAuthority"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&dry_run,
|
||||
&["stopClassification", "privateStateExposed"],
|
||||
false,
|
||||
)?;
|
||||
|
||||
git(
|
||||
[
|
||||
"checkout",
|
||||
"-B",
|
||||
"epiphany/swarm-stop-classification/gate-proof",
|
||||
],
|
||||
&repo,
|
||||
)?;
|
||||
let publication_pipeline = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"run",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--max-iterations",
|
||||
"4",
|
||||
"--max-items",
|
||||
"1",
|
||||
"--cooldown-seconds",
|
||||
"0",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(
|
||||
&publication_pipeline,
|
||||
&["stopClassification", "category"],
|
||||
"iteration-limit",
|
||||
)?;
|
||||
let publication_gate_run = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"run",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--max-iterations",
|
||||
"1",
|
||||
"--max-items",
|
||||
"1",
|
||||
"--cooldown-seconds",
|
||||
"0",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "category"],
|
||||
"authority-gated",
|
||||
)?;
|
||||
require_eq(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "owner"],
|
||||
"Bifrost",
|
||||
)?;
|
||||
require_eq(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "authorityGate"],
|
||||
"awaiting-publication",
|
||||
)?;
|
||||
require_eq(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "blocker"],
|
||||
"bifrost-publication-missing",
|
||||
)?;
|
||||
require_bool(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "mutatesState"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "requiresElevatedAuthority"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&publication_gate_run,
|
||||
&["stopClassification", "privateStateExposed"],
|
||||
false,
|
||||
)?;
|
||||
|
||||
let closure_receipt = repo
|
||||
.join(".epiphany")
|
||||
.join("work")
|
||||
.join(format!("work-close-{item}.json"));
|
||||
let publish = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&[
|
||||
"publish",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--item",
|
||||
item,
|
||||
"--closure-receipt",
|
||||
path_str(&closure_receipt)?,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--change-summary",
|
||||
"Swarm stop classification publication gate proof.",
|
||||
"--justification",
|
||||
"Disposable Bifrost proof for gated repo-swarm stop classification.",
|
||||
"--ledger-entry-id",
|
||||
"swarm-stop-classification-publication-ledger",
|
||||
"--pull-request-url",
|
||||
"https://example.invalid/GameCult/swarm-stop-classification/pull/1",
|
||||
"--pull-request-number",
|
||||
"1",
|
||||
"--pull-request-title",
|
||||
"Swarm stop classification publication gate proof",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(&publish, &["status"], "publication-receipts-recorded")?;
|
||||
cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&["overview", "--workspace", path_str(&repo)?, "--item", item],
|
||||
&root,
|
||||
)?;
|
||||
let sync_gate_run = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-swarm",
|
||||
&[
|
||||
"run",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--epiphany-root",
|
||||
path_str(&root)?,
|
||||
"--runtime-id",
|
||||
runtime_id,
|
||||
"--local-verse-store",
|
||||
path_str(&local_verse)?,
|
||||
"--max-iterations",
|
||||
"1",
|
||||
"--max-items",
|
||||
"1",
|
||||
"--cooldown-seconds",
|
||||
"0",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "category"],
|
||||
"authority-gated",
|
||||
)?;
|
||||
require_eq(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "owner"],
|
||||
"Bifrost/GitHub",
|
||||
)?;
|
||||
require_eq(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "authorityGate"],
|
||||
"awaiting-upstream-sync",
|
||||
)?;
|
||||
require_eq(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "blocker"],
|
||||
"merge-or-sync-receipt-missing",
|
||||
)?;
|
||||
require_bool(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "mutatesState"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "requiresElevatedAuthority"],
|
||||
false,
|
||||
)?;
|
||||
require_bool(
|
||||
&sync_gate_run,
|
||||
&["stopClassification", "privateStateExposed"],
|
||||
false,
|
||||
)?;
|
||||
|
||||
git(["push", "origin", "HEAD:main"], &repo)?;
|
||||
git(["fetch", "origin", "main"], &repo)?;
|
||||
let publish_receipt = repo
|
||||
.join(".epiphany")
|
||||
.join("work")
|
||||
.join(format!("work-publish-{item}.json"));
|
||||
let sync = cargo_json(
|
||||
&manifest,
|
||||
"epiphany-work",
|
||||
&[
|
||||
"sync",
|
||||
"--workspace",
|
||||
path_str(&repo)?,
|
||||
"--item",
|
||||
item,
|
||||
"--publish-receipt",
|
||||
path_str(&publish_receipt)?,
|
||||
"--upstream-ref",
|
||||
"origin/main",
|
||||
"--merge-receipt",
|
||||
"maintainer-merge:swarm-stop-classification-smoke",
|
||||
],
|
||||
&root,
|
||||
)?;
|
||||
require_eq(&sync, &["status"], "upstream-main-synced")?;
|
||||
|
||||
let summary = json!({
|
||||
"schemaVersion": "epiphany.repo_swarm_stop_classification_smoke.v0",
|
||||
"status": "ok",
|
||||
"smokeDir": smoke_dir,
|
||||
"repo": repo,
|
||||
"origin": origin,
|
||||
"emptyStopCategory": empty_run["stopClassification"]["category"],
|
||||
"emptyStopOwner": empty_run["stopClassification"]["owner"],
|
||||
"emptyStopGate": empty_run["stopClassification"]["authorityGate"],
|
||||
"dryRunStopCategory": dry_run["stopClassification"]["category"],
|
||||
"dryRunStopOwner": dry_run["stopClassification"]["owner"],
|
||||
"dryRunStopGate": dry_run["stopClassification"]["authorityGate"],
|
||||
"dryRunSelectedItem": dry_run["stopClassification"]["selectedItem"],
|
||||
"publicationStopCategory": publication_gate_run["stopClassification"]["category"],
|
||||
"publicationStopOwner": publication_gate_run["stopClassification"]["owner"],
|
||||
"publicationStopGate": publication_gate_run["stopClassification"]["authorityGate"],
|
||||
"publicationStopBlocker": publication_gate_run["stopClassification"]["blocker"],
|
||||
"syncStopCategory": sync_gate_run["stopClassification"]["category"],
|
||||
"syncStopOwner": sync_gate_run["stopClassification"]["owner"],
|
||||
"syncStopGate": sync_gate_run["stopClassification"]["authorityGate"],
|
||||
"syncStopBlocker": sync_gate_run["stopClassification"]["blocker"],
|
||||
"syncStatus": sync["status"],
|
||||
"upstreamMainSynced": sync["authority"]["upstreamMainSynced"],
|
||||
"mutatesState": false,
|
||||
"requiresElevatedAuthority": false,
|
||||
"privateStateExposed": false,
|
||||
});
|
||||
write_json(&smoke_dir.join("summary.json"), &summary)?;
|
||||
Ok(summary)
|
||||
}
|
||||
|
||||
fn cargo_json(manifest: &Path, bin: &str, args: &[&str], cwd: &Path) -> Result<Value> {
|
||||
let output = Command::new("cargo")
|
||||
.arg("run")
|
||||
.arg("--quiet")
|
||||
.arg("--manifest-path")
|
||||
.arg(manifest)
|
||||
.arg("--bin")
|
||||
.arg(bin)
|
||||
.arg("--")
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.output()
|
||||
.with_context(|| format!("failed to run cargo bin {bin}"))?;
|
||||
if !output.status.success() {
|
||||
return Err(anyhow!(
|
||||
"{bin} failed with status {:?}\nstdout:\n{}\nstderr:\n{}",
|
||||
output.status.code(),
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
serde_json::from_slice(&output.stdout).with_context(|| {
|
||||
format!(
|
||||
"{bin} did not emit JSON on stdout:\n{}",
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn git<const N: usize>(args: [&str; N], cwd: &Path) -> Result<()> {
|
||||
let output = Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.output()
|
||||
.with_context(|| format!("failed to run git in {}", cwd.display()))?;
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"git failed in {} with status {:?}\nstdout:\n{}\nstderr:\n{}",
|
||||
cwd.display(),
|
||||
output.status.code(),
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn write_json(path: &Path, value: &Value) -> Result<()> {
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
.with_context(|| format!("failed to create {}", parent.display()))?;
|
||||
}
|
||||
fs::write(path, serde_json::to_vec_pretty(value)?)
|
||||
.with_context(|| format!("failed to write {}", path.display()))
|
||||
}
|
||||
|
||||
fn require_eq(value: &Value, path: &[&str], expected: &str) -> Result<()> {
|
||||
let actual = path
|
||||
.iter()
|
||||
.try_fold(value, |current, key| current.get(*key))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("<missing>");
|
||||
if actual == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"expected {} to be {expected:?}, got {actual:?}",
|
||||
path.join(".")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn require_bool(value: &Value, path: &[&str], expected: bool) -> Result<()> {
|
||||
let actual = path
|
||||
.iter()
|
||||
.try_fold(value, |current, key| current.get(*key))
|
||||
.and_then(Value::as_bool);
|
||||
if actual == Some(expected) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"expected {} to be {expected}, got {:?}",
|
||||
path.join("."),
|
||||
actual
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn take_path(
|
||||
args: &mut std::iter::Peekable<impl Iterator<Item = String>>,
|
||||
flag: &str,
|
||||
) -> Result<PathBuf> {
|
||||
args.next()
|
||||
.map(PathBuf::from)
|
||||
.with_context(|| format!("missing value for {flag}"))
|
||||
}
|
||||
|
||||
fn path_str(path: &Path) -> Result<&str> {
|
||||
path.to_str()
|
||||
.ok_or_else(|| anyhow!("path is not valid UTF-8: {}", path.display()))
|
||||
}
|
||||
|
|
@ -630,10 +630,10 @@ fn classify_run_stop(
|
|||
};
|
||||
let recommended = match selected_gate.as_str() {
|
||||
"awaiting-publication" => {
|
||||
"epiphany-work publish --workspace <repo> --closure-receipt <receipt>"
|
||||
"submit a Bifrost publication intent through the owning integration"
|
||||
}
|
||||
"awaiting-sync" | "awaiting-upstream-sync" => {
|
||||
"epiphany-work sync --workspace <repo> --publish-receipt <receipt>"
|
||||
"inspect provider-authored publication and upstream Git ancestry evidence"
|
||||
}
|
||||
"awaiting-closure" => {
|
||||
"epiphany-work close --workspace <repo> --execute-receipt <receipt>"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use epiphany_core::HANDS_ACTION_INTENT_SCHEMA_VERSION;
|
|||
use epiphany_core::HANDS_COMMAND_RECEIPT_TYPE;
|
||||
use epiphany_core::HANDS_COMMIT_RECEIPT_TYPE;
|
||||
use epiphany_core::HANDS_PATCH_RECEIPT_TYPE;
|
||||
use epiphany_core::HANDS_PR_RECEIPT_TYPE;
|
||||
use epiphany_core::HandsActionIntent;
|
||||
use epiphany_core::MIND_GATEWAY_REVIEW_SCHEMA_VERSION;
|
||||
use epiphany_core::MindGatewayDecision;
|
||||
|
|
@ -54,7 +53,6 @@ use epiphany_core::hands_action_review_for_intent;
|
|||
use epiphany_core::hands_command_receipt_for_review;
|
||||
use epiphany_core::hands_commit_receipt_for_review;
|
||||
use epiphany_core::hands_patch_receipt_for_review;
|
||||
use epiphany_core::hands_pr_receipt_for_review;
|
||||
use epiphany_core::initialize_runtime_spine;
|
||||
use epiphany_core::load_agent_memory_entry_for_role;
|
||||
use epiphany_core::load_epiphany_cultmesh_idunn_aftercare_audit_receipt;
|
||||
|
|
@ -75,7 +73,6 @@ use epiphany_core::put_hands_action_review;
|
|||
use epiphany_core::put_hands_command_receipt;
|
||||
use epiphany_core::put_hands_commit_receipt;
|
||||
use epiphany_core::put_hands_patch_receipt;
|
||||
use epiphany_core::put_hands_pr_receipt;
|
||||
use epiphany_core::put_soul_verdict_receipt;
|
||||
use epiphany_core::put_substrate_gate_repo_access_grant_receipt;
|
||||
use epiphany_core::record_weksa_target_lowering_receipt;
|
||||
|
|
@ -84,7 +81,6 @@ use epiphany_core::runtime_hands_action_intent;
|
|||
use epiphany_core::runtime_hands_action_review;
|
||||
use epiphany_core::runtime_hands_commit_receipt;
|
||||
use epiphany_core::runtime_job_snapshot;
|
||||
use epiphany_core::runtime_latest_hands_receipt_chain_after;
|
||||
use epiphany_core::runtime_repo_work_map_entry;
|
||||
use epiphany_core::runtime_repo_work_modeling_finding;
|
||||
use epiphany_core::runtime_repo_work_modeling_request;
|
||||
|
|
@ -129,8 +125,6 @@ fn main() -> Result<()> {
|
|||
"revise-modeling" | "modeling-retry" => {
|
||||
run_revise_modeling(parse_revise_modeling_args(args)?)
|
||||
}
|
||||
"publish" => run_publish(parse_publish_args(args)?),
|
||||
"sync" | "sync-main" => run_sync(parse_sync_args(args)?),
|
||||
"overview" | "proof-bundle" | "status" => run_overview(parse_overview_args(args)?),
|
||||
"readiness" | "readiness-report" | "mvp-readiness" => {
|
||||
run_readiness(parse_readiness_args(args)?)
|
||||
|
|
@ -313,43 +307,6 @@ enum ClosurePhase {
|
|||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct PublishArgs {
|
||||
workspace: PathBuf,
|
||||
epiphany_root: PathBuf,
|
||||
item: Option<String>,
|
||||
adopt_receipt: Option<PathBuf>,
|
||||
closure_receipt: Option<PathBuf>,
|
||||
runtime_store: Option<PathBuf>,
|
||||
local_verse_store: Option<PathBuf>,
|
||||
artifact_dir: Option<PathBuf>,
|
||||
commit_receipt_id: Option<String>,
|
||||
hands_pr_receipt_id: Option<String>,
|
||||
target_branch: Option<String>,
|
||||
change_summary: String,
|
||||
justification: String,
|
||||
verification_receipts: Vec<String>,
|
||||
review_receipts: Vec<String>,
|
||||
author_agents: Vec<String>,
|
||||
credit_subjects: Vec<String>,
|
||||
credit_receipt_ids: Option<Vec<String>>,
|
||||
ledger_entry_id: String,
|
||||
pull_request_url: String,
|
||||
pull_request_number: String,
|
||||
pull_request_title: String,
|
||||
publication_status: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct SyncArgs {
|
||||
workspace: PathBuf,
|
||||
item: Option<String>,
|
||||
publish_receipt: Option<PathBuf>,
|
||||
artifact_dir: Option<PathBuf>,
|
||||
upstream_ref: String,
|
||||
merge_receipts: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct OverviewArgs {
|
||||
workspace: PathBuf,
|
||||
|
|
@ -981,192 +938,6 @@ fn parse_revise_modeling_args(args: impl Iterator<Item = String>) -> Result<Revi
|
|||
})
|
||||
}
|
||||
|
||||
fn parse_publish_args(args: impl Iterator<Item = String>) -> Result<PublishArgs> {
|
||||
let mut workspace = None;
|
||||
let mut epiphany_root = None;
|
||||
let mut item = None;
|
||||
let mut adopt_receipt = None;
|
||||
let mut closure_receipt = None;
|
||||
let mut runtime_store = None;
|
||||
let mut local_verse_store = None;
|
||||
let mut artifact_dir = None;
|
||||
let mut commit_receipt_id = None;
|
||||
let mut hands_pr_receipt_id = None;
|
||||
let mut target_branch = None;
|
||||
let mut change_summary = None;
|
||||
let mut justification = None;
|
||||
let mut verification_receipts = Vec::new();
|
||||
let mut review_receipts = Vec::new();
|
||||
let mut author_agents = Vec::new();
|
||||
let mut credit_subjects = Vec::new();
|
||||
let mut credit_receipt_ids = Vec::new();
|
||||
let mut ledger_entry_id = None;
|
||||
let mut pull_request_url = None;
|
||||
let mut pull_request_number = None;
|
||||
let mut pull_request_title = None;
|
||||
let mut publication_status = None;
|
||||
|
||||
let mut args = args.peekable();
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--workspace" => workspace = Some(take_path(&mut args, "--workspace")?),
|
||||
"--epiphany-root" => epiphany_root = Some(take_path(&mut args, "--epiphany-root")?),
|
||||
"--item" => item = Some(take_string(&mut args, "--item")?),
|
||||
"--adopt-receipt" => adopt_receipt = Some(take_path(&mut args, "--adopt-receipt")?),
|
||||
"--closure-receipt" | "--close-receipt" => {
|
||||
closure_receipt = Some(take_path(&mut args, "--closure-receipt")?);
|
||||
}
|
||||
"--runtime-store" => runtime_store = Some(take_path(&mut args, "--runtime-store")?),
|
||||
"--local-verse-store" | "--store" => {
|
||||
local_verse_store = Some(take_path(&mut args, "--local-verse-store")?);
|
||||
}
|
||||
"--artifact-dir" => artifact_dir = Some(take_path(&mut args, "--artifact-dir")?),
|
||||
"--commit-receipt-id" => {
|
||||
commit_receipt_id = Some(take_string(&mut args, "--commit-receipt-id")?);
|
||||
}
|
||||
"--hands-pr-receipt-id" => {
|
||||
hands_pr_receipt_id = Some(take_string(&mut args, "--hands-pr-receipt-id")?);
|
||||
}
|
||||
"--target-branch" => target_branch = Some(take_string(&mut args, "--target-branch")?),
|
||||
"--change-summary" => {
|
||||
change_summary = Some(take_string(&mut args, "--change-summary")?)
|
||||
}
|
||||
"--justification" => justification = Some(take_string(&mut args, "--justification")?),
|
||||
"--verification-receipt" | "--soul-receipt" => {
|
||||
verification_receipts.push(take_string(&mut args, "--verification-receipt")?);
|
||||
}
|
||||
"--review-receipt" | "--mind-review-receipt" => {
|
||||
review_receipts.push(take_string(&mut args, "--review-receipt")?);
|
||||
}
|
||||
"--author-agent" => author_agents.push(take_string(&mut args, "--author-agent")?),
|
||||
"--credit-subject" => credit_subjects.push(take_string(&mut args, "--credit-subject")?),
|
||||
"--credit-receipt-id" => {
|
||||
credit_receipt_ids.push(take_string(&mut args, "--credit-receipt-id")?);
|
||||
}
|
||||
"--ledger-entry-id" => {
|
||||
ledger_entry_id = Some(take_string(&mut args, "--ledger-entry-id")?);
|
||||
}
|
||||
"--pull-request-url" | "--publication-url" => {
|
||||
pull_request_url = Some(take_string(&mut args, "--pull-request-url")?);
|
||||
}
|
||||
"--pull-request-number" => {
|
||||
pull_request_number = Some(take_string(&mut args, "--pull-request-number")?);
|
||||
}
|
||||
"--pull-request-title" => {
|
||||
pull_request_title = Some(take_string(&mut args, "--pull-request-title")?);
|
||||
}
|
||||
"--publication-status" => {
|
||||
publication_status = Some(take_string(&mut args, "--publication-status")?);
|
||||
}
|
||||
other => return Err(anyhow!("unexpected publish argument {other:?}")),
|
||||
}
|
||||
}
|
||||
if let Some(path) = closure_receipt.as_ref() {
|
||||
let closure = read_json(path)?;
|
||||
if closure.get("status").and_then(Value::as_str) != Some("closed") {
|
||||
return Err(anyhow!("closure receipt {} is not closed", path.display()));
|
||||
}
|
||||
if verification_receipts.is_empty() {
|
||||
if let Some(id) = string_from_json(&closure, &["soul", "verdictReceiptId"]) {
|
||||
verification_receipts.push(id);
|
||||
}
|
||||
}
|
||||
if review_receipts.is_empty() {
|
||||
if let Some(id) = string_from_json(&closure, &["mind", "stateCommitReceiptId"]) {
|
||||
review_receipts.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if verification_receipts.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"publish requires at least one --verification-receipt from Soul"
|
||||
));
|
||||
}
|
||||
if review_receipts.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"publish requires at least one --review-receipt from Mind or maintainer review"
|
||||
));
|
||||
}
|
||||
Ok(PublishArgs {
|
||||
workspace: workspace.context("missing --workspace")?,
|
||||
epiphany_root: epiphany_root
|
||||
.unwrap_or(env::current_dir().context("failed to resolve current directory")?),
|
||||
item,
|
||||
adopt_receipt,
|
||||
closure_receipt,
|
||||
runtime_store,
|
||||
local_verse_store,
|
||||
artifact_dir,
|
||||
commit_receipt_id,
|
||||
hands_pr_receipt_id,
|
||||
target_branch,
|
||||
change_summary: change_summary.context("missing --change-summary")?,
|
||||
justification: justification.context("missing --justification")?,
|
||||
verification_receipts,
|
||||
review_receipts,
|
||||
author_agents: if author_agents.is_empty() {
|
||||
vec!["epiphany.Hands".to_string()]
|
||||
} else {
|
||||
author_agents
|
||||
},
|
||||
credit_subjects: if credit_subjects.is_empty() {
|
||||
vec!["epiphany.Hands".to_string()]
|
||||
} else {
|
||||
credit_subjects
|
||||
},
|
||||
credit_receipt_ids: if credit_receipt_ids.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(credit_receipt_ids)
|
||||
},
|
||||
ledger_entry_id: ledger_entry_id.context("missing --ledger-entry-id")?,
|
||||
pull_request_url: pull_request_url.context("missing --pull-request-url")?,
|
||||
pull_request_number: pull_request_number.unwrap_or_else(|| "unknown".to_string()),
|
||||
pull_request_title: pull_request_title.context("missing --pull-request-title")?,
|
||||
publication_status: publication_status
|
||||
.unwrap_or_else(|| "accepted-for-github-publication".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_sync_args(args: impl Iterator<Item = String>) -> Result<SyncArgs> {
|
||||
let mut workspace = None;
|
||||
let mut item = None;
|
||||
let mut publish_receipt = None;
|
||||
let mut artifact_dir = None;
|
||||
let mut upstream_ref = None;
|
||||
let mut merge_receipts = Vec::new();
|
||||
|
||||
let mut args = args.peekable();
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--workspace" => workspace = Some(take_path(&mut args, "--workspace")?),
|
||||
"--item" => item = Some(take_string(&mut args, "--item")?),
|
||||
"--publish-receipt" => {
|
||||
publish_receipt = Some(take_path(&mut args, "--publish-receipt")?);
|
||||
}
|
||||
"--artifact-dir" => artifact_dir = Some(take_path(&mut args, "--artifact-dir")?),
|
||||
"--upstream-ref" => upstream_ref = Some(take_string(&mut args, "--upstream-ref")?),
|
||||
"--merge-receipt" | "--sync-receipt" => {
|
||||
merge_receipts.push(take_string(&mut args, "--merge-receipt")?);
|
||||
}
|
||||
other => return Err(anyhow!("unexpected sync argument {other:?}")),
|
||||
}
|
||||
}
|
||||
if merge_receipts.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"sync requires at least one --merge-receipt or --sync-receipt from Bifrost or maintainer review"
|
||||
));
|
||||
}
|
||||
Ok(SyncArgs {
|
||||
workspace: workspace.context("missing --workspace")?,
|
||||
item,
|
||||
publish_receipt,
|
||||
artifact_dir,
|
||||
upstream_ref: upstream_ref.unwrap_or_else(|| "origin/main".to_string()),
|
||||
merge_receipts,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_overview_args(args: impl Iterator<Item = String>) -> Result<OverviewArgs> {
|
||||
let mut workspace = None;
|
||||
let mut item = None;
|
||||
|
|
@ -11122,7 +10893,7 @@ fn run_execute(args: ExecuteArgs) -> Result<Value> {
|
|||
"privateStateExposed": false
|
||||
},
|
||||
"privateStateExposed": false,
|
||||
"nextSafeMove": "Route Soul verification and Mind review before publication; use epiphany-work publish only after review receipts exist."
|
||||
"nextSafeMove": "Route Soul verification and Mind review, then submit a Bifrost publication intent through the owning integration."
|
||||
});
|
||||
let receipt_path = artifact_dir.join(format!("work-execute-{item_slug}.json"));
|
||||
write_json(&receipt_path, &execute_receipt)?;
|
||||
|
|
@ -11869,7 +11640,7 @@ fn run_closure_pipeline(args: CloseArgs, phase: ClosurePhase) -> Result<Value> {
|
|||
"privateStateExposed": false
|
||||
},
|
||||
"privateStateExposed": false,
|
||||
"nextSafeMove": "Use epiphany-work publish --closure-receipt <receipt> with Bifrost/GitHub refs; merge remains gated by maintainers and sync receipts."
|
||||
"nextSafeMove": "Submit a Bifrost publication intent through the owning integration; merge remains gated by provider-authored maintainer evidence."
|
||||
});
|
||||
write_json(&closure_receipt_path, &closure_receipt)?;
|
||||
Ok(json!({
|
||||
|
|
@ -11890,416 +11661,6 @@ fn run_closure_pipeline(args: CloseArgs, phase: ClosurePhase) -> Result<Value> {
|
|||
}))
|
||||
}
|
||||
|
||||
fn run_publish(args: PublishArgs) -> Result<Value> {
|
||||
let workspace = args
|
||||
.workspace
|
||||
.canonicalize()
|
||||
.with_context(|| format!("failed to resolve {}", args.workspace.display()))?;
|
||||
ensure_git_repo(&workspace)?;
|
||||
let epiphany_root = args
|
||||
.epiphany_root
|
||||
.canonicalize()
|
||||
.with_context(|| format!("failed to resolve {}", args.epiphany_root.display()))?;
|
||||
let manifest_path = epiphany_root.join("epiphany-core").join("Cargo.toml");
|
||||
if !manifest_path.exists() {
|
||||
return Err(anyhow!(
|
||||
"could not find epiphany-core manifest at {}",
|
||||
manifest_path.display()
|
||||
));
|
||||
}
|
||||
let adopt_receipt_path =
|
||||
resolve_adopt_receipt(&workspace, args.item.as_deref(), args.adopt_receipt)?;
|
||||
let adopt_receipt = read_json(&adopt_receipt_path)?;
|
||||
let run_receipt_path = path_from_json(&adopt_receipt, &["runReceiptPath"])
|
||||
.ok_or_else(|| anyhow!("adopt receipt has no runReceiptPath"))?;
|
||||
let run_receipt = read_json(&run_receipt_path)?;
|
||||
let online_receipt_path = path_from_json(&run_receipt, &["onlineReceiptPath"])
|
||||
.ok_or_else(|| anyhow!("run receipt has no onlineReceiptPath"))?;
|
||||
let online_receipt = read_json(&online_receipt_path)?;
|
||||
let runtime_id = string_from_json(&adopt_receipt, &["runtimeId"])
|
||||
.or_else(|| string_from_json(&run_receipt, &["runtimeId"]))
|
||||
.or_else(|| string_from_json(&online_receipt, &["runtimeId"]))
|
||||
.unwrap_or_else(|| "repo-swarm-local".to_string());
|
||||
let runtime_store = args.runtime_store.unwrap_or_else(|| {
|
||||
path_from_json(&adopt_receipt, &["runtimeStore"]).unwrap_or_else(|| {
|
||||
workspace
|
||||
.join(".epiphany")
|
||||
.join("state")
|
||||
.join("runtime-spine.msgpack")
|
||||
})
|
||||
});
|
||||
let local_verse_store = args.local_verse_store.unwrap_or_else(|| {
|
||||
path_from_json(&online_receipt, &["localVerseStore"])
|
||||
.unwrap_or_else(|| workspace.join(".epiphany").join("local-verse.ccmp"))
|
||||
});
|
||||
let artifact_dir = args
|
||||
.artifact_dir
|
||||
.unwrap_or_else(|| workspace.join(".epiphany").join("work"));
|
||||
fs::create_dir_all(&artifact_dir)
|
||||
.with_context(|| format!("failed to create {}", artifact_dir.display()))?;
|
||||
|
||||
let gate = adopt_receipt
|
||||
.get("handsActionGate")
|
||||
.ok_or_else(|| anyhow!("adopt receipt has no handsActionGate"))?;
|
||||
let intent_id = gate
|
||||
.get("intentId")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| anyhow!("adopt receipt handsActionGate has no intentId"))?;
|
||||
let review_id = gate
|
||||
.get("reviewId")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| anyhow!("adopt receipt handsActionGate has no reviewId"))?;
|
||||
let intent = runtime_hands_action_intent(&runtime_store, intent_id)?
|
||||
.ok_or_else(|| anyhow!("runtime-spine has no Hands intent {intent_id}"))?;
|
||||
let review = runtime_hands_action_review(&runtime_store, review_id)?
|
||||
.ok_or_else(|| anyhow!("runtime-spine has no Hands review {review_id}"))?;
|
||||
if review.intent_id != intent.intent_id {
|
||||
return Err(anyhow!(
|
||||
"Hands review {} belongs to {}, not {}",
|
||||
review.review_id,
|
||||
review.intent_id,
|
||||
intent.intent_id
|
||||
));
|
||||
}
|
||||
if review.decision != "approved" {
|
||||
return Err(anyhow!(
|
||||
"Hands review {} decision is {}, not approved",
|
||||
review.review_id,
|
||||
review.decision
|
||||
));
|
||||
}
|
||||
|
||||
let commit_receipt = if let Some(commit_receipt_id) = args.commit_receipt_id.clone() {
|
||||
runtime_hands_commit_receipt(&runtime_store, &commit_receipt_id)?
|
||||
.ok_or_else(|| anyhow!("Hands commit receipt {commit_receipt_id} was not found"))?
|
||||
} else {
|
||||
let lower_bound = adopt_receipt
|
||||
.get("createdAt")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| anyhow!("adopt receipt has no createdAt timestamp"))?;
|
||||
let chain = runtime_latest_hands_receipt_chain_after(&runtime_store, lower_bound)?
|
||||
.ok_or_else(|| anyhow!("no complete Hands patch/command/commit chain after adoption; pass --commit-receipt-id after recording branch-local work"))?;
|
||||
runtime_hands_commit_receipt(&runtime_store, &chain.commit_receipt_id)?.ok_or_else(
|
||||
|| {
|
||||
anyhow!(
|
||||
"Hands commit receipt {} was not found",
|
||||
chain.commit_receipt_id
|
||||
)
|
||||
},
|
||||
)?
|
||||
};
|
||||
if commit_receipt.intent_id != intent.intent_id || commit_receipt.review_id != review.review_id
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"Hands commit receipt {} belongs to intent {}/review {}, not {}/{}",
|
||||
commit_receipt.receipt_id,
|
||||
commit_receipt.intent_id,
|
||||
commit_receipt.review_id,
|
||||
intent.intent_id,
|
||||
review.review_id
|
||||
));
|
||||
}
|
||||
|
||||
let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||
let mut publication_review = review.clone();
|
||||
if !publication_review
|
||||
.allowed_operations
|
||||
.iter()
|
||||
.any(|operation| operation == "pr")
|
||||
{
|
||||
publication_review.allowed_operations.push("pr".to_string());
|
||||
}
|
||||
if !publication_review
|
||||
.required_receipts
|
||||
.iter()
|
||||
.any(|receipt| receipt == HANDS_PR_RECEIPT_TYPE)
|
||||
{
|
||||
publication_review
|
||||
.required_receipts
|
||||
.push(HANDS_PR_RECEIPT_TYPE.to_string());
|
||||
}
|
||||
publication_review.reviewed_at = now.clone();
|
||||
publication_review.reasons.push(format!(
|
||||
"Bifrost publication routing approved after verification refs [{}] and review refs [{}].",
|
||||
args.verification_receipts.join(", "),
|
||||
args.review_receipts.join(", ")
|
||||
));
|
||||
put_hands_action_review(&runtime_store, &publication_review)?;
|
||||
|
||||
let item = adopt_receipt
|
||||
.get("item")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("work-item")
|
||||
.to_string();
|
||||
let item_slug = sanitize(&item);
|
||||
let hands_pr_receipt_id = args
|
||||
.hands_pr_receipt_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("repo-work-publish-{item_slug}-hands-pr"));
|
||||
let bifrost_intent_id = format!("repo-work-publish-{item_slug}-bifrost-intent");
|
||||
let bifrost_publication_receipt_id =
|
||||
format!("repo-work-publish-{item_slug}-bifrost-publication");
|
||||
let github_publication_receipt_id = format!("repo-work-publish-{item_slug}-github");
|
||||
let target_branch = args
|
||||
.target_branch
|
||||
.clone()
|
||||
.unwrap_or_else(|| commit_receipt.branch.clone());
|
||||
let target_repository = format!("repo:{}", workspace.display());
|
||||
let body_domain = target_repository.clone();
|
||||
|
||||
let mut verse_args = vec![
|
||||
"bifrost-publication".to_string(),
|
||||
"--store".to_string(),
|
||||
local_verse_store.display().to_string(),
|
||||
"--runtime-id".to_string(),
|
||||
runtime_id.clone(),
|
||||
"--intent-id".to_string(),
|
||||
bifrost_intent_id.clone(),
|
||||
"--receipt-id".to_string(),
|
||||
bifrost_publication_receipt_id.clone(),
|
||||
"--github-receipt-id".to_string(),
|
||||
github_publication_receipt_id.clone(),
|
||||
"--target-repository".to_string(),
|
||||
target_repository.clone(),
|
||||
"--target-branch".to_string(),
|
||||
target_branch.clone(),
|
||||
"--change-summary".to_string(),
|
||||
args.change_summary.clone(),
|
||||
"--justification".to_string(),
|
||||
args.justification.clone(),
|
||||
"--ledger-entry-id".to_string(),
|
||||
args.ledger_entry_id.clone(),
|
||||
"--hands-pr-receipt-id".to_string(),
|
||||
hands_pr_receipt_id.clone(),
|
||||
"--publication-url".to_string(),
|
||||
args.pull_request_url.clone(),
|
||||
"--pull-request-number".to_string(),
|
||||
args.pull_request_number.clone(),
|
||||
"--commit-sha".to_string(),
|
||||
commit_receipt.commit_sha.clone(),
|
||||
"--source-cluster-id".to_string(),
|
||||
"epiphany.cluster.hands".to_string(),
|
||||
"--source-agent-id".to_string(),
|
||||
"epiphany.Hands".to_string(),
|
||||
"--body-domain".to_string(),
|
||||
body_domain,
|
||||
"--receipt-status".to_string(),
|
||||
args.publication_status.clone(),
|
||||
];
|
||||
for path in &commit_receipt.changed_paths {
|
||||
verse_args.extend(["--changed-path".to_string(), path.clone()]);
|
||||
}
|
||||
for receipt in &args.verification_receipts {
|
||||
verse_args.extend(["--verification-receipt".to_string(), receipt.clone()]);
|
||||
}
|
||||
for receipt in &args.review_receipts {
|
||||
verse_args.extend(["--review-receipt".to_string(), receipt.clone()]);
|
||||
}
|
||||
for author in &args.author_agents {
|
||||
verse_args.extend(["--author-agent".to_string(), author.clone()]);
|
||||
}
|
||||
for subject in &args.credit_subjects {
|
||||
verse_args.extend(["--credit-subject".to_string(), subject.clone()]);
|
||||
}
|
||||
if let Some(credit_receipts) = &args.credit_receipt_ids {
|
||||
for receipt in credit_receipts {
|
||||
verse_args.extend(["--credit-receipt".to_string(), receipt.clone()]);
|
||||
}
|
||||
}
|
||||
let bifrost = cargo_json(&manifest_path, "epiphany-verse-query", &verse_args)?;
|
||||
|
||||
let pr_receipt = hands_pr_receipt_for_review(
|
||||
hands_pr_receipt_id.clone(),
|
||||
&intent,
|
||||
&publication_review,
|
||||
&commit_receipt,
|
||||
args.pull_request_url.clone(),
|
||||
args.pull_request_number.clone(),
|
||||
args.pull_request_title.clone(),
|
||||
bifrost_publication_receipt_id.clone(),
|
||||
format!("Published repo work item {item} through Bifrost receipts."),
|
||||
now.clone(),
|
||||
);
|
||||
put_hands_pr_receipt(&runtime_store, &pr_receipt)?;
|
||||
|
||||
let publish_receipt = json!({
|
||||
"schemaVersion": "epiphany.repo_work_publish_receipt.v0",
|
||||
"createdAt": now,
|
||||
"workspace": workspace,
|
||||
"runtimeId": runtime_id,
|
||||
"runtimeStore": runtime_store,
|
||||
"localVerseStore": local_verse_store,
|
||||
"adoptReceiptPath": adopt_receipt_path,
|
||||
"runReceiptPath": run_receipt_path,
|
||||
"onlineReceiptPath": online_receipt_path,
|
||||
"closureReceiptPath": args.closure_receipt,
|
||||
"item": item,
|
||||
"status": "publication-receipts-recorded",
|
||||
"targetRepository": target_repository,
|
||||
"targetBranch": target_branch,
|
||||
"changedPaths": commit_receipt.changed_paths,
|
||||
"handsActionGate": {
|
||||
"intentId": intent.intent_id,
|
||||
"reviewId": publication_review.review_id,
|
||||
"decision": publication_review.decision,
|
||||
"allowedOperations": publication_review.allowed_operations,
|
||||
"requiredReceipts": publication_review.required_receipts
|
||||
},
|
||||
"handsReceipts": {
|
||||
"commitReceiptId": commit_receipt.receipt_id,
|
||||
"commitSha": commit_receipt.commit_sha,
|
||||
"branch": commit_receipt.branch,
|
||||
"prReceiptId": pr_receipt.receipt_id,
|
||||
"pullRequestUrl": pr_receipt.pull_request_url,
|
||||
"pullRequestNumber": pr_receipt.pull_request_number,
|
||||
"pullRequestTitle": pr_receipt.pull_request_title
|
||||
},
|
||||
"bifrost": {
|
||||
"intentId": bifrost["intentId"],
|
||||
"publicationReceiptId": bifrost["publicationReceiptId"],
|
||||
"githubPublicationReceiptId": bifrost["githubPublicationReceiptId"],
|
||||
"ledgerEntryId": bifrost["ledgerEntryId"],
|
||||
"creditReceiptIds": bifrost["creditReceiptIds"],
|
||||
"pullRequestUrl": bifrost["pullRequestUrl"]
|
||||
},
|
||||
"verificationReceipts": args.verification_receipts,
|
||||
"reviewReceipts": args.review_receipts,
|
||||
"authority": {
|
||||
"publicationAuthorized": true,
|
||||
"upstreamMainSynced": false,
|
||||
"mergeAuthorized": false,
|
||||
"mergeGate": "maintainer_or_bifrost_merge_receipt",
|
||||
"privateStateExposed": false
|
||||
},
|
||||
"nextSafeMove": "Maintain the PR/publication receipt chain; do not claim upstream main is synced until a merge/sync receipt exists."
|
||||
});
|
||||
let receipt_path = artifact_dir.join(format!("work-publish-{item_slug}.json"));
|
||||
write_json(&receipt_path, &publish_receipt)?;
|
||||
Ok(json!({
|
||||
"schemaVersion": "epiphany.repo_work_publish.v0",
|
||||
"status": publish_receipt["status"],
|
||||
"workspace": publish_receipt["workspace"],
|
||||
"runtimeId": publish_receipt["runtimeId"],
|
||||
"runtimeStore": publish_receipt["runtimeStore"],
|
||||
"localVerseStore": publish_receipt["localVerseStore"],
|
||||
"receiptPath": receipt_path,
|
||||
"item": publish_receipt["item"],
|
||||
"handsReceipts": publish_receipt["handsReceipts"],
|
||||
"bifrost": publish_receipt["bifrost"],
|
||||
"authority": publish_receipt["authority"],
|
||||
"privateStateExposed": false,
|
||||
"nextSafeMove": publish_receipt["nextSafeMove"],
|
||||
}))
|
||||
}
|
||||
|
||||
fn run_sync(args: SyncArgs) -> Result<Value> {
|
||||
let workspace = args
|
||||
.workspace
|
||||
.canonicalize()
|
||||
.with_context(|| format!("failed to resolve {}", args.workspace.display()))?;
|
||||
ensure_git_repo(&workspace)?;
|
||||
let publish_receipt_path =
|
||||
resolve_publish_receipt(&workspace, args.item.as_deref(), args.publish_receipt)?;
|
||||
let publish_receipt = read_json(&publish_receipt_path)?;
|
||||
let artifact_dir = args
|
||||
.artifact_dir
|
||||
.unwrap_or_else(|| workspace.join(".epiphany").join("work"));
|
||||
fs::create_dir_all(&artifact_dir)
|
||||
.with_context(|| format!("failed to create {}", artifact_dir.display()))?;
|
||||
|
||||
let item = publish_receipt
|
||||
.get("item")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("work-item")
|
||||
.to_string();
|
||||
let item_slug = sanitize(&item);
|
||||
let published_commit_sha = publish_receipt
|
||||
.get("handsReceipts")
|
||||
.and_then(|hands| hands.get("commitSha"))
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| anyhow!("publish receipt has no handsReceipts.commitSha"))?
|
||||
.to_string();
|
||||
let commit_receipt_id = publish_receipt
|
||||
.get("handsReceipts")
|
||||
.and_then(|hands| hands.get("commitReceiptId"))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
let pr_receipt_id = publish_receipt
|
||||
.get("handsReceipts")
|
||||
.and_then(|hands| hands.get("prReceiptId"))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
let upstream_commit_sha =
|
||||
git_output(&workspace, &["rev-parse", "--verify", &args.upstream_ref])
|
||||
.with_context(|| format!("failed to resolve upstream ref {}", args.upstream_ref))?;
|
||||
let canonical_published_commit = git_output(
|
||||
&workspace,
|
||||
&["rev-parse", "--verify", &published_commit_sha],
|
||||
)
|
||||
.with_context(|| format!("failed to resolve published commit {published_commit_sha}"))?;
|
||||
let upstream_main_synced = git_status_success(
|
||||
&workspace,
|
||||
&[
|
||||
"merge-base",
|
||||
"--is-ancestor",
|
||||
&canonical_published_commit,
|
||||
&args.upstream_ref,
|
||||
],
|
||||
)?;
|
||||
let status = if upstream_main_synced {
|
||||
"upstream-main-synced"
|
||||
} else {
|
||||
"upstream-main-not-synced"
|
||||
};
|
||||
let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||
let sync_receipt = json!({
|
||||
"schemaVersion": "epiphany.repo_work_sync_receipt.v0",
|
||||
"createdAt": now,
|
||||
"workspace": workspace,
|
||||
"publishReceiptPath": publish_receipt_path,
|
||||
"item": item,
|
||||
"status": status,
|
||||
"upstreamRef": args.upstream_ref,
|
||||
"upstreamCommitSha": upstream_commit_sha,
|
||||
"publishedCommitSha": canonical_published_commit,
|
||||
"mergeReceipts": args.merge_receipts,
|
||||
"handsReceipts": {
|
||||
"commitReceiptId": commit_receipt_id,
|
||||
"prReceiptId": pr_receipt_id
|
||||
},
|
||||
"bifrost": publish_receipt["bifrost"],
|
||||
"authority": {
|
||||
"publicationAuthorized": true,
|
||||
"upstreamMainSynced": upstream_main_synced,
|
||||
"mergeAuthorized": upstream_main_synced,
|
||||
"mergeAuthorityReceipts": args.merge_receipts,
|
||||
"privateStateExposed": false
|
||||
},
|
||||
"nextSafeMove": if upstream_main_synced {
|
||||
"Update durable map/Mind receipts and proof bundle; upstream main now contains the published work."
|
||||
} else {
|
||||
"Wait for maintainer/Bifrost merge, then rerun epiphany-work sync against upstream main."
|
||||
}
|
||||
});
|
||||
let receipt_path = artifact_dir.join(format!("work-sync-{item_slug}.json"));
|
||||
write_json(&receipt_path, &sync_receipt)?;
|
||||
Ok(json!({
|
||||
"schemaVersion": "epiphany.repo_work_sync.v0",
|
||||
"status": sync_receipt["status"],
|
||||
"workspace": sync_receipt["workspace"],
|
||||
"receiptPath": receipt_path,
|
||||
"item": sync_receipt["item"],
|
||||
"upstreamRef": sync_receipt["upstreamRef"],
|
||||
"upstreamCommitSha": sync_receipt["upstreamCommitSha"],
|
||||
"publishedCommitSha": sync_receipt["publishedCommitSha"],
|
||||
"authority": sync_receipt["authority"],
|
||||
"privateStateExposed": false,
|
||||
"nextSafeMove": sync_receipt["nextSafeMove"],
|
||||
}))
|
||||
}
|
||||
|
||||
fn run_overview(args: OverviewArgs) -> Result<Value> {
|
||||
let workspace = args
|
||||
.workspace
|
||||
|
|
@ -14660,7 +14021,8 @@ fn run_tick(args: TickArgs) -> Result<Value> {
|
|||
reason =
|
||||
"publication receipt exists; scheduler stops before merge/sync authority".to_string();
|
||||
next_safe_move =
|
||||
"Wait for maintainer/Bifrost merge receipt, then run epiphany-work sync.".to_string();
|
||||
"Wait for provider-authored merge evidence, then inspect upstream Git ancestry."
|
||||
.to_string();
|
||||
} else if close_receipt_path.exists()
|
||||
&& read_json(&close_receipt_path)?
|
||||
.get("status")
|
||||
|
|
@ -14671,8 +14033,7 @@ fn run_tick(args: TickArgs) -> Result<Value> {
|
|||
status = "noop".to_string();
|
||||
reason = "Soul/Modeling/Mind closure is recorded; scheduler stops before Bifrost publication authority".to_string();
|
||||
next_safe_move =
|
||||
"Route Bifrost/GitHub publication through epiphany-work publish --closure-receipt."
|
||||
.to_string();
|
||||
"Submit a Bifrost publication intent through the owning integration.".to_string();
|
||||
} else if close_receipt_path.exists() {
|
||||
let partial_close = read_json(&close_receipt_path)?;
|
||||
let runtime_store = args
|
||||
|
|
@ -15092,25 +14453,6 @@ fn resolve_adopt_receipt(
|
|||
})
|
||||
}
|
||||
|
||||
fn resolve_publish_receipt(
|
||||
workspace: &Path,
|
||||
item: Option<&str>,
|
||||
explicit: Option<PathBuf>,
|
||||
) -> Result<PathBuf> {
|
||||
if let Some(path) = explicit {
|
||||
return Ok(path);
|
||||
}
|
||||
let work_dir = workspace.join(".epiphany").join("work");
|
||||
if let Some(item) = item {
|
||||
return Ok(work_dir.join(format!("work-publish-{}.json", sanitize(item))));
|
||||
}
|
||||
latest_receipt_in(&work_dir, "work-publish-").ok_or_else(|| {
|
||||
anyhow!(
|
||||
"no work publish receipt found; run epiphany-work publish first or pass --publish-receipt"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_execute_receipt(
|
||||
workspace: &Path,
|
||||
item: Option<&str>,
|
||||
|
|
@ -15228,7 +14570,7 @@ fn bifrost_publication_readiness_row(path: &Path, receipt: Option<&Value>) -> Re
|
|||
"bifrost-publication",
|
||||
"Bifrost/GitHub",
|
||||
"epiphany.repo_work_publish_receipt.v0 + gamecult.bifrost.body_change_publication_receipt.v0 + gamecult.bifrost.github_publication_receipt.v0",
|
||||
"Run epiphany-work publish so Bifrost and GitHub publication receipts exist before readiness review.",
|
||||
"Wait for Bifrost and the GitHub adapter to publish their receipts before readiness review.",
|
||||
));
|
||||
};
|
||||
|
||||
|
|
@ -15374,7 +14716,7 @@ fn upstream_main_sync_readiness_row(
|
|||
"upstream-main-sync",
|
||||
"Bifrost/GitHub",
|
||||
"epiphany.repo_work_sync_receipt.v0 + git merge-base ancestry proof",
|
||||
"Run epiphany-work sync after maintainer/Bifrost merge authority so upstream main containment is proved.",
|
||||
"Inspect provider-authored merge evidence and upstream Git ancestry.",
|
||||
));
|
||||
};
|
||||
|
||||
|
|
@ -16015,7 +15357,7 @@ fn repo_work_overview_gate(
|
|||
(
|
||||
"awaiting-upstream-sync",
|
||||
"merge-or-sync-receipt-missing",
|
||||
"After maintainer/Bifrost merge, run epiphany-work sync.",
|
||||
"After provider-authored merge evidence arrives, inspect upstream Git ancestry.",
|
||||
)
|
||||
} else if close
|
||||
.and_then(|receipt| receipt.get("status"))
|
||||
|
|
@ -16025,7 +15367,7 @@ fn repo_work_overview_gate(
|
|||
(
|
||||
"awaiting-publication",
|
||||
"bifrost-publication-missing",
|
||||
"Run epiphany-work publish --closure-receipt when publication is authorized.",
|
||||
"Submit a Bifrost publication intent through the owning integration.",
|
||||
)
|
||||
} else if close
|
||||
.and_then(|receipt| receipt.get("status"))
|
||||
|
|
@ -17442,7 +16784,7 @@ fn sanitize(value: &str) -> String {
|
|||
|
||||
fn print_usage() {
|
||||
eprintln!(
|
||||
"usage: epiphany-work <persona-intake|accept|derive-plan|plan|run|adopt|execute|close|publish|sync|overview|readiness|deployment-config-audit|deployment-execution-runbook|deployment-aftercare-audit|export-proof|tick|queue-run|serve> ...\n\
|
||||
"usage: epiphany-work <persona-intake|accept|derive-plan|plan|run|adopt|execute|close|overview|readiness|deployment-config-audit|deployment-execution-runbook|deployment-aftercare-audit|export-proof|tick|queue-run|serve> ...\n\
|
||||
persona-intake --workspace <repo> --item <id> --message <text> [--topic <topic>] [--store <local-verse.ccmp>] [--runtime-id <id>]\n\
|
||||
accept --workspace <repo> --from <persona|bifrost|persona-or-bifrost> --item <id> [--summary <text>] [--topic <topic>] [--store <local-verse.ccmp>] [--runtime-id <id>] [--online-receipt <path>] [--public-discussion-ref <ref>] [--candidate-action-ref <ref>]\n\
|
||||
derive-plan --workspace <repo> [--item <id>] [--accept-receipt <path>] [--action-family append-worklog|planning-note|checklist-note|section-note|repo-status-section|task-card|repo-manifest|repo-tool-capabilities|repo-tool-request|repo-eve-surface|repo-collaboration-policy|repo-collaboration-topic|repo-consensus-brief|repo-planning-brief|repo-interpreter-brief|repo-objective-draft|repo-adoption-request|repo-scheduling-request|repo-work-order|repo-verification-request|repo-publication-request|repo-sync-request|repo-maintainer-review-request|repo-pr-request|repo-credit-request|repo-artifact-acceptance-request|repo-metrics-request|repo-readiness-review-request|repo-doctrine-update-request|repo-secret-policy-request|repo-dependency-policy-request|repo-deployment-config|repo-deployment-request] [--target-path <path>] [--model-ref <ref>] [--model-authored] [--action-summary <text>] [--verification-ask <text>] [--stop-condition <text>] [--escalation-reason <text>] [--assumption <text>] [--constraint <text>] [--non-goal <text>] [--open-question <text>] [--decision-point <text>] [--evidence-need <text>]\n\
|
||||
|
|
@ -17451,8 +16793,6 @@ fn print_usage() {
|
|||
adopt --workspace <repo> [--item <id>] [--run-receipt <path>] [--from-plan <path>] [--plan-summary <text>] [--adoption-evidence-ref <ref>] [--mind-adoption-rationale <text>]\n\
|
||||
execute --workspace <repo> [--item <id>] [--from-plan <path>] [--command <command>] [--changed-path <path>] [--commit-message <text>]\n\
|
||||
close --workspace <repo> [--item <id>] [--execute-receipt <path>] [--verification-command <command>] --closure-model-ref <ref> --model-authored --closure-model-verdict passed|failed|needs-work|blocked --closure-model-finding <text> [--require-source-grounding]\n\
|
||||
publish --workspace <repo> [--item <id>] --change-summary <text> --justification <text> --verification-receipt <ref> --review-receipt <ref> --ledger-entry-id <id> --pull-request-url <url> --pull-request-title <text>\n\
|
||||
sync --workspace <repo> [--item <id>] [--publish-receipt <path>] [--upstream-ref origin/main] --merge-receipt <ref>\n\
|
||||
overview --workspace <repo> [--item <id>] [--accept-receipt <path>] [--no-write]\n\
|
||||
readiness --workspace <repo> [--item <id>] [--accept-receipt <path>] [--public-proof <path>] [--idunn-lifecycle-receipt <path>] [--deployment-aftercare-audit-receipt <path>|--deployment-aftercare-audit-receipt-ref <ref>] [--tool-directory-receipt <path>] [--no-write]\n\
|
||||
deployment-config-audit --workspace <repo> [--artifact-dir <path>] [--no-write]\n\
|
||||
|
|
|
|||
|
|
@ -328,3 +328,15 @@ and merge-receipt string. It then asserted Bifrost publication, Soul approval,
|
|||
and upstream publication as one integrated proof. Git can prove ancestry; it
|
||||
cannot impersonate the absent publisher, reviewer, or verification organ. The
|
||||
binary and its live-proof claims are deleted.
|
||||
|
||||
## Caller-authored publication and merge authority
|
||||
|
||||
The deleted smoke exposed a surviving production authority. `epiphany-work
|
||||
publish` accepted arbitrary verification/review/PR/ledger strings, rewrote a
|
||||
Hands review to add PR permission, invoked the now request-only Bifrost command
|
||||
with forbidden provider-result arguments, and wrote
|
||||
`publicationAuthorized=true`. `epiphany-work sync` accepted arbitrary merge
|
||||
strings and promoted Git ancestry into `mergeAuthorized=true`. Both commands
|
||||
and the whole-path stop-classification smoke that depended on them are deleted.
|
||||
The coherent split is Bifrost intent submission, provider-authored publication
|
||||
and review receipts, and read-only Git ancestry inspection.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ The confirmed conceptual substitutions have been cut:
|
|||
- the fresh-repo MVP smoke is deleted. Its disposable Git operations were real,
|
||||
but its PR, maintainer-review, merge, Bifrost, and Soul evidence was supplied
|
||||
by the same local caller rather than those owning bodies.
|
||||
- `epiphany-work publish` and `sync` are deleted. The first promoted arbitrary
|
||||
receipt strings into publication authority; the second promoted arbitrary
|
||||
merge strings plus Git ancestry into merge authority. Bifrost intent,
|
||||
provider receipts, and read-only ancestry are separate paths.
|
||||
|
||||
The presentation boundary is now plain: `swarm overview` is a generic compact read-only projection. Gjallar is a downstream TUI application on Nightwing and is not an Epiphany organ, provider, owner, runtime, or architectural dependency. Eve/CultUI graphs may be lowered or composited downstream without Epiphany caring which presentation client does it.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -660,4 +660,4 @@ current_status:
|
|||
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
|
||||
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
|
||||
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. swarm overview readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
|
||||
next_action: Continue the conceptual-substitution modeling pass beyond receipt writers. The fresh-repo MVP smoke is deleted because it wrapped real disposable Git operations in caller-authored PR, review, merge, Bifrost, and Soul authority. Inventory remaining production projections, caches, adapters, schedulers, coordinators, and compatibility surfaces that assert facts owned elsewhere; map owner, inputs, outputs, forbidden writers, and negative proof before cutting. Audit remaining non-receipt smoke binaries for path escape or destructive scope.
|
||||
next_action: Continue the conceptual-substitution modeling pass beyond receipt writers. Fresh-repo MVP proof plus `epiphany-work publish`/`sync` are deleted: caller strings can no longer become PR, review, Bifrost, Soul, or merge authority, and Git ancestry remains read-only substrate evidence. Audit legacy publish/sync artifact readers so old local aggregates cannot remain readiness owners. Then inventory remaining projections, caches, adapters, schedulers, coordinators, and compatibility surfaces that assert facts owned elsewhere.
|
||||
|
|
|
|||
Reference in a new issue