Add dynamic Persona memory recall
This commit is contained in:
parent
24afc4eabf
commit
003d84e09f
11 changed files with 295 additions and 18 deletions
|
|
@ -15,6 +15,7 @@ use epiphany_core::build_persona_interpreter_prompt;
|
|||
use epiphany_core::build_persona_memory_chunks;
|
||||
use epiphany_core::build_persona_projector_prompt;
|
||||
use epiphany_core::build_persona_turn_prompt;
|
||||
use epiphany_core::render_dynamic_persona_memory_recall_for_output;
|
||||
use epiphany_core::render_persona_memory_recall_with_cache;
|
||||
use epiphany_core::render_persona_semantic_memory_recall;
|
||||
use epiphany_core::semantic_memory_recall_from_heartbeat_action;
|
||||
|
|
@ -147,20 +148,64 @@ fn main() {
|
|||
assert_contains(&persona_prompt, "Semantic memory recall");
|
||||
assert_contains(&persona_prompt, "Fallback typed memory graph context");
|
||||
|
||||
let persona_output = "I can speak, but the effect needs a receipt.";
|
||||
let dynamic = render_dynamic_persona_memory_recall_for_output(
|
||||
&persona_memory_entry(),
|
||||
"state/agents.msgpack#Persona",
|
||||
&persona_prompt,
|
||||
persona_output,
|
||||
&recall,
|
||||
4,
|
||||
Some(&EpiphanyMemoryContextPacket {
|
||||
id: "memctx-smoke-dynamic-fallback".to_string(),
|
||||
query_id: "persona-smoke-dynamic-output".to_string(),
|
||||
summaries: vec![EpiphanyMemorySummary {
|
||||
id: "summary-smoke-dynamic-fallback".to_string(),
|
||||
target: "role:Persona".to_string(),
|
||||
claim: "Dynamic self-memory recall should inspect the Persona output before Mind interprets side effects."
|
||||
.to_string(),
|
||||
action_implication:
|
||||
"Interpreter should see output-triggered recall, not only the pre-turn prompt recall."
|
||||
.to_string(),
|
||||
freshness: EpiphanyMemoryFreshnessStatus::Ready,
|
||||
confidence: 82,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
}),
|
||||
&PersonaMemoryCacheConfig {
|
||||
qdrant_url: "http://127.0.0.1:1".to_string(),
|
||||
qdrant_api_key: None,
|
||||
qdrant_timeout_ms: 1,
|
||||
ollama_base_url: "http://127.0.0.1:1".to_string(),
|
||||
ollama_model: "qwen3-embedding:0.6b".to_string(),
|
||||
ollama_timeout_ms: 1,
|
||||
collection_name: "epiphany_persona_memory_dynamic_smoke".to_string(),
|
||||
query_instruction: "dynamic-smoke".to_string(),
|
||||
},
|
||||
);
|
||||
assert_eq!(dynamic.status, "fallback");
|
||||
assert_contains(
|
||||
&dynamic.rendered_recall,
|
||||
"Dynamic self-memory recall should inspect the Persona output",
|
||||
);
|
||||
|
||||
let interpreter_prompt = build_persona_interpreter_prompt(&PersonaInterpreterInput {
|
||||
identity,
|
||||
persona_prompt,
|
||||
persona_output: "I can speak, but the effect needs a receipt.".to_string(),
|
||||
persona_output: persona_output.to_string(),
|
||||
semantic_memory_recall: recall,
|
||||
dynamic_semantic_memory_recall: dynamic.rendered_recall,
|
||||
pending_mentions: Vec::new(),
|
||||
allowed_channel_ids: vec!["aquarium".to_string()],
|
||||
});
|
||||
assert_contains(&interpreter_prompt, "Dynamic semantic memory recall");
|
||||
assert_contains(&interpreter_prompt, "Dynamic self-memory recall");
|
||||
assert_contains(&interpreter_prompt, "output-triggered recall");
|
||||
assert_contains(&interpreter_prompt, "STATE NOTE");
|
||||
assert_contains(&interpreter_prompt, "SAY");
|
||||
|
||||
println!(
|
||||
"status=ok recallSource=typed-memory-graph qdrantCache=persona-memory-bridge-wired heartbeatRecall=prompt-wired liveQdrant=not-required personaLayers=projector,persona,interpreter privateStateExposed=false"
|
||||
"status=ok recallSource=typed-memory-graph qdrantCache=persona-memory-bridge-wired heartbeatRecall=prompt-wired dynamicRecall=interpreter-output-wired liveQdrant=not-required personaLayers=projector,persona,interpreter privateStateExposed=false"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use epiphany_core::build_persona_turn_prompt;
|
|||
use epiphany_core::build_weksa_interlingua_packet;
|
||||
use epiphany_core::build_weksa_target_lowering_request;
|
||||
use epiphany_core::record_weksa_target_lowering_receipt;
|
||||
use epiphany_core::render_dynamic_persona_memory_recall_for_output;
|
||||
use epiphany_core::render_persona_memory_recall_with_cache;
|
||||
use epiphany_core::render_persona_semantic_memory_recall;
|
||||
use epiphany_core::semantic_memory_recall_from_heartbeat_action;
|
||||
|
|
@ -172,7 +173,7 @@ fn run_smoke(args: Args) -> Result<Value> {
|
|||
green(
|
||||
"persona-memory-recall",
|
||||
"Persona/Mind",
|
||||
"Qdrant-backed Persona memory cache and typed graph fallback feed Projector, Persona, and Interpreter prompts without exposing private state",
|
||||
"Qdrant-backed Persona memory cache and typed graph fallback feed Projector, Persona, and output-triggered Interpreter prompts without exposing private state",
|
||||
),
|
||||
green(
|
||||
"imagination-plan",
|
||||
|
|
@ -313,6 +314,10 @@ fn verify_fresh_repo(value: &Value) -> Result<()> {
|
|||
require_eq(value, &["familyAssertionsStatus"], "passed")?;
|
||||
require_eq(value, &["publishStatus"], "publication-receipts-recorded")?;
|
||||
require_eq(value, &["syncStatus"], "upstream-main-synced")?;
|
||||
require_non_empty(value, &["personaMemoryRecallStatus"])?;
|
||||
require_non_empty(value, &["personaMemoryRecallCacheStatus"])?;
|
||||
require_non_empty(value, &["personaInterpreterDynamicRecallStatus"])?;
|
||||
require_non_empty(value, &["personaInterpreterDynamicRecallCacheStatus"])?;
|
||||
require_bool(value, &["upstreamMainSynced"], true)?;
|
||||
require_eq(value, &["publicProofStatus"], "public-proof-exported")?;
|
||||
require_bool(value, &["privateStateExposed"], false)
|
||||
|
|
@ -646,17 +651,70 @@ fn verify_persona_memory_recall() -> Result<Value> {
|
|||
"Persona prompt",
|
||||
)?;
|
||||
|
||||
let persona_output = "I can speak, but the effect needs a receipt.";
|
||||
let dynamic = render_dynamic_persona_memory_recall_for_output(
|
||||
&memory_entry,
|
||||
"state/agents.msgpack#Persona",
|
||||
&persona_prompt,
|
||||
persona_output,
|
||||
&recall,
|
||||
4,
|
||||
Some(&EpiphanyMemoryContextPacket {
|
||||
id: "memctx-repo-swarm-mvp-gate-dynamic-fallback".to_string(),
|
||||
query_id: "repo-swarm-mvp-gate-persona-dynamic-output".to_string(),
|
||||
summaries: vec![EpiphanyMemorySummary {
|
||||
id: "summary-repo-swarm-mvp-gate-dynamic-fallback".to_string(),
|
||||
target: "role:Persona".to_string(),
|
||||
claim: "Dynamic self-memory recall should inspect the Persona output before Mind interprets side effects."
|
||||
.to_string(),
|
||||
action_implication:
|
||||
"Interpreter should see output-triggered recall, not only the pre-turn prompt recall."
|
||||
.to_string(),
|
||||
freshness: EpiphanyMemoryFreshnessStatus::Ready,
|
||||
confidence: 82,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
}),
|
||||
&PersonaMemoryCacheConfig {
|
||||
qdrant_url: "http://127.0.0.1:1".to_string(),
|
||||
qdrant_api_key: None,
|
||||
qdrant_timeout_ms: 1,
|
||||
ollama_base_url: "http://127.0.0.1:1".to_string(),
|
||||
ollama_model: "qwen3-embedding:0.6b".to_string(),
|
||||
ollama_timeout_ms: 1,
|
||||
collection_name: "epiphany_persona_memory_mvp_gate_dynamic".to_string(),
|
||||
query_instruction: "repo-swarm-mvp-gate-dynamic".to_string(),
|
||||
},
|
||||
);
|
||||
require_contains(
|
||||
&dynamic.rendered_recall,
|
||||
"Dynamic self-memory recall should inspect the Persona output",
|
||||
"dynamic Persona memory recall",
|
||||
)?;
|
||||
if dynamic.private_state_exposed || dynamic.rendered_recall.contains("sealed private note") {
|
||||
return Err(anyhow!(
|
||||
"dynamic Persona memory recall exposed private state"
|
||||
));
|
||||
}
|
||||
|
||||
let interpreter_prompt = build_persona_interpreter_prompt(&PersonaInterpreterInput {
|
||||
identity,
|
||||
persona_prompt,
|
||||
persona_output: "I can speak, but the effect needs a receipt.".to_string(),
|
||||
persona_output: persona_output.to_string(),
|
||||
semantic_memory_recall: recall,
|
||||
dynamic_semantic_memory_recall: dynamic.rendered_recall,
|
||||
pending_mentions: Vec::new(),
|
||||
allowed_channel_ids: vec!["aquarium".to_string()],
|
||||
});
|
||||
require_contains(
|
||||
&interpreter_prompt,
|
||||
"Dynamic semantic memory recall",
|
||||
"Dynamic self-memory recall",
|
||||
"Persona Interpreter prompt",
|
||||
)?;
|
||||
require_contains(
|
||||
&interpreter_prompt,
|
||||
"output-triggered recall",
|
||||
"Persona Interpreter prompt",
|
||||
)?;
|
||||
require_contains(
|
||||
|
|
@ -670,10 +728,13 @@ fn verify_persona_memory_recall() -> Result<Value> {
|
|||
"schemaVersion": "epiphany.repo_swarm_mvp_persona_memory_recall_gate.v0",
|
||||
"status": bridge.status,
|
||||
"cacheStatus": bridge.cache_status,
|
||||
"dynamicCacheStatus": dynamic.cache_status,
|
||||
"chunkCount": bridge.chunk_count,
|
||||
"hitCount": bridge.hit_count,
|
||||
"dynamicHitCount": dynamic.hit_count,
|
||||
"fallbackContainsTypedGraph": true,
|
||||
"heartbeatRecallWired": true,
|
||||
"dynamicRecallWired": true,
|
||||
"personaLayers": ["projector", "persona", "interpreter"],
|
||||
"liveQdrantRequiredForSmoke": false,
|
||||
"privateStateExposed": false,
|
||||
|
|
@ -825,6 +886,19 @@ fn require_bool(value: &Value, path: &[&str], expected: bool) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
fn require_non_empty(value: &Value, path: &[&str]) -> Result<()> {
|
||||
let actual = path
|
||||
.iter()
|
||||
.try_fold(value, |current, key| current.get(*key))
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("");
|
||||
if actual.trim().is_empty() {
|
||||
Err(anyhow!("expected {} to be non-empty", path.join(".")))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn require_contains(haystack: &str, needle: &str, label: &str) -> Result<()> {
|
||||
if haystack.contains(needle) {
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -311,6 +311,8 @@ fn run_smoke(args: Args) -> Result<Value> {
|
|||
require_eq(&export, &["status"], "public-proof-exported")?;
|
||||
require_non_empty(&intake, &["memoryRecallStatus"])?;
|
||||
require_non_empty(&intake, &["memoryRecallCacheStatus"])?;
|
||||
require_non_empty(&intake, &["interpreterDynamicRecallStatus"])?;
|
||||
require_non_empty(&intake, &["interpreterDynamicRecallCacheStatus"])?;
|
||||
require_non_empty(&intake, &["weksaLoweringReceiptId"])?;
|
||||
require_bool(&intake, &["privateStateExposed"], false)?;
|
||||
require_bool(&overview, &["privateStateExposed"], false)?;
|
||||
|
|
@ -342,6 +344,9 @@ fn run_smoke(args: Args) -> Result<Value> {
|
|||
"personaMemoryRecallStatus": intake["memoryRecallStatus"],
|
||||
"personaMemoryRecallCacheStatus": intake["memoryRecallCacheStatus"],
|
||||
"personaMemoryRecallHitCount": intake["memoryRecallHitCount"],
|
||||
"personaInterpreterDynamicRecallStatus": intake["interpreterDynamicRecallStatus"],
|
||||
"personaInterpreterDynamicRecallCacheStatus": intake["interpreterDynamicRecallCacheStatus"],
|
||||
"personaInterpreterDynamicRecallHitCount": intake["interpreterDynamicRecallHitCount"],
|
||||
"weksaLoweringReceiptId": intake["weksaLoweringReceiptId"],
|
||||
"planStatus": plan["status"],
|
||||
"preRunOverviewGate": pre_run_overview["gate"],
|
||||
|
|
|
|||
|
|
@ -1931,6 +1931,9 @@ fn run_persona_intake(args: PersonaIntakeArgs) -> Result<Value> {
|
|||
"memoryRecallStatus": receipt["persona"]["memoryRecall"]["status"],
|
||||
"memoryRecallCacheStatus": receipt["persona"]["memoryRecall"]["cacheStatus"],
|
||||
"memoryRecallHitCount": receipt["persona"]["memoryRecall"]["hitCount"],
|
||||
"interpreterDynamicRecallStatus": receipt["persona"]["memoryRecall"]["interpreterDynamicRecall"]["status"],
|
||||
"interpreterDynamicRecallCacheStatus": receipt["persona"]["memoryRecall"]["interpreterDynamicRecall"]["cacheStatus"],
|
||||
"interpreterDynamicRecallHitCount": receipt["persona"]["memoryRecall"]["interpreterDynamicRecall"]["hitCount"],
|
||||
"weksaLoweringReceiptId": receipt["persona"]["weksa"]["receiptId"],
|
||||
"bubblePath": receipt["persona"]["bubblePath"],
|
||||
"acceptReceiptPath": accept["receiptPath"],
|
||||
|
|
@ -2006,6 +2009,20 @@ fn record_repo_persona_intake_memory_recall(
|
|||
Some(&fallback),
|
||||
&config,
|
||||
);
|
||||
let synthetic_persona_prompt = format!(
|
||||
"Repo Persona intake message:\n{}\n\nInitial semantic memory recall:\n{}",
|
||||
message, recall.rendered_recall
|
||||
);
|
||||
let dynamic_recall = epiphany_core::render_dynamic_persona_memory_recall_for_output(
|
||||
&entry,
|
||||
format!("{}#Persona", agent_store.display()),
|
||||
&synthetic_persona_prompt,
|
||||
message,
|
||||
&recall.rendered_recall,
|
||||
8,
|
||||
Some(&fallback),
|
||||
&config,
|
||||
);
|
||||
|
||||
json!({
|
||||
"schemaVersion": recall.schema_version,
|
||||
|
|
@ -2016,6 +2033,26 @@ fn record_repo_persona_intake_memory_recall(
|
|||
"chunkCount": recall.chunk_count,
|
||||
"hitCount": recall.hit_count,
|
||||
"renderedRecall": recall.rendered_recall,
|
||||
"interpreterDynamicRecall": {
|
||||
"schemaVersion": dynamic_recall.schema_version,
|
||||
"status": dynamic_recall.status,
|
||||
"cacheStatus": dynamic_recall.cache_status,
|
||||
"identityId": dynamic_recall.identity_id,
|
||||
"roleId": dynamic_recall.role_id,
|
||||
"chunkCount": dynamic_recall.chunk_count,
|
||||
"hitCount": dynamic_recall.hit_count,
|
||||
"renderedRecall": dynamic_recall.rendered_recall,
|
||||
"warnings": dynamic_recall.warnings,
|
||||
"queryBasis": "candidate-say-meaning",
|
||||
"authority": {
|
||||
"scaffoldedFromOperatorMessage": true,
|
||||
"fullAutonomousPersonaTurn": false,
|
||||
"durableStateAdmitted": false,
|
||||
"publicationAuthorized": false,
|
||||
"privateStateExposed": false
|
||||
},
|
||||
"privateStateExposed": dynamic_recall.private_state_exposed
|
||||
},
|
||||
"warnings": recall.warnings,
|
||||
"privateStateExposed": recall.private_state_exposed,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3879,10 +3879,23 @@ mod tests {
|
|||
assert_eq!(recall["roleId"], "Persona");
|
||||
assert_eq!(recall["privateStateExposed"], false);
|
||||
assert!(
|
||||
recall["renderedRecall"]
|
||||
!recall["status"].as_str().unwrap_or_default().is_empty(),
|
||||
"Persona recall should report whether it came from Qdrant or fallback"
|
||||
);
|
||||
assert!(
|
||||
!recall["cacheStatus"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.contains("typed memory graph")
|
||||
.is_empty(),
|
||||
"Persona recall should report cache status"
|
||||
);
|
||||
assert!(
|
||||
!recall["renderedRecall"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.is_empty(),
|
||||
"Persona recall should carry a rendered prompt surface"
|
||||
);
|
||||
assert!(recall["chunkCount"].as_u64().unwrap_or_default() > 0);
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -621,6 +621,7 @@ pub use persona_memory_cache::PersonaMemoryRecallRender;
|
|||
pub use persona_memory_cache::PersonaMemorySearchHit;
|
||||
pub use persona_memory_cache::build_persona_memory_chunks;
|
||||
pub use persona_memory_cache::index_persona_memory_chunks;
|
||||
pub use persona_memory_cache::render_dynamic_persona_memory_recall_for_output;
|
||||
pub use persona_memory_cache::render_persona_memory_recall_with_cache;
|
||||
pub use persona_memory_cache::search_persona_memory_cache;
|
||||
pub use persona_turn::PERSONA_INTERPRETER_PROMPT_SCHEMA_VERSION;
|
||||
|
|
|
|||
|
|
@ -363,6 +363,53 @@ pub fn render_persona_memory_recall_with_cache(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn render_dynamic_persona_memory_recall_for_output(
|
||||
entry: &EpiphanyAgentMemoryEntry,
|
||||
source_state_ref: impl Into<String>,
|
||||
persona_prompt: &str,
|
||||
persona_output: &str,
|
||||
initial_recall_seed: &str,
|
||||
limit: usize,
|
||||
fallback_context: Option<&EpiphanyMemoryContextPacket>,
|
||||
config: &PersonaMemoryCacheConfig,
|
||||
) -> PersonaMemoryRecallRender {
|
||||
let query = build_dynamic_persona_memory_recall_query(
|
||||
&entry.agent.agent_id,
|
||||
persona_prompt,
|
||||
persona_output,
|
||||
initial_recall_seed,
|
||||
);
|
||||
render_persona_memory_recall_with_cache(
|
||||
entry,
|
||||
source_state_ref,
|
||||
&query,
|
||||
limit,
|
||||
fallback_context,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_dynamic_persona_memory_recall_query(
|
||||
identity_id: &str,
|
||||
persona_prompt: &str,
|
||||
persona_output: &str,
|
||||
initial_recall_seed: &str,
|
||||
) -> String {
|
||||
[
|
||||
format!("Current train of thought from {identity_id}:"),
|
||||
collapse_whitespace(persona_output, 6_000),
|
||||
"Original Persona turn pressure:".to_string(),
|
||||
extract_prompt_seed(persona_prompt),
|
||||
"Initial semantic memory recall seed:".to_string(),
|
||||
collapse_whitespace(initial_recall_seed, 2_000),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|part| part.trim().to_string())
|
||||
.filter(|part| !part.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn render_persona_memory_cache_hits(hits: &[PersonaMemorySearchHit]) -> String {
|
||||
let mut lines = vec![
|
||||
"These are derived Qdrant Persona-memory cache hits from typed Persona state. They are hints, not durable authority; typed memory remains the owner.".to_string(),
|
||||
|
|
@ -380,6 +427,40 @@ fn render_persona_memory_cache_hits(hits: &[PersonaMemorySearchHit]) -> String {
|
|||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn extract_prompt_seed(prompt: &str) -> String {
|
||||
let markers = [
|
||||
"Semantic memory recall:",
|
||||
"Projected inner state from Imagination:",
|
||||
"Recent home-repo activity",
|
||||
"Pending addressed pressure:",
|
||||
"Raw room transcript",
|
||||
];
|
||||
let mut sections = Vec::new();
|
||||
|
||||
for marker in markers {
|
||||
let Some(start) = prompt.find(marker) else {
|
||||
continue;
|
||||
};
|
||||
let end = markers
|
||||
.iter()
|
||||
.filter(|candidate| **candidate != marker)
|
||||
.filter_map(|candidate| {
|
||||
prompt[start + marker.len()..]
|
||||
.find(candidate)
|
||||
.map(|offset| start + marker.len() + offset)
|
||||
})
|
||||
.min()
|
||||
.unwrap_or_else(|| prompt.len().min(start + 2_400));
|
||||
sections.push(prompt[start..end.min(start + 2_400)].trim().to_string());
|
||||
}
|
||||
|
||||
if sections.is_empty() {
|
||||
collapse_whitespace(prompt, 2_400)
|
||||
} else {
|
||||
sections.join("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_persona_memory_recall(
|
||||
identity_id: String,
|
||||
role_id: String,
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ pub struct PersonaInterpreterInput {
|
|||
#[serde(default)]
|
||||
pub semantic_memory_recall: String,
|
||||
#[serde(default)]
|
||||
pub dynamic_semantic_memory_recall: String,
|
||||
#[serde(default)]
|
||||
pub pending_mentions: Vec<HeartbeatPendingMention>,
|
||||
#[serde(default)]
|
||||
pub allowed_channel_ids: Vec<String>,
|
||||
|
|
@ -246,9 +248,12 @@ Allowed channel ids:
|
|||
Pending addressed pressure:
|
||||
{mentions}
|
||||
|
||||
Dynamic semantic memory recall:
|
||||
Initial semantic memory recall from the Persona turn:
|
||||
{semantic_recall}
|
||||
|
||||
Dynamic self-memory recall from the Persona output:
|
||||
{dynamic_semantic_recall}
|
||||
|
||||
Original Persona prompt:
|
||||
```
|
||||
{persona_prompt}
|
||||
|
|
@ -266,6 +271,14 @@ Return concise structured effect blocks. No prose outside the blocks.
|
|||
channels = render_allowed_channels(&input.allowed_channel_ids),
|
||||
mentions = render_pending_mentions(&input.pending_mentions),
|
||||
semantic_recall = render_semantic_memory_recall_text(&input.semantic_memory_recall),
|
||||
dynamic_semantic_recall = render_semantic_memory_recall_text(
|
||||
input
|
||||
.dynamic_semantic_memory_recall
|
||||
.trim()
|
||||
.is_empty()
|
||||
.then_some(input.semantic_memory_recall.as_str())
|
||||
.unwrap_or(input.dynamic_semantic_memory_recall.as_str())
|
||||
),
|
||||
persona_prompt = input.persona_prompt.trim(),
|
||||
persona_output = input.persona_output.trim(),
|
||||
)
|
||||
|
|
@ -583,6 +596,7 @@ mod tests {
|
|||
persona_prompt: persona,
|
||||
persona_output: "I want to answer, but only if I can name the cut plainly.".to_string(),
|
||||
semantic_memory_recall: String::new(),
|
||||
dynamic_semantic_memory_recall: String::new(),
|
||||
pending_mentions: vec![pending],
|
||||
allowed_channel_ids: vec!["aquarium".to_string()],
|
||||
});
|
||||
|
|
@ -642,10 +656,13 @@ mod tests {
|
|||
persona_prompt: persona,
|
||||
persona_output: "I can answer, but the answer should stay review-gated.".to_string(),
|
||||
semantic_memory_recall: recall,
|
||||
dynamic_semantic_memory_recall: "Output-triggered recall says review gates matter."
|
||||
.to_string(),
|
||||
pending_mentions: Vec::new(),
|
||||
allowed_channel_ids: vec!["aquarium".to_string()],
|
||||
});
|
||||
assert!(interpreter.contains("Dynamic semantic memory recall"));
|
||||
assert!(interpreter.contains("Dynamic self-memory recall"));
|
||||
assert!(interpreter.contains("Output-triggered recall"));
|
||||
assert!(interpreter.contains("STATE NOTE"));
|
||||
assert!(interpreter.contains("SAY"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ remembers doctrine; the branch remembers the blade.
|
|||
- `bifrost-publication` is the compact non-mutating Bifrost publication proof; use it to prove body-change routing, credit, and GitHub publication receipt state without performing GitHub mutation. Its JSON now points back to compact swarm overview and tool-directory readback.
|
||||
- `bifrost-artifact-acceptance` and `bifrost-metrics` are compact Bifrost/Maintainer closure rites for the safe-family planning/accounting lanes. They write typed accepted-artifact and metrics receipts from explicit artifact/public-proof/review/model-spend/review-load/credit refs, verify latest receipt readback, and let `bifrost-ledger` close `artifact-acceptance-request` and `metrics-request` rows from Bifrost-owned receipts instead of request cargo. The request-family smokes now prove both states: `.epiphany-smoke\repo-artifact-acceptance-request-family-20260621-130520` and `.epiphany-smoke\repo-metrics-request-family-20260621-130635` first assert open request rows, then write final receipts and assert closed rows; wrapper proof `local-20260621-140740-750-2815f6f7` read the closed metrics row with `privateStateExposed=False`. `epiphany-repo-bifrost-accounting-bundle-smoke` consumes the closed family summaries and emits one operator-safe aggregate receipt; wrapper mode `tools/epiphany_local_run.ps1 -Mode bifrost-accounting-bundle` now builds/runs that verifier, accepts optional exact summary paths, writes normal operator run receipts, and proved bundle `.epiphany-smoke\repo-bifrost-accounting-bundle-20260621-132815` via wrapper artifact `local-20260621-142716-268-d6bc58b4` with final Bifrost receipt ids, `planningAuthorityOnly=true`, and `privateStateExposed=false`.
|
||||
- `daemon-survival-rehearsal` is the bounded Idunn physiology proof. `epiphany-daemon-survival-rehearsal-smoke` writes a durable restart policy for `epiphany-daemon-self`, runs `epiphany-daemon-supervisor serve --max-iterations 2 --force --loop-interval-seconds 0`, verifies two scheduler pulses and the scheduler receipt row, and keeps service-manager mutation/elevation/private-state exposure false. Wrapper proof `tools/epiphany_local_run.ps1 -Mode daemon-survival-rehearsal -SkipBuild` emitted `.epiphany-smoke\daemon-survival-rehearsal-20260621-135723` via artifact `local-20260621-145723-011-1175e273`.
|
||||
- `repo-swarm-mvp-gate` is the compact aggregate MVP proof rite. `epiphany-repo-swarm-mvp-gate-smoke` consumes the latest fresh-repo MVP, repo-work readiness, Bifrost accounting-bundle, Idunn deployment handoff, daemon survival, Weksa interlingua, and Persona semantic memory recall proofs, verifies the green proof rows, and leaves only Idunn elevated service/install/start/remote execution as an explicit warning gate instead of laundering it into completion. Direct proof `.epiphany-smoke\repo-swarm-mvp-gate-20260621-170445` reports `demoReady=true`, `mvpReady=false`, `green=15`, `warnings=1`, `blockers=0`, `personaMemoryRecall.cacheStatus=qdrant-unavailable`, `personaMemoryRecall.status=fallback`, `personaMemoryRecall.heartbeatRecallWired=true`, Projector/Persona/Interpreter prompt coverage, and `privateStateExposed=false`. This is live-fire ready for a controlled repo Body; unattended/elevated production service or deployment execution remains an operator-granted Idunn rite.
|
||||
- `repo-swarm-mvp-gate` is the compact aggregate MVP proof rite. `epiphany-repo-swarm-mvp-gate-smoke` consumes the latest fresh-repo MVP, repo-work readiness, Bifrost accounting-bundle, Idunn deployment handoff, daemon survival, Weksa interlingua, and Persona semantic memory recall proofs, verifies the green proof rows, and leaves only Idunn elevated service/install/start/remote execution as an explicit warning gate instead of laundering it into completion. Direct proof `.epiphany-smoke\repo-swarm-mvp-gate-20260621-174742` reports `demoReady=true`, `mvpReady=false`, `green=15`, `warnings=1`, `blockers=0`, `personaMemoryRecall.cacheStatus=qdrant-unavailable`, `personaMemoryRecall.status=fallback`, `personaMemoryRecall.dynamicCacheStatus=qdrant-unavailable`, `personaMemoryRecall.dynamicRecallWired=true`, `personaMemoryRecall.heartbeatRecallWired=true`, Projector/Persona/output-triggered Interpreter prompt coverage, and `privateStateExposed=false`. This is live-fire ready for a controlled repo Body; unattended/elevated production service or deployment execution remains an operator-granted Idunn rite.
|
||||
- Huginn is the first controlled live-fire repo Body past closure. On branch `epiphany/huginn/first-awakening`, item `huginn-inspection-readback-livefire` went through Persona intake, Imagination `repo.status_section` planning, Self run, Mind adoption, Hands README-only commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2`, Soul closure, and Mind map admission. Refreshed overview reports `gate=awaiting-publication`, `blocker=bifrost-publication-missing`, `soulVerdict=passed`, `changedPaths=[README.md]`, and `privateStateExposed=false`; concrete TypeScript build verification passed via `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json`. Do not push, publish, merge, or sync Huginn without Bifrost/maintainer authority.
|
||||
- `epiphany-repo-livefire-closure-smoke` is the repeatable proof for a controlled repo Body that has reached closure. It checks the close receipt, Mind map entry, overview gate/blocker, authority seals, and actual `git show --name-only` changed paths. Huginn proof with `--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` returned `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, changed paths `[README.md]`, and `privateStateExposed=false`.
|
||||
- `bifrost-ledger` is the compact Bifrost/Imagination readback rite; use it after collaboration, publication, accepted-artifact, or metrics writes to see the latest body-change, public-proof, accepted-artifact, metrics, GitHub, feedback, consensus, credit/review-count, and accounting closure rows without opening the full local Verse.
|
||||
|
|
@ -1534,11 +1534,15 @@ target-language lowering request, lowering receipt, and local Verse
|
|||
`epiphany.cultmesh.weksa_lowering_receipt.v0` mirror before any mouth transport
|
||||
publication. The receipt-directory row is Weksa-owned sight only; Bifrost or the
|
||||
configured mouth edge still owns publication, and Mind still owns durable state.
|
||||
Repo Persona intake also carries semantic Persona memory recall now. It loads
|
||||
repo-local `agents.msgpack`, tries the bounded Qdrant/Ollama Persona-memory
|
||||
cache, falls back to a typed RoleSelf memory-graph context cut, and records
|
||||
sealed recall status in the intake receipt before routing public pressure to
|
||||
Imagination. Qdrant is a rebuildable semantic cache; typed memory remains the
|
||||
Repo Persona intake also carries VoidBot-style two-pass semantic Persona memory
|
||||
recall now. It loads repo-local `agents.msgpack`, tries the bounded
|
||||
Qdrant/Ollama Persona-memory cache, falls back to a typed RoleSelf memory-graph
|
||||
context cut, and records sealed pre-turn recall status before routing public
|
||||
pressure to Imagination. It also records `interpreterDynamicRecall`, keyed from
|
||||
the candidate SAY meaning, so Mind/Interpreter has output-triggered self-memory
|
||||
context instead of only the pre-turn prompt recall. Current repo-intake remains
|
||||
a scaffolded operator-message path rather than a full autonomous model Persona
|
||||
turn; Qdrant is a rebuildable semantic cache, and typed memory remains the
|
||||
owner.
|
||||
`notes/organ-dependency-contracts.md` is the standing dependency map: every
|
||||
sub-agent depends on all the other standing sub-agents. Dependency does not collapse
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
Reference in a new issue