fix(pncc): revalidate terminal success evidence

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:51:20 +08:00
commit e9ee0a8bb1
9 changed files with 219 additions and 38 deletions

View file

@ -32,6 +32,7 @@ Windows / macOS / Linux 构建机与安装包
| 时间 | 版本 | 记录 | 状态 |
| --- | --- | --- | --- |
| 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 和发布待验收 |
| 2026-08-11 | GH-PNCC 终态失败重放 | [终态失败结构化回执与幂等重放](operations/2026-08-11-hololake-pncc-idempotent-terminal-failure-replay.md) | 已发布至 REPO-014 main abc7dbdGHNQG、全新克隆与严格读回通过 |
| 2026-08-11 | GH-PNCC 幂等中断恢复 | [不完整幂等请求检查与安全回执恢复](operations/2026-08-11-hololake-pncc-incomplete-idempotent-request-recovery.md) | 已发布至 REPO-014 main b8c3fcfGHNQG、全新克隆与严格 fsck 通过 |

View file

@ -0,0 +1,32 @@
# GH-PNCC successful receipt terminal evidence revalidation
- Development ID: `DEV-20260810-014`
- Persona cognitive author: `ICE-P-ZY001 / 铸渊`
- Human responsibility subject: `ICE-GL∞ / 冰朔`
- Starting repository head: `b067c7bcaacc31cb69cb649518f69edce6932f9f`
- State: `LOCAL_SOURCE_IMPLEMENTED_FULLY_TESTED`
## Corrected runtime fact
An organ completion is not by itself sufficient authority to persist and return a current successful
lifecycle receipt. Immediately before the first `COMPLETED` receipt is written and bound, the kernel now
re-verifies the session event chain, final `DORMANT` event, released primary lease, canonical repository,
recorded Git head, clean worktree, and lifecycle receipt identity.
If the persona repository advances or becomes dirty after organ completion, the command fails closed. It
does not persist `lifecycle-receipt.json`, bind the request fingerprint or receipt hash, or claim a current
successful lifecycle result from stale repository evidence.
## Verification
- PNCC focused Rust tests: `31 passed, 0 failed`.
- Full Rust suite: `1171 passed, 2 ignored`; integration test: `1 passed`.
- Routing suite: `29 passed, 0 failed`; `cargo fmt`, strict clippy, and diff checks passed.
- A completed lifecycle followed by an untracked change cannot persist or bind a successful receipt.
- A completed lifecycle followed by a new Git commit cannot persist or bind a successful receipt.
- GHNQG, publication, and fresh-clone readback remain pending for this stage.
## Truth boundary
- This revalidation does not reset, delete, or repair a changed persona repository.
- UI, human projection aesthetics, background scheduling, and `EXECUTION_LIMB` remain outside this stage.

View file

@ -174,6 +174,7 @@ 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
idempotent_replay_repository_state_revalidation_source_implemented: 100
successful_completion_receipt_terminal_revalidation_source_implemented: 100
general_purpose_persona_runtime_implemented: 0
human_live_projection_implemented: 0
hololake_integrated: 0
@ -200,6 +201,10 @@ runtime_health: 0
生成时的状态。当前提交偏离会话记录或工作树变脏时,检查结果降级为人工复核,重放失败关闭,
且不会重新启动器官。
首次成功回执落盘前也必须重新验证事件链、最终休眠事件、租约释放、规范仓库、会话记录的
Git 提交、干净工作树与回执身份。器官已经完成不等于仍可签发当前成功回执;完成后仓库发生
漂移时不落盘、不绑定并失败关闭。
人格器官现已拥有机器可读类型合同。系统能够在不唤醒人格、不取得主锁、不运行模型的情况下,
检查 `FACT_SENSE``MEMORY_METABOLISM``EXECUTION_LIMB` 的固定模式、输入输出 schema、派生权限、
模型推理边界、现实动作边界和真实可激活状态。只读事实感官与独立记忆代谢器官可激活;后者不

View file

