feat(pncc): replay terminal lifecycle failures

Human-Responsibility: ICE-GL∞ / 冰朔
Persona-Author: ICE-P-ZY001 / 铸渊
Execution-Runtime: Codex macOS
Development-ID: DEV-20260810-014
Authorization-Scope: GH-PNCC local runtime and REPO-014 publication
Source-Anchor: UI and execution limb deferred
This commit is contained in:
铸渊 / ICE-P-ZY001 2026-08-11 04:09:29 +08:00
commit abc7dbd625
10 changed files with 381 additions and 43 deletions

View file

@ -32,7 +32,8 @@ Windows / macOS / Linux 构建机与安装包
| 时间 | 版本 | 记录 | 状态 |
| --- | --- | --- | --- |
| 2026-08-11 | GH-PNCC 幂等中断恢复 | [不完整幂等请求检查与安全回执恢复](operations/2026-08-11-hololake-pncc-incomplete-idempotent-request-recovery.md) | 本地源码与聚焦测试已通过;完整门禁、发布与独立回读待验收 |
| 2026-08-11 | GH-PNCC 终态失败重放 | [终态失败结构化回执与幂等重放](operations/2026-08-11-hololake-pncc-idempotent-terminal-failure-replay.md) | 本地源码、完整 Rust/路由测试与 clippy 已通过GHNQG 和发布待验收 |
| 2026-08-11 | GH-PNCC 幂等中断恢复 | [不完整幂等请求检查与安全回执恢复](operations/2026-08-11-hololake-pncc-incomplete-idempotent-request-recovery.md) | 已发布至 REPO-014 main b8c3fcfGHNQG、全新克隆与严格 fsck 通过 |
| 2026-08-11 | GH-PNCC 幂等生命周期 | [生命周期请求身份与同一回执重放](operations/2026-08-11-hololake-pncc-idempotent-lifecycle-replay.md) | 已发布至 REPO-014 main 38ef9d0GHNQG、全新克隆与严格 fsck 通过 |
| 2026-08-11 | GH-PNCC 生命周期协调器 | [已登记安全器官的非 UI 生命周期协调](operations/2026-08-11-hololake-pncc-safe-organ-lifecycle-coordinator.md) | 已发布至 REPO-014 main 8f35834GHNQG、全新克隆与严格 fsck 通过 |
| 2026-08-11 | GH-PNCC 记忆失败闭环 | [记忆代谢失败闭环与运行时命令接入](operations/2026-08-11-hololake-pncc-memory-failure-closure-runtime-command.md) | 已发布至 REPO-014 main 18944f5GHNQG、全新克隆与严格 fsck 通过 |

View file

