fix: bind successful receipt wake checkpoint
Human-Responsibility: ICE-GL∞ / 冰朔 Persona-Author: ICE-P-ZY001 / 铸渊 Execution-Runtime: Codex desktop / DEV-20260810-014 Development-ID: DEV-20260810-014 Authorization-Scope: GH-PNCC persona runtime source and tests only; no UI, deployment, or execution limb Source-Anchor: user instruction to continue GH-PNCC from repository facts and verifiable receipts
This commit is contained in:
parent
5630338285
commit
869611962e
7 changed files with 175 additions and 4 deletions
|
|
@ -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 和发布待验收 |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -431,6 +431,8 @@ struct PersonaSessionRecord {
|
|||
repository_path: String,
|
||||
git_head: String,
|
||||
brain_entry: String,
|
||||
#[serde(default)]
|
||||
wake_checkpoint_path: Option<String>,
|
||||
checkpoint_path: String,
|
||||
node_id: String,
|
||||
model_provider_id: String,
|
||||
|
|
@ -587,6 +589,22 @@ fn load_manifest(repository: &Path) -> Result<PersonaManifest, String> {
|
|||
Ok(manifest)
|
||||
}
|
||||
|
||||
fn load_manifest_at_git_head(repository: &Path, head: &str) -> Result<PersonaManifest, String> {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue