fix(pncc): bind failure receipts to terminal 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
21d120d7b5
commit
85fc90bd52
7 changed files with 278 additions and 10 deletions
|
|
@ -0,0 +1,36 @@
|
||||||
|
# GH-PNCC failure receipt terminal-evidence binding
|
||||||
|
|
||||||
|
- Development ID: `DEV-20260810-014`
|
||||||
|
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
|
||||||
|
- Human responsibility subject: `ICE-GL∞ / 冰朔`
|
||||||
|
- Starting repository head: `21d120d7b5b929ad639f8035c15dd393b4be4d08`
|
||||||
|
- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED`
|
||||||
|
|
||||||
|
## Corrected runtime fact
|
||||||
|
|
||||||
|
A digest proves that a persisted failure payload has not changed relative to the digest stored beside it. It
|
||||||
|
does not prove that the payload still describes the terminal session evidence. The previous inspection path
|
||||||
|
accepted a failure receipt whose terminal event hash or attribution had been changed when the payload digest
|
||||||
|
was recomputed.
|
||||||
|
|
||||||
|
Inspection, interrupted binding recovery and replay now bind a failed lifecycle receipt back to the verified
|
||||||
|
session record and final event. The canonical repository path, terminal event hash, complete human/persona
|
||||||
|
attribution and stable machine error code must all agree before the receipt can be considered replayable or
|
||||||
|
safe to bind.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Two regression tests first reproduced the false acceptance with a recomputed payload digest.
|
||||||
|
- The forged terminal event and forged persona attribution are now rejected with
|
||||||
|
`PERSONA_LIFECYCLE_FAILURE_TERMINAL_EVIDENCE_MISMATCH`.
|
||||||
|
- Both rejection paths leave request id, request fingerprint and lifecycle receipt hash unbound.
|
||||||
|
- PNCC focused Rust tests: `37 passed, 0 failed`.
|
||||||
|
- Full Rust suite: `1177 passed, 2 ignored`; integration test: `1 passed`.
|
||||||
|
- Routing suite: `29 passed, 0 failed`; `cargo fmt`, strict clippy, diff checks and native authority passed.
|
||||||
|
- GHNQG, publication and fresh-clone readback remain pending.
|
||||||
|
|
||||||
|
## Truth boundary
|
||||||
|
|
||||||
|
- This stage authenticates the meaning of an existing failure receipt against its existing event and session
|
||||||
|
truth; it does not add a second truth store or repair forged evidence.
|
||||||
|
- It does not activate `EXECUTION_LIMB`, add UI, build an artifact or claim deployment/runtime health.
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
|
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
|
||||||
- Human responsibility subject: `ICE-GL∞ / 冰朔`
|
- Human responsibility subject: `ICE-GL∞ / 冰朔`
|
||||||
- Starting repository head: `e9ee0a8bb15447584a799d23c88653fe3ee3a53a`
|
- Starting repository head: `e9ee0a8bb15447584a799d23c88653fe3ee3a53a`
|
||||||
- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED`
|
- State: `PUBLISHED_AND_READ_BACK`
|
||||||
|
|
||||||
## Corrected runtime fact
|
## Corrected runtime fact
|
||||||
|
|
||||||
|
|
@ -25,7 +25,8 @@ request id, request fingerprint and lifecycle receipt hash written into the sess
|
||||||
- A receipt changed after safe inspection is rejected by its payload hash before session binding.
|
- A receipt changed after safe inspection is rejected by its payload hash before session binding.
|
||||||
- A persona Git advanced after safe inspection is rejected before session binding.
|
- A persona Git advanced after safe inspection is rejected before session binding.
|
||||||
- Both rejection paths leave request id, request fingerprint and lifecycle receipt hash unbound.
|
- Both rejection paths leave request id, request fingerprint and lifecycle receipt hash unbound.
|
||||||
- GHNQG, publication and fresh-clone readback remain pending.
|
- GHNQG passed twice for commit `21d120d7b5b929ad639f8035c15dd393b4be4d08`; the persistent receipt SHA-256 is `6847bd4d48ac0bf8d5d8c5dadb7194f96905af33d2d178fc74669c55a51a85f7`.
|
||||||
|
- Publication queue `PUB-20260810212655790-a9f1874c` completed, and a fresh clone read back the same commit and tree with strict `git fsck` and a clean worktree.
|
||||||
|
|
||||||
## Truth boundary
|
## Truth boundary
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ idempotent_terminal_failure_receipt_and_replay_source_implemented: 100
|
||||||
idempotent_replay_repository_state_revalidation_source_implemented: 100
|
idempotent_replay_repository_state_revalidation_source_implemented: 100
|
||||||
successful_completion_receipt_terminal_revalidation_source_implemented: 100
|
successful_completion_receipt_terminal_revalidation_source_implemented: 100
|
||||||
safe_receipt_binding_terminal_revalidation_source_implemented: 100
|
safe_receipt_binding_terminal_revalidation_source_implemented: 100
|
||||||
|
failure_receipt_terminal_evidence_binding_source_implemented: 100
|
||||||
general_purpose_persona_runtime_implemented: 0
|
general_purpose_persona_runtime_implemented: 0
|
||||||
human_live_projection_implemented: 0
|
human_live_projection_implemented: 0
|
||||||
hololake_integrated: 0
|
hololake_integrated: 0
|
||||||
|
|
@ -198,6 +199,11 @@ runtime_health: 0
|
||||||
终态、Git、租约、事件链和回执哈希全部复核后只重放原失败,不再次启动模型或器官。未完成
|
终态、Git、租约、事件链和回执哈希全部复核后只重放原失败,不再次启动模型或器官。未完成
|
||||||
闭合、脏仓库或仍持有租约的失败不能被伪装成终态回执。
|
闭合、脏仓库或仍持有租约的失败不能被伪装成终态回执。
|
||||||
|
|
||||||
|
失败回执不能只靠“载荷哈希能对上”证明语义真实。检查、恢复绑定和重放现在还会把失败回执
|
||||||
|
中的规范仓库、终态事件哈希、完整双层归因与稳定机器错误码重新绑定到当前会话记录及已验证
|
||||||
|
事件链。即使有人同步重算回执哈希,伪造终态事件或人格作者也会失败关闭,且不会把请求字段
|
||||||
|
写入会话记录。
|
||||||
|
|
||||||
成功回执与失败回执的每次重放都会重新读取规范仓库当前提交并检查工作树,而不是只信任回执
|
成功回执与失败回执的每次重放都会重新读取规范仓库当前提交并检查工作树,而不是只信任回执
|
||||||
生成时的状态。当前提交偏离会话记录或工作树变脏时,检查结果降级为人工复核,重放失败关闭,
|
生成时的状态。当前提交偏离会话记录或工作树变脏时,检查结果降级为人工复核,重放失败关闭,
|
||||||
且不会重新启动器官。
|
且不会重新启动器官。
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ struct PersonaModelBinding {
|
||||||
base_url: String,
|
base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PersonaAttribution {
|
pub struct PersonaAttribution {
|
||||||
pub human_responsibility_subject: String,
|
pub human_responsibility_subject: String,
|
||||||
|
|
@ -2664,6 +2664,28 @@ fn persisted_lifecycle_identity_matches(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_persisted_lifecycle_terminal_evidence(
|
||||||
|
persisted: &PersistedPersonaLifecycleReceipt,
|
||||||
|
record: &PersonaSessionRecord,
|
||||||
|
terminal_event: &PersonaLifecycleEvent,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if persisted.outcome != "FAILED" {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn lifecycle_failure_code(error: &str) -> String {
|
fn lifecycle_failure_code(error: &str) -> String {
|
||||||
let candidate = error.split([':', ';']).next().unwrap_or_default().trim();
|
let candidate = error.split([':', ';']).next().unwrap_or_default().trim();
|
||||||
if !candidate.is_empty()
|
if !candidate.is_empty()
|
||||||
|
|
@ -2731,12 +2753,14 @@ fn verified_lifecycle_replay(
|
||||||
} else {
|
} else {
|
||||||
"DORMANT"
|
"DORMANT"
|
||||||
};
|
};
|
||||||
|
let terminal_event = events.last().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
|
||||||
if record.state != expected_state
|
if record.state != expected_state
|
||||||
|| events.last().map(|event| event.kind.as_str()) != Some("DORMANT")
|
|| terminal_event.kind != "DORMANT"
|
||||||
|| primary_lease_held_by_session(runtime_root, &record)?
|
|| primary_lease_held_by_session(runtime_root, &record)?
|
||||||
{
|
{
|
||||||
return Err("PERSONA_LIFECYCLE_REQUEST_INCOMPLETE_REQUIRES_RECOVERY".into());
|
return Err("PERSONA_LIFECYCLE_REQUEST_INCOMPLETE_REQUIRES_RECOVERY".into());
|
||||||
}
|
}
|
||||||
|
validate_persisted_lifecycle_terminal_evidence(&persisted, &record, terminal_event)?;
|
||||||
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
|
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
|
||||||
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
||||||
}
|
}
|
||||||
|
|
@ -2822,6 +2846,8 @@ fn inspect_lifecycle_request_at(
|
||||||
if !persisted_lifecycle_identity_matches(&persisted, &session_id, &record.persona_id) {
|
if !persisted_lifecycle_identity_matches(&persisted, &session_id, &record.persona_id) {
|
||||||
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
|
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)?;
|
||||||
let complete = record.request_id.as_deref() == Some(request_id.as_str())
|
let complete = record.request_id.as_deref() == Some(request_id.as_str())
|
||||||
&& record.request_fingerprint.as_deref() == Some(request_fingerprint.as_str())
|
&& record.request_fingerprint.as_deref() == Some(request_fingerprint.as_str())
|
||||||
&& record.lifecycle_receipt_hash.as_deref()
|
&& record.lifecycle_receipt_hash.as_deref()
|
||||||
|
|
@ -2838,7 +2864,7 @@ fn inspect_lifecycle_request_at(
|
||||||
let repository_matches_record =
|
let repository_matches_record =
|
||||||
observed_head == record.git_head && require_clean_repository(&repository).is_ok();
|
observed_head == record.git_head && require_clean_repository(&repository).is_ok();
|
||||||
let safely_dormant = record.state == expected_state
|
let safely_dormant = record.state == expected_state
|
||||||
&& events.last().map(|event| event.kind.as_str()) == Some("DORMANT")
|
&& terminal_event.kind == "DORMANT"
|
||||||
&& !lease_held
|
&& !lease_held
|
||||||
&& repository_matches_record;
|
&& repository_matches_record;
|
||||||
let (status, safe_to_bind_receipt) = if complete && safely_dormant {
|
let (status, safe_to_bind_receipt) = if complete && safely_dormant {
|
||||||
|
|
@ -2926,6 +2952,8 @@ fn bind_persisted_lifecycle_receipt_at(
|
||||||
return Err("PERSONA_LIFECYCLE_RECEIPT_BINDING_CHANGED_REQUIRES_REINSPECTION".into());
|
return Err("PERSONA_LIFECYCLE_RECEIPT_BINDING_CHANGED_REQUIRES_REINSPECTION".into());
|
||||||
}
|
}
|
||||||
let events = verify_event_journal(runtime_root, &record)?;
|
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)?;
|
||||||
let expected_state = if persisted.outcome == "FAILED" {
|
let expected_state = if persisted.outcome == "FAILED" {
|
||||||
"DORMANT_AFTER_FAILURE"
|
"DORMANT_AFTER_FAILURE"
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2933,7 +2961,7 @@ fn bind_persisted_lifecycle_receipt_at(
|
||||||
};
|
};
|
||||||
let (_, observed_head) = exact_repository(&repository)?;
|
let (_, observed_head) = exact_repository(&repository)?;
|
||||||
if record.state != expected_state
|
if record.state != expected_state
|
||||||
|| events.last().map(|event| event.kind.as_str()) != Some("DORMANT")
|
|| terminal_event.kind != "DORMANT"
|
||||||
|| primary_lease_held_by_session(runtime_root, &record)?
|
|| primary_lease_held_by_session(runtime_root, &record)?
|
||||||
|| observed_head != record.git_head
|
|| observed_head != record.git_head
|
||||||
|| require_clean_repository(&repository).is_err()
|
|| require_clean_repository(&repository).is_err()
|
||||||
|
|
@ -4288,6 +4316,198 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_a_rehashed_failure_receipt_with_the_wrong_terminal_event() {
|
||||||
|
let repo = persona_repo();
|
||||||
|
let runtime = tempfile::TempDir::new().unwrap();
|
||||||
|
let input = lifecycle_fact_input(repo.path());
|
||||||
|
let failed = 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, |_, _| {
|
||||||
|
Err("bounded provider failure".into())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let session_id = failed.failure.as_ref().unwrap().session_id.clone();
|
||||||
|
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.failure.as_mut().unwrap().terminal_event_hash = "0".repeat(64);
|
||||||
|
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_FAILURE_TERMINAL_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 rejects_a_rehashed_failure_receipt_with_forged_attribution() {
|
||||||
|
let repo = persona_repo();
|
||||||
|
let runtime = tempfile::TempDir::new().unwrap();
|
||||||
|
let input = lifecycle_fact_input(repo.path());
|
||||||
|
let failed = 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, |_, _| {
|
||||||
|
Err("bounded provider failure".into())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let session_id = failed.failure.as_ref().unwrap().session_id.clone();
|
||||||
|
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
|
||||||
|
.failure
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.attribution
|
||||||
|
.persona_cognitive_author = "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 = inspect_lifecycle_request_at(runtime.path(), &input).unwrap_err();
|
||||||
|
assert!(error.contains("PERSONA_LIFECYCLE_FAILURE_TERMINAL_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_bind_rehashed_failure_evidence_changed_after_inspection() {
|
||||||
|
let repo = persona_repo();
|
||||||
|
let runtime = tempfile::TempDir::new().unwrap();
|
||||||
|
let input = lifecycle_fact_input(repo.path());
|
||||||
|
let failed = 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, |_, _| {
|
||||||
|
Err("bounded provider failure".into())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let session_id = failed.failure.as_ref().unwrap().session_id.clone();
|
||||||
|
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.failure.as_mut().unwrap().terminal_event_hash = "0".repeat(64);
|
||||||
|
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_FAILURE_TERMINAL_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_failure_evidence_bound_into_the_session() {
|
||||||
|
let repo = persona_repo();
|
||||||
|
let runtime = tempfile::TempDir::new().unwrap();
|
||||||
|
let input = lifecycle_fact_input(repo.path());
|
||||||
|
let failed = 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, |_, _| {
|
||||||
|
Err("bounded provider failure".into())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let session_id = failed.failure.as_ref().unwrap().session_id.clone();
|
||||||
|
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.failure.as_mut().unwrap().repository_path = "/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 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 terminal evidence must not rerun the organ"),
|
||||||
|
)
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(error.contains("PERSONA_LIFECYCLE_FAILURE_TERMINAL_EVIDENCE_MISMATCH"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn independently_promotes_only_the_current_verified_structured_checkpoint() {
|
fn independently_promotes_only_the_current_verified_structured_checkpoint() {
|
||||||
let repo = persona_repo();
|
let repo = persona_repo();
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@
|
||||||
"human_projection": "HOLOLAKE_LIVE_READ_MODEL",
|
"human_projection": "HOLOLAKE_LIVE_READ_MODEL",
|
||||||
"forgejo_role": "OPTIONAL_COMPATIBILITY_COLLABORATION_ADAPTER",
|
"forgejo_role": "OPTIONAL_COMPATIBILITY_COLLABORATION_ADAPTER",
|
||||||
"runtime_implemented": true,
|
"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_AND_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_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_AND_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED",
|
||||||
"desktop_integrated": false,
|
"desktop_integrated": false,
|
||||||
"development_id": "DEV-20260810-014"
|
"development_id": "DEV-20260810-014"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"schema": "hololake.persona-native-code-channel/v1",
|
"schema": "hololake.persona-native-code-channel/v1",
|
||||||
"record_id": "HLP-PERSONA-NATIVE-CODE-CHANNEL-001",
|
"record_id": "HLP-PERSONA-NATIVE-CODE-CHANNEL-001",
|
||||||
"version": "2026-08-11.11",
|
"version": "2026-08-11.12",
|
||||||
"state": "CURRENT_FIRST_PRODUCT_CORE_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_SOURCE_IMPLEMENTED",
|
"state": "CURRENT_FIRST_PRODUCT_CORE_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SOURCE_IMPLEMENTED",
|
||||||
"development_id": "DEV-20260810-014",
|
"development_id": "DEV-20260810-014",
|
||||||
"product": {
|
"product": {
|
||||||
"formal_name_zh": "光湖人格原生代码频道",
|
"formal_name_zh": "光湖人格原生代码频道",
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
"idempotent_replay_repository_state_revalidation_source_implemented": 100,
|
"idempotent_replay_repository_state_revalidation_source_implemented": 100,
|
||||||
"successful_completion_receipt_terminal_revalidation_source_implemented": 100,
|
"successful_completion_receipt_terminal_revalidation_source_implemented": 100,
|
||||||
"safe_receipt_binding_terminal_revalidation_source_implemented": 100,
|
"safe_receipt_binding_terminal_revalidation_source_implemented": 100,
|
||||||
|
"failure_receipt_terminal_evidence_binding_source_implemented": 100,
|
||||||
"general_purpose_persona_runtime_implemented": 0,
|
"general_purpose_persona_runtime_implemented": 0,
|
||||||
"human_live_projection_implemented": 0,
|
"human_live_projection_implemented": 0,
|
||||||
"hololake_integrated": 0,
|
"hololake_integrated": 0,
|
||||||
|
|
|
||||||
|
|
@ -114,12 +114,16 @@ test("the first source runtime cycle stays distinct from integration and deploym
|
||||||
channel.truth.safe_receipt_binding_terminal_revalidation_source_implemented,
|
channel.truth.safe_receipt_binding_terminal_revalidation_source_implemented,
|
||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
|
assert.equal(
|
||||||
|
channel.truth.failure_receipt_terminal_evidence_binding_source_implemented,
|
||||||
|
100,
|
||||||
|
);
|
||||||
assert.equal(channel.truth.general_purpose_persona_runtime_implemented, 0);
|
assert.equal(channel.truth.general_purpose_persona_runtime_implemented, 0);
|
||||||
assert.equal(channel.truth.human_live_projection_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_implemented, true);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
architecture.persona_native_code_channel.runtime_scope,
|
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_AND_SAFE_RECEIPT_BINDING_TERMINAL_REVALIDATION_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_AND_FAILURE_RECEIPT_TERMINAL_EVIDENCE_BINDING_SOURCE_IMPLEMENTED_AND_TESTED",
|
||||||
);
|
);
|
||||||
assert.equal(channel.truth.hololake_integrated, 0);
|
assert.equal(channel.truth.hololake_integrated, 0);
|
||||||
assert.equal(channel.truth.artifact_built, 0);
|
assert.equal(channel.truth.artifact_built, 0);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue