fix: bind successful receipt organ mode

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:
铸渊 / ICE-P-ZY001 2026-08-11 08:10:14 +08:00
commit 47c8fc4751
7 changed files with 94 additions and 4 deletions

View file

@ -2729,6 +2729,11 @@ fn validate_persisted_lifecycle_terminal_evidence(
} else {
"PNCC-MEMORY-"
};
let expected_organ_mode = if completion_kind == Some("FACT_SENSE") {
"read-only"
} else {
"checkpoint-write"
};
let expected_attribution = serde_json::to_value(&record.attribution)
.map_err(|error| format!("PERSONA_LIFECYCLE_EVIDENCE_SERIALIZATION_FAILED: {error}"))?;
let expected_events = serde_json::to_value(events)
@ -2820,6 +2825,7 @@ fn validate_persisted_lifecycle_terminal_evidence(
== Some(false)
&& wake.get("activeOrgan").and_then(serde_json::Value::as_str)
== Some(record.active_organ.as_str())
&& wake.get("organMode").and_then(serde_json::Value::as_str) == Some(expected_organ_mode)
&& wake.get("attribution") == Some(&expected_attribution)
&& wake.get("receiptId").and_then(serde_json::Value::as_str)
== Some(expected_wake_receipt_id.as_str())
@ -4482,6 +4488,48 @@ mod tests {
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH"));
}
#[test]
fn rejects_a_rehashed_completed_receipt_with_forged_wake_organ_mode() {
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"]["organMode"] = "bounded-execution".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();