Prove repo swarm stop classification cases

This commit is contained in:
Meta Vens 2026-06-20 19:19:06 +01:00
commit f780984b9a
6 changed files with 475 additions and 36 deletions

View file

@ -0,0 +1,393 @@
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 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,
)?;
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"], "Gjallar")?;
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 = "dry-run-preview";
cargo_json(
&manifest,
"epiphany-work",
&[
"accept",
"--workspace",
path_str(&repo)?,
"--from",
"persona",
"--item",
item,
"--summary",
"Prove a dry-run swarm pulse can classify a ready-to-run queue row without mutation.",
"--candidate-action-ref",
"candidate-action://swarm-stop-classification/dry-run-preview",
"--public-discussion-ref",
"eve://epiphany/persona/swarm-stop-classification",
],
&root,
)?;
cargo_json(
&manifest,
"epiphany-work",
&[
"derive-plan",
"--workspace",
path_str(&repo)?,
"--item",
item,
"--action-family",
"planning-note",
"--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,
)?;
let summary = json!({
"schemaVersion": "epiphany.repo_swarm_stop_classification_smoke.v0",
"status": "ok",
"smokeDir": smoke_dir,
"repo": repo,
"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"],
"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()))
}

View file

@ -543,34 +543,54 @@ fn classify_run_stop(
.and_then(|iteration| iteration.get("selectedRows"))
.and_then(Value::as_array)
.and_then(|rows| rows.first());
let selected_gate = selected_row
.and_then(|row| row.get("gate"))
.and_then(Value::as_str)
.unwrap_or("none");
let selected_blocker = selected_row
.and_then(|row| row.get("blocker"))
.and_then(Value::as_str)
.unwrap_or("none");
let selected_next_safe_move = selected_row
.and_then(|row| row.get("nextSafeMove"))
.and_then(Value::as_str)
.unwrap_or(next_safe_move);
let selected_item = selected_row
.and_then(|row| row.get("item"))
.and_then(Value::as_str)
.unwrap_or("none");
let selected_branch = selected_row
.and_then(|row| row.get("branch"))
.and_then(Value::as_str)
.unwrap_or("none");
let selected_output = final_iteration
.and_then(|iteration| iteration.get("outputs"))
.and_then(Value::as_array)
.and_then(|outputs| outputs.first());
let refreshed_overview = selected_output.and_then(|output| output.get("refreshedOverview"));
let selected_gate = selected_output
.and_then(|output| non_empty_string(output.get("gateBefore")))
.or_else(|| {
refreshed_overview.and_then(|overview| non_empty_string(overview.get("currentGate")))
})
.or_else(|| selected_row.and_then(|row| non_empty_string(row.get("gate"))))
.or_else(|| compact_row_field(selected_row, "gate"))
.unwrap_or_else(|| "none".to_string());
let selected_blocker = selected_output
.and_then(|output| non_empty_string(output.get("blockerBefore")))
.or_else(|| {
refreshed_overview.and_then(|overview| non_empty_string(overview.get("blocker")))
})
.or_else(|| selected_row.and_then(|row| non_empty_string(row.get("blocker"))))
.or_else(|| compact_row_field(selected_row, "blocker"))
.unwrap_or_else(|| "none".to_string());
let selected_next_safe_move = selected_output
.and_then(|output| output.get("tick"))
.and_then(|tick| non_empty_string(tick.get("nextSafeMove")))
.or_else(|| {
refreshed_overview.and_then(|overview| non_empty_string(overview.get("nextSafeMove")))
})
.or_else(|| selected_row.and_then(|row| non_empty_string(row.get("nextSafeMove"))))
.or_else(|| compact_row_field(selected_row, "next"))
.unwrap_or_else(|| next_safe_move.to_string());
let selected_item = selected_output
.and_then(|output| non_empty_string(output.get("item")))
.or_else(|| selected_row.and_then(|row| non_empty_string(row.get("item"))))
.or_else(|| compact_row_field(selected_row, "item"))
.unwrap_or_else(|| "none".to_string());
let selected_branch = refreshed_overview
.and_then(|overview| non_empty_string(overview.get("branch")))
.or_else(|| selected_row.and_then(|row| non_empty_string(row.get("branch"))))
.or_else(|| compact_row_field(selected_row, "branch"))
.unwrap_or_else(|| "none".to_string());
let (category, owner, gate, blocker, recommended_command, mutates_state, requires_elevation) =
match stop_reason {
"dry-run-preview-complete" => (
"dry-run-preview",
"Self",
selected_gate,
selected_blocker,
selected_gate.as_str(),
selected_blocker.as_str(),
"epiphany-swarm run --workspace <repo> --until blocked-or-published",
false,
false,
@ -594,14 +614,14 @@ fn classify_run_stop(
false,
),
"blocked-or-noop" if actionable_count == 0 => {
let owner = match selected_gate {
let owner = match selected_gate.as_str() {
"awaiting-publication" => "Bifrost",
"awaiting-sync" => "Bifrost/GitHub",
"awaiting-closure" => "Soul",
"ready-to-run" | "ready-to-adopt" | "ready-to-execute" => "Self",
_ => "Self",
};
let recommended = match selected_gate {
let recommended = match selected_gate.as_str() {
"awaiting-publication" => {
"epiphany-work publish --workspace <repo> --closure-receipt <receipt>"
}
@ -616,8 +636,8 @@ fn classify_run_stop(
(
"authority-gated",
owner,
selected_gate,
selected_blocker,
selected_gate.as_str(),
selected_blocker.as_str(),
recommended,
false,
false,
@ -626,8 +646,8 @@ fn classify_run_stop(
_ => (
"unknown",
"Self",
selected_gate,
selected_blocker,
selected_gate.as_str(),
selected_blocker.as_str(),
"epiphany-work overview --workspace <repo>",
false,
false,
@ -663,6 +683,27 @@ fn classify_run_stop(
})
}
fn non_empty_string(value: Option<&Value>) -> Option<String> {
value
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToString::to_string)
}
fn compact_row_field(row: Option<&Value>, field: &str) -> Option<String> {
let prefix = format!("{field}=");
row.and_then(Value::as_str)
.and_then(|row| {
row.split('|')
.map(str::trim)
.find_map(|part| part.strip_prefix(&prefix))
})
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToString::to_string)
}
fn queue_run_args(
args: &RunArgs,
workspace: &Path,

View file

@ -1464,8 +1464,9 @@ Build the remaining MVP in this order:
`epiphany.repo_swarm_run_stop_classification.v0` into the run receipt so
agents can see stop category, owner, authority gate, blocker, recommended
command, mutability/elevation flags, queue status, and private-state seal
without parsing prose. Remaining work is broader stop-case proof coverage and
safe-family depth.
without parsing prose. Fresh proof now covers empty-queue and dry-run
planning-preview stops; remaining stop-depth work belongs to gated
publication/sync/service cases and safe-family depth.
5. **Execute-to-close handoff.** Let the scheduler route from branch-local
Hands execution into Soul/Modeling/Mind closure when the required execute
receipts exist. The closure may be deterministic for mechanical work and
@ -1816,9 +1817,13 @@ Required organs before MVP:
`.epiphany-smoke\fresh-repo-mvp-20260620-180024` shows the bounded run stops
with category `iteration-limit`, owner `Self`, gate
`self.scheduler-iteration-limit`, non-mutating/non-elevated flags, and
`privateStateExposed=false`. Remaining work is broader stop-case proof
coverage and richer closure depth rather than basic safe-family planning
cargo.
`privateStateExposed=false`. Focused stop proof
`.epiphany-smoke\swarm-stop-classification-20260620-181606` now also proves
`queue-empty` / Gjallar / `repo.work.overview` for an empty repo queue and
`dry-run-preview` / Self / `ready-to-run` for a planning item preview, both
non-mutating, non-elevated, and private-state sealed. Remaining work is
gated publication/sync/service stop depth and richer closure depth rather
than basic safe-family planning cargo.
- Proof bundle depth: maintainers and future agents can inspect local
operator-safe receipt chains, artifact schema/status rows, SHA-256 receipt
hashes, compact TUI rows, commit refs, verification verdicts, map admission,

View file

@ -109,7 +109,7 @@ remembers doctrine; the branch remembers the blade.
- 2026-06-20: Typed Imagination action-item receipts are live in `epiphany-work derive-plan`. The command now writes `work-action-items-<item>.json` with schema `epiphany.repo_work_imagination_action_items_receipt.v0` before writing `work-plan-<item>.json`; the receipt carries model provenance, safe action family, requested paths, verification asks, stop conditions, escalation reasons, rollback hints, public/candidate refs, `operatorAuthoredShellDetails=false`, and no Hands/durable-state/publication/service/cross-repo authority. The plan derivation cites the action-item receipt, while command text remains deterministic safe-family lowering. Disposable proof `.epiphany-smoke\action-items-20260620-023240` wrote action receipt `repo-work-action-items-action-items-request` with `modelAuthored=true`, `safeActionFamily=repo.markdown_planning_note`, requested path `notes/epiphany-work/action-items-request.md`, plan citation present, and `privateStateExposed=false`. The next cut is deeper model-authored closure review, further non-planning action classes, or Idunn-owned queue-run service execution; not another basic action-item schema.
- 2026-06-20: The first richer model-authored safe family is live. `epiphany-work derive-plan --action-family checklist-note` lowers accepted Persona/Bifrost pressure into a contained markdown checklist under `notes/epiphany-work/<item>-checklist.md` with safe family `repo.checklist_note`, model provenance, candidate/public refs, branch-local checklist items, verification asks, rollback hints, and authority seals forbidding publication, merge, service lifecycle, cross-repo mutation, durable-state admission, and private-state exposure. Disposable proof `.epiphany-smoke\checklist-note-20260620-031347` ran accept -> derive-plan with `--model-ref smoke-imagination-v0 --model-authored` -> run -> adopt -> execute on an `epiphany/*` branch; the derivation and action-item receipt both reported `modelAuthored=true`, Hands created a branch-local commit, overview reported `awaiting-closure`, and `privateStateExposed=false`.
- 2026-06-20: `epiphany-swarm run` is live as the bounded repo-swarm operator mouth over Self's queue/tick physiology. It requires the online receipt/local Verse queue, delegates queue selection to `epiphany-work queue-run`, delegates item actuation to `epiphany-work tick`, writes `epiphany.repo_swarm_run_receipt.v0` at `.epiphany\swarm-run\repo-swarm-run-receipt.json`, and keeps publication, merge, service lifecycle, elevation, cross-repo mutation, and private-state exposure sealed. Wrapper mode `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` exposes the surface with brake preflight and compact pulse summaries. Disposable proof `.epiphany-smoke\swarm-run-20260620-024252` first returned dry-run preview `would-advance/run-from-plan`; wrapper artifact `local-20260620-024413-360-85548195` printed the same queue/tick delegation; a non-dry single pulse advanced `swarm-run-request` from `ready-to-run` to refreshed overview `ready-to-adopt` with `privateStateExposed=false`. The next cut is deeper model-authored closure review, further non-planning action classes, or Idunn-owned queue-run service execution; not another queue mouth.
- 2026-06-20: `epiphany-swarm run` stop classification is now typed. The run receipt/result embeds `epiphany.repo_swarm_run_stop_classification.v0` with status, stop reason, category, owner, authority gate, blocker, selected queue context, recommended command, mutability/elevation flags, and private-state seal. Fresh-repo proof `.epiphany-smoke\fresh-repo-mvp-20260620-180024` ran init -> online -> Persona intake -> model-authored repo-status plan -> swarm run -> close -> publish -> upstream-main sync -> export proof; the run stopped as `paused-at-iteration-limit` with stop category `iteration-limit`, owner `Self`, authority gate `self.scheduler-iteration-limit`, no state mutation/elevation in the stop packet, and `privateStateExposed=false`. Remaining repo-swarm work is broader stop-case proof coverage, deeper interpreter/closure semantics, more useful repo-owned configuration/action families, and Idunn-owned queue-run lifecycle under explicit operator authority.
- 2026-06-20: `epiphany-swarm run` stop classification is now typed. The run receipt/result embeds `epiphany.repo_swarm_run_stop_classification.v0` with status, stop reason, category, owner, authority gate, blocker, selected queue context, recommended command, mutability/elevation flags, and private-state seal. Fresh-repo proof `.epiphany-smoke\fresh-repo-mvp-20260620-180024` ran init -> online -> Persona intake -> model-authored repo-status plan -> swarm run -> close -> publish -> upstream-main sync -> export proof; the run stopped as `paused-at-iteration-limit` with stop category `iteration-limit`, owner `Self`, authority gate `self.scheduler-iteration-limit`, no state mutation/elevation in the stop packet, and `privateStateExposed=false`. Focused proof `.epiphany-smoke\swarm-stop-classification-20260620-181606` now also proves `queue-empty` / Gjallar / `repo.work.overview` for an empty repo queue and `dry-run-preview` / Self / `ready-to-run` for a planning item preview, both non-mutating, non-elevated, and `privateStateExposed=false`. Remaining repo-swarm work is gated publication/sync/service stop depth, deeper interpreter/closure semantics, more useful repo-owned configuration/action families, and Idunn-owned queue-run lifecycle under explicit operator authority.
- 2026-06-20: Execute-to-close scheduler handoff is live. `epiphany-work tick` now treats `awaiting-closure` as tick-actionable, delegates executed branch-local work to the existing `epiphany-work close` gate, writes Soul verdict plus Modeling summary plus Mind state-commit receipts, then stops at `awaiting-publication` with Bifrost as blocker. Disposable proof `.epiphany-smoke\tick-close-20260620-025526` ran `epiphany-swarm run --max-iterations 4` through `run-from-plan,adopt-from-plan,execute-from-plan,close-from-execute`; the final overview reported `awaiting-publication` / `bifrost-publication-missing`, and the close receipt was `closed`, Soul `passed`, Mind commit `repo-work-close-tick-close-request-mind-commit`, `durableStateAdmitted=true`, and `privateStateExposed=false` with publication/merge/service/cross-repo authority sealed. The next cut is deeper model-authored closure review, further non-planning action classes, or Idunn-owned queue-run service execution; not another scheduler handoff.
- 2026-06-20: Local proof-bundle packaging is live. `epiphany-work overview` now embeds `epiphany.repo_work_proof_bundle.v0` with generated time, gate/blocker/next-safe-move, compact TUI rows, expected artifact paths, present/missing artifact status, document schema/status, SHA-256 receipt hashes, and `privateStateExposed=false`. Disposable proof `.epiphany-smoke\tick-close-20260620-025526\08-overview-proof-bundle.json` reported six present artifacts from accept through close with hashes, missing publish/sync rows, `awaiting-publication` / `bifrost-publication-missing`, and no private-state exposure. The next cut is deeper model-authored closure review, further non-planning action classes, or Idunn-owned queue-run service execution; not another local proof-bundle wrapper.
- 2026-06-20: Published/credited proof-bundle rows are live. `epiphany-work overview` now adds `publicationRows` to `epiphany.repo_work_proof_bundle.v0`, lifting Bifrost intent/publication/GitHub/ledger/credit/PR fields, Hands commit/PR fields, and upstream-main ancestry fields from publish/sync receipts without granting publication, merge, scheduler, service lifecycle, cross-repo, or private-state authority. Disposable proof `.epiphany-smoke\checklist-note-20260620-031347\12-overview-published-proof.json` ran close -> publish -> sync -> overview after the checklist safe-family proof; overview reported `complete-or-awaiting-new-work`, `upstreamMainSynced=true` from `authority.upstreamMainSynced`, three publication rows, and `privateStateExposed=false`. The next cut is deeper model-authored closure review, further non-planning action classes, or Idunn-owned queue-run service execution; not basic Bifrost/GitHub/sync row visibility.

Binary file not shown.

View file

@ -88,7 +88,7 @@ active_subgoals:
- Keep the landed Phase 6 coordinator, limited safe-boundary CRRC automation, and pre-compaction checkpoint intervention ready for the next bridge-equipped dogfood run.
- Preserve the Ghostlight-derived heartbeat initiative seam: every lane, including coordinator/Self and Persona, has a organ-state record and competes for heartbeat slots through readiness/reaction timing; initiative heat can raise or cool global, group, arena, role, and agent tempo through typed heartbeat policy; active thinking freezes initiative until completion, so heat never queues a lane already mid-turn; idle winners ruminate instead of inventing project work, and sleep windows distill the accumulated thought pressure into reviewed self-memory or doctrine.
- Use `notes/epiphany-swarm-readiness-plan.md` as the live-fire direction gate. The local run path is a compass, not launch clearance: before unattended swarm operation, Epiphany needs typed pause/brake enforcement, one initiative scheduler, completion-gated cooldown, stale active-turn recovery receipts, parent-side Persona speech eligibility, memory lifecycle operations, and CultNet-first operator snapshots/intents/receipts. The local Verse brake document exists, `epiphany-verse-query swarm-brake` can engage/release it, `tools/epiphany_local_run.ps1` refuses live `plan`/`run`/`mvp` modes when the brake is engaged while leaving `status` readable, `epiphany-mvp-coordinator` checks the local Verse brake before app-server startup, and `epiphany-heartbeat-store` guards tick/pump/heat/queue-mention/routine when given `--local-verse-store`. Daemon liveness is now a typed local Verse authority: `epiphany-verse-query daemon-status` writes one daemon status without repainting the table, local Verse seeding preserves existing daemon statuses and swarm brakes, `invoke-tool` refuses when the host daemon is not ready or lacks `submitTypedToolIntent`, and both daemon tool invocation and lifecycle poke fail closed under an engaged matching brake. `epiphany-daemon-supervisor policy` writes durable per-daemon restart policy with command/args/cwd/cooldown/backoff/attempt state plus reconcile cadence and stale-heartbeat threshold; `epiphany-daemon-supervisor reconcile` loads that policy when no override command is supplied, refuses under a matching lifecycle brake, refuses during cooldown/backoff, writes daemon poke intent/receipt, updates daemon status, and updates policy attempt state; `epiphany-daemon-supervisor tick` is an explicit scheduler pulse that scans durable policies, records ready/noop pulses, skips before the configured reconcile interval, marks stale heartbeat status degraded, routes restarts through the same brake/cooldown/poke receipt path, and writes a latest `epiphany.cultmesh.daemon_scheduler_receipt.v0`; `epiphany-daemon-supervisor serve` runs that same pulse on a configurable cadence with bounded `--max-iterations` proof mode or unbounded service mode. `epiphany-daemon-supervisor service-plan`/`install-service` now writes a planned `epiphany.cultmesh.daemon_service_lifecycle_receipt.v0` for the `serve` command, while `service-launch`/`start-service` spawns that `serve` loop through the current executable or `--service-command`, can `--wait-child` for bounded proof, and records command/args/cwd, scheduler id, daemon selector, pid/exit/status, artifact ref, and private-state guard. `service-runbook`, `windows-service-install`/`service-install-plan`, `windows-service-status`, `windows-service-reconcile`, `windows-service-execution-readiness`, and `windows-service-start`/`stop` now record service lifecycle receipts through the same local Verse; the read-only status path has been proved against the real Windows `EventLog` service with receipt `daemon-service-lifecycle-receipt-windows-eventlog-proof-windows-service-status`, status `running`, exit code `0`, and `privateStateExposed=false`; the read-only service-policy reconciliation path has been proved for missing service, real-service drift against `EventLog`, lifecycle-brake refusal, and wrapper/local-Verse receipt `daemon-service-lifecycle-receipt-windows-eventlog-reconcile-proof-windows-service-reconcile`; execution readiness has been proved as a non-mutating `not-elevated` receipt through direct and wrapper paths; start/stop now produce non-mutating `start-planned`/`stop-planned` receipts unless `--execute-control`/`--execute-service-control` is passed, and wrapper `service-start-plan`/`service-stop-plan` proves those receipts against `EventLog`; explicit install/control execution now refuses as `execution-refused-not-elevated` with `executed=false` when the shell lacks Windows administrator authority, and wrapper `service-install-execute`, `service-start-execute`, and `service-stop-execute` prove those refusal receipts without mutation. Service lifecycle actions check the local Verse swarm brake before process launch, artifact writes, service-manager queries, service-policy reconciliation, execution-readiness checks, control planning, elevation-gated execution, or start/stop execution. The local wrapper builds/checks this binary.
- Use `notes/epiphany-repo-swarm-mvp-contract.md` as the repo-swarm autonomy authority map. Autonomous branch-local work inside an Epiphany-owned repo Body is in scope and desired; the safety boundary is typed authority, branch containment, interruption, receipts, private-state sealing, and Bifrost-gated publication/merge/escalation, not asking a human before every local edit. Current safe-family cargo includes repo manifest, tool capability manifest, daemon-hosted tool request cargo, Eve/CultUI surface contract, repo collaboration policy, collaboration topics, consensus/objective/adoption/scheduling/work-order/verification/publication/sync/review/PR/credit/artifact/metrics requests, action-item receipts, Mind adoption interpretation/decision receipts, overview/public-proof/Gjallar/Eve readbacks, typed swarm-run stop classification, Idunn non-mutating queue-run lifecycle receipts, and fresh-repo upstream-main proof. Latest swarm-run stop proof `.epiphany-smoke\fresh-repo-mvp-20260620-180024` shows `epiphany.repo_swarm_run_stop_classification.v0` embedded in the run receipt with category `iteration-limit`, owner `Self`, authority gate `self.scheduler-iteration-limit`, non-mutating/non-elevated stop flags, upstream-main sync proof, public-proof export, and `privateStateExposed=false`.
- Use `notes/epiphany-repo-swarm-mvp-contract.md` as the repo-swarm autonomy authority map. Autonomous branch-local work inside an Epiphany-owned repo Body is in scope and desired; the safety boundary is typed authority, branch containment, interruption, receipts, private-state sealing, and Bifrost-gated publication/merge/escalation, not asking a human before every local edit. Current safe-family cargo includes repo manifest, tool capability manifest, daemon-hosted tool request cargo, Eve/CultUI surface contract, repo collaboration policy, collaboration topics, consensus/objective/adoption/scheduling/work-order/verification/publication/sync/review/PR/credit/artifact/metrics requests, action-item receipts, Mind adoption interpretation/decision receipts, overview/public-proof/Gjallar/Eve readbacks, typed swarm-run stop classification, Idunn non-mutating queue-run lifecycle receipts, and fresh-repo upstream-main proof. Swarm-run stop proofs now include `.epiphany-smoke\fresh-repo-mvp-20260620-180024` for `iteration-limit` / Self / `self.scheduler-iteration-limit` with upstream-main sync and public-proof export, plus `.epiphany-smoke\swarm-stop-classification-20260620-181606` for `queue-empty` / Gjallar / `repo.work.overview` and `dry-run-preview` / Self / `ready-to-run`; all stop packets are non-mutating, non-elevated, and `privateStateExposed=false`.
- Gjallar/swarm-overview now projects typed repo-work overview history into the compact global sight report, Persona's public Eve surface lowers the same queue for peers, and Self has a first runnable queue surface over it. `epiphany-verse-query gjallar` reads all `epiphany.cultmesh.repo_work_overview.v0` event documents from local Verse, excludes the `latest` mirror, preserves latest scalar fields, emits multi-item `repoWorkOverviewRows`/TUI rows, and publishes bounded priority 35-39 non-mutating `repo-work-overview` action rows with `owner=Gjallar`, `hostedBody=repo-work`, `authorityGate=repo.work.overview`, `mutatesState=false`, `requiresElevatedAuthority=false`, proof-bundle refs, current gate/blocker/next-safe-move, and `privateStateExposed=false`. `epiphany-verse-query eve-surfaces`, `connect-eve`, and globally invokable Persona `eve-connect` tool readbacks expose compact `REPO-WORK-PEER` rows with item, gate, blocker, next safe move, branch, publication/sync state, and private seal for the public Persona surface only. `epiphany-work queue-run` selects current-repo rows at `ready-to-run`, `ready-to-adopt`, or `ready-to-execute`, delegates action to `tick`, refreshes overview after real advancement, and writes `epiphany.repo_work_queue_run_receipt.v0`; wrapper mode `tools/epiphany_local_run.ps1 -Mode repo-work-queue-run` exposes the same surface. Disposable proofs `.epiphany-smoke\gjallar-repo-work-queue-20260620-012027`, `.epiphany-smoke\eve-repo-work-queue-20260620-013322`, `.epiphany-smoke\queue-run-20260620-014314`, and wrapper artifact `local-20260620-014627-174-aada0241` showed two-item queues through sight, public Eve, direct queue-run, and wrapper dry-run without publication, merge, service lifecycle mutation, elevation, cross-repo mutation, or private-state exposure.
- Agent memory lifecycle now has a first typed v0 operation surface in `epiphany-core::agent_memory`: `AgentMemoryLifecycleOperation` can revise, retire, crystallize, prune, and merge bounded memory records after role/agent review, and `epiphany-agent-memory-store review-lifecycle|apply-lifecycle` exposes the path at the operator edge. This closes the first explicit lifecycle-operation slice from the swarm-readiness gate; it does not yet make sleep silently mutate identity or project truth.
- Persona public speech now has a parent-side eligibility receipt at the Discord/Aquarium mouth edge. `epiphany.persona_speech_audit.v0` records action kind, content fingerprint, opening/topic keys, recent-window counts, repeated opening/topic counts, channel saturation, and allow/block reasons. `epiphany-persona-discord post` blocks repeated public openings before token/webhook work; draft and bubble still emit audit receipts for operator inspection. The compatibility JSON artifact remains for display, but the durable witness is now `epiphany.cultmesh.persona_speech_audit.v0` in CultMesh with latest-key projection through local Verse context. Local MVP Persona bubbles now write that durable witness to `.epiphany-run/cultmesh/local-verse.ccmp`, not a separate mouth-side store. This is posting-edge policy, not a Persona prompt wish.
@ -634,4 +634,4 @@ current_status:
- `tools/epiphany_local_run.ps1 -Mode swarm-online-runbook` now emits an explicit Idunn preflight summary before the operator elevation boundary. Its JSON and one-line summary report `preflightStatus`, child artifact present/hash-verified/mismatch/missing counts, aggregate service execution failed/missing check counts, child preflight rows with expected/actual SHA-256s, and private-state exposure. Live proof `local-20260619-025627-410-9724e2f6` generated the non-mutating parent artifact with `preflightStatus=ready-for-elevated-operator`, two child runbooks present, two hashes verified, zero mismatches, zero missing artifacts, eleven failed sealed service checks, zero missing checks, explicit aftercare audits, and `privateStateExposed=false`. The remaining scar is still explicit elevated operator execution followed by complete Idunn audits, not missing parent/child artifact readiness.
- 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.
next_action: Continue repo-swarm MVP work from the documented Epiphany Online cut order in `notes/epiphany-repo-swarm-mvp-contract.md`. Typed Gjallar repo-work history/queue sight, peer-readable Persona Eve lowering, Self-owned `epiphany-work queue-run`, bounded `epiphany-swarm run`, execute-to-close scheduler handoff through `close-from-execute`, deterministic `derive-plan` families `append-worklog`, `planning-note`, `checklist-note`, `section-note` / `repo.markdown_managed_section`, `task-card` / `repo.task_card`, `repo-status-section` / `repo.status_section`, `repo-manifest` / `repo.body_manifest`, `repo-tool-capabilities` / `repo.tool_capabilities`, `repo-tool-request` / `repo.tool_request`, `repo-eve-surface` / `repo.eve_surface`, `repo-collaboration-policy` / `repo.collaboration_policy`, `repo-collaboration-topic` / `repo.collaboration_topic`, `repo-consensus-brief` / `repo.consensus_brief`, `repo-objective-draft` / `repo.objective_draft`, `repo-adoption-request` / `repo.adoption_request`, `repo-scheduling-request` / `repo.scheduling_request`, `repo-work-order` / `repo.work_order`, `repo-verification-request` / `repo.verification_request`, `repo-publication-request` / `repo.publication_request`, `repo-sync-request` / `repo.sync_request`, `repo-maintainer-review-request` / `repo.maintainer_review_request`, `repo-pr-request` / `repo.pr_request`, `repo-credit-request` / `repo.credit_request`, `repo-artifact-acceptance-request` / `repo.artifact_acceptance_request`, and `repo-metrics-request` / `repo.metrics_request`, typed Imagination action-item receipts over allowlisted safe families, `epiphany.repo_work_mind_interpretation.v0` plus `epiphany.repo_work_mind_adoption_decision.v0` Mind interpreter receipts before branch-local Hands authority, hash-backed local proof bundles with Bifrost/GitHub/credit/PR/upstream-main publication rows, redacted `epiphany-work export-proof` public bundles plus local Verse `epiphany.cultmesh.repo_work_public_proof.v0` Gjallar readback, Bifrost-owned `gamecult.bifrost.public_proof_publication_receipt.v0` closure plus wrapper `bifrost-public-proof` for redacted public proofs, Idunn-hosted non-mutating queue-run lifecycle plan/runbook receipts, and the speech-audited `epiphany-work persona-intake` mouth plus wrapper `repo-persona-intake` are live with `privateStateExposed=false`; proof `.epiphany-smoke\fresh-repo-mvp-20260620-180024` shows fresh-repo swarm run stop classification, closure, publication receipts, upstream-main sync, public-proof export, and private-state sealing. The contract note now explicitly answers that Epiphany is headed toward a functional repo swarm but is not fully online until the typed artery runs as a living repo organism. The next cut is deeper interpreter semantics beyond the first Mind decision packet, more useful repo-owned config/action families, or Idunn-owned queue-run service lifecycle under explicit operator authority. Preserve Idunn service lifecycle ownership and existing CultMesh/Odin/Eve tool directories; service install/start execution still requires explicit elevated operator authority.
next_action: Continue repo-swarm MVP work from the documented Epiphany Online cut order in `notes/epiphany-repo-swarm-mvp-contract.md`. Typed Gjallar repo-work history/queue sight, peer-readable Persona Eve lowering, Self-owned `epiphany-work queue-run`, bounded `epiphany-swarm run`, execute-to-close scheduler handoff through `close-from-execute`, deterministic `derive-plan` families `append-worklog`, `planning-note`, `checklist-note`, `section-note` / `repo.markdown_managed_section`, `task-card` / `repo.task_card`, `repo-status-section` / `repo.status_section`, `repo-manifest` / `repo.body_manifest`, `repo-tool-capabilities` / `repo.tool_capabilities`, `repo-tool-request` / `repo.tool_request`, `repo-eve-surface` / `repo.eve_surface`, `repo-collaboration-policy` / `repo.collaboration_policy`, `repo-collaboration-topic` / `repo.collaboration_topic`, `repo-consensus-brief` / `repo.consensus_brief`, `repo-objective-draft` / `repo.objective_draft`, `repo-adoption-request` / `repo.adoption_request`, `repo-scheduling-request` / `repo.scheduling_request`, `repo-work-order` / `repo.work_order`, `repo-verification-request` / `repo.verification_request`, `repo-publication-request` / `repo.publication_request`, `repo-sync-request` / `repo.sync_request`, `repo-maintainer-review-request` / `repo.maintainer_review_request`, `repo-pr-request` / `repo.pr_request`, `repo-credit-request` / `repo.credit_request`, `repo-artifact-acceptance-request` / `repo.artifact_acceptance_request`, and `repo-metrics-request` / `repo.metrics_request`, typed Imagination action-item receipts over allowlisted safe families, `epiphany.repo_work_mind_interpretation.v0` plus `epiphany.repo_work_mind_adoption_decision.v0` Mind interpreter receipts before branch-local Hands authority, hash-backed local proof bundles with Bifrost/GitHub/credit/PR/upstream-main publication rows, redacted `epiphany-work export-proof` public bundles plus local Verse `epiphany.cultmesh.repo_work_public_proof.v0` Gjallar readback, Bifrost-owned `gamecult.bifrost.public_proof_publication_receipt.v0` closure plus wrapper `bifrost-public-proof` for redacted public proofs, Idunn-hosted non-mutating queue-run lifecycle plan/runbook receipts, and the speech-audited `epiphany-work persona-intake` mouth plus wrapper `repo-persona-intake` are live with `privateStateExposed=false`; proofs `.epiphany-smoke\fresh-repo-mvp-20260620-180024` and `.epiphany-smoke\swarm-stop-classification-20260620-181606` show fresh-repo iteration-limit stop classification, empty-queue Gjallar classification, dry-run planning-preview Self classification, closure, publication receipts, upstream-main sync, public-proof export, and private-state sealing. The contract note now explicitly answers that Epiphany is headed toward a functional repo swarm but is not fully online until the typed artery runs as a living repo organism. The next cut is deeper interpreter semantics beyond the first Mind decision packet, more useful repo-owned config/action families, gated publication/sync/service stop depth, or Idunn-owned queue-run service lifecycle under explicit operator authority. Preserve Idunn service lifecycle ownership and existing CultMesh/Odin/Eve tool directories; service install/start execution still requires explicit elevated operator authority.