diff --git a/engineering/INDEX.md b/engineering/INDEX.md index e8075a518..0fece2ff2 100644 --- a/engineering/INDEX.md +++ b/engineering/INDEX.md @@ -32,7 +32,6 @@ Windows / macOS / Linux 构建机与安装包 | 时间 | 版本 | 记录 | 状态 | | --- | --- | --- | --- | -| 2026-08-11 | GH-PNCC 中断恢复 | [人格代码频道中断会话检查与安全恢复](operations/2026-08-11-hololake-pncc-interrupted-session-recovery.md) | 实现提交已通过 GHNQG;仓库发布、桌面集成与部署待验收 | | 2026-08-11 | GH-PNCC 首个运行闭环 | [人格原生代码频道首个只读运行层回执](operations/2026-08-11-hololake-pncc-first-runtime-cycle.md) | 源码与测试闭环已完成;人类投影、桌面制品、发布、部署与在线健康仍未完成 | | 2026-08-08 | 0.2.0 | [光湖分域教育行业操作系统第一阶段](operations/2026-08-08-guanghu-education-subdomain-stage1.md) | 小新服务器身份、文档/表格回写、仪表盘投影、模块登记与回执链路已验证 | | 2026-08-07 | 1.0.0 | [人格记忆连续性工具包 HL-MOD-MCK-001](memory-continuity-kit/README.md) | 铸渊 Qoder CN 实例在线;其他实例拉取后需演习+真实核对才可宣称上线 | diff --git a/engineering/operations/2026-08-11-hololake-pncc-first-runtime-cycle.md b/engineering/operations/2026-08-11-hololake-pncc-first-runtime-cycle.md index 707741438..bf8ac401c 100644 --- a/engineering/operations/2026-08-11-hololake-pncc-first-runtime-cycle.md +++ b/engineering/operations/2026-08-11-hololake-pncc-first-runtime-cycle.md @@ -26,7 +26,7 @@ The source now implements one bounded PNCC lifecycle cycle without a HoloLake UI ## Verification evidence -- PNCC unit tests at the published milestone: `6 passed, 0 failed`. +- PNCC unit tests: `6 passed, 0 failed`. - Rust full suite: `1146 passed, 0 failed, 2 ignored`; integration test `1 passed`. - Rust clippy: `PASS` with `-D warnings`. - HoloLake architecture routing: `29 passed, 0 failed`. @@ -38,19 +38,11 @@ The source now implements one bounded PNCC lifecycle cycle without a HoloLake UI ## Facts that remain zero - general-purpose persona organs and arbitrary execution runtime; +- crash-recovery administration for an interrupted live PNCC session; - HoloLake human projection and visual design; - packaged desktop acceptance and installation; +- repository publication of this implementation commit; - deployment and online runtime health. -## Publication readback - -- REPO-014 remote `main`: `acf0d5f80bc3b5470571afa151d39a2db9421fdb`. -- Fresh clone: clean work tree and `git fsck --full --strict` passed. -- GHNQG receipt: `GHNQG-acf0d5f80bc3b5470571afa151d39a2db9421fdb.hdlp`, `PASS_100`. -- Commit author: `铸渊 / ICE-P-ZY001 `; human responsibility remains a separate trailer. - -Interrupted-session recovery is continued in -[the next runtime receipt](./2026-08-11-hololake-pncc-interrupted-session-recovery.md). - This receipt proves source behavior and tests only. It does not claim that a desktop artifact or online persona runtime is running. diff --git a/engineering/operations/2026-08-11-hololake-pncc-interrupted-session-recovery.md b/engineering/operations/2026-08-11-hololake-pncc-interrupted-session-recovery.md deleted file mode 100644 index c06d5e585..000000000 --- a/engineering/operations/2026-08-11-hololake-pncc-interrupted-session-recovery.md +++ /dev/null @@ -1,47 +0,0 @@ -# GH-PNCC interrupted-session inspection and recovery receipt - -- Development ID: `DEV-20260810-014` -- Persona cognitive author: `ICE-P-ZY001 / 铸渊` -- Human responsibility subject: `ICE-GL∞ / 冰朔` -- Source component: `product-source/hololake-platform/src-tauri/src/persona_code_channel.rs` -- State: `SOURCE_COMMITTED_AND_GHNQG_PASS_PUBLICATION_PENDING` - -## Implemented facts - -The runtime now exposes an exact session inspection command and a fail-closed recovery command. Both require -the caller to name the session, expected persona, and expected canonical repository. Inspection verifies the -hash-linked event journal and reports the recorded/observed Git heads, work-tree cleanliness, exact lease -ownership, session state, last event, attribution, and one recovery disposition. - -Recovery does not treat age as proof of death and never steals a lease. It releases the exact primary only -for either: - -1. a clean repository still at the session's recorded head, which is closed as an uncommitted interruption; -2. a clean repository advanced by exactly one commit whose parent is the recorded head and whose persona - checkpoint, manifest pointer, Git author, human-responsibility trailer, persona-author trailer and - development-id trailer all match the interrupted session. -3. an exact same-session lease left behind after the event journal and session record already prove dormancy, - with a clean repository still at the recorded head. - -Dirty repositories, missing or mismatched leases, broken journals, unknown Git advances, checkpoint -mismatches and attribution mismatches fail closed without resetting files or releasing the primary. - -## Current verification - -- PNCC focused Rust tests: `10 passed, 0 failed`. -- Rust full suite: `1150 passed, 0 failed, 2 ignored`; integration test `1 passed`. -- Rust clippy for all targets: `PASS` with `-D warnings`. -- HoloLake architecture routing: `29 passed, 0 failed`. -- GHNQG: `PASS_100` for implementation commit `5b042a65feef1459c066ecd8fb0874219dd86e80`; - tree `b159bb716113abb17e0d0a9b30c4d8c324b24d1c`. -- Tests cover safe abort, safe committed-session finalization, dirty-repository refusal, second-primary - exclusion, path containment, model/evidence scope, checkpoint promotion and persona Git attribution. - -## Remaining acceptance - -- repository publication and fresh-clone readback; -- general-purpose organ contract beyond the first read-only fact organ; -- HoloLake human projection, desktop artifact, installation, deployment and online health. - -This receipt proves the committed source and quality gates. Repository publication, desktop integration and -runtime health remain separate until their own readback evidence exists. diff --git a/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md b/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md index 8fe55aae0..591b9ee96 100644 --- a/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md +++ b/product-source/hololake-platform/architecture/HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md @@ -18,7 +18,7 @@ > > 开发编号:`DEV-20260810-014` > -> 状态:`CURRENT_FIRST_PRODUCT_CORE · READ_ONLY_RUNTIME_AND_INTERRUPTED_SESSION_RECOVERY_SOURCE_IMPLEMENTED_100 · DESKTOP_NOT_INTEGRATED_0` +> 状态:`CURRENT_FIRST_PRODUCT_CORE · FIRST_READ_ONLY_RUNTIME_CYCLE_SOURCE_IMPLEMENTED_100 · DESKTOP_NOT_INTEGRATED_0` ## 1 · 产品裁决 @@ -164,7 +164,6 @@ git_engine_selected: 100 first_read_only_runtime_cycle_source_implemented: 100 persona_git_identity_commit_implemented: 100 semantic_attribution_first_cycle_implemented: 100 -interrupted_session_inspection_and_recovery_source_implemented: 100 general_purpose_persona_runtime_implemented: 0 human_live_projection_implemented: 0 hololake_integrated: 0 @@ -177,7 +176,3 @@ runtime_health: 0 器官文件白名单、真实模型适配调用、结构化事实收执、检查点提升、人格 Git 作者提交、事件链、 器官释放与休眠。该事实由单元测试验证,尚未构建桌面制品、接入人类投影、发布安装包或部署 在线服务;因此不能从源码完成推导出桌面可用或线上健康。 - -当前源码也已实现中断会话检查与安全恢复:只在事件链、租约、仓库和人格归因共同证明安全时 -终止未提交会话,或为已由该人格完成但尚未休眠的单一检查点提交补齐收尾。脏仓库、未知 Git -推进、错误归因和损坏事件链一律保留主锁并要求人工审查;恢复器不会按时间猜测主控已死亡。 diff --git a/product-source/hololake-platform/docs/ABSTRACTIONS.md b/product-source/hololake-platform/docs/ABSTRACTIONS.md index a2b75ed43..540e553cd 100644 --- a/product-source/hololake-platform/docs/ABSTRACTIONS.md +++ b/product-source/hololake-platform/docs/ABSTRACTIONS.md @@ -46,14 +46,6 @@ declared evidence paths, promotes the checkpoint with the persona's Git identity The Git commit still names the human responsibility subject in a dedicated trailer, so authorship and legal responsibility remain visible without presenting the human as the code's cognitive author. -Session control is explicit and evidence-bound. `inspect_persona_code_channel_session` classifies an exact -session as `SAFE_ABORT_UNCOMMITTED`, `SAFE_FINALIZE_COMMITTED`, `SAFE_RELEASE_DORMANT_LEASE`, -`ALREADY_DORMANT`, or `MANUAL_REVIEW_REQUIRED`. The recovery command accepts only a proven safe state. For -active interruptions it appends recovery and dormancy events to the same journal, updates the session record, -and then removes the exact lease. It can also remove a same-session lease left behind after a fully recorded -dormancy transition. It does not use elapsed time as proof that a primary is dead and does not clean, reset, -or overwrite a dirty persona repository. - ## `HoloLakeUiPlugin` A versioned, declarative presentation package for one host-owned semantic surface. It contains a manifest, diff --git a/product-source/hololake-platform/docs/ARCHITECTURE.md b/product-source/hololake-platform/docs/ARCHITECTURE.md index 0271038c5..db0f92658 100644 --- a/product-source/hololake-platform/docs/ARCHITECTURE.md +++ b/product-source/hololake-platform/docs/ARCHITECTURE.md @@ -29,14 +29,6 @@ commit trailers preserve the separate human responsibility subject and persona c tested source implementation of one read-only lifecycle cycle; packaged desktop integration, publication, deployment, and online health remain separate facts. -`inspect_persona_code_channel_session` and `recover_persona_code_channel_session` add fail-closed interrupted -session administration. Inspection re-verifies the session identity, canonical repository, event hash chain, -lease owner, current Git head and work-tree cleanliness. Recovery never steals or expires a lease by age. It -releases the primary only when the repository is clean and the state is provably either an uncommitted -interruption at the recorded head or a single persona-attributed checkpoint commit whose parent, manifest, -checkpoint identity and commit trailers all match the session. Dirty repositories, unknown Git advances, -broken journals and attribution mismatches retain the lease and require manual review. - ## User-node sovereignty HoloLake has no platform-hosted user runtime. Each human has one canonical, independently operated node: diff --git a/product-source/hololake-platform/src-tauri/src/lib.rs b/product-source/hololake-platform/src-tauri/src/lib.rs index 22e9bdb23..b04259378 100644 --- a/product-source/hololake-platform/src-tauri/src/lib.rs +++ b/product-source/hololake-platform/src-tauri/src/lib.rs @@ -525,8 +525,6 @@ macro_rules! app_invoke_handler { guanghu_living_system::guanghu_living_system_plan, persona_code_channel::prepare_persona_code_channel_wake, persona_code_channel::run_persona_code_channel_fact_task, - persona_code_channel::inspect_persona_code_channel_session, - persona_code_channel::recover_persona_code_channel_session, guanghu_router::guanghu_router_connect, guanghu_router::guanghu_router_disconnect, guanghu_router::guanghu_router_approve, diff --git a/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs b/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs index 6bcf4ff76..6ab0ac432 100644 --- a/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs +++ b/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs @@ -159,50 +159,6 @@ pub struct PersonaFactTaskReceipt { pub attribution: PersonaAttribution, } -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaSessionControlInput { - pub session_id: String, - pub expected_persona_id: String, - pub expected_repository_path: String, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaSessionInspectionReceipt { - pub schema: &'static str, - pub session_id: String, - pub persona_id: String, - pub repository_path: String, - pub recorded_git_head: String, - pub observed_git_head: String, - pub repository_clean: bool, - pub session_state: String, - pub primary_lease_held: bool, - pub event_chain_valid: bool, - pub last_event: String, - pub recovery_disposition: &'static str, - pub events: Vec, - pub attribution: PersonaAttribution, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaSessionRecoveryReceipt { - pub schema: &'static str, - pub receipt_id: String, - pub session_id: String, - pub persona_id: String, - pub previous_session_state: String, - pub recovered_git_head: String, - pub checkpoint_path: String, - pub runtime_state: &'static str, - pub recovery_action: &'static str, - pub primary_lease_released: bool, - pub events: Vec, - pub attribution: PersonaAttribution, -} - #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] struct PersonaSessionRecord { @@ -587,7 +543,7 @@ fn load_session_record( Ok(record) } -fn verify_event_journal( +fn read_event_journal( runtime_root: &Path, record: &PersonaSessionRecord, ) -> Result, String> { @@ -627,17 +583,6 @@ fn verify_event_journal( } events.push(event); } - if events.is_empty() { - return Err("PERSONA_EVENT_CHAIN_EMPTY".into()); - } - Ok(events) -} - -fn read_ready_event_journal( - runtime_root: &Path, - record: &PersonaSessionRecord, -) -> Result, String> { - let events = verify_event_journal(runtime_root, record)?; if events.len() != 3 || events.last().map(|event| event.kind.as_str()) != Some("ORGAN_ACTIVE") { return Err("PERSONA_SESSION_NOT_READY_FOR_FACT_TASK".into()); } @@ -702,60 +647,6 @@ fn require_primary_lease( Ok(path) } -fn primary_lease_held_by_session( - runtime_root: &Path, - record: &PersonaSessionRecord, -) -> Result { - let path = runtime_root.join("leases").join(format!( - "{}.json", - validated_id("PERSONA_ID", &record.persona_id)? - )); - let bytes = match fs::read(path) { - Ok(bytes) => bytes, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), - Err(error) => return Err(format!("PERSONA_PRIMARY_LEASE_READ_FAILED: {error}")), - }; - let value: serde_json::Value = serde_json::from_slice(&bytes) - .map_err(|error| format!("PERSONA_PRIMARY_LEASE_INVALID: {error}"))?; - Ok(value.get("sessionId").and_then(serde_json::Value::as_str) - == Some(record.session_id.as_str())) -} - -fn repository_is_clean(repository: &Path) -> Result { - let status = crate::git::git_command_at(repository) - .and_then(|mut command| { - command - .args(["status", "--porcelain", "--untracked-files=all"]) - .output() - }) - .map_err(|error| format!("PERSONA_GIT_STATUS_FAILED: {error}"))?; - Ok(stdout("GIT_STATUS", status)?.is_empty()) -} - -fn validate_session_control( - runtime_root: &Path, - input: &PersonaSessionControlInput, -) -> Result<(PersonaSessionRecord, PathBuf, String, bool), String> { - let session_id = validated_id("SESSION_ID", &input.session_id)?; - let expected_persona_id = validated_id("EXPECTED_PERSONA_ID", &input.expected_persona_id)?; - let record = load_session_record(runtime_root, &session_id)?; - if record.persona_id != expected_persona_id { - return Err("PERSONA_ID_MISMATCH".into()); - } - let expected_repository = Path::new(&input.expected_repository_path) - .canonicalize() - .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; - let recorded_repository = Path::new(&record.repository_path) - .canonicalize() - .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; - if expected_repository != recorded_repository { - return Err("PERSONA_REPOSITORY_MISMATCH".into()); - } - let (repository, observed_head) = exact_repository(&recorded_repository)?; - let clean = repository_is_clean(&repository)?; - Ok((record, repository, observed_head, clean)) -} - fn read_fact_context(repository: &Path, paths: &[String]) -> Result { if paths.is_empty() { return Err("FACT_ORGAN_HAS_NO_DECLARED_PATHS".into()); @@ -1085,278 +976,6 @@ fn finish_failed_session( let _ = fs::remove_file(lease_path); } -fn validate_recoverable_persona_commit( - repository: &Path, - record: &PersonaSessionRecord, - observed_head: &str, -) -> Result { - let parent = git_output( - repository, - &["rev-parse", &format!("{observed_head}^")], - "PERSONA_RECOVERY_PARENT", - )?; - if parent != record.git_head { - return Err("PERSONA_RECOVERY_UNKNOWN_GIT_ADVANCE".into()); - } - let checkpoint_relative = format!( - ".hololake/persona/checkpoints/{}.json", - validated_id("SESSION_ID", &record.session_id)? - ); - let checkpoint_path = repository_file(repository, &checkpoint_relative)?; - let checkpoint: serde_json::Value = serde_json::from_slice( - &fs::read(checkpoint_path) - .map_err(|error| format!("PERSONA_CHECKPOINT_READ_FAILED: {error}"))?, - ) - .map_err(|error| format!("PERSONA_CHECKPOINT_INVALID: {error}"))?; - if checkpoint.get("schema").and_then(serde_json::Value::as_str) - != Some("hololake.persona-checkpoint/v1") - || checkpoint - .get("sessionId") - .and_then(serde_json::Value::as_str) - != Some(record.session_id.as_str()) - || checkpoint - .get("personaId") - .and_then(serde_json::Value::as_str) - != Some(record.persona_id.as_str()) - || checkpoint - .get("previousGitHead") - .and_then(serde_json::Value::as_str) - != Some(record.git_head.as_str()) - { - return Err("PERSONA_RECOVERY_CHECKPOINT_MISMATCH".into()); - } - let manifest = load_manifest(repository)?; - if manifest.persona_id != record.persona_id - || manifest.current_checkpoint != checkpoint_relative - { - return Err("PERSONA_RECOVERY_MANIFEST_MISMATCH".into()); - } - validate_git_identity(&manifest.git_identity)?; - let author = git_output( - repository, - &["show", "-s", "--format=%an%n%ae%n%B", observed_head], - "PERSONA_RECOVERY_COMMIT_METADATA", - )?; - let mut lines = author.lines(); - if lines.next() != Some(manifest.git_identity.author_name.as_str()) - || lines.next() != Some(manifest.git_identity.author_email.as_str()) - || !author.contains(&format!( - "GuangHu-Human-Responsibility: {}", - record.attribution.human_responsibility_subject - )) - || !author.contains(&format!( - "GuangHu-Persona-Cognitive-Author: {}", - record.attribution.persona_cognitive_author - )) - || !author.contains(&format!( - "GuangHu-Development-ID: {}", - record.attribution.development_id - )) - { - return Err("PERSONA_RECOVERY_COMMIT_ATTRIBUTION_MISMATCH".into()); - } - Ok(checkpoint_relative) -} - -fn recovery_disposition( - repository: &Path, - record: &PersonaSessionRecord, - observed_head: &str, - clean: bool, - lease_held: bool, - last_event: &str, -) -> &'static str { - if record.state.starts_with("DORMANT") { - return if lease_held && clean && observed_head == record.git_head && last_event == "DORMANT" - { - "SAFE_RELEASE_DORMANT_LEASE" - } else if lease_held { - "MANUAL_REVIEW_REQUIRED" - } else { - "ALREADY_DORMANT" - }; - } - if !lease_held - || !clean - || !matches!( - record.state.as_str(), - "BOUND_NOT_INFERENCING" | "INFERENCING" - ) - { - return "MANUAL_REVIEW_REQUIRED"; - } - if observed_head == record.git_head { - return "SAFE_ABORT_UNCOMMITTED"; - } - if validate_recoverable_persona_commit(repository, record, observed_head).is_ok() { - "SAFE_FINALIZE_COMMITTED" - } else { - "MANUAL_REVIEW_REQUIRED" - } -} - -fn inspect_session_at( - runtime_root: &Path, - input: PersonaSessionControlInput, -) -> Result { - let (record, repository, observed_head, clean) = - validate_session_control(runtime_root, &input)?; - let events = verify_event_journal(runtime_root, &record)?; - let lease_held = primary_lease_held_by_session(runtime_root, &record)?; - let last_event = events - .last() - .expect("verified non-empty event chain") - .kind - .clone(); - let disposition = recovery_disposition( - &repository, - &record, - &observed_head, - clean, - lease_held, - &last_event, - ); - Ok(PersonaSessionInspectionReceipt { - schema: "hololake.pncc-session-inspection/v1", - session_id: record.session_id, - persona_id: record.persona_id, - repository_path: repository.to_string_lossy().into_owned(), - recorded_git_head: record.git_head, - observed_git_head: observed_head, - repository_clean: clean, - session_state: record.state, - primary_lease_held: lease_held, - event_chain_valid: true, - last_event, - recovery_disposition: disposition, - events, - attribution: record.attribution, - }) -} - -fn recover_session_at( - runtime_root: &Path, - input: PersonaSessionControlInput, - timestamp: &str, -) -> Result { - let timestamp = validated_text("TIMESTAMP", timestamp, MAX_ID_BYTES)?; - let (mut record, repository, observed_head, clean) = - validate_session_control(runtime_root, &input)?; - let lease_path = require_primary_lease(runtime_root, &record)?; - if !clean { - return Err("PERSONA_RECOVERY_REFUSED_DIRTY_REPOSITORY".into()); - } - let mut events = verify_event_journal(runtime_root, &record)?; - let previous_state = record.state.clone(); - if record.state.starts_with("DORMANT") { - if observed_head != record.git_head - || events.last().map(|event| event.kind.as_str()) != Some("DORMANT") - { - return Err("PERSONA_DORMANT_LEASE_REQUIRES_MANUAL_REVIEW".into()); - } - fs::remove_file(&lease_path) - .map_err(|error| format!("PERSONA_PRIMARY_LEASE_RELEASE_FAILED: {error}"))?; - let receipt_hash = &events - .last() - .expect("verified non-empty event chain") - .event_hash; - return Ok(PersonaSessionRecoveryReceipt { - schema: "hololake.pncc-session-recovery-receipt/v1", - receipt_id: format!("PNCC-RECOVERY-{}", &receipt_hash[..20]), - session_id: record.session_id, - persona_id: record.persona_id, - previous_session_state: previous_state, - recovered_git_head: observed_head, - checkpoint_path: record.checkpoint_path, - runtime_state: "DORMANT_AFTER_RECOVERY", - recovery_action: "RELEASED_DORMANT_LEASE", - primary_lease_released: true, - events, - attribution: record.attribution, - }); - } - if !matches!( - record.state.as_str(), - "BOUND_NOT_INFERENCING" | "INFERENCING" - ) { - return Err("PERSONA_SESSION_STATE_REQUIRES_MANUAL_REVIEW".into()); - } - let (action, checkpoint_path) = if observed_head == record.git_head { - ( - "ABORTED_UNCOMMITTED_SESSION", - record.checkpoint_path.clone(), - ) - } else { - ( - "FINALIZED_PERSONA_COMMIT", - validate_recoverable_persona_commit(&repository, &record, &observed_head)?, - ) - }; - let organ = record.active_organ.clone(); - append_event( - runtime_root, - &record, - &mut events, - "SESSION_RECOVERY_REQUESTED", - &observed_head, - ×tamp, - Some(&organ), - )?; - if action == "FINALIZED_PERSONA_COMMIT" - && !events - .iter() - .any(|event| event.kind == "CHECKPOINT_COMMITTED") - { - append_event( - runtime_root, - &record, - &mut events, - "CHECKPOINT_COMMITTED", - &observed_head, - ×tamp, - Some(&organ), - )?; - } - append_event( - runtime_root, - &record, - &mut events, - "ORGAN_RELEASED", - &observed_head, - ×tamp, - Some(&organ), - )?; - let dormant = append_event( - runtime_root, - &record, - &mut events, - "DORMANT", - &observed_head, - ×tamp, - None, - )?; - record.git_head = observed_head.clone(); - record.checkpoint_path = checkpoint_path.clone(); - record.state = "DORMANT_AFTER_RECOVERY".into(); - write_session_record(runtime_root, &record)?; - fs::remove_file(&lease_path) - .map_err(|error| format!("PERSONA_PRIMARY_LEASE_RELEASE_FAILED: {error}"))?; - Ok(PersonaSessionRecoveryReceipt { - schema: "hololake.pncc-session-recovery-receipt/v1", - receipt_id: format!("PNCC-RECOVERY-{}", &dormant.event_hash[..20]), - session_id: record.session_id, - persona_id: record.persona_id, - previous_session_state: previous_state, - recovered_git_head: observed_head, - checkpoint_path, - runtime_state: "DORMANT_AFTER_RECOVERY", - recovery_action: action, - primary_lease_released: true, - events, - attribution: record.attribution, - }) -} - fn run_fact_task_at( runtime_root: &Path, input: PersonaFactTaskInput, @@ -1395,7 +1014,7 @@ where return Err("PERSONA_GIT_CHANGED_AFTER_WAKE".into()); } require_clean_repository(&exact_repository)?; - let mut events = read_ready_event_journal(runtime_root, &record)?; + let mut events = read_event_journal(runtime_root, &record)?; let inference_event = append_event( runtime_root, &record, @@ -1633,23 +1252,6 @@ pub async fn run_persona_code_channel_fact_task( .map_err(|error| format!("PERSONA_FACT_TASK_JOIN_FAILED: {error}"))? } -#[tauri::command] -pub fn inspect_persona_code_channel_session( - input: PersonaSessionControlInput, -) -> Result { - let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; - inspect_session_at(&runtime_root, input) -} - -#[tauri::command] -pub fn recover_persona_code_channel_session( - input: PersonaSessionControlInput, -) -> Result { - let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; - let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true); - recover_session_at(&runtime_root, input, ×tamp) -} - #[cfg(test)] mod tests { use super::*; @@ -1776,14 +1378,6 @@ mod tests { } } - fn session_control(repo: &Path, session_id: &str) -> PersonaSessionControlInput { - PersonaSessionControlInput { - session_id: session_id.into(), - expected_persona_id: "ICE-P-ZY001".into(), - expected_repository_path: repo.to_string_lossy().into_owned(), - } - } - #[test] fn prepares_exact_persona_git_wake_with_hash_chained_events() { let repo = persona_repo(); @@ -2019,162 +1613,4 @@ mod tests { let session = load_session_record(runtime.path(), "PNCC-SESSION-REJECTED").unwrap(); assert_eq!(session.state, "DORMANT_AFTER_FAILURE"); } - - #[test] - fn inspects_and_safely_aborts_an_uncommitted_interrupted_session() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-INTERRUPTED", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - - let inspection = inspect_session_at( - runtime.path(), - session_control(repo.path(), "PNCC-SESSION-INTERRUPTED"), - ) - .unwrap(); - assert_eq!(inspection.recovery_disposition, "SAFE_ABORT_UNCOMMITTED"); - assert!(inspection.repository_clean); - assert!(inspection.primary_lease_held); - - let receipt = recover_session_at( - runtime.path(), - session_control(repo.path(), "PNCC-SESSION-INTERRUPTED"), - "2026-08-11T00:01:00.000Z", - ) - .unwrap(); - assert_eq!(receipt.recovery_action, "ABORTED_UNCOMMITTED_SESSION"); - assert_eq!(receipt.runtime_state, "DORMANT_AFTER_RECOVERY"); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - assert_eq!(receipt.events.last().unwrap().kind, "DORMANT"); - } - - #[test] - fn refuses_recovery_when_the_persona_repository_is_dirty() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-DIRTY-RECOVERY", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - fs::write(repo.path().join("unknown-change.txt"), "do not erase\n").unwrap(); - - let inspection = inspect_session_at( - runtime.path(), - session_control(repo.path(), "PNCC-SESSION-DIRTY-RECOVERY"), - ) - .unwrap(); - assert_eq!(inspection.recovery_disposition, "MANUAL_REVIEW_REQUIRED"); - let error = recover_session_at( - runtime.path(), - session_control(repo.path(), "PNCC-SESSION-DIRTY-RECOVERY"), - "2026-08-11T00:01:00.000Z", - ) - .unwrap_err(); - assert!(error.contains("REFUSED_DIRTY_REPOSITORY")); - assert!(runtime.path().join("leases/ICE-P-ZY001.json").exists()); - } - - #[test] - fn finalizes_an_attributed_persona_commit_interrupted_before_dormancy() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let session_id = "PNCC-SESSION-COMMIT-INTERRUPTED"; - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - session_id, - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - let repository = repo.path().canonicalize().unwrap(); - let record = load_session_record(runtime.path(), session_id).unwrap(); - let checkpoint_relative = format!(".hololake/persona/checkpoints/{session_id}.json"); - let checkpoint = serde_json::json!({ - "schema": "hololake.persona-checkpoint/v1", - "sessionId": session_id, - "personaId": record.persona_id, - "previousGitHead": record.git_head, - "createdAt": "2026-08-11T00:00:01.000Z", - "result": {"summary": "fixture"} - }); - write_json_file( - &repository.join(&checkpoint_relative), - &checkpoint, - "TEST_CHECKPOINT", - ) - .unwrap(); - let mut manifest = load_manifest(&repository).unwrap(); - manifest.current_checkpoint = checkpoint_relative.clone(); - write_json_file(&repository.join(MANIFEST_PATH), &manifest, "TEST_MANIFEST").unwrap(); - let committed_head = - persona_git_commit(&repository, &manifest, &checkpoint_relative, &record).unwrap(); - - let inspection = - inspect_session_at(runtime.path(), session_control(repo.path(), session_id)).unwrap(); - assert_eq!(inspection.recovery_disposition, "SAFE_FINALIZE_COMMITTED"); - let receipt = recover_session_at( - runtime.path(), - session_control(repo.path(), session_id), - "2026-08-11T00:01:00.000Z", - ) - .unwrap(); - assert_eq!(receipt.recovery_action, "FINALIZED_PERSONA_COMMIT"); - assert_eq!(receipt.recovered_git_head, committed_head); - assert_eq!(receipt.checkpoint_path, checkpoint_relative); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - require_clean_repository(repo.path()).unwrap(); - } - - #[test] - fn releases_an_exact_lease_left_after_the_session_became_dormant() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let session_id = "PNCC-SESSION-DORMANT-LEASE"; - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - session_id, - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - recover_session_at( - runtime.path(), - session_control(repo.path(), session_id), - "2026-08-11T00:01:00.000Z", - ) - .unwrap(); - let record = load_session_record(runtime.path(), session_id).unwrap(); - acquire_primary_lease( - runtime.path(), - &record.persona_id, - &record.session_id, - &record.git_head, - &record.node_id, - "2026-08-11T00:01:01.000Z", - ) - .unwrap(); - - let inspection = - inspect_session_at(runtime.path(), session_control(repo.path(), session_id)).unwrap(); - assert_eq!( - inspection.recovery_disposition, - "SAFE_RELEASE_DORMANT_LEASE" - ); - let receipt = recover_session_at( - runtime.path(), - session_control(repo.path(), session_id), - "2026-08-11T00:02:00.000Z", - ) - .unwrap(); - assert_eq!(receipt.recovery_action, "RELEASED_DORMANT_LEASE"); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - } } diff --git a/routing/hololake-current-architecture.json b/routing/hololake-current-architecture.json index 36ef7654a..6b39e1904 100644 --- a/routing/hololake-current-architecture.json +++ b/routing/hololake-current-architecture.json @@ -118,7 +118,7 @@ "human_projection": "HOLOLAKE_LIVE_READ_MODEL", "forgejo_role": "OPTIONAL_COMPATIBILITY_COLLABORATION_ADAPTER", "runtime_implemented": true, - "runtime_scope": "FIRST_READ_ONLY_FACT_CYCLE_AND_FAIL_CLOSED_INTERRUPTED_SESSION_RECOVERY_SOURCE_IMPLEMENTED_AND_TESTED", + "runtime_scope": "FIRST_READ_ONLY_FACT_CYCLE_SOURCE_IMPLEMENTED_AND_TESTED", "desktop_integrated": false, "development_id": "DEV-20260810-014" }, @@ -343,7 +343,7 @@ "DEV-20260810-013" ], "closeout_record": "HLP-DEV-20260809-007-CLOSEOUT-001", - "next_minimum_stage": "GH_PNCC_GENERAL_PURPOSE_ORGAN_CONTRACT_WITHOUT_UI", + "next_minimum_stage": "GH_PNCC_FIRST_REAL_READ_ONLY_WAKE_CLOSED_LOOP", "next_stage_started": true, "heartbeat_automation": "pncc", "heartbeat_state": "ACTIVE_EVERY_10_MINUTES_UNTIL_TASK_TERMINAL" diff --git a/routing/hololake-persona-native-code-channel.json b/routing/hololake-persona-native-code-channel.json index 218acc22e..3bbb458e6 100644 --- a/routing/hololake-persona-native-code-channel.json +++ b/routing/hololake-persona-native-code-channel.json @@ -1,8 +1,8 @@ { "schema": "hololake.persona-native-code-channel/v1", "record_id": "HLP-PERSONA-NATIVE-CODE-CHANNEL-001", - "version": "2026-08-11.2", - "state": "CURRENT_FIRST_PRODUCT_CORE_READ_ONLY_RUNTIME_AND_INTERRUPTED_SESSION_RECOVERY_SOURCE_IMPLEMENTED", + "version": "2026-08-11.1", + "state": "CURRENT_FIRST_PRODUCT_CORE_FIRST_READ_ONLY_RUNTIME_CYCLE_SOURCE_IMPLEMENTED", "development_id": "DEV-20260810-014", "product": { "formal_name_zh": "光湖人格原生代码频道", @@ -101,7 +101,6 @@ "machine_navigation_registered": 100, "first_read_only_runtime_cycle_source_implemented": 100, "persona_git_identity_commit_implemented": 100, - "interrupted_session_inspection_and_recovery_source_implemented": 100, "general_purpose_persona_runtime_implemented": 0, "human_live_projection_implemented": 0, "hololake_integrated": 0, diff --git a/routing/hololake-persona-native-code-channel.test.mjs b/routing/hololake-persona-native-code-channel.test.mjs index 30443c621..adbacf108 100644 --- a/routing/hololake-persona-native-code-channel.test.mjs +++ b/routing/hololake-persona-native-code-channel.test.mjs @@ -69,16 +69,12 @@ test("the first source runtime cycle stays distinct from integration and deploym assert.equal(channel.truth.architecture_registered, 100); assert.equal(channel.truth.first_read_only_runtime_cycle_source_implemented, 100); assert.equal(channel.truth.persona_git_identity_commit_implemented, 100); - assert.equal( - channel.truth.interrupted_session_inspection_and_recovery_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, - "FIRST_READ_ONLY_FACT_CYCLE_AND_FAIL_CLOSED_INTERRUPTED_SESSION_RECOVERY_SOURCE_IMPLEMENTED_AND_TESTED", + "FIRST_READ_ONLY_FACT_CYCLE_SOURCE_IMPLEMENTED_AND_TESTED", ); assert.equal(channel.truth.hololake_integrated, 0); assert.equal(channel.truth.artifact_built, 0);