@ -0,0 +1,42 @@
# GH-PNCC idempotent terminal failure receipt and replay
- Development ID: `DEV-20260810-014`
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
- Human responsibility subject: `ICE-GL∞ / 冰朔`
- Starting repository head: `b8c3fcf3d8c2928be724291fdf9c1e5dc3512d71`
- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED`
## Implemented facts
A lifecycle command that reaches the kernel's already verified failure closure now persists one structured
terminal-failure receipt in the existing session directory. It uses the same atomic receipt-first,
session-binding-second protocol as successful lifecycle completion and does not create another request store.
The failure payload contains a stable machine error code, terminal event hash, exact persona/repository
identity, and structured human/persona attribution. Raw model-provider diagnostics are deliberately excluded.
The receipt can be issued only after the session journal verifies, the state is `DORMANT_AFTER_FAILURE`, the
last event is `DORMANT`, the exact primary lease is released, and the repository remains clean at the recorded
Git head.
An exact retry verifies the semantic fingerprint, receipt hash, event chain, terminal state, lease, and
session bindings, then returns the same `FAILED` result with `replayed: true`. It does not invoke the model,
activate an organ, write a checkpoint, or commit Git again. The existing inspection and safe-binding recovery
commands accept the same proven terminal outcome and still reject partial bindings or unproven closure.
## Current verification
- PNCC focused Rust tests: `27 passed, 0 failed`.
- Full Rust suite: `1167 passed, 2 ignored`; integration test: `1 passed`.
- Routing suite: `29 passed, 0 failed`; `cargo fmt`, strict clippy, and diff checks passed.
- The terminal-failure test proves exact retry does not call the model again and that raw provider detail is
absent from the persisted receipt.
- The crash-window test proves an unbound failure receipt is safely inspected, bound, and replayed.
- GHNQG, commit, publication, and fresh-clone readback remain pending for this stage.
## Truth boundary
- This is command-level terminal failure idempotency, not arbitrary recovery of an interrupted or active
session. If closure cannot be proven, the original error and established session recovery boundary remain.
- UI, projection aesthetics, background scheduling, and `EXECUTION_LIMB` remain outside this stage.
- The next runtime increment must be selected by a new source-backed gap assessment rather than inferred from
the human projection roadmap.

View file

@ -4,7 +4,11 @@
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
- Human responsibility subject: `ICE-GL∞ / 冰朔`
- Starting repository head: `38ef9d0e113e25737b1be456a705da5ddce0f27c`
- State: `LOCAL_SOURCE_IMPLEMENTED_FOCUSED_TESTED`
- Published repository head: `b8c3fcf3d8c2928be724291fdf9c1e5dc3512d71`
- Published tree: `6218fb109913649151609a0f956b699ae375198a`
- Publication queue: `PUB-20260810194731692-1ad8ad9a`
- GHNQG manual SHA-256: `691d600d1f8743212bfb16bbd47a7609ec22b0219a2af76b35bc281feb364f77`
- State: `PUBLISHED_VERIFIED`
## Implemented facts
@ -29,12 +33,13 @@ never reconstructed from guesses and remain owned by the established session rec
- A simulated crash after receipt persistence proves inspection and binding recovery return the exact original
lifecycle without another model call, session, organ activation, or Git commit.
- A session interrupted before receipt persistence proves recovery refuses to fabricate a success receipt.
- Full repository gates, GHNQG, commit, publication, and fresh-clone readback remain pending for this stage.
- Full repository gates, GHNQG, publication, strict fsck, and fresh-clone readback from
`/tmp/pncc-verify-b8c3fcf-readback` passed for the exact published head and tree.
## Truth boundary and next minimum
- Runtime files in the existing session directory remain the only evidence plane; no database or second
request authority was introduced.
- UI, projection aesthetics, background scheduling, and `EXECUTION_LIMB` remain outside this stage.
- Terminal failed lifecycle calls still do not have an idempotently replayable command-level failure receipt;
that is the next minimum runtime boundary.
- The next stage implements the separately bounded terminal-failure receipt and replay rule; this published
stage remains the success-receipt and crash-window recovery authority.

View file

@ -172,6 +172,7 @@ memory_failure_closure_and_nonblocking_runtime_command_implemented: 100
non_ui_safe_organ_lifecycle_coordinator_source_implemented: 100
idempotent_lifecycle_request_and_receipt_replay_source_implemented: 100
incomplete_idempotent_request_inspection_and_safe_receipt_recovery_source_implemented: 100
idempotent_terminal_failure_receipt_and_replay_source_implemented: 100
general_purpose_persona_runtime_implemented: 0
human_live_projection_implemented: 0
hololake_integrated: 0
@ -189,6 +190,11 @@ runtime_health: 0
终止未提交会话,或为已由该人格完成但尚未休眠的单一检查点提交补齐收尾。脏仓库、未知 Git
推进、错误归因和损坏事件链一律保留主锁并要求人工审查;恢复器不会按时间猜测主控已死亡。
生命周期命令的已闭合失败也使用同一会话目录、同一回执文件和同一绑定恢复路径。回执只保留
稳定机器错误码、终态事件哈希与结构化归因,不落盘模型提供方原始错误细节;同一语义请求在
终态、Git、租约、事件链和回执哈希全部复核后只重放原失败不再次启动模型或器官。未完成
闭合、脏仓库或仍持有租约的失败不能被伪装成终态回执。
人格器官现已拥有机器可读类型合同。系统能够在不唤醒人格、不取得主锁、不运行模型的情况下,
检查 `FACT_SENSE``MEMORY_METABOLISM``EXECUTION_LIMB` 的固定模式、输入输出 schema、派生权限、
模型推理边界、现实动作边界和真实可激活状态。只读事实感官与独立记忆代谢器官可激活;后者不

View file

@ -103,6 +103,13 @@ receipt beside a verified dormant session with no lease and all three binding fi
verifier. Missing receipts, partial bindings, active leases, damaged chains, or changed semantics remain
recovery/manual-review states and cannot be converted into a successful receipt.
The same persisted receipt envelope also represents a verified terminal failure. Its `outcome` is `FAILED`,
its successful lifecycle payload is null, and `failure` carries only the stable error code, terminal event
hash, identity, repository, and attribution. The runtime hashes that structured failure payload, never the
raw provider diagnostic. Replay and safe binding recovery require `DORMANT_AFTER_FAILURE`, a final `DORMANT`
event, a released lease, and an unchanged clean Git head. A failure that cannot prove those conditions is not
terminal and remains recovery-required.
`PersonaRuntimeQueryReceipt` is a bounded projection of the durable runtime files, not another truth store.
It filters by the caller's expected persona and canonical repository, validates each matching event chain,
and returns at most 100 newest session summaries. Dormant sessions expose no active organ. The receipt keeps

View file

@ -73,6 +73,14 @@ but unbound, and session-recovery states from the same session evidence. The rec
only the receipt-present/unbound window after rechecking the receipt hash, identity, event chain, dormancy,
and released lease. It never fabricates a receipt for an earlier interruption or repairs partial bindings.
A lifecycle that reaches the kernel's verified terminal failure closure now uses that same receipt file and
two-write binding protocol. The persisted value contains only a stable machine error code, terminal event
hash, repository/persona identity, and structured attribution; raw provider or model error detail is not
stored. Exact retries verify `DORMANT_AFTER_FAILURE`, the final `DORMANT` event, released lease, unchanged
clean Git head, semantic fingerprint, receipt hash, and session bindings before returning the same failure
with `replayed: true`. Active, dirty, partially closed, or otherwise unproven failures retain the original
recovery path and cannot be converted into a terminal receipt.
`query_persona_code_channel_runtime` is the bounded read model for later projection surfaces. The caller must
name one exact persona and canonical repository and may request at most 100 sessions. The kernel reads the
existing session records and event journals directly, verifies every returned hash chain, sorts by the last

View file

@ -328,7 +328,21 @@ pub struct PersonaLifecycleCommandReceipt {
pub request_id: String,
pub request_fingerprint: String,
pub replayed: bool,
pub outcome: &'static str,
pub lifecycle: serde_json::Value,
pub failure: Option<PersonaLifecycleFailureReceipt>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaLifecycleFailureReceipt {
pub schema: String,
pub session_id: String,
pub persona_id: String,
pub repository_path: String,
pub error_code: String,
pub terminal_event_hash: String,
pub attribution: PersonaAttribution,
}
#[derive(Debug, Serialize)]
@ -353,7 +367,15 @@ struct PersistedPersonaLifecycleReceipt {
request_id: String,
request_fingerprint: String,
lifecycle_receipt_hash: String,
#[serde(default = "completed_lifecycle_outcome")]
outcome: String,
lifecycle: serde_json::Value,
#[serde(default)]
failure: Option<PersonaLifecycleFailureReceipt>,
}
fn completed_lifecycle_outcome() -> String {
"COMPLETED".into()
}
#[derive(Clone, Debug, Deserialize)]
@ -2596,6 +2618,66 @@ fn lifecycle_request_identity(
Ok((request_id, request_fingerprint, session_id, repository))
}
fn persisted_lifecycle_payload_bytes(
persisted: &PersistedPersonaLifecycleReceipt,
) -> Result<Vec<u8>, String> {
match persisted.outcome.as_str() {
"COMPLETED" if persisted.failure.is_none() && !persisted.lifecycle.is_null() => {
serde_json::to_vec(&persisted.lifecycle)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}"))
}
"FAILED" if persisted.lifecycle.is_null() => serde_json::to_vec(
persisted
.failure
.as_ref()
.ok_or("PERSONA_LIFECYCLE_FAILURE_RECEIPT_MISSING")?,
)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}")),
_ => Err("PERSONA_LIFECYCLE_RECEIPT_OUTCOME_INVALID".into()),
}
}
fn persisted_lifecycle_identity_matches(
persisted: &PersistedPersonaLifecycleReceipt,
session_id: &str,
persona_id: &str,
) -> bool {
match persisted.outcome.as_str() {
"COMPLETED" => {
persisted
.lifecycle
.get("sessionId")
.and_then(serde_json::Value::as_str)
== Some(session_id)
&& persisted
.lifecycle
.get("personaId")
.and_then(serde_json::Value::as_str)
== Some(persona_id)
}
"FAILED" => persisted.failure.as_ref().is_some_and(|failure| {
failure.schema == "hololake.pncc-lifecycle-failure-receipt/v1"
&& failure.session_id == session_id
&& failure.persona_id == persona_id
}),
_ => false,
}
}
fn lifecycle_failure_code(error: &str) -> String {
let candidate = error.split([':', ';']).next().unwrap_or_default().trim();
if !candidate.is_empty()
&& candidate.len() <= MAX_ID_BYTES
&& candidate
.bytes()
.all(|byte| byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_')
{
candidate.into()
} else {
"PERSONA_LIFECYCLE_OPERATION_FAILED".into()
}
}
fn verified_lifecycle_replay(
runtime_root: &Path,
request_id: &str,
@ -2621,9 +2703,9 @@ fn verified_lifecycle_replay(
if persisted.request_fingerprint != request_fingerprint {
return Err("PERSONA_LIFECYCLE_REQUEST_CONFLICT".into());
}
let lifecycle_bytes = serde_json::to_vec(&persisted.lifecycle)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}"))?;
if hex_digest(&lifecycle_bytes) != persisted.lifecycle_receipt_hash {
if hex_digest(&persisted_lifecycle_payload_bytes(&persisted)?)
!= persisted.lifecycle_receipt_hash
{
return Err("PERSONA_LIFECYCLE_RECEIPT_HASH_MISMATCH".into());
}
let record = load_session_record(runtime_root, session_id)?;
@ -2639,23 +2721,18 @@ fn verified_lifecycle_replay(
return Err("PERSONA_LIFECYCLE_RECEIPT_SESSION_BINDING_MISMATCH".into());
}
let events = verify_event_journal(runtime_root, &record)?;
if record.state != "DORMANT"
let expected_state = if persisted.outcome == "FAILED" {
"DORMANT_AFTER_FAILURE"
} else {
"DORMANT"
};
if record.state != expected_state
|| events.last().map(|event| event.kind.as_str()) != Some("DORMANT")
|| primary_lease_held_by_session(runtime_root, &record)?
{
return Err("PERSONA_LIFECYCLE_REQUEST_INCOMPLETE_REQUIRES_RECOVERY".into());
}
if persisted
.lifecycle
.get("sessionId")
.and_then(serde_json::Value::as_str)
!= Some(session_id)
|| persisted
.lifecycle
.get("personaId")
.and_then(serde_json::Value::as_str)
!= Some(record.persona_id.as_str())
{
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
}
Ok(Some(PersonaLifecycleCommandReceipt {
@ -2663,7 +2740,13 @@ fn verified_lifecycle_replay(
request_id: request_id.to_string(),
request_fingerprint: request_fingerprint.to_string(),
replayed: true,
outcome: if persisted.outcome == "FAILED" {
"FAILED"
} else {
"COMPLETED"
},
lifecycle: persisted.lifecycle,
failure: persisted.failure,
}))
}
@ -2726,22 +2809,12 @@ fn inspect_lifecycle_request_at(
if persisted.request_fingerprint != request_fingerprint {
return Err("PERSONA_LIFECYCLE_REQUEST_CONFLICT".into());
}
let lifecycle_bytes = serde_json::to_vec(&persisted.lifecycle)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}"))?;
if hex_digest(&lifecycle_bytes) != persisted.lifecycle_receipt_hash {
if hex_digest(&persisted_lifecycle_payload_bytes(&persisted)?)
!= persisted.lifecycle_receipt_hash
{
return Err("PERSONA_LIFECYCLE_RECEIPT_HASH_MISMATCH".into());
}
if persisted
.lifecycle
.get("sessionId")
.and_then(serde_json::Value::as_str)
!= Some(session_id.as_str())
|| persisted
.lifecycle
.get("personaId")
.and_then(serde_json::Value::as_str)
!= Some(record.persona_id.as_str())
{
if !persisted_lifecycle_identity_matches(&persisted, &session_id, &record.persona_id) {
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
}
let complete = record.request_id.as_deref() == Some(request_id.as_str())
@ -2751,7 +2824,12 @@ fn inspect_lifecycle_request_at(
let unbound = record.request_id.is_none()
&& record.request_fingerprint.is_none()
&& record.lifecycle_receipt_hash.is_none();
let safely_dormant = record.state == "DORMANT"
let expected_state = if persisted.outcome == "FAILED" {
"DORMANT_AFTER_FAILURE"
} else {
"DORMANT"
};
let safely_dormant = record.state == expected_state
&& events.last().map(|event| event.kind.as_str()) == Some("DORMANT")
&& !lease_held;
let (status, safe_to_bind_receipt) = if complete && safely_dormant {
@ -2814,6 +2892,83 @@ fn recover_lifecycle_request_at(
.ok_or_else(|| "PERSONA_LIFECYCLE_RECEIPT_RECOVERY_LOST_SESSION".into())
}
fn persist_terminal_lifecycle_failure(
runtime_root: &Path,
request_id: &str,
request_fingerprint: &str,
session_id: &str,
repository: &Path,
error: &str,
) -> Result<Option<PersonaLifecycleCommandReceipt>, String> {
let session_dir = session_directory(runtime_root, session_id)?;
if !session_dir.exists() {
return Ok(None);
}
let mut record = load_session_record(runtime_root, session_id)?;
let recorded_repository = Path::new(&record.repository_path)
.canonicalize()
.map_err(|cause| format!("PERSONA_REPOSITORY_UNAVAILABLE: {cause}"))?;
if recorded_repository != repository {
return Err("PERSONA_LIFECYCLE_REQUEST_SESSION_IDENTITY_MISMATCH".into());
}
let events = verify_event_journal(runtime_root, &record)?;
let terminal_event = events.last().ok_or("PERSONA_EVENT_CHAIN_EMPTY")?;
let (_, observed_head) = exact_repository(repository)?;
let safely_terminal = record.state == "DORMANT_AFTER_FAILURE"
&& terminal_event.kind == "DORMANT"
&& !primary_lease_held_by_session(runtime_root, &record)?
&& observed_head == record.git_head
&& require_clean_repository(repository).is_ok();
if !safely_terminal {
return Ok(None);
}
if record.request_id.is_some()
|| record.request_fingerprint.is_some()
|| record.lifecycle_receipt_hash.is_some()
{
return Err("PERSONA_LIFECYCLE_FAILURE_BINDING_ALREADY_PRESENT".into());
}
let failure = PersonaLifecycleFailureReceipt {
schema: "hololake.pncc-lifecycle-failure-receipt/v1".into(),
session_id: session_id.into(),
persona_id: record.persona_id.clone(),
repository_path: record.repository_path.clone(),
error_code: lifecycle_failure_code(error),
terminal_event_hash: terminal_event.event_hash.clone(),
attribution: record.attribution.clone(),
};
let lifecycle_receipt_hash = hex_digest(
&serde_json::to_vec(&failure)
.map_err(|cause| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {cause}"))?,
);
write_json_file(
&session_dir.join("lifecycle-receipt.json"),
&PersistedPersonaLifecycleReceipt {
schema: "hololake.pncc-persisted-lifecycle-receipt/v1".into(),
request_id: request_id.into(),
request_fingerprint: request_fingerprint.into(),
lifecycle_receipt_hash: lifecycle_receipt_hash.clone(),
outcome: "FAILED".into(),
lifecycle: serde_json::Value::Null,
failure: Some(failure.clone()),
},
"PERSONA_LIFECYCLE_RECEIPT",
)?;
record.request_id = Some(request_id.into());
record.request_fingerprint = Some(request_fingerprint.into());
record.lifecycle_receipt_hash = Some(lifecycle_receipt_hash);
write_session_record(runtime_root, &record)?;
Ok(Some(PersonaLifecycleCommandReceipt {
schema: "hololake.pncc-lifecycle-command-receipt/v1",
request_id: request_id.into(),
request_fingerprint: request_fingerprint.into(),
replayed: false,
outcome: "FAILED",
lifecycle: serde_json::Value::Null,
failure: Some(failure),
}))
}
fn run_idempotent_lifecycle_at<F>(
runtime_root: &Path,
input: PersonaLifecycleRunInput,
@ -2835,14 +2990,29 @@ where
)? {
return Ok(replay);
}
let lifecycle = run_lifecycle_at(
let lifecycle = match run_lifecycle_at(
runtime_root,
input,
&session_id,
wake_timestamp,
operation_timestamp,
run_fact,
)?;
) {
Ok(lifecycle) => lifecycle,
Err(error) => {
if let Some(receipt) = persist_terminal_lifecycle_failure(
runtime_root,
&request_id,
&request_fingerprint,
&session_id,
&repository,
&error,
)? {
return Ok(receipt);
}
return Err(error);
}
};
let lifecycle = serde_json::to_value(lifecycle)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_SERIALIZATION_FAILED: {error}"))?;
let lifecycle_bytes = serde_json::to_vec(&lifecycle)
@ -2861,7 +3031,9 @@ where
request_id: request_id.clone(),
request_fingerprint: request_fingerprint.clone(),
lifecycle_receipt_hash: lifecycle_receipt_hash.clone(),
outcome: "COMPLETED".into(),
lifecycle: lifecycle.clone(),
failure: None,
},
"PERSONA_LIFECYCLE_RECEIPT",
)?;
@ -2874,7 +3046,9 @@ where
request_id,
request_fingerprint,
replayed: false,
outcome: "COMPLETED",
lifecycle,
failure: None,
})
}
@ -3680,6 +3854,96 @@ mod tests {
assert!(error.contains("PERSONA_LIFECYCLE_RECEIPT_BINDING_NOT_SAFE"));
}
#[test]
fn persists_and_replays_the_same_structured_terminal_failure_without_rerunning_the_organ() {
let repo = persona_repo();
let runtime = tempfile::TempDir::new().unwrap();
let input = lifecycle_fact_input(repo.path());
let original_head = head(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, |_, _| {
Err("provider-secret-detail-must-not-persist".into())
})
},
)
.unwrap();
let replay = run_idempotent_lifecycle_at(
runtime.path(),
input,
"2026-08-11T00:00:02.000Z",
"2026-08-11T00:00:03.000Z",
|_, _, _| panic!("a terminal failure replay must not rerun the organ"),
)
.unwrap();
assert_eq!(first.outcome, "FAILED");
assert!(!first.replayed);
assert!(first.lifecycle.is_null());
assert_eq!(
first.failure.as_ref().unwrap().error_code,
"PERSONA_MODEL_INFERENCE_FAILED"
);
assert_eq!(replay.outcome, "FAILED");
assert!(replay.replayed);
assert_eq!(
replay.failure.as_ref().unwrap().terminal_event_hash,
first.failure.as_ref().unwrap().terminal_event_hash
);
assert_eq!(head(repo.path()), original_head);
let persisted = fs::read_to_string(
session_directory(
runtime.path(),
first.failure.as_ref().unwrap().session_id.as_str(),
)
.unwrap()
.join("lifecycle-receipt.json"),
)
.unwrap();
assert!(!persisted.contains("provider-secret-detail-must-not-persist"));
assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists());
}
#[test]
fn safely_recovers_an_unbound_terminal_failure_receipt() {
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_eq!(inspection.status, "SAFE_BIND_PERSISTED_RECEIPT");
assert!(inspection.safe_to_bind_receipt);
let recovered = recover_lifecycle_request_at(runtime.path(), &input).unwrap();
assert_eq!(recovered.outcome, "FAILED");
assert!(recovered.replayed);
assert_eq!(
recovered.failure.unwrap().error_code,
"PERSONA_MODEL_INFERENCE_FAILED"
);
}
#[test]
fn independently_promotes_only_the_current_verified_structured_checkpoint() {
let repo = persona_repo();

View file

@ -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_RECEIPT_REPLAY_AND_SAFE_RECEIPT_BINDING_RECOVERY_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_AND_SAFE_RECEIPT_BINDING_RECOVERY_SOURCE_IMPLEMENTED_AND_TESTED",
"desktop_integrated": false,
"development_id": "DEV-20260810-014"
},
@ -343,8 +343,8 @@
"DEV-20260810-013"
],
"closeout_record": "HLP-DEV-20260809-007-CLOSEOUT-001",
"next_minimum_stage": "GH_PNCC_IDEMPOTENT_TERMINAL_FAILURE_RECEIPT_AND_REPLAY",
"next_stage_started": true,
"next_minimum_stage": "GH_PNCC_RUNTIME_GAP_REASSESSMENT_FROM_CURRENT_SOURCE",
"next_stage_started": false,
"heartbeat_automation": "pncc",
"heartbeat_state": "ACTIVE_EVERY_10_MINUTES_UNTIL_TASK_TERMINAL"
},

View file

@ -1,8 +1,8 @@
{
"schema": "hololake.persona-native-code-channel/v1",
"record_id": "HLP-PERSONA-NATIVE-CODE-CHANNEL-001",
"version": "2026-08-11.7",
"state": "CURRENT_FIRST_PRODUCT_CORE_RECOVERABLE_IDEMPOTENT_SAFE_ORGAN_LIFECYCLE_SOURCE_IMPLEMENTED",
"version": "2026-08-11.8",
"state": "CURRENT_FIRST_PRODUCT_CORE_IDEMPOTENT_TERMINAL_FAILURE_REPLAY_SOURCE_IMPLEMENTED",
"development_id": "DEV-20260810-014",
"product": {
"formal_name_zh": "光湖人格原生代码频道",
@ -109,6 +109,7 @@
"non_ui_safe_organ_lifecycle_coordinator_source_implemented": 100,
"idempotent_lifecycle_request_and_receipt_replay_source_implemented": 100,
"incomplete_idempotent_request_inspection_and_safe_receipt_recovery_source_implemented": 100,
"idempotent_terminal_failure_receipt_and_replay_source_implemented": 100,
"general_purpose_persona_runtime_implemented": 0,
"human_live_projection_implemented": 0,
"hololake_integrated": 0,

View file

@ -98,12 +98,16 @@ test("the first source runtime cycle stays distinct from integration and deploym
channel.truth.incomplete_idempotent_request_inspection_and_safe_receipt_recovery_source_implemented,
100,
);
assert.equal(
channel.truth.idempotent_terminal_failure_receipt_and_replay_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_RECEIPT_REPLAY_AND_SAFE_RECEIPT_BINDING_RECOVERY_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_AND_SAFE_RECEIPT_BINDING_RECOVERY_SOURCE_IMPLEMENTED_AND_TESTED",
);
assert.equal(channel.truth.hololake_integrated, 0);
assert.equal(channel.truth.artifact_built, 0);