fix: bind successful lifecycle semantic evidence
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
85fc90bd52
commit
a6504ca749
6 changed files with 370 additions and 19 deletions
|
|
@ -0,0 +1,37 @@
|
|||
# GH-PNCC successful receipt semantic-evidence binding
|
||||
|
||||
- Development ID: `DEV-20260810-014`
|
||||
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
|
||||
- Human responsibility subject: `ICE-GL∞ / 冰朔`
|
||||
- Starting repository head: `85fc90bd52542a1e2ddf0a88f12c3f2fac6b7cb0`
|
||||
- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED`
|
||||
|
||||
## Corrected runtime fact
|
||||
|
||||
A digest can prove that a successful lifecycle payload agrees with the digest stored beside it. It cannot by
|
||||
itself prove that the payload still describes the verified persona session. The previous recovery paths only
|
||||
bound the successful receipt's session and persona identifiers, so a changed repository, attribution or final
|
||||
Git result could be accepted if the payload digest was recomputed.
|
||||
|
||||
Initial persistence, inspection, interrupted binding recovery and replay now bind successful receipts back to
|
||||
the verified session record and complete event journal. Lifecycle and completion schemas, canonical repository,
|
||||
wake identity, node, model instance, organ, complete human/persona attribution, completion kind, committed Git
|
||||
head, checkpoint, derived receipt identifiers and the complete event chain must agree.
|
||||
|
||||
## Verification
|
||||
|
||||
- Four regression tests first reproduced false acceptance after recomputing the payload digest.
|
||||
- Forged repository, malformed wake event evidence, forged completion attribution and forged committed Git
|
||||
evidence are now rejected with `PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH`.
|
||||
- Inspection and binding rejection leave the request fields unbound; replay never reruns the organ.
|
||||
- PNCC focused Rust tests: `41 passed, 0 failed`.
|
||||
- Full Rust suite: `1181 passed, 2 ignored`; integration test: `1 passed`.
|
||||
- Routing suite: `29 passed, 0 failed`; `cargo fmt`, strict clippy and diff checks passed.
|
||||
- Guanghu native authority: `PASS_100`.
|
||||
- GHNQG, publication and fresh-clone readback remain pending.
|
||||
|
||||
## Truth boundary
|
||||
|
||||
- This stage authenticates the meaning of an existing successful receipt against the existing session and event
|
||||
truth. It does not add a second truth store or reconstruct missing evidence.
|
||||
- It does not activate `EXECUTION_LIMB`, add UI, build an artifact or claim deployment/runtime health.
|
||||
|
|
@ -177,6 +177,7 @@ idempotent_replay_repository_state_revalidation_source_implemented: 100
|
|||
successful_completion_receipt_terminal_revalidation_source_implemented: 100
|
||||
safe_receipt_binding_terminal_revalidation_source_implemented: 100
|
||||
failure_receipt_terminal_evidence_binding_source_implemented: 100
|
||||
successful_receipt_semantic_evidence_binding_source_implemented: 100
|
||||
general_purpose_persona_runtime_implemented: 0
|
||||
human_live_projection_implemented: 0
|
||||
hololake_integrated: 0
|
||||
|
|
@ -204,6 +205,12 @@ runtime_health: 0
|
|||
事件链。即使有人同步重算回执哈希,伪造终态事件或人格作者也会失败关闭,且不会把请求字段
|
||||
写入会话记录。
|
||||
|
||||
成功回执同样不能把“载荷哈希一致”当成语义真实。首次落盘、检查、恢复绑定和重放现在会把
|
||||
生命周期 schema、规范仓库、唤醒身份、节点、模型实例、器官、完整双层归因、完成类型、最终
|
||||
Git 提交、检查点、回执编号和完整事件链重新绑定到已验证的会话记录。同步改写载荷与哈希不能
|
||||
伪造另一个仓库、人格作者或提交结果;任何语义证据不一致都以
|
||||
`PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH` 失败关闭。
|
||||
|
||||
成功回执与失败回执的每次重放都会重新读取规范仓库当前提交并检查工作树,而不是只信任回执
|
||||
生成时的状态。当前提交偏离会话记录或工作树变脏时,检查结果降级为人工复核,重放失败关闭,
|
||||
且不会重新启动器官。
|
||||
|
|
|
|||
|
|
@ -2667,21 +2667,139 @@ fn persisted_lifecycle_identity_matches(
|
|||
fn validate_persisted_lifecycle_terminal_evidence(
|
||||
persisted: &PersistedPersonaLifecycleReceipt,
|
||||
record: &PersonaSessionRecord,
|
||||
terminal_event: &PersonaLifecycleEvent,
|
||||
events: &[PersonaLifecycleEvent],
|
||||
) -> Result<(), String> {
|
||||
if persisted.outcome != "FAILED" {
|
||||
let terminal_event = events.last().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
|
||||
if persisted.outcome == "FAILED" {
|
||||
let failure = persisted
|
||||
.failure
|
||||
.as_ref()
|
||||
.ok_or("PERSONA_LIFECYCLE_FAILURE_RECEIPT_MISSING")?;
|
||||
if failure.repository_path != record.repository_path
|
||||
|| failure.terminal_event_hash != terminal_event.event_hash
|
||||
|| failure.attribution != record.attribution
|
||||
|| lifecycle_failure_code(&failure.error_code) != failure.error_code
|
||||
{
|
||||
return Err("PERSONA_LIFECYCLE_FAILURE_TERMINAL_EVIDENCE_MISMATCH".into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
let failure = persisted
|
||||
.failure
|
||||
.as_ref()
|
||||
.ok_or("PERSONA_LIFECYCLE_FAILURE_RECEIPT_MISSING")?;
|
||||
if failure.repository_path != record.repository_path
|
||||
|| failure.terminal_event_hash != terminal_event.event_hash
|
||||
|| failure.attribution != record.attribution
|
||||
|| lifecycle_failure_code(&failure.error_code) != failure.error_code
|
||||
{
|
||||
return Err("PERSONA_LIFECYCLE_FAILURE_TERMINAL_EVIDENCE_MISMATCH".into());
|
||||
if persisted.outcome != "COMPLETED" {
|
||||
return Err("PERSONA_LIFECYCLE_RECEIPT_OUTCOME_INVALID".into());
|
||||
}
|
||||
|
||||
let lifecycle = &persisted.lifecycle;
|
||||
let wake = lifecycle
|
||||
.get("wakeReceipt")
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let completion = lifecycle
|
||||
.get("completion")
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let completion_kind = completion.get("kind").and_then(serde_json::Value::as_str);
|
||||
let completion_receipt = completion
|
||||
.get("receipt")
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let expected_completion_schema = match completion_kind {
|
||||
Some("FACT_SENSE") => "hololake.pncc-fact-task-receipt/v1",
|
||||
Some("MEMORY_METABOLISM") => "hololake.pncc-memory-metabolism-receipt/v1",
|
||||
_ => return Err("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".into()),
|
||||
};
|
||||
let expected_receipt_prefix = if completion_kind == Some("FACT_SENSE") {
|
||||
"PNCC-TASK-"
|
||||
} else {
|
||||
"PNCC-MEMORY-"
|
||||
};
|
||||
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)
|
||||
.map_err(|error| format!("PERSONA_LIFECYCLE_EVIDENCE_SERIALIZATION_FAILED: {error}"))?;
|
||||
let wake_events = wake
|
||||
.get("events")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let expected_wake_events = expected_events
|
||||
.as_array()
|
||||
.and_then(|all| all.get(..wake_events.len()))
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let wake_terminal_hash = wake_events
|
||||
.last()
|
||||
.and_then(|event| event.get("eventHash"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let wake_receipt_suffix = wake_terminal_hash
|
||||
.get(..20)
|
||||
.ok_or("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH")?;
|
||||
let expected_wake_receipt_id = format!("PNCC-WAKE-{wake_receipt_suffix}");
|
||||
let expected_completion_receipt_id = format!(
|
||||
"{}{}",
|
||||
expected_receipt_prefix,
|
||||
&terminal_event.event_hash[..20]
|
||||
);
|
||||
let first_event = events.first().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
|
||||
let matches = lifecycle.get("schema").and_then(serde_json::Value::as_str)
|
||||
== Some("hololake.pncc-lifecycle-run-receipt/v1")
|
||||
&& lifecycle
|
||||
.get("repositoryPath")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.repository_path.as_str())
|
||||
&& wake.get("schema").and_then(serde_json::Value::as_str)
|
||||
== Some("hololake.pncc-wake-receipt/v1")
|
||||
&& wake.get("sessionId").and_then(serde_json::Value::as_str)
|
||||
== Some(record.session_id.as_str())
|
||||
&& wake.get("personaId").and_then(serde_json::Value::as_str)
|
||||
== Some(record.persona_id.as_str())
|
||||
&& wake
|
||||
.get("repositoryPath")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.repository_path.as_str())
|
||||
&& wake.get("gitHead").and_then(serde_json::Value::as_str)
|
||||
== Some(first_event.git_head.as_str())
|
||||
&& wake.get("nodeId").and_then(serde_json::Value::as_str) == Some(record.node_id.as_str())
|
||||
&& wake
|
||||
.get("modelInstanceId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.model_instance_id.as_str())
|
||||
&& wake.get("activeOrgan").and_then(serde_json::Value::as_str)
|
||||
== Some(record.active_organ.as_str())
|
||||
&& wake.get("attribution") == Some(&expected_attribution)
|
||||
&& wake.get("receiptId").and_then(serde_json::Value::as_str)
|
||||
== Some(expected_wake_receipt_id.as_str())
|
||||
&& wake_events.as_slice() == expected_wake_events
|
||||
&& completion_receipt
|
||||
.get("schema")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(expected_completion_schema)
|
||||
&& completion_receipt
|
||||
.get("sessionId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.session_id.as_str())
|
||||
&& completion_receipt
|
||||
.get("personaId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.persona_id.as_str())
|
||||
&& completion_receipt
|
||||
.get("committedGitHead")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.git_head.as_str())
|
||||
&& completion_receipt
|
||||
.get("checkpointPath")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(record.checkpoint_path.as_str())
|
||||
&& completion_receipt
|
||||
.get("runtimeState")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some("DORMANT")
|
||||
&& completion_receipt
|
||||
.get("activeOrgan")
|
||||
.is_some_and(serde_json::Value::is_null)
|
||||
&& completion_receipt.get("attribution") == Some(&expected_attribution)
|
||||
&& completion_receipt.get("events") == Some(&expected_events)
|
||||
&& completion_receipt
|
||||
.get("receiptId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
== Some(expected_completion_receipt_id.as_str());
|
||||
if !matches {
|
||||
return Err("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2760,7 +2878,7 @@ fn verified_lifecycle_replay(
|
|||
{
|
||||
return Err("PERSONA_LIFECYCLE_REQUEST_INCOMPLETE_REQUIRES_RECOVERY".into());
|
||||
}
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, terminal_event)?;
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, &events)?;
|
||||
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
|
||||
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
||||
}
|
||||
|
|
@ -2847,7 +2965,7 @@ fn inspect_lifecycle_request_at(
|
|||
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
||||
}
|
||||
let terminal_event = events.last().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, terminal_event)?;
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, &events)?;
|
||||
let complete = record.request_id.as_deref() == Some(request_id.as_str())
|
||||
&& record.request_fingerprint.as_deref() == Some(request_fingerprint.as_str())
|
||||
&& record.lifecycle_receipt_hash.as_deref()
|
||||
|
|
@ -2953,7 +3071,7 @@ fn bind_persisted_lifecycle_receipt_at(
|
|||
}
|
||||
let events = verify_event_journal(runtime_root, &record)?;
|
||||
let terminal_event = events.last().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, terminal_event)?;
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, &events)?;
|
||||
let expected_state = if persisted.outcome == "FAILED" {
|
||||
"DORMANT_AFTER_FAILURE"
|
||||
} else {
|
||||
|
|
@ -3101,6 +3219,7 @@ fn persist_successful_lifecycle_receipt(
|
|||
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
|
||||
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
||||
}
|
||||
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, &events)?;
|
||||
// Persist the immutable full receipt before binding it into the mutable session record. A
|
||||
// crash between these atomic writes leaves a verifiable, explicitly recoverable state.
|
||||
write_json_file(
|
||||
|
|
@ -4081,6 +4200,189 @@ mod tests {
|
|||
assert!(!after.safe_to_bind_receipt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_a_rehashed_completed_receipt_with_the_wrong_repository() {
|
||||
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["repositoryPath"] = "/forged/repository".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"));
|
||||
let record = load_session_record(runtime.path(), session_id).unwrap();
|
||||
assert!(record.request_id.is_none());
|
||||
assert!(record.lifecycle_receipt_hash.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_a_rehashed_completed_receipt_with_a_malformed_wake_event_hash() {
|
||||
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"]["events"][2]["eventHash"] = "short".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 refuses_to_bind_rehashed_completed_attribution_changed_after_inspection() {
|
||||
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 inspection = inspect_lifecycle_request_at(runtime.path(), &input).unwrap();
|
||||
assert!(inspection.safe_to_bind_receipt);
|
||||
|
||||
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["completion"]["receipt"]["attribution"]["personaCognitiveAuthor"] =
|
||||
"FORGED-PERSONA".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 =
|
||||
bind_persisted_lifecycle_receipt_at(runtime.path(), &input, &inspection).unwrap_err();
|
||||
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH"));
|
||||
let record = load_session_record(runtime.path(), session_id).unwrap();
|
||||
assert!(record.request_id.is_none());
|
||||
assert!(record.request_fingerprint.is_none());
|
||||
assert!(record.lifecycle_receipt_hash.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refuses_to_replay_rehashed_completed_git_evidence_bound_into_the_session() {
|
||||
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":"Bound receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let session_id = first.lifecycle["sessionId"].as_str().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["completion"]["receipt"]["committedGitHead"] = "0".repeat(40).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 mut record = load_session_record(runtime.path(), session_id).unwrap();
|
||||
record.lifecycle_receipt_hash = Some(persisted.lifecycle_receipt_hash);
|
||||
write_session_record(runtime.path(), &record).unwrap();
|
||||
|
||||
let error = run_idempotent_lifecycle_at(
|
||||
runtime.path(),
|
||||
input,
|
||||
"2026-08-11T00:00:02.000Z",
|
||||
"2026-08-11T00:00:03.000Z",
|
||||
|_, _, _| panic!("forged completion evidence must not rerun the organ"),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refuses_to_bind_a_receipt_tampered_after_safe_inspection() {
|
||||
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_AND_FAILURE_RECEIPT_TERMINAL_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_AND_SUCCESS_RECEIPT_SEMANTIC_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.12",
|
||||
"state": "CURRENT_FIRST_PRODUCT_CORE_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SOURCE_IMPLEMENTED",
|
||||
"version": "2026-08-11.13",
|
||||
"state": "CURRENT_FIRST_PRODUCT_CORE_SUCCESS_RECEIPT_SEMANTIC_EVIDENCE_BINDING_SOURCE_IMPLEMENTED",
|
||||
"development_id": "DEV-20260810-014",
|
||||
"product": {
|
||||
"formal_name_zh": "光湖人格原生代码频道",
|
||||
|
|
@ -114,6 +114,7 @@
|
|||
"successful_completion_receipt_terminal_revalidation_source_implemented": 100,
|
||||
"safe_receipt_binding_terminal_revalidation_source_implemented": 100,
|
||||
"failure_receipt_terminal_evidence_binding_source_implemented": 100,
|
||||
"successful_receipt_semantic_evidence_binding_source_implemented": 100,
|
||||
"general_purpose_persona_runtime_implemented": 0,
|
||||
"human_live_projection_implemented": 0,
|
||||
"hololake_integrated": 0,
|
||||
|
|
|
|||
|
|
@ -118,12 +118,16 @@ test("the first source runtime cycle stays distinct from integration and deploym
|
|||
channel.truth.failure_receipt_terminal_evidence_binding_source_implemented,
|
||||
100,
|
||||
);
|
||||
assert.equal(
|
||||
channel.truth.successful_receipt_semantic_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_AND_FAILURE_RECEIPT_TERMINAL_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_AND_SUCCESS_RECEIPT_SEMANTIC_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