feat: query verified PNCC runtime history
GuangHu-Human-Responsibility: ICE-GL∞ / 冰朔 GuangHu-Persona-Cognitive-Author: ICE-P-ZY001 / 铸渊 GuangHu-Execution-Runtime: Codex thread 019febf6-3902-70f3-9d82-74c9680b0c7e GuangHu-Development-ID: DEV-20260810-014 GuangHu-Authorization-Scope: LOCAL_PNCC_RUNTIME_DEVELOPMENT_AND_PUBLICATION
This commit is contained in:
parent
c3e7dbff10
commit
33b3b67ce7
10 changed files with 289 additions and 3 deletions
|
|
@ -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) | 源码与测试闭环已完成;人类投影、桌面制品、发布、部署与在线健康仍未完成 |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 头、节点、模型实例、活跃器官、事件数量、链头和
|
||||
双层归因。该查询不复制第二套数据库、不启动模型、不取得主锁;匹配会话的事件链损坏时失败关闭。
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<PersonaOrganContract>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PersonaRuntimeQueryInput {
|
||||
pub expected_persona_id: String,
|
||||
pub expected_repository_path: String,
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
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<PersonaRuntimeSessionProjection>,
|
||||
}
|
||||
|
||||
#[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<PersonaRuntimeQueryReceipt, String> {
|
||||
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<PersonaRuntimeQueryReceipt, String> {
|
||||
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<usize>) -> 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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue