diff --git a/engineering/INDEX.md b/engineering/INDEX.md index 920c2c8..ebbaad3 100644 --- a/engineering/INDEX.md +++ b/engineering/INDEX.md @@ -33,6 +33,7 @@ Windows / macOS / Linux 构建机与安装包 | 时间 | 版本 | 记录 | 状态 | | --- | --- | --- | --- | | 2026-08-11 | GH-PNCC 成功回执大脑入口证据绑定 | [从已验证会话重建并核对真实大脑入口](operations/2026-08-11-hololake-pncc-success-receipt-brain-entry-evidence-binding.md) | 本地源码、完整 Rust/前端/路由测试、原生权威与严格 clippy 已通过;GHNQG 和发布待验收 | +| 2026-08-11 | GH-PNCC 成功回执唤醒检查点证据绑定 | [把唤醒时检查点路径绑定到不可变会话证据](operations/2026-08-11-hololake-pncc-success-receipt-wake-checkpoint-evidence-binding.md) | 本地源码、完整 Rust/前端/路由测试、原生权威与严格 clippy 已通过;GHNQG 和发布待验收 | | 2026-08-11 | GH-PNCC 安全回执绑定终态复核 | [检查后、绑定前重新验证回执与人格 Git 终态](operations/2026-08-11-hololake-pncc-safe-receipt-binding-terminal-revalidation.md) | 本地源码、完整 Rust/路由测试与严格 clippy 已通过;GHNQG 和发布待验收 | | 2026-08-11 | GH-PNCC 成功回执终态复核 | [成功回执持久化前重新验证终态证据](operations/2026-08-11-hololake-pncc-success-receipt-terminal-revalidation.md) | 本地源码、完整 Rust/路由测试与严格 clippy 已通过;GHNQG 和发布待验收 | | 2026-08-11 | GH-PNCC 重放仓库复核 | [幂等重放时重新验证人格 Git 状态](operations/2026-08-11-hololake-pncc-replay-repository-state-revalidation.md) | 本地源码、完整 Rust/路由测试与严格 clippy 已通过;GHNQG 和发布待验收 | diff --git a/engineering/operations/2026-08-11-hololake-pncc-success-receipt-wake-checkpoint-evidence-binding.md b/engineering/operations/2026-08-11-hololake-pncc-success-receipt-wake-checkpoint-evidence-binding.md new file mode 100644 index 0000000..b1ab88b --- /dev/null +++ b/engineering/operations/2026-08-11-hololake-pncc-success-receipt-wake-checkpoint-evidence-binding.md @@ -0,0 +1,38 @@ +# GH-PNCC successful receipt wake-checkpoint evidence binding + +- Development ID: `DEV-20260810-014` +- Persona cognitive author: `ICE-P-ZY001 / 铸渊` +- Human responsibility subject: `ICE-GL∞ / 冰朔` +- Starting repository head: `56303382856de025199e75fdffa57be7507bb418` +- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED` + +## Corrected runtime fact + +The successful lifecycle receipt already rebound the brain entry to the verified session, but the wake +receipt's `checkpointPath` was not independently rebound. A modified receipt could therefore name another +checkpoint path after recomputing its payload digest. + +New sessions now retain the exact canonical checkpoint path observed at wake as immutable session evidence. +Inspection, interrupted binding and replay require the persisted wake receipt to name that same path. Legacy +session records without the new field derive it from the persona manifest at the hash-chained wake Git head; +if that evidence cannot be reconstructed, validation fails closed. + +## Verification + +- A regression test first reproduced acceptance of a forged wake `checkpointPath` after recomputing the + payload digest. +- The same test passes after immutable wake-checkpoint evidence binding was added. +- A compatibility regression proves a legacy session without the new field reconstructs the same evidence from + the hash-chained wake Git head. +- PNCC focused Rust tests: `44 passed, 0 failed`. +- Full Rust suite: `1184 passed, 2 ignored`; integration test: `1 passed`. +- Routing suite: `29 passed, 0 failed`; formatting, strict clippy and diff checks passed. +- Frontend lint and TypeScript checks passed; full Vitest suite: `5008 passed, 0 failed`. +- Guanghu native authority: `PASS_100`; native core: `15 passed` with 100% lines and functions. +- GHNQG, publication and fresh-clone readback remain pending. + +## Truth boundary + +- This stage authenticates which existing checkpoint file was mounted when the persona woke. +- It does not change the promoted completion checkpoint, expose hidden reasoning, activate an execution limb, + add UI, build an artifact or claim deployment. 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 a8b8081..4c6b78b 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 @@ -179,6 +179,7 @@ safe_receipt_binding_terminal_revalidation_source_implemented: 100 failure_receipt_terminal_evidence_binding_source_implemented: 100 successful_receipt_semantic_evidence_binding_source_implemented: 100 successful_receipt_brain_entry_evidence_binding_source_implemented: 100 +successful_receipt_wake_checkpoint_evidence_binding_source_implemented: 100 general_purpose_persona_runtime_implemented: 0 human_live_projection_implemented: 0 hololake_integrated: 0 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 7e3df37..597f07f 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 @@ -431,6 +431,8 @@ struct PersonaSessionRecord { repository_path: String, git_head: String, brain_entry: String, + #[serde(default)] + wake_checkpoint_path: Option, checkpoint_path: String, node_id: String, model_provider_id: String, @@ -587,6 +589,22 @@ fn load_manifest(repository: &Path) -> Result { Ok(manifest) } +fn load_manifest_at_git_head(repository: &Path, head: &str) -> Result { + let head = validated_head(head)?; + let object = format!("{head}:{MANIFEST_PATH}"); + let bytes = git_output( + repository, + &["show", &object], + "PERSONA_MANIFEST_AT_GIT_HEAD", + )?; + let manifest: PersonaManifest = serde_json::from_str(&bytes) + .map_err(|error| format!("PERSONA_MANIFEST_AT_GIT_HEAD_INVALID: {error}"))?; + if manifest.schema != "hololake.persona/v1" { + return Err("PERSONA_MANIFEST_SCHEMA_UNSUPPORTED".into()); + } + Ok(manifest) +} + fn validate_attribution( attribution: &PersonaAttribution, manifest: &PersonaManifest, @@ -1294,6 +1312,7 @@ fn prepare_wake_at( repository_path: repository.to_string_lossy().into_owned(), git_head: git_head.clone(), brain_entry: manifest.brain_entry.clone(), + wake_checkpoint_path: Some(checkpoint.to_string_lossy().into_owned()), checkpoint_path: manifest.current_checkpoint.clone(), node_id: node_id.clone(), model_provider_id, @@ -2740,6 +2759,26 @@ fn validate_persisted_lifecycle_terminal_evidence( repository_file(Path::new(&record.repository_path), &record.brain_entry) .map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())?; let expected_brain_entry = expected_brain_entry.to_string_lossy(); + let expected_wake_checkpoint = match record.wake_checkpoint_path.as_ref() { + Some(path) => path.clone(), + None => { + let wake_manifest = load_manifest_at_git_head( + Path::new(&record.repository_path), + &first_event.git_head, + ) + .map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())?; + if wake_manifest.persona_id != record.persona_id { + return Err("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".into()); + } + repository_file( + Path::new(&record.repository_path), + &wake_manifest.current_checkpoint, + ) + .map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())? + .to_string_lossy() + .into_owned() + } + }; let matches = lifecycle.get("schema").and_then(serde_json::Value::as_str) == Some("hololake.pncc-lifecycle-run-receipt/v1") && lifecycle @@ -2760,6 +2799,10 @@ fn validate_persisted_lifecycle_terminal_evidence( == Some(first_event.git_head.as_str()) && wake.get("brainEntry").and_then(serde_json::Value::as_str) == Some(expected_brain_entry.as_ref()) + && wake + .get("checkpointPath") + .and_then(serde_json::Value::as_str) + == Some(expected_wake_checkpoint.as_str()) && wake.get("nodeId").and_then(serde_json::Value::as_str) == Some(record.node_id.as_str()) && wake .get("modelInstanceId") @@ -4293,6 +4336,89 @@ mod tests { assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")); } + #[test] + fn rejects_a_rehashed_completed_receipt_with_a_forged_wake_checkpoint_path() { + let repo = persona_repo(); + let runtime = tempfile::TempDir::new().unwrap(); + let input = lifecycle_fact_input(repo.path()); + let first = run_idempotent_lifecycle_at( + runtime.path(), + input.clone(), + "2026-08-11T00:00:00.000Z", + "2026-08-11T00:00:01.000Z", + |runtime_root, input, timestamp| { + run_fact_task_at(runtime_root, input, timestamp, |_, _| { + Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into()) + }) + }, + ) + .unwrap(); + let session_id = first.lifecycle["sessionId"].as_str().unwrap(); + let mut record = load_session_record(runtime.path(), session_id).unwrap(); + record.request_id = None; + record.request_fingerprint = None; + record.lifecycle_receipt_hash = None; + write_session_record(runtime.path(), &record).unwrap(); + + let receipt_path = session_directory(runtime.path(), session_id) + .unwrap() + .join("lifecycle-receipt.json"); + let mut persisted: PersistedPersonaLifecycleReceipt = + serde_json::from_slice(&fs::read(&receipt_path).unwrap()).unwrap(); + persisted.lifecycle["wakeReceipt"]["checkpointPath"] = "/forged/CURRENT.hdlp".into(); + persisted.lifecycle_receipt_hash = + hex_digest(&persisted_lifecycle_payload_bytes(&persisted).unwrap()); + fs::write( + &receipt_path, + serde_json::to_vec_pretty(&persisted).unwrap(), + ) + .unwrap(); + + let error = inspect_lifecycle_request_at(runtime.path(), &input).unwrap_err(); + assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")); + } + + #[test] + fn reconstructs_legacy_wake_checkpoint_evidence_from_the_wake_git_head() { + let repo = persona_repo(); + let runtime = tempfile::TempDir::new().unwrap(); + let input = lifecycle_fact_input(repo.path()); + let first = run_idempotent_lifecycle_at( + runtime.path(), + input.clone(), + "2026-08-11T00:00:00.000Z", + "2026-08-11T00:00:01.000Z", + |runtime_root, input, timestamp| { + run_fact_task_at(runtime_root, input, timestamp, |_, _| { + Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into()) + }) + }, + ) + .unwrap(); + let session_id = first.lifecycle["sessionId"].as_str().unwrap(); + let session_path = session_directory(runtime.path(), session_id) + .unwrap() + .join("session.json"); + let mut legacy_record: serde_json::Value = + serde_json::from_slice(&fs::read(&session_path).unwrap()).unwrap(); + legacy_record + .as_object_mut() + .unwrap() + .remove("wakeCheckpointPath"); + legacy_record["requestId"] = serde_json::Value::Null; + legacy_record["requestFingerprint"] = serde_json::Value::Null; + legacy_record["lifecycleReceiptHash"] = serde_json::Value::Null; + fs::write( + &session_path, + serde_json::to_vec_pretty(&legacy_record).unwrap(), + ) + .unwrap(); + + let inspection = inspect_lifecycle_request_at(runtime.path(), &input).unwrap(); + assert_eq!(inspection.status, "SAFE_BIND_PERSISTED_RECEIPT"); + assert!(inspection.safe_to_bind_receipt); + } + #[test] fn rejects_a_rehashed_completed_receipt_with_a_malformed_wake_event_hash() { let repo = persona_repo(); diff --git a/routing/hololake-current-architecture.json b/routing/hololake-current-architecture.json index a7998be..6b6b984 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_DURABLE_VERIFIED_SESSION_QUERY_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_CHECKED_FAILURE_CLOSURE_NONBLOCKING_RUNTIME_COMMAND_SAFE_ORGAN_LIFECYCLE_COORDINATOR_IDEMPOTENT_SUCCESS_AND_TERMINAL_FAILURE_RECEIPT_REPLAY_SAFE_RECEIPT_BINDING_RECOVERY_REPLAY_TIME_REPOSITORY_STATE_REVALIDATION_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SUCCESS_RECEIPT_SEMANTIC_EVIDENCE_BINDING_AND_SUCCESS_RECEIPT_BRAIN_ENTRY_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED", + "runtime_scope": "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_DURABLE_VERIFIED_SESSION_QUERY_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_CHECKED_FAILURE_CLOSURE_NONBLOCKING_RUNTIME_COMMAND_SAFE_ORGAN_LIFECYCLE_COORDINATOR_IDEMPOTENT_SUCCESS_AND_TERMINAL_FAILURE_RECEIPT_REPLAY_SAFE_RECEIPT_BINDING_RECOVERY_REPLAY_TIME_REPOSITORY_STATE_REVALIDATION_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SUCCESS_RECEIPT_SEMANTIC_EVIDENCE_BINDING_SUCCESS_RECEIPT_BRAIN_ENTRY_EVIDENCE_BINDING_AND_SUCCESS_RECEIPT_WAKE_CHECKPOINT_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED", "desktop_integrated": false, "development_id": "DEV-20260810-014" }, diff --git a/routing/hololake-persona-native-code-channel.json b/routing/hololake-persona-native-code-channel.json index c1b8faf..3879875 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.14", - "state": "CURRENT_FIRST_PRODUCT_CORE_SUCCESS_RECEIPT_BRAIN_ENTRY_EVIDENCE_BINDING_SOURCE_IMPLEMENTED", + "version": "2026-08-11.15", + "state": "CURRENT_FIRST_PRODUCT_CORE_SUCCESS_RECEIPT_WAKE_CHECKPOINT_EVIDENCE_BINDING_SOURCE_IMPLEMENTED", "development_id": "DEV-20260810-014", "product": { "formal_name_zh": "光湖人格原生代码频道", @@ -116,6 +116,7 @@ "failure_receipt_terminal_evidence_binding_source_implemented": 100, "successful_receipt_semantic_evidence_binding_source_implemented": 100, "successful_receipt_brain_entry_evidence_binding_source_implemented": 100, + "successful_receipt_wake_checkpoint_evidence_binding_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 ca74e5b..fa1fd68 100644 --- a/routing/hololake-persona-native-code-channel.test.mjs +++ b/routing/hololake-persona-native-code-channel.test.mjs @@ -126,12 +126,16 @@ test("the first source runtime cycle stays distinct from integration and deploym channel.truth.successful_receipt_brain_entry_evidence_binding_source_implemented, 100, ); + assert.equal( + channel.truth.successful_receipt_wake_checkpoint_evidence_binding_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_DURABLE_VERIFIED_SESSION_QUERY_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_CHECKED_FAILURE_CLOSURE_NONBLOCKING_RUNTIME_COMMAND_SAFE_ORGAN_LIFECYCLE_COORDINATOR_IDEMPOTENT_SUCCESS_AND_TERMINAL_FAILURE_RECEIPT_REPLAY_SAFE_RECEIPT_BINDING_RECOVERY_REPLAY_TIME_REPOSITORY_STATE_REVALIDATION_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SUCCESS_RECEIPT_SEMANTIC_EVIDENCE_BINDING_AND_SUCCESS_RECEIPT_BRAIN_ENTRY_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED", + "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_DURABLE_VERIFIED_SESSION_QUERY_INDEPENDENT_VERIFIED_MEMORY_METABOLISM_CHECKED_FAILURE_CLOSURE_NONBLOCKING_RUNTIME_COMMAND_SAFE_ORGAN_LIFECYCLE_COORDINATOR_IDEMPOTENT_SUCCESS_AND_TERMINAL_FAILURE_RECEIPT_REPLAY_SAFE_RECEIPT_BINDING_RECOVERY_REPLAY_TIME_REPOSITORY_STATE_REVALIDATION_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SUCCESS_RECEIPT_SEMANTIC_EVIDENCE_BINDING_SUCCESS_RECEIPT_BRAIN_ENTRY_EVIDENCE_BINDING_AND_SUCCESS_RECEIPT_WAKE_CHECKPOINT_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED", ); assert.equal(channel.truth.hololake_integrated, 0); assert.equal(channel.truth.artifact_built, 0);