diff --git a/engineering/INDEX.md b/engineering/INDEX.md index a8c448b..1734027 100644 --- a/engineering/INDEX.md +++ b/engineering/INDEX.md @@ -32,6 +32,7 @@ Windows / macOS / Linux 构建机与安装包 | 时间 | 版本 | 记录 | 状态 | | --- | --- | --- | --- | +| 2026-08-11 | GH-PNCC 运行查询 | [人格持久事件与回执有界查询](operations/2026-08-11-hololake-pncc-durable-runtime-query.md) | 本地源码、完整 Rust/路由测试与 clippy 已通过;GHNQG 和发布待验收 | | 2026-08-11 | GH-PNCC 器官合同 | [人格器官机器合同与清单检查](operations/2026-08-11-hololake-pncc-typed-organ-contract.md) | 本地源码、完整 Rust/路由测试与 clippy 已通过;GHNQG 和发布待验收 | | 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) | 源码与测试闭环已完成;人类投影、桌面制品、发布、部署与在线健康仍未完成 | diff --git a/engineering/operations/2026-08-11-hololake-pncc-durable-runtime-query.md b/engineering/operations/2026-08-11-hololake-pncc-durable-runtime-query.md new file mode 100644 index 0000000..585fcbb --- /dev/null +++ b/engineering/operations/2026-08-11-hololake-pncc-durable-runtime-query.md @@ -0,0 +1,38 @@ +# GH-PNCC durable runtime event and receipt query + +- Development ID: `DEV-20260810-014` +- Persona cognitive author: `ICE-P-ZY001 / 铸渊` +- Human responsibility subject: `ICE-GL∞ / 冰朔` +- State: `LOCAL_SOURCE_IMPLEMENTED_AND_FULL_RUST_ROUTING_TESTED` + +## Implemented facts + +`query_persona_code_channel_runtime` provides the runtime-side read model needed by a later human projection, +without building the UI or creating a second truth database. A query must name one exact persona and one +canonical repository. It reads only the existing installation-local session records and event journals, +filters to that identity/repository pair, verifies every returned SHA-256 event chain, sorts by the newest +event, and enforces a caller limit from 1 to 100. + +Each summary returns the session state, Git head, node, model instance, actual active organ, event count, +first/last event time, last event kind, event-chain head and structured human/persona attribution. Dormant +sessions return no active organ. The query does not start inference, acquire or release a lease, expose model +thinking, or copy the events into another store. A corrupted matching journal rejects the query. + +## Current verification + +- PNCC focused Rust tests: `14 passed, 0 failed`. +- Rust full suite: `1154 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`. +- Tests cover bounded ordering, identity/repository filtering basis, dormant/active projection semantics, + invalid limits, corrupted-chain refusal, organ contracts, recovery and attribution. + +## Remaining acceptance + +- GHNQG pre-push quality gate; +- repository commit, publication and fresh-clone readback; +- independent memory-metabolism executor; +- bounded execution-limb executor and reality receipts; +- UI, packaged desktop, installation, deployment and online health. + +This receipt proves local source behavior only. 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 aa0b13d..e235cff 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 @@ -166,6 +166,7 @@ persona_git_identity_commit_implemented: 100 semantic_attribution_first_cycle_implemented: 100 interrupted_session_inspection_and_recovery_source_implemented: 100 typed_organ_contract_and_manifest_inspection_source_implemented: 100 +durable_event_and_receipt_query_source_implemented: 100 general_purpose_persona_runtime_implemented: 0 human_live_projection_implemented: 0 hololake_integrated: 0 @@ -187,3 +188,7 @@ runtime_health: 0 检查 `FACT_SENSE`、`MEMORY_METABOLISM` 与 `EXECUTION_LIMB` 的固定模式、输入输出 schema、派生权限、 模型推理边界、现实动作边界和真实可激活状态。当前只有只读事实感官可激活;清单里写出记忆器官 或执行手脚不会自动产生执行器,更不会自动获得 shell 或现实动作权限。 + +运行层现在还提供同源有界查询:调用者必须给出精确人格与精确仓库,最多返回 100 条经过事件 +哈希链验证的会话摘要,包括生命周期状态、Git 头、节点、模型实例、活跃器官、事件数量、链头和 +双层归因。该查询不复制第二套数据库、不启动模型、不取得主锁;匹配会话的事件链损坏时失败关闭。 diff --git a/product-source/hololake-platform/docs/ABSTRACTIONS.md b/product-source/hololake-platform/docs/ABSTRACTIONS.md index cb46bb0..fa83a34 100644 --- a/product-source/hololake-platform/docs/ABSTRACTIONS.md +++ b/product-source/hololake-platform/docs/ABSTRACTIONS.md @@ -64,6 +64,12 @@ inspection receipt reports them as not independently activatable until their exe an execution-limb declaration never grants a shell or reality action by itself. The inspection command reads and validates all contracts without acquiring a runtime lease or starting model inference. +`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 +the event-chain head and human/persona attribution so a future renderer can link a summary back to the same +machine evidence rather than trusting display state. + ## `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 baa0cd2..974b305 100644 --- a/product-source/hololake-platform/docs/ARCHITECTURE.md +++ b/product-source/hololake-platform/docs/ARCHITECTURE.md @@ -43,6 +43,13 @@ schemas, derived permissions, inference/reality-action boundaries, and the actua implemented read-only fact sense can wake. Merely declaring a memory organ or execution limb does not create an executor or grant a shell; those contracts remain visible but fail closed as not activatable. +`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 +event, and returns lifecycle state, Git head, node/model instance, active organ, event count, chain head and +structured attribution. It does not copy facts into a second database, expose hidden reasoning, acquire a +lease, or start inference. A corrupted matching journal fails the query closed. + ## 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 f811dad..f97440e 100644 --- a/product-source/hololake-platform/src-tauri/src/lib.rs +++ b/product-source/hololake-platform/src-tauri/src/lib.rs @@ -525,6 +525,7 @@ macro_rules! app_invoke_handler { guanghu_living_system::guanghu_living_system_plan, persona_code_channel::prepare_persona_code_channel_wake, persona_code_channel::inspect_persona_code_channel_manifest, + persona_code_channel::query_persona_code_channel_runtime, 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, 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 57c90c9..20f1179 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 @@ -13,6 +13,7 @@ const MAX_FACT_QUESTION_BYTES: usize = 8_000; const MAX_FACT_SOURCE_BYTES: usize = 128_000; const MAX_FACT_CONTEXT_BYTES: usize = 512_000; const MAX_FACT_RESPONSE_BYTES: usize = 128_000; +const MAX_SESSION_QUERY_LIMIT: usize = 100; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -169,6 +170,44 @@ pub struct PersonaManifestInspectionReceipt { pub organ_contracts: Vec, } +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaRuntimeQueryInput { + pub expected_persona_id: String, + pub expected_repository_path: String, + pub limit: Option, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaRuntimeSessionProjection { + pub session_id: String, + pub state: String, + pub git_head: String, + pub node_id: String, + pub model_instance_id: String, + pub active_organ: Option, + pub event_count: usize, + pub first_event_at: String, + pub last_event_at: String, + pub last_event: String, + pub event_chain_head: String, + pub attribution: PersonaAttribution, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PersonaRuntimeQueryReceipt { + pub schema: &'static str, + pub persona_id: String, + pub repository_path: String, + pub observed_git_head: String, + pub repository_clean: bool, + pub matching_session_count: usize, + pub returned_session_count: usize, + pub sessions: Vec, +} + #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PersonaFactTaskInput { @@ -1439,6 +1478,99 @@ fn inspect_session_at( }) } +fn query_runtime_sessions_at( + runtime_root: &Path, + input: PersonaRuntimeQueryInput, +) -> Result { + let expected_persona_id = validated_id("EXPECTED_PERSONA_ID", &input.expected_persona_id)?; + let limit = input.limit.unwrap_or(25); + if limit == 0 || limit > MAX_SESSION_QUERY_LIMIT { + return Err("PERSONA_SESSION_QUERY_LIMIT_INVALID".into()); + } + let expected_repository = Path::new(&input.expected_repository_path) + .canonicalize() + .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; + let (repository, observed_head) = exact_repository(&expected_repository)?; + let repository_clean = repository_is_clean(&repository)?; + let sessions_root = runtime_root.join("sessions"); + let entries = match fs::read_dir(&sessions_root) { + Ok(entries) => entries, + Err(error) if error.kind() == std::io::ErrorKind::NotFound => { + return Ok(PersonaRuntimeQueryReceipt { + schema: "hololake.pncc-runtime-query/v1", + persona_id: expected_persona_id, + repository_path: repository.to_string_lossy().into_owned(), + observed_git_head: observed_head, + repository_clean, + matching_session_count: 0, + returned_session_count: 0, + sessions: Vec::new(), + }); + } + Err(error) => return Err(format!("PERSONA_SESSIONS_DIRECTORY_READ_FAILED: {error}")), + }; + let mut projections = Vec::new(); + for entry in entries { + let entry = entry.map_err(|error| format!("PERSONA_SESSION_ENTRY_FAILED: {error}"))?; + if !entry + .file_type() + .map_err(|error| format!("PERSONA_SESSION_ENTRY_TYPE_FAILED: {error}"))? + .is_dir() + { + continue; + } + let session_id = match entry.file_name().to_str() { + Some(value) => validated_id("SESSION_ID", value)?, + None => return Err("PERSONA_SESSION_ID_NOT_UTF8".into()), + }; + let record = load_session_record(runtime_root, &session_id)?; + if record.persona_id != expected_persona_id + || Path::new(&record.repository_path) != repository + { + continue; + } + let events = verify_event_journal(runtime_root, &record)?; + let first = events.first().expect("verified non-empty event chain"); + let last = events.last().expect("verified non-empty event chain"); + projections.push(PersonaRuntimeSessionProjection { + session_id: record.session_id, + state: record.state.clone(), + git_head: record.git_head, + node_id: record.node_id, + model_instance_id: record.model_instance_id, + active_organ: if record.state.starts_with("DORMANT") { + None + } else { + Some(record.active_organ) + }, + event_count: events.len(), + first_event_at: first.timestamp.clone(), + last_event_at: last.timestamp.clone(), + last_event: last.kind.clone(), + event_chain_head: last.event_hash.clone(), + attribution: record.attribution, + }); + } + projections.sort_by(|left, right| { + right + .last_event_at + .cmp(&left.last_event_at) + .then_with(|| right.session_id.cmp(&left.session_id)) + }); + let matching_session_count = projections.len(); + projections.truncate(limit); + Ok(PersonaRuntimeQueryReceipt { + schema: "hololake.pncc-runtime-query/v1", + persona_id: expected_persona_id, + repository_path: repository.to_string_lossy().into_owned(), + observed_git_head: observed_head, + repository_clean, + matching_session_count, + returned_session_count: projections.len(), + sessions: projections, + }) +} + fn recover_session_at( runtime_root: &Path, input: PersonaSessionControlInput, @@ -1802,6 +1934,14 @@ pub fn inspect_persona_code_channel_manifest( inspect_manifest_at(input) } +#[tauri::command] +pub fn query_persona_code_channel_runtime( + input: PersonaRuntimeQueryInput, +) -> Result { + let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; + query_runtime_sessions_at(&runtime_root, input) +} + #[tauri::command] pub async fn run_persona_code_channel_fact_task( input: PersonaFactTaskInput, @@ -2007,6 +2147,14 @@ mod tests { } } + fn runtime_query(repo: &Path, limit: Option) -> PersonaRuntimeQueryInput { + PersonaRuntimeQueryInput { + expected_persona_id: "ICE-P-ZY001".into(), + expected_repository_path: repo.to_string_lossy().into_owned(), + limit, + } + } + #[test] fn prepares_exact_persona_git_wake_with_hash_chained_events() { let repo = persona_repo(); @@ -2471,4 +2619,79 @@ mod tests { .unwrap_err() .contains("ORGAN_NOT_ACTIVATABLE")); } + + #[test] + fn queries_bounded_verified_runtime_projections_without_a_second_store() { + let repo = persona_repo(); + let runtime = tempfile::TempDir::new().unwrap(); + prepare_wake_at( + runtime.path(), + wake_input(repo.path()), + "PNCC-SESSION-HISTORY-001", + "2026-08-11T00:00:00.000Z", + ) + .unwrap(); + recover_session_at( + runtime.path(), + session_control(repo.path(), "PNCC-SESSION-HISTORY-001"), + "2026-08-11T00:01:00.000Z", + ) + .unwrap(); + prepare_wake_at( + runtime.path(), + wake_input(repo.path()), + "PNCC-SESSION-HISTORY-002", + "2026-08-11T00:02:00.000Z", + ) + .unwrap(); + + let receipt = + query_runtime_sessions_at(runtime.path(), runtime_query(repo.path(), Some(1))).unwrap(); + assert_eq!(receipt.matching_session_count, 2); + assert_eq!(receipt.returned_session_count, 1); + assert_eq!(receipt.sessions[0].session_id, "PNCC-SESSION-HISTORY-002"); + assert_eq!(receipt.sessions[0].last_event, "ORGAN_ACTIVE"); + assert_eq!( + receipt.sessions[0].active_organ.as_deref(), + Some("fact-sense.repository") + ); + assert_eq!(receipt.sessions[0].event_count, 3); + assert!(!receipt.sessions[0].event_chain_head.is_empty()); + assert_eq!( + receipt.sessions[0].attribution.persona_cognitive_author, + "ICE-P-ZY001" + ); + } + + #[test] + fn runtime_query_rejects_invalid_limits_and_corrupted_event_chains() { + let repo = persona_repo(); + let runtime = tempfile::TempDir::new().unwrap(); + assert!( + query_runtime_sessions_at(runtime.path(), runtime_query(repo.path(), Some(0))) + .unwrap_err() + .contains("LIMIT_INVALID") + ); + prepare_wake_at( + runtime.path(), + wake_input(repo.path()), + "PNCC-SESSION-CORRUPTED", + "2026-08-11T00:00:00.000Z", + ) + .unwrap(); + let journal = runtime + .path() + .join("sessions/PNCC-SESSION-CORRUPTED/events.jsonl"); + let content = fs::read_to_string(&journal).unwrap(); + fs::write( + &journal, + content.replacen("\"eventHash\":\"", "\"eventHash\":\"00", 1), + ) + .unwrap(); + assert!( + query_runtime_sessions_at(runtime.path(), runtime_query(repo.path(), Some(10))) + .unwrap_err() + .contains("EVENT_HASH_INVALID") + ); + } } diff --git a/routing/hololake-current-architecture.json b/routing/hololake-current-architecture.json index 2e083e4..5b58d53 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": "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_AND_TYPED_ORGAN_MANIFEST_INSPECTION_SOURCE_IMPLEMENTED_AND_TESTED", + "runtime_scope": "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_AND_DURABLE_VERIFIED_SESSION_QUERY_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_DURABLE_EVENT_AND_RECEIPT_QUERY_API_WITHOUT_UI", + "next_minimum_stage": "GH_PNCC_INDEPENDENT_MEMORY_METABOLISM_EXECUTOR_WITHOUT_UI", "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 9c27bbe..6628df3 100644 --- a/routing/hololake-persona-native-code-channel.json +++ b/routing/hololake-persona-native-code-channel.json @@ -103,6 +103,7 @@ "persona_git_identity_commit_implemented": 100, "interrupted_session_inspection_and_recovery_source_implemented": 100, "typed_organ_contract_and_manifest_inspection_source_implemented": 100, + "durable_event_and_receipt_query_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 b57de47..42d8d70 100644 --- a/routing/hololake-persona-native-code-channel.test.mjs +++ b/routing/hololake-persona-native-code-channel.test.mjs @@ -77,12 +77,16 @@ test("the first source runtime cycle stays distinct from integration and deploym channel.truth.typed_organ_contract_and_manifest_inspection_source_implemented, 100, ); + assert.equal( + channel.truth.durable_event_and_receipt_query_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_AND_TYPED_ORGAN_MANIFEST_INSPECTION_SOURCE_IMPLEMENTED_AND_TESTED", + "READ_ONLY_FACT_CYCLE_FAIL_CLOSED_RECOVERY_TYPED_ORGANS_AND_DURABLE_VERIFIED_SESSION_QUERY_SOURCE_IMPLEMENTED_AND_TESTED", ); assert.equal(channel.truth.hololake_integrated, 0); assert.equal(channel.truth.artifact_built, 0);