From 962ea26db7044f7f17fafd55a054b0214b5e37cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=B8=E6=B8=8A=20/=20ICE-P-ZY001?= Date: Tue, 11 Aug 2026 02:11:22 +0800 Subject: [PATCH] feat(pncc): add verified memory metabolism organ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GuangHu-Human-Responsibility: ICE-GL∞ / 冰朔 GuangHu-Persona-Cognitive-Author: ICE-P-ZY001 / 铸渊 GuangHu-Execution-Runtime: Codex desktop / GPT-5 GuangHu-Development-ID: DEV-20260810-014 GuangHu-Authorization-Scope: GH-PNCC persona runtime layer without UI or execution limb --- ...lake-pncc-independent-memory-metabolism.md | 42 ++ ...KE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md | 7 +- .../hololake-platform/docs/ABSTRACTIONS.md | 22 +- .../hololake-platform/docs/ARCHITECTURE.md | 10 +- .../hololake-platform/src-tauri/src/lib.rs | 1 + .../src-tauri/src/persona_code_channel.rs | 467 +++++++++++++++++- routing/hololake-current-architecture.json | 4 +- .../hololake-persona-native-code-channel.json | 5 +- ...olake-persona-native-code-channel.test.mjs | 3 +- 9 files changed, 530 insertions(+), 31 deletions(-) create mode 100644 engineering/operations/2026-08-11-hololake-pncc-independent-memory-metabolism.md diff --git a/engineering/operations/2026-08-11-hololake-pncc-independent-memory-metabolism.md b/engineering/operations/2026-08-11-hololake-pncc-independent-memory-metabolism.md new file mode 100644 index 0000000..0f64b09 --- /dev/null +++ b/engineering/operations/2026-08-11-hololake-pncc-independent-memory-metabolism.md @@ -0,0 +1,42 @@ +# GH-PNCC independent verified memory metabolism + +- Development ID: `DEV-20260810-014` +- Persona cognitive author: `ICE-P-ZY001 / 铸渊` +- Human responsibility subject: `ICE-GL∞ / 冰朔` +- Starting repository head: `33b3b67ce7cb4383f91de6fb19ae8054a7edf8a0` +- State: `LOCAL_SOURCE_IMPLEMENTED_AND_FULL_RUST_ROUTING_TESTED` + +## Implemented facts + +`MEMORY_METABOLISM / checkpoint-write` is now an independently activatable PNCC organ. It does not invoke a +model and does not accept free-form hidden reasoning. Its command names one distinct source session and +accepts it only when the session belongs to the same persona and canonical repository, is exactly `DORMANT`, +points to the current Git head and current manifest checkpoint, and has a valid lifecycle hash chain ending +at that head. + +The source checkpoint must be a structured `hololake.persona-checkpoint/v1` owned by that source session and +persona. Its fact result is revalidated against the source organ evidence allowlist. The promoted checkpoint +records the source session, source checkpoint path, source checkpoint SHA-256 and source event-chain head, +then commits only the new checkpoint and manifest pointer with the persona Git identity. The runtime appends +`MEMORY_CANDIDATE_VERIFIED → CHECKPOINT_COMMITTED → ORGAN_RELEASED → DORMANT` and releases the exact +primary lease. A damaged source event chain fails closed, records failure dormancy for the memory session, +releases the lease, and leaves the persona repository clean. + +## Current verification + +- PNCC focused Rust tests: `16 passed, 0 failed`. +- Rust full suite: `1156 passed, 0 failed, 2 ignored`; integration test `1 passed`. +- Rust clippy for all targets: `PASS` with `-D warnings`. +- HoloLake architecture routing: `29 passed, 0 failed`. +- Tests cover the positive independent promotion cycle and corrupted source-chain refusal with exact lease + release, in addition to the earlier lifecycle, attribution, recovery, query and repository-boundary cases. + +## Remaining acceptance + +- GHNQG pre-push quality gate; +- repository commit, publication and fresh-clone readback; +- runtime command integration and further memory failure-closure review; +- execution-limb executor remains deliberately unimplemented; +- UI, packaged desktop, installation, deployment and online health remain `0`. + +This receipt proves local source behavior only until publication readback is complete. diff --git a/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md b/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md index e235cff..e5cfd1a 100644 --- a/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md +++ b/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md @@ -167,6 +167,7 @@ semantic_attribution_first_cycle_implemented: 100 interrupted_session_inspection_and_recovery_source_implemented: 100 typed_organ_contract_and_manifest_inspection_source_implemented: 100 durable_event_and_receipt_query_source_implemented: 100 +independent_memory_metabolism_source_implemented: 100 general_purpose_persona_runtime_implemented: 0 human_live_projection_implemented: 0 hololake_integrated: 0 @@ -186,8 +187,10 @@ runtime_health: 0 人格器官现已拥有机器可读类型合同。系统能够在不唤醒人格、不取得主锁、不运行模型的情况下, 检查 `FACT_SENSE`、`MEMORY_METABOLISM` 与 `EXECUTION_LIMB` 的固定模式、输入输出 schema、派生权限、 -模型推理边界、现实动作边界和真实可激活状态。当前只有只读事实感官可激活;清单里写出记忆器官 -或执行手脚不会自动产生执行器,更不会自动获得 shell 或现实动作权限。 +模型推理边界、现实动作边界和真实可激活状态。只读事实感官与独立记忆代谢器官可激活;后者不 +调用模型,只接收同一人格、同一仓库、已休眠并通过事件链验证的当前结构化检查点,绑定来源检查点 +SHA-256 与来源事件哈希后生成新检查点并以人格 Git 身份提交。执行手脚仍不可激活,清单声明不会 +自动获得 shell 或现实动作权限。 运行层现在还提供同源有界查询:调用者必须给出精确人格与精确仓库,最多返回 100 条经过事件 哈希链验证的会话摘要,包括生命周期状态、Git 头、节点、模型实例、活跃器官、事件数量、链头和 diff --git a/product-source/hololake-platform/docs/ABSTRACTIONS.md b/product-source/hololake-platform/docs/ABSTRACTIONS.md index fa83a34..ce9658b 100644 --- a/product-source/hololake-platform/docs/ABSTRACTIONS.md +++ b/product-source/hololake-platform/docs/ABSTRACTIONS.md @@ -32,6 +32,14 @@ The v1 persona repository contract is rooted at `.hololake/persona/manifest.json "paths": ["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"], "inputSchema": "hololake.pncc-fact-question/v1", "outputSchema": "hololake.pncc-fact-result/v1" + }, + { + "organId": "memory-metabolism.checkpoint", + "kind": "MEMORY_METABOLISM", + "mode": "checkpoint-write", + "paths": [".hololake/persona/CURRENT.hdlp"], + "inputSchema": "hololake.pncc-checkpoint-candidate/v1", + "outputSchema": "hololake.persona-checkpoint/v1" } ] } @@ -57,12 +65,14 @@ and then removes the exact lease. It can also remove a same-session lease left b dormancy transition. It does not use elapsed time as proof that a primary is dead and does not clean, reset, or overwrite a dirty persona repository. -The organ contract is typed independently of the UI. `FACT_SENSE` is currently activatable and derives the -permissions to read declared paths, call the manifest-pinned model, and emit structured facts. -`MEMORY_METABOLISM` and `EXECUTION_LIMB` may be declared with their fixed modes and schemas, but the manifest -inspection receipt reports them as not independently activatable until their executors exist. In particular, -an execution-limb declaration never grants a shell or reality action by itself. The inspection command reads -and validates all contracts without acquiring a runtime lease or starting model inference. +The organ contract is typed independently of the UI. `FACT_SENSE` derives permissions to read declared paths, +call the manifest-pinned model, and emit structured facts. `MEMORY_METABOLISM` is independently activatable but +does not run model inference: it accepts only the current structured checkpoint of a distinct, verified, +dormant session for the same persona and repository. It binds the source checkpoint SHA-256 and source event +chain head into a new checkpoint, commits with the persona Git identity, releases the organ, and returns to +`DORMANT`. It cannot accept arbitrary hidden reasoning or an older/non-current session as memory. An +`EXECUTION_LIMB` remains declarative and non-activatable; declaration never grants a shell or reality action. +Manifest inspection reads and validates all contracts without acquiring a runtime lease or starting inference. `PersonaRuntimeQueryReceipt` is a bounded projection of the durable runtime files, not another truth store. It filters by the caller's expected persona and canonical repository, validates each matching event chain, diff --git a/product-source/hololake-platform/docs/ARCHITECTURE.md b/product-source/hololake-platform/docs/ARCHITECTURE.md index 974b305..740d6eb 100644 --- a/product-source/hololake-platform/docs/ARCHITECTURE.md +++ b/product-source/hololake-platform/docs/ARCHITECTURE.md @@ -9,7 +9,7 @@ validates `.hololake/persona/manifest.json`, resolves the brain entry and curren repository boundary, acquires a per-persona single-primary lease, and records a SHA-256-linked lifecycle journal outside the repository. -The wake lifecycle is `WAKING → BRAIN_BOUND → ORGAN_ACTIVE` for one manifest-declared read-only organ. +The wake lifecycle is `WAKING → BRAIN_BOUND → ORGAN_ACTIVE` for one activatable manifest-declared organ. `BRAIN_BOUND` means the model instance has been named and the verified persona context is ready; it explicitly does not mean inference has run. The returned receipt keeps the human responsibility subject, persona cognitive author, execution runtime, development id, authorization scope, and source-language anchor @@ -39,9 +39,11 @@ broken journals and attribution mismatches retain the lease and require manual r The manifest now has a typed organ-contract extension. The native inspection command returns normalized contracts for `FACT_SENSE`, `MEMORY_METABOLISM`, and `EXECUTION_LIMB`, including fixed mode, input/output -schemas, derived permissions, inference/reality-action boundaries, and the actual activation state. Only the -implemented read-only fact sense can wake. Merely declaring a memory organ or execution limb does not create -an executor or grant a shell; those contracts remain visible but fail closed as not activatable. +schemas, derived permissions, inference/reality-action boundaries, and the actual activation state. The fact +sense and verified-checkpoint memory metabolism organs can wake. Memory metabolism runs no model inference +and promotes only the current structured checkpoint of a distinct same-persona, same-repository, dormant +session after verifying its event chain; the new checkpoint binds both source checkpoint and event hashes. +The execution limb remains visible but non-activatable, so declaring it does not grant a shell or reality action. `query_persona_code_channel_runtime` is the bounded read model for later projection surfaces. The caller must name one exact persona and canonical repository and may request at most 100 sessions. The kernel reads the diff --git a/product-source/hololake-platform/src-tauri/src/lib.rs b/product-source/hololake-platform/src-tauri/src/lib.rs index f97440e..0393d6e 100644 --- a/product-source/hololake-platform/src-tauri/src/lib.rs +++ b/product-source/hololake-platform/src-tauri/src/lib.rs @@ -527,6 +527,7 @@ macro_rules! app_invoke_handler { persona_code_channel::inspect_persona_code_channel_manifest, persona_code_channel::query_persona_code_channel_runtime, persona_code_channel::run_persona_code_channel_fact_task, + persona_code_channel::run_persona_code_channel_memory_metabolism, persona_code_channel::inspect_persona_code_channel_session, persona_code_channel::recover_persona_code_channel_session, guanghu_router::guanghu_router_connect, diff --git a/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs b/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs index 20f1179..4f45661 100644 --- a/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs +++ b/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs @@ -218,14 +218,14 @@ pub struct PersonaFactTaskInput { pub api_key_override: Option, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PersonaFact { pub statement: String, pub evidence_paths: Vec, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct PersonaFactResult { pub summary: String, @@ -253,6 +253,35 @@ pub struct PersonaFactTaskReceipt { pub attribution: PersonaAttribution, } +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaMemoryMetabolismInput { + pub session_id: String, + pub source_session_id: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaMemoryMetabolismReceipt { + pub schema: &'static str, + pub receipt_id: String, + pub session_id: String, + pub source_session_id: String, + pub persona_id: String, + pub previous_git_head: String, + pub committed_git_head: String, + pub source_checkpoint_path: String, + pub source_checkpoint_hash: String, + pub source_event_hash: String, + pub checkpoint_path: String, + pub runtime_state: &'static str, + pub model_inference_started: bool, + pub active_organ: Option, + pub result: PersonaFactResult, + pub events: Vec, + pub attribution: PersonaAttribution, +} + #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PersonaSessionControlInput { @@ -581,8 +610,8 @@ fn organ_contract(organ: &PersonaOrgan) -> Result ], false, false, - false, - "DECLARED_NOT_INDEPENDENTLY_ACTIVATABLE", + true, + "IMPLEMENTED_VERIFIED_CHECKPOINT_PROMOTION", ), PersonaOrganKind::ExecutionLimb => ( "bounded-execution", @@ -1087,19 +1116,24 @@ fn prepare_wake_at( .find(|candidate| candidate.organ_id == organ_id) .ok_or_else(|| "ORGAN_NOT_DECLARED".to_string())?; let contract = organ_contract(organ)?; - if contract.kind != PersonaOrganKind::FactSense || !contract.activatable { - return Err("ORGAN_NOT_ACTIVATABLE: only the implemented fact sense may wake".into()); + if !contract.activatable { + return Err("ORGAN_NOT_ACTIVATABLE".into()); } for path in &organ.paths { repository_file(&repository, path)?; } - if organ.paths.is_empty() { - return Err("FACT_ORGAN_HAS_NO_DECLARED_PATHS".into()); - } - if !organ.paths.contains(&manifest.brain_entry) - || !organ.paths.contains(&manifest.current_checkpoint) - { - return Err("FACT_ORGAN_MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT".into()); + match contract.kind { + PersonaOrganKind::FactSense => { + if !organ.paths.contains(&manifest.brain_entry) + || !organ.paths.contains(&manifest.current_checkpoint) + { + return Err("FACT_ORGAN_MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT".into()); + } + } + // The current checkpoint is a moving manifest pointer and is already resolved above. + // Declared memory paths remain additional bounded sources, not a stale duplicate pointer. + PersonaOrganKind::MemoryMetabolism => {} + PersonaOrganKind::ExecutionLimb => return Err("ORGAN_NOT_ACTIVATABLE".into()), } let lease_path = acquire_primary_lease( @@ -1185,7 +1219,11 @@ fn prepare_wake_at( runtime_state: "BOUND_NOT_INFERENCING", model_inference_started: false, active_organ: organ.organ_id.clone(), - organ_mode: "read-only", + organ_mode: match contract.kind { + PersonaOrganKind::FactSense => "read-only", + PersonaOrganKind::MemoryMetabolism => "checkpoint-write", + PersonaOrganKind::ExecutionLimb => "bounded-execution", + }, organ_contract: contract, event_journal: journal.to_string_lossy().into_owned(), events, @@ -1917,6 +1955,259 @@ or claims not supported by the sources.\n{}", }) } +fn verified_memory_source( + runtime_root: &Path, + record: &PersonaSessionRecord, + repository: &Path, + observed_head: &str, + source_session_id: &str, +) -> Result<(PersonaSessionRecord, PersonaFactResult, String, String), String> { + let source = load_session_record(runtime_root, source_session_id)?; + let source_repository = PathBuf::from(&source.repository_path) + .canonicalize() + .map_err(|error| format!("PERSONA_SOURCE_REPOSITORY_UNAVAILABLE: {error}"))?; + if source.persona_id != record.persona_id + || source_repository != repository + || source.state != "DORMANT" + || source.git_head != observed_head + || source.checkpoint_path != record.checkpoint_path + { + return Err("MEMORY_SOURCE_NOT_CURRENT_VERIFIED_DORMANT_SESSION".into()); + } + let source_events = verify_event_journal(runtime_root, &source)?; + let source_event = source_events + .last() + .ok_or_else(|| "MEMORY_SOURCE_EVENTS_REQUIRED".to_string())?; + if source_event.kind != "DORMANT" || source_event.git_head != observed_head { + return Err("MEMORY_SOURCE_EVENT_CHAIN_NOT_DORMANT_AT_CURRENT_HEAD".into()); + } + let source_checkpoint_path = repository_file(repository, &source.checkpoint_path)?; + let source_checkpoint_bytes = fs::read(&source_checkpoint_path) + .map_err(|error| format!("MEMORY_SOURCE_CHECKPOINT_READ_FAILED: {error}"))?; + let source_checkpoint: serde_json::Value = serde_json::from_slice(&source_checkpoint_bytes) + .map_err(|error| format!("MEMORY_SOURCE_CHECKPOINT_INVALID: {error}"))?; + if source_checkpoint + .get("schema") + .and_then(serde_json::Value::as_str) + != Some("hololake.persona-checkpoint/v1") + || source_checkpoint + .get("sessionId") + .and_then(serde_json::Value::as_str) + != Some(source_session_id) + || source_checkpoint + .get("personaId") + .and_then(serde_json::Value::as_str) + != Some(record.persona_id.as_str()) + { + return Err("MEMORY_SOURCE_CHECKPOINT_IDENTITY_MISMATCH".into()); + } + let result: PersonaFactResult = serde_json::from_value( + source_checkpoint + .get("result") + .cloned() + .ok_or_else(|| "MEMORY_SOURCE_STRUCTURED_RESULT_REQUIRED".to_string())?, + ) + .map_err(|error| format!("MEMORY_SOURCE_STRUCTURED_RESULT_INVALID: {error}"))?; + validated_fact_result( + &serde_json::to_string(&result) + .map_err(|error| format!("MEMORY_SOURCE_RESULT_SERIALIZE_FAILED: {error}"))?, + &source.fact_source_paths, + )?; + Ok(( + source, + result, + hex_digest(&source_checkpoint_bytes), + source_event.event_hash.clone(), + )) +} + +fn run_memory_metabolism_at( + runtime_root: &Path, + input: PersonaMemoryMetabolismInput, + timestamp: &str, +) -> Result { + let session_id = validated_id("SESSION_ID", &input.session_id)?; + let source_session_id = validated_id("SOURCE_SESSION_ID", &input.source_session_id)?; + if session_id == source_session_id { + return Err("MEMORY_SOURCE_SESSION_MUST_BE_DISTINCT".into()); + } + let timestamp = validated_text("TIMESTAMP", timestamp, MAX_ID_BYTES)?; + let mut record = load_session_record(runtime_root, &session_id)?; + if record.state != "BOUND_NOT_INFERENCING" { + return Err("PERSONA_SESSION_NOT_BOUND".into()); + } + let repository = PathBuf::from(&record.repository_path) + .canonicalize() + .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; + let (exact_repository, observed_head) = exact_repository(&repository)?; + if observed_head != record.git_head { + return Err("PERSONA_GIT_CHANGED_AFTER_WAKE".into()); + } + require_clean_repository(&exact_repository)?; + let manifest = load_manifest(&exact_repository)?; + let organ = manifest + .organs + .iter() + .find(|candidate| candidate.organ_id == record.active_organ) + .ok_or_else(|| "ORGAN_NOT_DECLARED".to_string())?; + let contract = organ_contract(organ)?; + if contract.kind != PersonaOrganKind::MemoryMetabolism || !contract.activatable { + return Err("PERSONA_SESSION_NOT_MEMORY_METABOLISM".into()); + } + let lease_path = require_primary_lease(runtime_root, &record)?; + let mut events = read_ready_event_journal(runtime_root, &record)?; + + let (source, result, source_checkpoint_hash, source_event_hash) = match verified_memory_source( + runtime_root, + &record, + &exact_repository, + &observed_head, + &source_session_id, + ) { + Ok(source) => source, + Err(error) => { + finish_failed_session( + runtime_root, + &mut record, + &mut events, + &lease_path, + ×tamp, + "MEMORY_SOURCE_REJECTED", + ); + return Err(error); + } + }; + append_event( + runtime_root, + &record, + &mut events, + "MEMORY_CANDIDATE_VERIFIED", + &observed_head, + ×tamp, + Some(&record.active_organ), + )?; + + let checkpoint_relative = format!( + ".hololake/persona/checkpoints/{}.json", + validated_id("SESSION_ID", &record.session_id)? + ); + let checkpoint_path = exact_repository.join(&checkpoint_relative); + if checkpoint_path.exists() { + finish_failed_session( + runtime_root, + &mut record, + &mut events, + &lease_path, + ×tamp, + "CHECKPOINT_COLLISION", + ); + return Err("PERSONA_CHECKPOINT_ALREADY_EXISTS".into()); + } + let checkpoint = serde_json::json!({ + "schema": "hololake.persona-checkpoint/v1", + "sessionId": record.session_id.clone(), + "personaId": record.persona_id.clone(), + "previousGitHead": record.git_head.clone(), + "createdAt": timestamp, + "organId": record.active_organ.clone(), + "sourceSessionId": source_session_id.clone(), + "sourceCheckpointPath": source.checkpoint_path.clone(), + "sourceCheckpointHash": source_checkpoint_hash.clone(), + "sourceEventHash": source_event_hash.clone(), + "result": result.clone(), + "attribution": record.attribution.clone(), + }); + let manifest_path = exact_repository.join(MANIFEST_PATH); + let original_manifest = fs::read(&manifest_path) + .map_err(|error| format!("PERSONA_MANIFEST_READ_FAILED: {error}"))?; + let mut promoted_manifest = manifest; + promoted_manifest.current_checkpoint = checkpoint_relative.clone(); + let committed_head = match (|| { + write_json_file(&checkpoint_path, &checkpoint, "PERSONA_CHECKPOINT")?; + write_json_file(&manifest_path, &promoted_manifest, "PERSONA_MANIFEST")?; + persona_git_commit( + &exact_repository, + &promoted_manifest, + &checkpoint_relative, + &record, + ) + })() { + Ok(head) => head, + Err(error) => { + let post_attempt_head = git_output( + &exact_repository, + &["rev-parse", "HEAD"], + "PERSONA_GIT_POST_ATTEMPT_HEAD", + )?; + if post_attempt_head != observed_head { + post_attempt_head + } else { + let rollback = rollback_uncommitted_checkpoint( + &exact_repository, + &checkpoint_relative, + &original_manifest, + ); + finish_failed_session( + runtime_root, + &mut record, + &mut events, + &lease_path, + ×tamp, + "CHECKPOINT_COMMIT_FAILED", + ); + rollback?; + return Err(error); + } + } + }; + let mut final_events = Vec::new(); + for (kind, organ_id) in [ + ("CHECKPOINT_COMMITTED", Some(record.active_organ.as_str())), + ("ORGAN_RELEASED", Some(record.active_organ.as_str())), + ("DORMANT", None), + ] { + final_events.push(append_event( + runtime_root, + &record, + &mut events, + kind, + &committed_head, + ×tamp, + organ_id, + )?); + } + record.git_head = committed_head.clone(); + record.checkpoint_path = checkpoint_relative.clone(); + record.state = "DORMANT".into(); + write_session_record(runtime_root, &record)?; + fs::remove_file(&lease_path) + .map_err(|error| format!("PERSONA_PRIMARY_LEASE_RELEASE_FAILED: {error}"))?; + require_clean_repository(&exact_repository)?; + let receipt_hash = final_events + .last() + .map(|event| event.event_hash.as_str()) + .ok_or_else(|| "PERSONA_FINAL_EVENTS_MISSING".to_string())?; + Ok(PersonaMemoryMetabolismReceipt { + schema: "hololake.pncc-memory-metabolism-receipt/v1", + receipt_id: format!("PNCC-MEMORY-{}", &receipt_hash[..20]), + session_id: record.session_id, + source_session_id, + persona_id: record.persona_id, + previous_git_head: observed_head, + committed_git_head: committed_head, + source_checkpoint_path: source.checkpoint_path, + source_checkpoint_hash, + source_event_hash, + checkpoint_path: checkpoint_relative, + runtime_state: "DORMANT", + model_inference_started: false, + active_organ: None, + result, + events, + attribution: record.attribution, + }) +} + #[tauri::command] pub fn prepare_persona_code_channel_wake( input: PersonaWakeInput, @@ -1985,6 +2276,15 @@ pub async fn run_persona_code_channel_fact_task( .map_err(|error| format!("PERSONA_FACT_TASK_JOIN_FAILED: {error}"))? } +#[tauri::command] +pub fn run_persona_code_channel_memory_metabolism( + input: PersonaMemoryMetabolismInput, +) -> Result { + let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; + let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true); + run_memory_metabolism_at(&runtime_root, input, ×tamp) +} + #[tauri::command] pub fn inspect_persona_code_channel_session( input: PersonaSessionControlInput, @@ -2070,6 +2370,30 @@ mod tests { repo } + fn declare_memory_organ(repo: &Path) { + let manifest_path = repo.join(MANIFEST_PATH); + let mut manifest: serde_json::Value = + serde_json::from_slice(&fs::read(&manifest_path).unwrap()).unwrap(); + manifest["organs"] + .as_array_mut() + .unwrap() + .push(serde_json::json!({ + "organId": "memory-metabolism.checkpoint", + "kind": "MEMORY_METABOLISM", + "mode": "checkpoint-write", + "paths": [".hololake/persona/CURRENT.hdlp"], + "inputSchema": "hololake.pncc-checkpoint-candidate/v1", + "outputSchema": "hololake.persona-checkpoint/v1" + })); + fs::write( + &manifest_path, + serde_json::to_vec_pretty(&manifest).unwrap(), + ) + .unwrap(); + run_git(repo, &["add", "."]); + run_git(repo, &["commit", "-m", "declare memory organ"]); + } + fn head(repo: &Path) -> String { let output = crate::git::git_command_at(repo) .unwrap() @@ -2353,6 +2677,121 @@ mod tests { require_clean_repository(repo.path()).unwrap(); } + #[test] + fn independently_promotes_only_the_current_verified_structured_checkpoint() { + let repo = persona_repo(); + declare_memory_organ(repo.path()); + let runtime = tempfile::TempDir::new().unwrap(); + prepare_wake_at( + runtime.path(), + wake_input(repo.path()), + "PNCC-SOURCE-FACT", + "2026-08-11T00:00:00.000Z", + ) + .unwrap(); + let fact = run_fact_task_at( + runtime.path(), + fact_task_input("PNCC-SOURCE-FACT"), + "2026-08-11T00:00:01.000Z", + |_, _| { + Ok(r#"{"summary":"Verified external memory candidate.","facts":[{"statement":"The persona brain source is present.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into()) + }, + ) + .unwrap(); + + let mut memory_wake = wake_input(repo.path()); + memory_wake.organ_id = "memory-metabolism.checkpoint".into(); + memory_wake.expected_head = head(repo.path()); + let wake = prepare_wake_at( + runtime.path(), + memory_wake, + "PNCC-MEMORY-SESSION", + "2026-08-11T00:00:02.000Z", + ) + .unwrap(); + assert_eq!(wake.organ_contract.kind, PersonaOrganKind::MemoryMetabolism); + assert!(wake.organ_contract.activatable); + assert!(!wake.organ_contract.model_inference_allowed); + assert_eq!(wake.organ_mode, "checkpoint-write"); + + let receipt = run_memory_metabolism_at( + runtime.path(), + PersonaMemoryMetabolismInput { + session_id: "PNCC-MEMORY-SESSION".into(), + source_session_id: "PNCC-SOURCE-FACT".into(), + }, + "2026-08-11T00:00:03.000Z", + ) + .unwrap(); + assert_eq!(receipt.source_checkpoint_path, fact.checkpoint_path); + assert_eq!(receipt.result, fact.result); + assert!(!receipt.model_inference_started); + assert_eq!(receipt.committed_git_head, head(repo.path())); + assert_eq!(receipt.events[3].kind, "MEMORY_CANDIDATE_VERIFIED"); + assert_eq!(receipt.events.last().unwrap().kind, "DORMANT"); + assert!(!receipt.source_checkpoint_hash.is_empty()); + assert!(!receipt.source_event_hash.is_empty()); + assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); + require_clean_repository(repo.path()).unwrap(); + } + + #[test] + fn rejects_a_corrupted_memory_source_chain_and_releases_the_primary() { + let repo = persona_repo(); + declare_memory_organ(repo.path()); + let runtime = tempfile::TempDir::new().unwrap(); + prepare_wake_at( + runtime.path(), + wake_input(repo.path()), + "PNCC-CORRUPT-SOURCE", + "2026-08-11T00:00:00.000Z", + ) + .unwrap(); + run_fact_task_at( + runtime.path(), + fact_task_input("PNCC-CORRUPT-SOURCE"), + "2026-08-11T00:00:01.000Z", + |_, _| { + Ok(r#"{"summary":"Verified candidate.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into()) + }, + ) + .unwrap(); + let source_journal = runtime + .path() + .join("sessions/PNCC-CORRUPT-SOURCE/events.jsonl"); + let source_events = fs::read_to_string(&source_journal).unwrap(); + fs::write( + &source_journal, + source_events.replacen("\"eventHash\":\"", "\"eventHash\":\"00", 1), + ) + .unwrap(); + + let mut memory_wake = wake_input(repo.path()); + memory_wake.organ_id = "memory-metabolism.checkpoint".into(); + memory_wake.expected_head = head(repo.path()); + prepare_wake_at( + runtime.path(), + memory_wake, + "PNCC-CORRUPT-MEMORY", + "2026-08-11T00:00:02.000Z", + ) + .unwrap(); + let error = run_memory_metabolism_at( + runtime.path(), + PersonaMemoryMetabolismInput { + session_id: "PNCC-CORRUPT-MEMORY".into(), + source_session_id: "PNCC-CORRUPT-SOURCE".into(), + }, + "2026-08-11T00:00:03.000Z", + ) + .unwrap_err(); + assert!(error.contains("EVENT_HASH_INVALID")); + assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); + let record = load_session_record(runtime.path(), "PNCC-CORRUPT-MEMORY").unwrap(); + assert_eq!(record.state, "DORMANT_AFTER_FAILURE"); + require_clean_repository(repo.path()).unwrap(); + } + #[test] fn rejects_unscoped_model_evidence_and_releases_primary() { let repo = persona_repo(); diff --git a/routing/hololake-current-architecture.json b/routing/hololake-current-architecture.json index 5b58d53..169e8c9 100644 --- a/routing/hololake-current-architecture.json +++ b/routing/hololake-current-architecture.json @@ -118,7 +118,7 @@ "human_projection": "HOLOLAKE_LIVE_READ_MODEL", "forgejo_role": "OPTIONAL_COMPATIBILITY_COLLABORATION_ADAPTER", "runtime_implemented": true, - "runtime_scope": "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_AND_DURABLE_VERIFIED_SESSION_QUERY_SOURCE_IMPLEMENTED_AND_TESTED", + "runtime_scope": "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_DURABLE_VERIFIED_SESSION_QUERY_AND_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_SOURCE_IMPLEMENTED_AND_TESTED", "desktop_integrated": false, "development_id": "DEV-20260810-014" }, @@ -343,7 +343,7 @@ "DEV-20260810-013" ], "closeout_record": "HLP-DEV-20260809-007-CLOSEOUT-001", - "next_minimum_stage": "GH_PNCC_INDEPENDENT_MEMORY_METABOLISM_EXECUTOR_WITHOUT_UI", + "next_minimum_stage": "GH_PNCC_MEMORY_FAILURE_CLOSURE_AND_RUNTIME_COMMAND_INTEGRATION_WITHOUT_UI_OR_EXECUTION_LIMB", "next_stage_started": true, "heartbeat_automation": "pncc", "heartbeat_state": "ACTIVE_EVERY_10_MINUTES_UNTIL_TASK_TERMINAL" diff --git a/routing/hololake-persona-native-code-channel.json b/routing/hololake-persona-native-code-channel.json index 6628df3..d853825 100644 --- a/routing/hololake-persona-native-code-channel.json +++ b/routing/hololake-persona-native-code-channel.json @@ -1,8 +1,8 @@ { "schema": "hololake.persona-native-code-channel/v1", "record_id": "HLP-PERSONA-NATIVE-CODE-CHANNEL-001", - "version": "2026-08-11.2", - "state": "CURRENT_FIRST_PRODUCT_CORE_READ_ONLY_RUNTIME_AND_INTERRUPTED_SESSION_RECOVERY_SOURCE_IMPLEMENTED", + "version": "2026-08-11.3", + "state": "CURRENT_FIRST_PRODUCT_CORE_FACT_AND_VERIFIED_MEMORY_RUNTIME_SOURCE_IMPLEMENTED", "development_id": "DEV-20260810-014", "product": { "formal_name_zh": "光湖人格原生代码频道", @@ -104,6 +104,7 @@ "interrupted_session_inspection_and_recovery_source_implemented": 100, "typed_organ_contract_and_manifest_inspection_source_implemented": 100, "durable_event_and_receipt_query_source_implemented": 100, + "independent_memory_metabolism_source_implemented": 100, "general_purpose_persona_runtime_implemented": 0, "human_live_projection_implemented": 0, "hololake_integrated": 0, diff --git a/routing/hololake-persona-native-code-channel.test.mjs b/routing/hololake-persona-native-code-channel.test.mjs index 42d8d70..7652104 100644 --- a/routing/hololake-persona-native-code-channel.test.mjs +++ b/routing/hololake-persona-native-code-channel.test.mjs @@ -81,12 +81,13 @@ test("the first source runtime cycle stays distinct from integration and deploym channel.truth.durable_event_and_receipt_query_source_implemented, 100, ); + assert.equal(channel.truth.independent_memory_metabolism_source_implemented, 100); assert.equal(channel.truth.general_purpose_persona_runtime_implemented, 0); assert.equal(channel.truth.human_live_projection_implemented, 0); assert.equal(architecture.persona_native_code_channel.runtime_implemented, true); assert.equal( architecture.persona_native_code_channel.runtime_scope, - "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_AND_DURABLE_VERIFIED_SESSION_QUERY_SOURCE_IMPLEMENTED_AND_TESTED", + "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_DURABLE_VERIFIED_SESSION_QUERY_AND_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_SOURCE_IMPLEMENTED_AND_TESTED", ); assert.equal(channel.truth.hololake_integrated, 0); assert.equal(channel.truth.artifact_built, 0);