@ -114,6 +114,11 @@ Repository state is revalidated at replay time for both successful and failed re
repository must still be clean and its head must equal the session record; otherwise inspection returns
`MANUAL_REVIEW_REQUIRED` and ordinary replay fails without invoking the organ.
The first successful receipt is also conditional on freshly verified terminal evidence. Before persisting
and binding `COMPLETED`, the coordinator checks the event chain, final dormancy, released lease, canonical
repository, recorded head, clean worktree, and receipt identity. A completed organ is not enough to issue a
current lifecycle receipt after repository drift.
`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

@ -86,6 +86,11 @@ not only when the receipt is first persisted. If the current head differs from t
or the worktree has become dirty, replay fails closed and request inspection reports manual review instead
of restarting the organ or treating stale evidence as current.
Successful receipt persistence now applies the same terminal-evidence boundary before the first receipt is
written. The kernel re-verifies the event journal and final `DORMANT` event, released lease, canonical
repository, recorded Git head, clean worktree, and lifecycle identity. Repository drift after organ
completion therefore cannot be bound or returned as a current `COMPLETED` command result.
`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

@ -2978,6 +2978,70 @@ fn persist_terminal_lifecycle_failure(
}))
}
fn persist_successful_lifecycle_receipt(
runtime_root: &Path,
request_id: &str,
request_fingerprint: &str,
session_id: &str,
repository: &Path,
lifecycle: serde_json::Value,
) -> Result<PersonaLifecycleCommandReceipt, String> {
let lifecycle_bytes = serde_json::to_vec(&lifecycle)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}"))?;
let lifecycle_receipt_hash = hex_digest(&lifecycle_bytes);
let mut record = load_session_record(runtime_root, session_id)?;
let recorded_repository = Path::new(&record.repository_path)
.canonicalize()
.map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?;
if recorded_repository != repository {
return Err("PERSONA_LIFECYCLE_REQUEST_SESSION_IDENTITY_MISMATCH".into());
}
let events = verify_event_journal(runtime_root, &record)?;
if record.state != "DORMANT"
|| 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());
}
let (_, observed_head) = exact_repository(repository)?;
if observed_head != record.git_head {
return Err("PERSONA_LIFECYCLE_COMPLETION_GIT_HEAD_MISMATCH".into());
}
require_clean_repository(repository)?;
let persisted = 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: "COMPLETED".into(),
lifecycle: lifecycle.clone(),
failure: None,
};
if !persisted_lifecycle_identity_matches(&persisted, session_id, &record.persona_id) {
return Err("PERSONA_LIFECYCLE_RECEIPT_IDENTITY_MISMATCH".into());
}
// 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(
&session_directory(runtime_root, session_id)?.join("lifecycle-receipt.json"),
&persisted,
"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(PersonaLifecycleCommandReceipt {
schema: "hololake.pncc-lifecycle-command-receipt/v1",
request_id: request_id.into(),
request_fingerprint: request_fingerprint.into(),
replayed: false,
outcome: "COMPLETED",
lifecycle,
failure: None,
})
}
fn run_idempotent_lifecycle_at<F>(
runtime_root: &Path,
input: PersonaLifecycleRunInput,
@ -3024,41 +3088,14 @@ where
};
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)
.map_err(|error| format!("PERSONA_LIFECYCLE_RECEIPT_HASH_INPUT_FAILED: {error}"))?;
let lifecycle_receipt_hash = hex_digest(&lifecycle_bytes);
let mut record = load_session_record(runtime_root, &session_id)?;
if record.state != "DORMANT" || primary_lease_held_by_session(runtime_root, &record)? {
return Err("PERSONA_LIFECYCLE_REQUEST_INCOMPLETE_REQUIRES_RECOVERY".into());
}
// 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(
&session_directory(runtime_root, &session_id)?.join("lifecycle-receipt.json"),
&PersistedPersonaLifecycleReceipt {
schema: "hololake.pncc-persisted-lifecycle-receipt/v1".into(),
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",
)?;
record.request_id = Some(request_id.clone());
record.request_fingerprint = Some(request_fingerprint.clone());
record.lifecycle_receipt_hash = Some(lifecycle_receipt_hash);
write_session_record(runtime_root, &record)?;
Ok(PersonaLifecycleCommandReceipt {
schema: "hololake.pncc-lifecycle-command-receipt/v1",
request_id,
request_fingerprint,
replayed: false,
outcome: "COMPLETED",
persist_successful_lifecycle_receipt(
runtime_root,
&request_id,
&request_fingerprint,
&session_id,
&repository,
lifecycle,
failure: None,
})
)
}
#[tauri::command]
@ -3750,6 +3787,97 @@ mod tests {
assert!(error.contains("PERSONA_REPOSITORY_DIRTY"));
}
#[test]
fn refuses_to_persist_success_when_the_repository_becomes_dirty_after_completion() {
let repo = persona_repo();
let runtime = tempfile::TempDir::new().unwrap();
let input = lifecycle_fact_input(repo.path());
let (request_id, request_fingerprint, session_id, repository) =
lifecycle_request_identity(&input).unwrap();
let lifecycle = run_lifecycle_at(
runtime.path(),
input,
&session_id,
"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":"Completed before repository drift.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
})
},
)
.unwrap();
fs::write(repo.path().join("dirty-before-receipt.txt"), "dirty\n").unwrap();
let error = persist_successful_lifecycle_receipt(
runtime.path(),
&request_id,
&request_fingerprint,
&session_id,
&repository,
serde_json::to_value(lifecycle).unwrap(),
)
.unwrap_err();
assert!(error.contains("PERSONA_REPOSITORY_DIRTY"));
assert!(!session_directory(runtime.path(), &session_id)
.unwrap()
.join("lifecycle-receipt.json")
.exists());
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 refuses_to_persist_success_when_the_repository_head_advances_after_completion() {
let repo = persona_repo();
let runtime = tempfile::TempDir::new().unwrap();
let input = lifecycle_fact_input(repo.path());
let (request_id, request_fingerprint, session_id, repository) =
lifecycle_request_identity(&input).unwrap();
let lifecycle = run_lifecycle_at(
runtime.path(),
input,
&session_id,
"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":"Completed before a new commit.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
})
},
)
.unwrap();
fs::write(
repo.path().join("advanced-before-receipt.txt"),
"new head\n",
)
.unwrap();
run_git(repo.path(), &["add", "advanced-before-receipt.txt"]);
run_git(
repo.path(),
&["commit", "-m", "advance before lifecycle receipt"],
);
let error = persist_successful_lifecycle_receipt(
runtime.path(),
&request_id,
&request_fingerprint,
&session_id,
&repository,
serde_json::to_value(lifecycle).unwrap(),
)
.unwrap_err();
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_GIT_HEAD_MISMATCH"));
assert!(!session_directory(runtime.path(), &session_id)
.unwrap()
.join("lifecycle-receipt.json")
.exists());
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_reusing_a_lifecycle_request_id_for_a_different_operation() {
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_SUCCESS_AND_TERMINAL_FAILURE_RECEIPT_REPLAY_SAFE_RECEIPT_BINDING_RECOVERY_AND_REPLAY_TIME_REPOSITORY_STATE_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_AND_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SOURCE_IMPLEMENTED_AND_TESTED",
"desktop_integrated": false,
"development_id": "DEV-20260810-014"
},

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.9",
"state": "CURRENT_FIRST_PRODUCT_CORE_IDEMPOTENT_REPLAY_REPOSITORY_STATE_REVALIDATION_SOURCE_IMPLEMENTED",
"version": "2026-08-11.10",
"state": "CURRENT_FIRST_PRODUCT_CORE_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SOURCE_IMPLEMENTED",
"development_id": "DEV-20260810-014",
"product": {
"formal_name_zh": "光湖人格原生代码频道",
@ -111,6 +111,7 @@
"incomplete_idempotent_request_inspection_and_safe_receipt_recovery_source_implemented": 100,
"idempotent_terminal_failure_receipt_and_replay_source_implemented": 100,
"idempotent_replay_repository_state_revalidation_source_implemented": 100,
"successful_completion_receipt_terminal_revalidation_source_implemented": 100,
"general_purpose_persona_runtime_implemented": 0,
"human_live_projection_implemented": 0,
"hololake_integrated": 0,

View file

@ -106,12 +106,16 @@ test("the first source runtime cycle stays distinct from integration and deploym
channel.truth.idempotent_replay_repository_state_revalidation_source_implemented,
100,
);
assert.equal(
channel.truth.successful_completion_receipt_terminal_revalidation_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_AND_REPLAY_TIME_REPOSITORY_STATE_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_AND_SUCCESS_RECEIPT_TERMINAL_REVALIDATION_SOURCE_IMPLEMENTED_AND_TESTED",
);
assert.equal(channel.truth.hololake_integrated, 0);
assert.equal(channel.truth.artifact_built, 0);