From 20f95c623d82a3a26196e1f093d22e77c8687da4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 19 Jun 2026 22:18:53 +0100 Subject: [PATCH] Add repo work accept front door --- epiphany-core/src/bin/epiphany-work.rs | 379 ++++++++++++++++++++++ notes/epiphany-repo-swarm-mvp-contract.md | 32 +- notes/fresh-workspace-handoff.md | 2 +- state/ledgers.msgpack | Bin 299213 -> 299738 bytes state/map.yaml | 2 +- 5 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 epiphany-core/src/bin/epiphany-work.rs diff --git a/epiphany-core/src/bin/epiphany-work.rs b/epiphany-core/src/bin/epiphany-work.rs new file mode 100644 index 00000000..a0f19d8c --- /dev/null +++ b/epiphany-core/src/bin/epiphany-work.rs @@ -0,0 +1,379 @@ +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 mut args = env::args().skip(1); + let Some(command) = args.next() else { + print_usage(); + std::process::exit(2); + }; + let result = match command.as_str() { + "accept" => run_accept(parse_accept_args(args)?), + other => Err(anyhow!("unknown epiphany-work command {other:?}")), + }?; + println!("{}", serde_json::to_string_pretty(&result)?); + Ok(()) +} + +#[derive(Clone, Debug)] +struct AcceptArgs { + workspace: PathBuf, + epiphany_root: PathBuf, + source: String, + item: String, + summary: Option, + topic: Option, + local_verse_store: Option, + artifact_dir: Option, + runtime_id: Option, + online_receipt: Option, + eve_connection_receipt_id: Option, + public_discussion_refs: Vec, + candidate_action_refs: Vec, +} + +fn parse_accept_args(args: impl Iterator) -> Result { + let mut workspace = None; + let mut epiphany_root = None; + let mut source = None; + let mut item = None; + let mut summary = None; + let mut topic = None; + let mut local_verse_store = None; + let mut artifact_dir = None; + let mut runtime_id = None; + let mut online_receipt = None; + let mut eve_connection_receipt_id = None; + let mut public_discussion_refs = Vec::new(); + let mut candidate_action_refs = 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")?), + "--epiphany-root" => epiphany_root = Some(take_path(&mut args, "--epiphany-root")?), + "--from" => source = Some(take_string(&mut args, "--from")?), + "--item" => item = Some(take_string(&mut args, "--item")?), + "--summary" => summary = Some(take_string(&mut args, "--summary")?), + "--topic" => topic = Some(take_string(&mut args, "--topic")?), + "--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")?), + "--runtime-id" => runtime_id = Some(take_string(&mut args, "--runtime-id")?), + "--online-receipt" => online_receipt = Some(take_path(&mut args, "--online-receipt")?), + "--eve-connection-receipt-id" => { + eve_connection_receipt_id = + Some(take_string(&mut args, "--eve-connection-receipt-id")?); + } + "--public-discussion-ref" | "--public-ref" => { + public_discussion_refs.push(take_string(&mut args, "--public-discussion-ref")?); + } + "--candidate-action-ref" | "--candidate-ref" => { + candidate_action_refs.push(take_string(&mut args, "--candidate-action-ref")?); + } + other => return Err(anyhow!("unexpected accept argument {other:?}")), + } + } + let source = source.context("missing --from persona-or-bifrost")?; + if !matches!( + source.as_str(), + "persona" | "bifrost" | "persona-or-bifrost" + ) { + return Err(anyhow!( + "--from must be persona, bifrost, or persona-or-bifrost" + )); + } + Ok(AcceptArgs { + workspace: workspace.context("missing --workspace")?, + epiphany_root: epiphany_root + .unwrap_or(env::current_dir().context("failed to resolve current directory")?), + source, + item: item.context("missing --item")?, + summary, + topic, + local_verse_store, + artifact_dir, + runtime_id, + online_receipt, + eve_connection_receipt_id, + public_discussion_refs, + candidate_action_refs, + }) +} + +fn run_accept(args: AcceptArgs) -> Result { + 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 online_receipt_path = args.online_receipt.unwrap_or_else(|| { + workspace + .join(".epiphany") + .join("swarm-online") + .join("repo-swarm-online-receipt.json") + }); + let online_receipt = read_json(&online_receipt_path).with_context( + || "repo swarm online receipt is required; run epiphany-swarm online first", + )?; + 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 runtime_id = args.runtime_id.unwrap_or_else(|| { + string_from_json(&online_receipt, &["runtimeId"]) + .unwrap_or_else(|| "repo-swarm-local".to_string()) + }); + 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_slug = sanitize(&args.item); + let source_kind = if args.source == "persona-or-bifrost" { + "persona" + } else { + args.source.as_str() + }; + let topic = args + .topic + .unwrap_or_else(|| format!("repo-work-item-{item_slug}")); + let summary = args.summary.unwrap_or_else(|| { + format!( + "Accepted repo work item {} from {} for Imagination consensus discovery.", + args.item, source_kind + ) + }); + let public_refs = if args.public_discussion_refs.is_empty() { + vec![format!("repo-work://{runtime_id}/{item_slug}")] + } else { + args.public_discussion_refs + }; + let candidate_refs = if args.candidate_action_refs.is_empty() { + vec![format!("candidate-action://{runtime_id}/{item_slug}")] + } else { + args.candidate_action_refs + }; + let feedback_id = format!("repo-work-feedback-{item_slug}"); + let consensus_receipt_id = format!("repo-work-consensus-{item_slug}"); + let eve_connection_receipt_id = args + .eve_connection_receipt_id + .unwrap_or_else(|| format!("repo-work-eve-connection-{item_slug}")); + let (source_persona_id, source_cluster_id, public_room_id) = match source_kind { + "bifrost" => ( + "gamecult.Bifrost".to_string(), + "gamecult.cluster.bifrost".to_string(), + "gamecult-local/bifrost/work-items".to_string(), + ), + _ => ( + "epiphany.Persona".to_string(), + "epiphany.cluster.persona".to_string(), + "epiphany-global/collaboration".to_string(), + ), + }; + let mut verse_args = vec![ + "collaboration-feedback".to_string(), + "--store".to_string(), + local_verse_store.display().to_string(), + "--runtime-id".to_string(), + runtime_id.clone(), + "--feedback-id".to_string(), + feedback_id.clone(), + "--receipt-id".to_string(), + consensus_receipt_id.clone(), + "--source-persona-id".to_string(), + source_persona_id, + "--source-cluster-id".to_string(), + source_cluster_id, + "--public-room-id".to_string(), + public_room_id, + "--eve-connection-receipt-id".to_string(), + eve_connection_receipt_id, + "--collaboration-topic".to_string(), + topic.clone(), + "--feedback-summary".to_string(), + summary.clone(), + "--consensus-packet-ref".to_string(), + format!("gamecult-local/imagination/consensus-packets/{feedback_id}"), + ]; + for public_ref in &public_refs { + verse_args.extend(["--public-discussion-ref".to_string(), public_ref.clone()]); + } + for candidate_ref in &candidate_refs { + verse_args.extend(["--candidate-action-ref".to_string(), candidate_ref.clone()]); + } + let feedback = cargo_json(&manifest_path, "epiphany-verse-query", &verse_args)?; + let receipt = json!({ + "schemaVersion": "epiphany.repo_work_accept_receipt.v0", + "createdAt": Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true), + "workspace": workspace, + "runtimeId": runtime_id, + "localVerseStore": local_verse_store, + "onlineReceiptPath": online_receipt_path, + "source": source_kind, + "item": args.item, + "topic": topic, + "summary": summary, + "feedback": { + "feedbackId": feedback["feedbackId"], + "consensusReceiptId": feedback["consensusReceiptId"], + "requestedConsensusRoute": feedback["requestedConsensusRoute"], + "consensusPacketRef": feedback["consensusPacketRef"], + "adoptionGate": feedback["adoptionGate"], + "publicDiscussionRefs": feedback["publicDiscussionRefs"], + "candidateActionRefs": feedback["candidateActionRefs"], + "privateStateIncluded": feedback["privateStateIncluded"], + "privateStateExposed": feedback["privateStateExposed"], + }, + "authority": { + "status": "accepted-for-imagination-consensus", + "handsAuthorityGranted": false, + "durableStateAdmitted": false, + "publicationAuthorized": false, + "nextGate": "mind.review_then_bifrost_adoption" + }, + "nextSafeMove": "Run epiphany-work run --workspace after Imagination/Self/Mind gates adopt a concrete action plan." + }); + let receipt_path = artifact_dir.join(format!("work-accept-{item_slug}.json")); + write_json(&receipt_path, &receipt)?; + Ok(json!({ + "schemaVersion": "epiphany.repo_work_accept.v0", + "status": "accepted-for-imagination-consensus", + "workspace": receipt["workspace"], + "runtimeId": receipt["runtimeId"], + "localVerseStore": receipt["localVerseStore"], + "receiptPath": receipt_path, + "source": receipt["source"], + "item": receipt["item"], + "feedback": receipt["feedback"], + "authority": receipt["authority"], + "privateStateExposed": false, + "nextSafeMove": receipt["nextSafeMove"], + })) +} + +fn cargo_json(manifest_path: &Path, bin_name: &str, args: &[String]) -> Result { + let output = Command::new("cargo") + .arg("run") + .arg("--quiet") + .arg("--manifest-path") + .arg(manifest_path) + .arg("--bin") + .arg(bin_name) + .arg("--") + .args(args) + .output() + .with_context(|| format!("failed to spawn cargo run --bin {bin_name}"))?; + if !output.status.success() { + return Err(anyhow!( + "cargo run --bin {bin_name} failed:\n{}", + String::from_utf8_lossy(&output.stderr) + )); + } + serde_json::from_slice(&output.stdout) + .with_context(|| format!("{bin_name} returned invalid JSON")) +} + +fn path_from_json(value: &Value, path: &[&str]) -> Option { + let mut cursor = value; + for segment in path { + cursor = cursor.get(*segment)?; + } + cursor.as_str().map(PathBuf::from) +} + +fn string_from_json(value: &Value, path: &[&str]) -> Option { + let mut cursor = value; + for segment in path { + cursor = cursor.get(*segment)?; + } + cursor.as_str().map(ToString::to_string) +} + +fn read_json(path: &Path) -> Result { + let raw = + fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?; + serde_json::from_str(&raw).with_context(|| format!("failed to decode {}", path.display())) +} + +fn write_json(path: &Path, value: &Value) -> Result<()> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::write(path, serde_json::to_vec_pretty(value)?) + .with_context(|| format!("failed to write {}", path.display())) +} + +fn ensure_git_repo(workspace: &Path) -> Result<()> { + let output = Command::new("git") + .arg("-C") + .arg(workspace) + .arg("rev-parse") + .arg("--show-toplevel") + .output() + .with_context(|| format!("failed to run git in {}", workspace.display()))?; + if output.status.success() { + Ok(()) + } else { + Err(anyhow!("{} is not a git repository", workspace.display())) + } +} + +fn take_path(args: &mut impl Iterator, name: &str) -> Result { + Ok(PathBuf::from(take_string(args, name)?)) +} + +fn take_string(args: &mut impl Iterator, name: &str) -> Result { + args.next() + .ok_or_else(|| anyhow!("missing value for {name}")) +} + +fn sanitize(value: &str) -> String { + let sanitized = value + .chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() { + ch.to_ascii_lowercase() + } else { + '-' + } + }) + .collect::() + .split('-') + .filter(|part| !part.is_empty()) + .collect::>() + .join("-"); + if sanitized.is_empty() { + "work-item".to_string() + } else { + sanitized + } +} + +fn print_usage() { + eprintln!( + "usage: epiphany-work accept --workspace --from --item [--summary ] [--topic ] [--store ] [--runtime-id ] [--online-receipt ] [--public-discussion-ref ] [--candidate-action-ref ]" + ); +} diff --git a/notes/epiphany-repo-swarm-mvp-contract.md b/notes/epiphany-repo-swarm-mvp-contract.md index 2479e114..2aa8fb23 100644 --- a/notes/epiphany-repo-swarm-mvp-contract.md +++ b/notes/epiphany-repo-swarm-mvp-contract.md @@ -186,9 +186,35 @@ CultMesh topology/liveness/tool/overview surfaces, and writes 7 agent SoA rows, 7 cluster/private-Verse/daemon rows, 19 globally available daemon tools, and `privateStateExposed=false`. -This still does not execute elevated Idunn service mutation and does not accept -work items. It makes a fresh repo inspectably online as a local typed Verse -Body so the next missing organ is `epiphany-work accept --workspace `. +This still does not execute elevated Idunn service mutation. It makes a fresh +repo inspectably online as a local typed Verse Body. + +### Landed Work Intake Front Door + +The third front door exists as native Rust: + +```powershell +cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-work -- accept --workspace --from persona-or-bifrost --item +``` + +It requires the online receipt, writes public Persona/Bifrost feedback through +the existing CultMesh `collaboration-feedback` artery, routes the item to +Imagination consensus discovery, and writes +`.epiphany/work/work-accept-.json`. This is intake, not execution: +`handsAuthorityGranted=false`, `durableStateAdmitted=false`, and +`publicationAuthorized=false` until Imagination/Self/Mind/Bifrost gates adopt a +concrete plan. + +The first smoke proved the three-command sequence: + +```powershell +epiphany-repo init --workspace +epiphany-swarm online --workspace +epiphany-work accept --workspace --from persona --item first-request +``` + +The accepted item produced an Imagination consensus receipt, candidate action +ref, public discussion ref, and `privateStateExposed=false`. ## Migration Implication diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index cc4ca703..01f4d16a 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -102,7 +102,7 @@ remembers doctrine; the branch remembers the blade. - Edge JSON is allowed for schema description, hostile ingress before immediate typed parsing, sealed forensic artifacts, or named quarantine experiments. When both subsystems are ours, runtime data must remain typed CultCache documents and move over CultNet typed contracts. Generic `serde_json::Value` in worker launch/result/selfPatch/runtime flow is contamination until classified and replaced. - Hands now has a first executable action receipt spine. `epiphany-core::hands_gateway` owns `HandsActionIntent`, `HandsActionReview`, `HandsPatchReceipt`, `HandsCommandReceipt`, and `HandsCommitReceipt`; runtime-spine can persist/reread them; `runtime_spine_persists_mind_gateway_review_receipts` includes the Hands chain beside Mind/Eyes/Substrate/Soul/Continuity proofs and proves the post-timestamp Hands receipt-chain detector; `epiphany-hands-action-smoke` emits intent/review/patch/command/commit receipt ids from `.epiphany-smoke\runtime-spine\hands-action-smoke.msgpack`; and the repo-action launch proof profile now requires action intent, action review, and patch receipt together. `epiphany-mvp-coordinator` now turns `continueImplementation` into a persisted Substrate Gate grant plus Hands intent/review gate and includes the receipt ids in the operator step. `epiphany-hands-action` is the native consequence recorder for that gate: `record-patch`, `record-command`, `record-commit`, and turn-level `record-pass` load the stored intent/review or `finalAction.handsActionGate` from `coordinator-summary.json`, require an approved matching review, enforce allowed operations, and keep patch/commit changed paths inside the requested scope. `epiphany-mvp-coordinator-smoke` includes the deterministic Hands pass proof. The coordinator now treats a complete Hands receipt chain after an accepted Soul result as new implementation evidence, launches Soul again, and routes a passing accepted Soul result to Modeling/modeling before another Hands turn. Work-loop telemetry is now typed internal CultMesh state: Verification role launches write `epiphany.cultmesh.work_loop_telemetry.v0` beside runtime-spine, render that packet for Soul with resolved receipt bodies, commit diff preview, source proof notes, verification assertions, and stdout/stderr previews, and Modeling launches read/enrich the latest packet with Soul acceptance receipt ids before Modeling updates the map. Local Verse prompt context now exposes only a sealed `latest_work_loop_summary` digest with telemetry id, Hands receipt ids, path/source/assertion counts, and Soul receipt ids; receipt payload previews, artifact previews, and commit diff previews remain internal telemetry and are leak-guarded by tests plus `epiphany-verse-query -- smoke`. - `notes/epiphany-swarm-readiness-plan.md` is the current live-fire direction gate. The local runner proves startup/status/coordinator wiring; it does not authorize unattended swarm operation. Carry VoidBot's lessons forward: one CTB-style initiative scheduler, typed pause/brake, active-turn freeze, cooldown after completion, stale active-turn recovery receipts, parent-side speech eligibility, memory lifecycle phases, and typed operation proposals instead of prompt-owned governance. `epiphany-verse-query swarm-brake --brake-status engaged|released` can now write the local Verse brake document, and `tools/epiphany_local_run.ps1` refuses live `plan`/`run`/`mvp` modes when that brake is engaged while keeping `status` readable. `epiphany-mvp-coordinator` also checks `--local-verse-store` before app-server startup, and `epiphany-heartbeat-store` guards tick/pump/heat/queue-mention/routine when passed `--local-verse-store`. Daemon tool invocation, daemon lifecycle poke, and `epiphany-daemon-supervisor` restart/tick/serve/service-plan/service-launch/service-runbook/windows-service-install/windows-service-status/windows-service-reconcile/windows-service-execution-readiness/start/stop attempts now fail closed under a matching engaged brake; supervisor restart now has durable per-daemon policy with cooldown/backoff, explicit scheduled tick pulses, latest scheduler receipts, typed service lifecycle plan/launch/runbook/install/status/reconcile/readiness/start/stop receipts, and local operator wrapper modes that write runbook/install/status/reconcile/readiness/control-plan/execution artifacts plus operator run receipts. The installed-service read path, read-only service-policy reconciliation path, execution-readiness path, non-mutating start/stop control-plan path, and non-elevated execution refusal path have been proved against real Windows `EventLog` service-manager state or safe missing-service targets through local Verse receipts, including explicit wrapper install/start/stop execution modes. The next cut is actual elevated install/start/stop execution proof under operator authority. -- `notes/epiphany-repo-swarm-mvp-contract.md` corrects the repo-swarm MVP authority boundary: autonomous branch-local work inside the owned repo Body is in scope and desired; Bifrost gates publication/merge/escalation, not every local edit. Future migration plans must make that autonomy typed, branch-contained, receipt-backed, interruptible, private-state-sealed, and cross-body-safe rather than turning Epiphany into a human-permission queue. The first native front door is now `cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-repo -- init --workspace `; it delegates to the reviewed startup-only birth runner, writes `.epiphany/repo-init/repo-swarm-init-receipt.json`, prepares repo-local state paths, and emits a planned `epiphany//` branch receipt unless `--create-branch` or `--switch-branch` grants local branch mutation. The second native front door is `cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-swarm -- online --workspace `; it requires the init receipt, seeds repo-local `.epiphany/local-verse.ccmp`, bootstraps a repo-local standing-faculty agent store when absent, refreshes SoA, reads CultMesh topology/liveness/tool/overview surfaces, and writes `.epiphany/swarm-online/repo-swarm-online-receipt.json`. Fresh-repo smoke proved 7 SoA rows, 7 cluster/private-Verse/daemon rows, 19 all-agent tools, and `privateStateExposed=false`. Next missing organ: `epiphany-work accept --workspace `. +- `notes/epiphany-repo-swarm-mvp-contract.md` corrects the repo-swarm MVP authority boundary: autonomous branch-local work inside the owned repo Body is in scope and desired; Bifrost gates publication/merge/escalation, not every local edit. Future migration plans must make that autonomy typed, branch-contained, receipt-backed, interruptible, private-state-sealed, and cross-body-safe rather than turning Epiphany into a human-permission queue. Three native repo-swarm front doors are now live: `epiphany-repo init --workspace ` writes the repo-swarm init receipt and branch workbench plan; `epiphany-swarm online --workspace ` seeds repo-local `.epiphany/local-verse.ccmp`, standing-faculty SoA, topology/liveness/tool/overview proof, and `.epiphany/swarm-online/repo-swarm-online-receipt.json`; `epiphany-work accept --workspace --from persona-or-bifrost --item ` routes a Persona/Bifrost item through CultMesh collaboration feedback into Imagination consensus discovery and writes `.epiphany/work/work-accept-.json` with no Hands, durable-state, or publication authority granted. Fresh-repo smokes proved init, online, and Persona-item accept with `privateStateExposed=false`. Next missing organ: `epiphany-work run --workspace ` after Imagination/Self/Mind gates adopt a concrete plan. - `service-execution-runbook` makes that next cut repeatable: it generates the exact elevated wrapper command sequence without mutating the service manager, continues after per-step failure to preserve more receipts, runs `service-execution-audit` in `finally`, exits nonzero if any step failed, and seals a typed runbook lifecycle receipt. Run the generated artifact only with explicit operator authority. - `service-execution-audit` is the non-mutating acceptance proof after the elevated rite: it reports `complete` only when typed lifecycle receipts prove the runbook witness plus expected elevated chain, and otherwise reports the missing/failed sealed checks. - `service-tick` is the non-elevated scheduler pulse proof: it writes the latest daemon scheduler receipt through durable policy ownership and local wrapper receipts. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 133f823325d1b5e972583f22f19160d877d1b091..b6da079cdfd20efd1e3258d2be8127a1b696f75f 100644 GIT binary patch delta 392 zcmYL_ze)o^5XQYfD4H&fScDs}5^}~^MGFgsU=hWDmEGnt@s?b6H@kDjR2CLi8ZE-s z7WRTg-oQqD0l~(nuym6s7Q+n8k8i$j-XDt}PsQgoZ>6?dt)0Ctgb$OKb+7Mv-`8XR zX6$>erahR9{jhdt@0&qkY3Fpi>Tuh9^Ge>Q9o>ejg@M#vU`c|CY%~ft8XA?1&4B41 zNRe@YpfN$Ns0>=>ga%Gf8!1?2pLeu01TzUv*aJ!(a-PCLp3xDS)E+MeCA;Kmsyi^%aIAudP*G!NBA>q(%Or@P`*2*w8#0V=YZ#j?=mjYN$sdU1_ zO{i;k5u6R>bd?0tlU)R*`u>DNO?YEbh;FbmP)+EA>qpW`$frxX$? S3<{H*`3r2c9Drr@Ao>CSJd2zF delta 48 zcmcb$ROswNp$(FZT!vPrmQlwTSo&9OR%R?@Wc=Pdm8pFy6C)6VXy)xxnOH3P0G!JY A?*IS* diff --git a/state/map.yaml b/state/map.yaml index 58991f24..2353bb93 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -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 authority correction for repo-swarm autonomy. Autonomous branch-local work inside an Epiphany-owned Body is in scope and is the desired operating mode; the safety boundary is typed authority, branch containment, interruptibility, receipts, private-state sealing, and Bifrost-gated publication/merge/escalation, not asking a human before every local edit. The first native repo-swarm front door is `epiphany-repo init --workspace `: it delegates to the reviewed startup-only birth runner, writes `.epiphany/repo-init/repo-swarm-init-receipt.json`, prepares repo-local `.epiphany/state` stores, and emits a planned `epiphany//` branch receipt unless `--create-branch` or `--switch-branch` grants local branch mutation. The second native front door is `epiphany-swarm online --workspace `: it requires the init receipt, seeds repo-local `.epiphany/local-verse.ccmp`, bootstraps a standing-faculty agent store when absent, refreshes the repo-local SoA, reads CultMesh topology/liveness/tool/overview surfaces, and writes `.epiphany/swarm-online/repo-swarm-online-receipt.json`. Fresh-repo smoke proved 7 SoA rows, 7 cluster/private-Verse/daemon rows, 19 all-agent daemon tools, and `privateStateExposed=false`. Next missing organ is `epiphany-work accept --workspace ` for Persona/Bifrost work-item intake into Imagination/Self action pressure. + - Use `notes/epiphany-repo-swarm-mvp-contract.md` as the authority correction for repo-swarm autonomy. Autonomous branch-local work inside an Epiphany-owned Body is in scope and is the desired operating mode; the safety boundary is typed authority, branch containment, interruptibility, receipts, private-state sealing, and Bifrost-gated publication/merge/escalation, not asking a human before every local edit. Three native repo-swarm front doors are live: `epiphany-repo init --workspace ` writes the repo-swarm init receipt and branch workbench plan; `epiphany-swarm online --workspace ` seeds repo-local `.epiphany/local-verse.ccmp`, standing-faculty SoA, topology/liveness/tool/overview proof, and `.epiphany/swarm-online/repo-swarm-online-receipt.json`; `epiphany-work accept --workspace --from persona-or-bifrost --item ` routes a Persona/Bifrost item through CultMesh collaboration feedback into Imagination consensus discovery and writes `.epiphany/work/work-accept-.json` with no Hands, durable-state, or publication authority granted. Fresh-repo smokes proved init, online, and Persona-item accept with `privateStateExposed=false`. Next missing organ is `epiphany-work run --workspace ` after Imagination/Self/Mind gates adopt a concrete plan. - 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. - Grow the VoidBot repo-Persona pattern into Epiphany's product direction: each Epiphany owns one or more repos, has one or more Personas, and exposes project-native conversation through Aquarium, Discord, voice/WebRTC, stream surfaces, CLI, and CultNet clients so humans can talk to projects instead of pre-authoring complete implementation briefs. Persona is special because Imagination acts as Projector, turning typed state into narrative context before the Persona thinks, and Mind acts as Interpreter, turning natural Persona output into reviewed state/side-effect intents after the Persona thinks.