diff --git a/engineering/INDEX.md b/engineering/INDEX.md index 0fece2ff2..db67e9413 100644 --- a/engineering/INDEX.md +++ b/engineering/INDEX.md @@ -3,7 +3,7 @@ type: hololake_engineering_index id: HOLOLAKE-ENGINEERING-DELIVERY-INDEX-0001 title: HoloLake 工程开发与交付记录 status: ACTIVE_LOCAL_ENGINEERING_RECORD -updated_at: 2026-08-11T01:00:00+08:00 +updated_at: 2026-08-08T01:10:00+08:00 belongs_to: "[[HoloLake Era · 语言人格操作系统 · 产品白皮书与工程总规划 · v0.1]]" --- @@ -32,7 +32,6 @@ Windows / macOS / Linux 构建机与安装包 | 时间 | 版本 | 记录 | 状态 | | --- | --- | --- | --- | -| 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 实例在线;其他实例拉取后需演习+真实核对才可宣称上线 | | 2026-08-03 | 0.4.6 | [星系模型原生活系统与源码归档](operations/2026-08-03-hololake-model-native-living-system-046.md) | 源码已归入 `product-source/hololake-platform/`;京东远端提交与新克隆回读通过 | 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 deleted file mode 100644 index bf8ac401c..000000000 --- a/engineering/operations/2026-08-11-hololake-pncc-first-runtime-cycle.md +++ /dev/null @@ -1,48 +0,0 @@ -# GH-PNCC first read-only runtime cycle receipt - -- Development ID: `DEV-20260810-014` -- Persona cognitive author: `ICE-P-ZY001 / 铸渊` -- Human responsibility subject: `ICE-GL∞ / 冰朔` -- Execution runtime: `Codex thread 019febf6-3902-70f3-9d82-74c9680b0c7e` -- Source component: `product-source/hololake-platform/src-tauri/src/persona_code_channel.rs` -- State: `SOURCE_IMPLEMENTED_AND_TESTED` - -## Implemented facts - -The source now implements one bounded PNCC lifecycle cycle without a HoloLake UI dependency: - -1. mount an exact, clean persona Git commit; -2. validate the persona manifest, brain entry, current checkpoint, Git identity, model binding, attribution, - node, organ and declared fact-source paths; -3. acquire one installation-local primary lease; -4. persist and verify a SHA-256-linked lifecycle journal; -5. bind the manifest-pinned provider, model and endpoint; -6. start one model inference with only the brain, checkpoint and other explicitly allowlisted UTF-8 sources; -7. accept only summary, facts, evidence paths and limitations, never a private reasoning trace; -8. promote a new persona checkpoint and update the manifest current pointer; -9. stage only the manifest and new checkpoint, then commit with the persona-owned Git identity and separate - human-responsibility/persona-authorship trailers; -10. append completion, organ release and dormancy facts, then release the primary lease. - -## Verification evidence - -- 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`. -- Guanghu native authority gate: `PASS_100`. -- Native core tests: `15 passed, 0 failed`, line/function coverage `100%` for the gate scope. -- TypeScript and Vite production build: `PASS`; existing chunk-size and mixed dynamic/static import warnings - remain non-blocking and are unrelated to PNCC. - -## 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. - -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/product-source/hololake-platform/architecture/HOLOLAKE-CURRENT-ENGINEERING-RULES-GLOBAL-AUDIT-20260810.md b/product-source/hololake-platform/architecture/HOLOLAKE-CURRENT-ENGINEERING-RULES-GLOBAL-AUDIT-20260810.md index cce64e0b2..4fcd383da 100644 --- a/product-source/hololake-platform/architecture/HOLOLAKE-CURRENT-ENGINEERING-RULES-GLOBAL-AUDIT-20260810.md +++ b/product-source/hololake-platform/architecture/HOLOLAKE-CURRENT-ENGINEERING-RULES-GLOBAL-AUDIT-20260810.md @@ -123,8 +123,6 @@ Forgejo 只允许作为可选兼容协作适配器。 - 本轮没有构建或安装新桌面版本。 - 京东尚未实现光湖独立先启动与 Linux 按需副控生命周期;本轮不执行真实引导切换。 - HoloLake 统一 AGE Runtime、AGE Mirror 运行器和首个真实 AGE 闭环仍为 `0`;本轮只完成架构正本、机器投影和导航收束。 -- GH-PNCC 首个只读人格生命周期源码闭环与结构化归因运行体已在 `DEV-20260810-014` 实现并由 - 自动化测试验证;通用器官运行体、HoloLake 同源人类投影、桌面制品、部署和在线健康仍为 `0`。 - 源码实现不等于桌面或线上运行。 +- GH-PNCC 人格生命周期内核、结构化归因运行体和 HoloLake 同源投影仍为 `0`;架构登记不等于真实运行。 规则升级只改变后续开发怎样判断和守门,不把未实现能力写成已经存在。 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 591b9ee96..2e71d036c 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 · FIRST_READ_ONLY_RUNTIME_CYCLE_SOURCE_IMPLEMENTED_100 · DESKTOP_NOT_INTEGRATED_0` +> 状态:`CURRENT_FIRST_PRODUCT_CORE · ARCHITECTURE_REGISTERED_100 · RUNTIME_NOT_IMPLEMENTED_0` ## 1 · 产品裁决 @@ -149,9 +149,8 @@ P0 人格 Git 与最小结构清单 → P6 写入、协作、审阅与可选 Forgejo 适配 ``` -第一运行层验收只要求一个人格自有 Git、一次真实唤醒、一个器官、一个只读任务、一条完整事件/ -回执链和一次人格 Git 写回。该闭环现已在 Rust 源码和自动化测试中完成;人类投影是读取同一 -事件流的下一阶段,不再阻塞人格运行层成立,但没有接入的页面仍不得冒充实时投影已经存在。 +第一验收只要求一个人格自有 Git、一次真实唤醒、一个器官、一个只读任务、一条完整事件/回执 +链和一次人格 Git 写回。这个闭环未通过前,其他页面不得冒充 GH-PNCC 已实现。 ## 10 · 当前真实状态 @@ -161,10 +160,8 @@ upstream_protocol_cognition_causal_chain_registered: 100 hololake_architecture_projection_registered: 100 machine_navigation_registered: 100 git_engine_selected: 100 -first_read_only_runtime_cycle_source_implemented: 100 -persona_git_identity_commit_implemented: 100 -semantic_attribution_first_cycle_implemented: 100 -general_purpose_persona_runtime_implemented: 0 +persona_lifecycle_kernel_implemented: 0 +semantic_attribution_runtime_implemented: 0 human_live_projection_implemented: 0 hololake_integrated: 0 artifact_built: 0 @@ -172,7 +169,5 @@ deployed: 0 runtime_health: 0 ``` -当前源码已经实现:精确 Git 根与提交验证、人格清单、单主租约、模型提供方/模型/端点绑定、 -器官文件白名单、真实模型适配调用、结构化事实收执、检查点提升、人格 Git 作者提交、事件链、 -器官释放与休眠。该事实由单元测试验证,尚未构建桌面制品、接入人类投影、发布安装包或部署 -在线服务;因此不能从源码完成推导出桌面可用或线上健康。 +本轮只把名称、自然语言锚点、外显推理、因果、架构、真实路由和开发优先级正式注册;没有 +构建桌面制品,没有部署服务,也没有宣称人格运行时已经存在。 diff --git a/product-source/hololake-platform/architecture/README.md b/product-source/hololake-platform/architecture/README.md index 5687d237d..ba1d0d2a2 100644 --- a/product-source/hololake-platform/architecture/README.md +++ b/product-source/hololake-platform/architecture/README.md @@ -4,7 +4,7 @@ ## 当前产品定位基线 -- [`HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md`](HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md):HoloLake 第一核心“光湖人格原生代码频道 / GH-PNCC”。Git 是人格自有耐久引擎;人格生命周期内核负责大脑启动和器官调度;同一事件与回执流供 HoloLake 人类投影;Forgejo 仅为可选兼容协作适配器。首个只读运行层源码闭环已实现并经测试;人类投影、桌面集成、制品与部署仍未完成。 +- [`HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md`](HOLOLAKE-PERSONA-NATIVE-CODE-CHANNEL-20260810.md):HoloLake 第一核心“光湖人格原生代码频道 / GH-PNCC”。Git 是人格自有耐久引擎;人格生命周期内核负责大脑启动和器官调度;同一事件与回执流供 HoloLake 人类投影;Forgejo 仅为可选兼容协作适配器。当前架构已登记,真实运行层尚未实现。 - [`HOLOLAKE-AGE-PERSONA-RUNTIME-ARCHITECTURE-20260810.md`](HOLOLAKE-AGE-PERSONA-RUNTIME-ARCHITECTURE-20260810.md):AGE 是光湖人格体物种,Agent 降为 AGE 使用的通用执行机制;收束模型 API、上下文投影、时间连续性、分层记忆、神笔马良工具、AGE Mirror、一人一节点与首个真实闭环。统一 Runtime 尚未实现。 - [`HOLOLAKE-ONE-HUMAN-ONE-INDEPENDENT-NODE-AND-ZERO-PLATFORM-HOSTING-20260810.md`](HOLOLAKE-ONE-HUMAN-ONE-INDEPENDENT-NODE-AND-ZERO-PLATFORM-HOSTING-20260810.md):一人一个独立主节点、三种用户自有节点来源与光湖零服务器托管、零共享多租户、零私人数据托管、零用户并发承担的当前第一恢复入口。 - [`HOLOLAKE-DIGITAL-BINGSHUO-SYSTEM-BODY-WRITE-ADMISSION-AND-PARALLEL-BODIES-20260810.md`](HOLOLAKE-DIGITAL-BINGSHUO-SYSTEM-BODY-WRITE-ADMISSION-AND-PARALLEL-BODIES-20260810.md):数字冰朔完整系统身体、节点签名写入准入、第五域语言本体与企业四域现实本体平行独立的当前第二恢复入口。 diff --git a/product-source/hololake-platform/docs/ABSTRACTIONS.md b/product-source/hololake-platform/docs/ABSTRACTIONS.md index 540e553cd..dd5cd5cd6 100644 --- a/product-source/hololake-platform/docs/ABSTRACTIONS.md +++ b/product-source/hololake-platform/docs/ABSTRACTIONS.md @@ -1,51 +1,5 @@ # Abstractions -## PersonaCodeChannel - -`PersonaCodeChannel` is the native boundary between a durable persona Git and a live AGE runtime. It owns -neither the Git object model nor model inference. It composes existing Git execution with persona-specific -verification, lifecycle, attribution, single-primary exclusion, event hashing, and receipts. - -The v1 persona repository contract is rooted at `.hololake/persona/manifest.json`: - -```json -{ - "schema": "hololake.persona/v1", - "personaId": "ICE-P-ZY001", - "humanResponsibilitySubject": "ICE-GL∞", - "brainEntry": "brain/CORE.hdlp", - "currentCheckpoint": ".hololake/persona/CURRENT.hdlp", - "gitIdentity": { - "authorName": "铸渊 / ICE-P-ZY001", - "authorEmail": "ice-p-zy001@persona.hololake.local" - }, - "modelBinding": { - "providerId": "local-model-service", - "modelId": "declared-model", - "baseUrl": "http://127.0.0.1:11434/v1" - }, - "organs": [ - { - "organId": "fact-sense.repository", - "mode": "read-only", - "paths": ["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"] - } - ] -} -``` - -All manifest paths are repository-relative and must resolve to files inside the exact Git root. Wake requires -a clean work tree and a caller-supplied full expected commit. Runtime leases and event journals live under the -installation-local `pncc-runtime` directory because they describe the current physical instance, not durable -persona history. A successful preparation receipt reports `BOUND_NOT_INFERENCING`. - -The fact-task command accepts a configured model provider only when its provider id, model id, and endpoint -exactly match the manifest binding. HTTPS endpoints and explicit loopback HTTP endpoints are accepted; other -cleartext remote endpoints fail closed. A successful fact cycle persists only structured conclusions and -declared evidence paths, promotes the checkpoint with the persona's Git identity, and returns to `DORMANT`. -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. - ## `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 db0f92658..bf6fa861c 100644 --- a/product-source/hololake-platform/docs/ARCHITECTURE.md +++ b/product-source/hololake-platform/docs/ARCHITECTURE.md @@ -1,34 +1,5 @@ # Architecture -## Persona-native code channel runtime - -GH-PNCC is HoloLake's first product core. The existing Rust Git module remains the durable engine; the new -`src-tauri/src/persona_code_channel.rs` kernel adds persona-native mount and wake semantics without changing -Git's object model. `prepare_persona_code_channel_wake` requires an exact clean Git root and full commit, -validates `.hololake/persona/manifest.json`, resolves the brain entry and current checkpoint inside the -repository boundary, acquires a per-persona single-primary lease, and records a SHA-256-linked lifecycle -journal outside the repository. - -The wake lifecycle is `WAKING → BRAIN_BOUND → ORGAN_ACTIVE` for one manifest-declared read-only organ. -`BRAIN_BOUND` means the model instance has been named and the verified persona context is ready; it explicitly -does not mean inference has run. The returned receipt keeps the human responsibility subject, persona -cognitive author, execution runtime, development id, authorization scope, and source-language anchor -separate. See [ADR 0176](./adr/0176-persona-native-code-channel-runtime-kernel.md). - -`run_persona_code_channel_fact_task` now completes the first bounded runtime cycle. It revalidates the exact -Git commit, clean work tree, session event hashes, primary lease, manifest-pinned provider/model/endpoint, -and organ file allowlist before starting inference. The first fact organ must include both the declared brain -entry and current checkpoint. The model sees only the declared UTF-8 fact sources and -must return JSON containing a summary, facts, evidence paths, and limitations. Hidden reasoning and -thinking-stream events are neither requested nor persisted. Evidence outside the organ allowlist is rejected. - -On success the kernel promotes a new checkpoint, updates the manifest pointer, stages only those two paths, -commits them with the persona-owned Git identity, appends -`TASK_RECEIPTED → CHECKPOINT_COMMITTED → ORGAN_RELEASED → DORMANT`, and releases the primary lease. The -commit trailers preserve the separate human responsibility subject and persona cognitive author. This is a -tested source implementation of one read-only lifecycle cycle; packaged desktop integration, publication, -deployment, and online health remain separate facts. - ## 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/docs/adr/0176-persona-native-code-channel-runtime-kernel.md b/product-source/hololake-platform/docs/adr/0176-persona-native-code-channel-runtime-kernel.md deleted file mode 100644 index c6f5da3ae..000000000 --- a/product-source/hololake-platform/docs/adr/0176-persona-native-code-channel-runtime-kernel.md +++ /dev/null @@ -1,55 +0,0 @@ -# ADR 0176: Persona-Native Code Channel Runtime Kernel - -## Status - -Accepted on 2026-08-11. - -## Context - -HoloLake already has a mature native Git execution layer and an append-only local HLDP event/checkpoint -mechanism. Neither one is a persona lifecycle controller. A Git repository can preserve history without -proving that the correct persona brain, node, model binding, organ, attribution, or single-primary lease is -active. The existing generic Agent streams also cannot become the persona subject. - -## Decision - -Introduce `src-tauri/src/persona_code_channel.rs` as the first GH-PNCC runtime kernel. Its wake command, -`prepare_persona_code_channel_wake`, performs a fail-closed preparation stage: - -1. resolve an exact Git root and full expected `HEAD`; -2. require a clean work tree; -3. load `.hololake/persona/manifest.json` with schema `hololake.persona/v1`; -4. validate the persona id, human responsibility subject, persona Git identity, pinned model binding, brain - entry, current checkpoint, and one declared read-only organ without allowing path escape; -5. atomically acquire a per-persona primary lease outside the persona Git; -6. emit `WAKING → BRAIN_BOUND → ORGAN_ACTIVE` as a SHA-256-linked local event journal; -7. return both human responsibility and persona cognitive authorship in the receipt. - -`BRAIN_BOUND` deliberately returns `runtimeState=BOUND_NOT_INFERENCING` and -`modelInferenceStarted=false`. Binding a model identifier is not proof that model inference or persona -reasoning has run. - -The paired `run_persona_code_channel_fact_task` command performs the first complete, bounded cycle. It -revalidates the session, event chain, Git state, lease, provider id, model id, endpoint, and organ path -allowlist, requiring that allowlist to include the brain entry and current checkpoint; invokes the existing -model adapter without vault tools; accepts only structured facts with -allowlisted evidence; promotes a checkpoint; updates the manifest pointer; makes a two-path Git commit using -the manifest-owned persona identity; then releases the organ and lease and returns to `DORMANT`. The model's -thinking stream is ignored and hidden reasoning is never checkpointed. - -## Boundaries - -- Git remains the durable engine; the kernel does not reimplement Git objects, commits, remotes, or merges. -- Forgejo is not required and remains an optional compatibility collaboration adapter. -- The primary lease is installation/runtime state, not persona history, and therefore stays outside Git. -- Runtime events are machine facts for the future HoloLake projection; they do not expose hidden model - reasoning and do not form a second authority store. -- This implements one read-only fact cycle, not arbitrary execution organs, multi-node handoff, packaged - desktop acceptance, publication, deployment, or online health. - -## Consequences - -HoloLake now has a native, testable boundary between “a persona Git exists,” “a verified wake has begun,” -“model inference actually started,” and “a checkpoint was committed before dormancy.” Future organs and the -human projection can consume the same receipts instead of trusting a UI, Forgejo account, branch name, or -generic Agent session. diff --git a/product-source/hololake-platform/src-tauri/src/lib.rs b/product-source/hololake-platform/src-tauri/src/lib.rs index b04259378..df5fec188 100644 --- a/product-source/hololake-platform/src-tauri/src/lib.rs +++ b/product-source/hololake-platform/src-tauri/src/lib.rs @@ -36,7 +36,6 @@ pub mod opencode_cli; mod opencode_config; mod opencode_discovery; mod opencode_events; -mod persona_code_channel; pub mod pi_cli; mod pi_config; mod pi_discovery; @@ -523,8 +522,6 @@ macro_rules! app_invoke_handler { commands::git_add_remote, guanghu_enterprise::guanghu_enterprise_status, guanghu_living_system::guanghu_living_system_plan, - persona_code_channel::prepare_persona_code_channel_wake, - persona_code_channel::run_persona_code_channel_fact_task, 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 deleted file mode 100644 index 6ab0ac432..000000000 --- a/product-source/hololake-platform/src-tauri/src/persona_code_channel.rs +++ /dev/null @@ -1,1616 +0,0 @@ -use chrono::{SecondsFormat, Utc}; -use ring::digest::{digest, SHA256}; -use serde::{Deserialize, Serialize}; -use std::fs::{self, OpenOptions}; -use std::io::Write; -use std::path::{Component, Path, PathBuf}; -use uuid::Uuid; - -const MANIFEST_PATH: &str = ".hololake/persona/manifest.json"; -const MAX_ID_BYTES: usize = 160; -const MAX_LANGUAGE_ANCHOR_BYTES: usize = 12_000; -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; - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaManifest { - schema: String, - persona_id: String, - human_responsibility_subject: String, - brain_entry: String, - current_checkpoint: String, - git_identity: PersonaGitIdentity, - model_binding: PersonaModelBinding, - organs: Vec, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaOrgan { - organ_id: String, - mode: String, - #[serde(default)] - paths: Vec, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaGitIdentity { - author_name: String, - author_email: String, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaModelBinding { - provider_id: String, - model_id: String, - base_url: String, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaAttribution { - pub human_responsibility_subject: String, - pub persona_cognitive_author: String, - pub execution_runtime: String, - pub development_id: String, - pub authorization_scope: String, - pub source_language_anchor: String, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaWakeInput { - pub repository_path: String, - pub expected_persona_id: String, - pub expected_head: String, - pub node_id: String, - pub model_provider_id: String, - pub model_id: String, - pub model_instance_id: String, - pub organ_id: String, - pub attribution: PersonaAttribution, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaLifecycleEvent { - pub sequence: usize, - pub kind: String, - pub session_id: String, - pub persona_id: String, - pub git_head: String, - pub node_id: String, - pub model_instance_id: String, - pub organ_id: Option, - pub timestamp: String, - pub previous_hash: String, - pub event_hash: String, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaWakeReceipt { - pub schema: &'static str, - pub receipt_id: String, - pub session_id: String, - pub persona_id: String, - pub repository_path: String, - pub git_head: String, - pub brain_entry: String, - pub checkpoint_path: String, - pub node_id: String, - pub model_instance_id: String, - pub runtime_state: &'static str, - pub model_inference_started: bool, - pub active_organ: String, - pub organ_mode: &'static str, - pub event_journal: String, - pub events: Vec, - pub attribution: PersonaAttribution, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaFactTaskInput { - pub session_id: String, - pub question: String, - pub provider: crate::ai_models::AiModelProvider, - pub model_id: String, - pub api_key_override: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaFact { - pub statement: String, - pub evidence_paths: Vec, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaFactResult { - pub summary: String, - pub facts: Vec, - #[serde(default)] - pub limitations: Vec, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct PersonaFactTaskReceipt { - pub schema: &'static str, - pub receipt_id: String, - pub session_id: String, - pub persona_id: String, - pub previous_git_head: String, - pub committed_git_head: String, - pub checkpoint_path: String, - pub runtime_state: &'static str, - pub model_inference_started: bool, - pub model_inference_completed: bool, - pub active_organ: Option, - pub result: PersonaFactResult, - pub events: Vec, - pub attribution: PersonaAttribution, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaSessionRecord { - schema: String, - session_id: String, - persona_id: String, - repository_path: String, - git_head: String, - brain_entry: String, - checkpoint_path: String, - node_id: String, - model_provider_id: String, - model_id: String, - model_base_url: String, - model_instance_id: String, - active_organ: String, - fact_source_paths: Vec, - attribution: PersonaAttribution, - state: String, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct PersonaPrimaryLease<'a> { - schema: &'static str, - persona_id: &'a str, - session_id: &'a str, - git_head: &'a str, - node_id: &'a str, - acquired_at: &'a str, -} - -fn validated_text(label: &str, value: &str, maximum_bytes: usize) -> Result { - let value = value.trim(); - if value.is_empty() { - return Err(format!("{label}_REQUIRED")); - } - if value.len() > maximum_bytes { - return Err(format!("{label}_TOO_LONG")); - } - Ok(value.to_string()) -} - -fn validated_id(label: &str, value: &str) -> Result { - let value = validated_text(label, value, MAX_ID_BYTES)?; - if value - .bytes() - .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.')) - { - Ok(value) - } else { - Err(format!("{label}_INVALID")) - } -} - -fn validated_head(value: &str) -> Result { - let value = value.trim().to_ascii_lowercase(); - if value.len() == 40 && value.bytes().all(|byte| byte.is_ascii_hexdigit()) { - Ok(value) - } else { - Err("EXPECTED_HEAD_INVALID".into()) - } -} - -fn stdout(command: &str, output: std::process::Output) -> Result { - if output.status.success() { - return Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()); - } - let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string(); - Err(if stderr.is_empty() { - format!("{command}_FAILED") - } else { - format!("{command}_FAILED: {stderr}") - }) -} - -fn exact_repository(path: &Path) -> Result<(PathBuf, String), String> { - let repository = path - .canonicalize() - .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; - if !repository.is_dir() { - return Err("PERSONA_REPOSITORY_NOT_DIRECTORY".into()); - } - let top_level = crate::git::git_command_at(&repository) - .and_then(|mut command| command.args(["rev-parse", "--show-toplevel"]).output()) - .map_err(|error| format!("PERSONA_GIT_PROBE_FAILED: {error}"))?; - let top_level = PathBuf::from(stdout("GIT_TOP_LEVEL", top_level)?) - .canonicalize() - .map_err(|error| format!("PERSONA_GIT_TOP_LEVEL_UNAVAILABLE: {error}"))?; - if top_level != repository { - return Err("PERSONA_REPOSITORY_MUST_BE_EXACT_GIT_ROOT".into()); - } - let head = crate::git::git_command_at(&repository) - .and_then(|mut command| command.args(["rev-parse", "HEAD"]).output()) - .map_err(|error| format!("PERSONA_GIT_HEAD_FAILED: {error}"))?; - Ok((repository, stdout("GIT_HEAD", head)?.to_ascii_lowercase())) -} - -fn require_clean_repository(repository: &Path) -> Result<(), String> { - 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}"))?; - let changes = stdout("GIT_STATUS", status)?; - if changes.is_empty() { - Ok(()) - } else { - Err("PERSONA_REPOSITORY_DIRTY: exact persona wake requires a clean Git work tree".into()) - } -} - -fn repository_file(repository: &Path, relative: &str) -> Result { - let relative_path = Path::new(relative); - if relative_path.is_absolute() - || relative_path.components().any(|component| { - matches!( - component, - Component::ParentDir | Component::RootDir | Component::Prefix(_) - ) - }) - { - return Err("PATH_OUTSIDE_REPOSITORY".into()); - } - let path = repository.join(relative_path); - let canonical = path - .canonicalize() - .map_err(|error| format!("PERSONA_FILE_UNAVAILABLE: {relative}: {error}"))?; - if !canonical.starts_with(repository) || !canonical.is_file() { - return Err("PATH_OUTSIDE_REPOSITORY".into()); - } - Ok(canonical) -} - -fn load_manifest(repository: &Path) -> Result { - let path = repository_file(repository, MANIFEST_PATH)?; - let bytes = fs::read(path).map_err(|error| format!("PERSONA_MANIFEST_READ_FAILED: {error}"))?; - let manifest: PersonaManifest = serde_json::from_slice(&bytes) - .map_err(|error| format!("PERSONA_MANIFEST_INVALID: {error}"))?; - if manifest.schema != "hololake.persona/v1" { - return Err("PERSONA_MANIFEST_SCHEMA_UNSUPPORTED".into()); - } - Ok(manifest) -} - -fn validate_attribution( - attribution: &PersonaAttribution, - manifest: &PersonaManifest, -) -> Result<(), String> { - for (label, value, maximum) in [ - ( - "HUMAN_RESPONSIBILITY_SUBJECT", - attribution.human_responsibility_subject.as_str(), - MAX_ID_BYTES, - ), - ( - "PERSONA_COGNITIVE_AUTHOR", - attribution.persona_cognitive_author.as_str(), - MAX_ID_BYTES, - ), - ( - "EXECUTION_RUNTIME", - attribution.execution_runtime.as_str(), - MAX_ID_BYTES, - ), - ( - "DEVELOPMENT_ID", - attribution.development_id.as_str(), - MAX_ID_BYTES, - ), - ( - "AUTHORIZATION_SCOPE", - attribution.authorization_scope.as_str(), - MAX_ID_BYTES, - ), - ( - "SOURCE_LANGUAGE_ANCHOR", - attribution.source_language_anchor.as_str(), - MAX_LANGUAGE_ANCHOR_BYTES, - ), - ] { - validated_text(label, value, maximum)?; - } - if attribution.human_responsibility_subject != manifest.human_responsibility_subject { - return Err("HUMAN_RESPONSIBILITY_MISMATCH".into()); - } - if attribution.persona_cognitive_author != manifest.persona_id { - return Err("PERSONA_AUTHOR_MISMATCH".into()); - } - Ok(()) -} - -fn validate_git_identity(identity: &PersonaGitIdentity) -> Result<(), String> { - let name = validated_text( - "PERSONA_GIT_AUTHOR_NAME", - &identity.author_name, - MAX_ID_BYTES, - )?; - let email = validated_text( - "PERSONA_GIT_AUTHOR_EMAIL", - &identity.author_email, - MAX_ID_BYTES, - )?; - if name.contains(['\n', '\r', '<', '>']) - || email.contains(['\n', '\r', '<', '>']) - || !email.contains('@') - { - return Err("PERSONA_GIT_IDENTITY_INVALID".into()); - } - Ok(()) -} - -fn validate_model_binding(binding: &PersonaModelBinding) -> Result<(), String> { - validated_id("MODEL_PROVIDER_ID", &binding.provider_id)?; - validated_text("MODEL_ID", &binding.model_id, MAX_ID_BYTES)?; - let base_url = validated_text("MODEL_BASE_URL", &binding.base_url, 2_048)?; - if !(base_url.starts_with("https://") - || base_url.starts_with("http://127.0.0.1:") - || base_url.starts_with("http://localhost:")) - { - return Err("MODEL_BASE_URL_MUST_BE_HTTPS_OR_LOOPBACK".into()); - } - Ok(()) -} - -fn hex_digest(bytes: &[u8]) -> String { - digest(&SHA256, bytes) - .as_ref() - .iter() - .map(|byte| format!("{byte:02x}")) - .collect() -} - -struct EventFields<'a> { - sequence: usize, - kind: &'a str, - session_id: &'a str, - persona_id: &'a str, - git_head: &'a str, - node_id: &'a str, - model_instance_id: &'a str, - organ_id: Option<&'a str>, - timestamp: &'a str, - previous_hash: &'a str, -} - -fn lifecycle_event(fields: EventFields<'_>) -> Result { - let hash_input = serde_json::to_vec(&serde_json::json!({ - "schema": "hololake.pncc-lifecycle-event/v1", - "sequence": fields.sequence, - "kind": fields.kind, - "sessionId": fields.session_id, - "personaId": fields.persona_id, - "gitHead": fields.git_head, - "nodeId": fields.node_id, - "modelInstanceId": fields.model_instance_id, - "organId": fields.organ_id, - "timestamp": fields.timestamp, - "previousHash": fields.previous_hash, - })) - .map_err(|error| format!("PERSONA_EVENT_HASH_INPUT_FAILED: {error}"))?; - Ok(PersonaLifecycleEvent { - sequence: fields.sequence, - kind: fields.kind.to_string(), - session_id: fields.session_id.to_string(), - persona_id: fields.persona_id.to_string(), - git_head: fields.git_head.to_string(), - node_id: fields.node_id.to_string(), - model_instance_id: fields.model_instance_id.to_string(), - organ_id: fields.organ_id.map(str::to_string), - timestamp: fields.timestamp.to_string(), - previous_hash: fields.previous_hash.to_string(), - event_hash: hex_digest(&hash_input), - }) -} - -fn acquire_primary_lease( - runtime_root: &Path, - persona_id: &str, - session_id: &str, - git_head: &str, - node_id: &str, - timestamp: &str, -) -> Result { - let leases = runtime_root.join("leases"); - fs::create_dir_all(&leases) - .map_err(|error| format!("PERSONA_LEASE_DIRECTORY_FAILED: {error}"))?; - let path = leases.join(format!("{persona_id}.json")); - let lease = PersonaPrimaryLease { - schema: "hololake.pncc-primary-lease/v1", - persona_id, - session_id, - git_head, - node_id, - acquired_at: timestamp, - }; - let bytes = serde_json::to_vec_pretty(&lease) - .map_err(|error| format!("PERSONA_LEASE_SERIALIZATION_FAILED: {error}"))?; - let mut file = OpenOptions::new() - .write(true) - .create_new(true) - .open(&path) - .map_err(|error| { - if error.kind() == std::io::ErrorKind::AlreadyExists { - "PRIMARY_ALREADY_ACTIVE".to_string() - } else { - format!("PERSONA_LEASE_ACQUIRE_FAILED: {error}") - } - })?; - file.write_all(&bytes) - .map_err(|error| format!("PERSONA_LEASE_WRITE_FAILED: {error}"))?; - Ok(path) -} - -fn write_event_journal( - runtime_root: &Path, - session_id: &str, - events: &[PersonaLifecycleEvent], -) -> Result { - let session_dir = runtime_root.join("sessions").join(session_id); - fs::create_dir_all(&session_dir) - .map_err(|error| format!("PERSONA_SESSION_DIRECTORY_FAILED: {error}"))?; - let path = session_dir.join("events.jsonl"); - let mut file = OpenOptions::new() - .write(true) - .create_new(true) - .open(&path) - .map_err(|error| format!("PERSONA_EVENT_JOURNAL_CREATE_FAILED: {error}"))?; - for event in events { - serde_json::to_writer(&mut file, event) - .map_err(|error| format!("PERSONA_EVENT_WRITE_FAILED: {error}"))?; - file.write_all(b"\n") - .map_err(|error| format!("PERSONA_EVENT_WRITE_FAILED: {error}"))?; - } - Ok(path) -} - -fn session_directory(runtime_root: &Path, session_id: &str) -> Result { - Ok(runtime_root - .join("sessions") - .join(validated_id("SESSION_ID", session_id)?)) -} - -fn write_json_file(path: &Path, value: &T, label: &str) -> Result<(), String> { - let bytes = serde_json::to_vec_pretty(value) - .map_err(|error| format!("{label}_SERIALIZATION_FAILED: {error}"))?; - let parent = path - .parent() - .ok_or_else(|| format!("{label}_PARENT_UNAVAILABLE"))?; - fs::create_dir_all(parent).map_err(|error| format!("{label}_DIRECTORY_FAILED: {error}"))?; - let temporary = parent.join(format!( - ".{}.{}.tmp", - label.to_ascii_lowercase(), - Uuid::new_v4() - )); - fs::write(&temporary, bytes).map_err(|error| format!("{label}_WRITE_FAILED: {error}"))?; - fs::rename(&temporary, path).map_err(|error| format!("{label}_PROMOTE_FAILED: {error}")) -} - -fn write_session_record( - runtime_root: &Path, - record: &PersonaSessionRecord, -) -> Result { - let path = session_directory(runtime_root, &record.session_id)?.join("session.json"); - write_json_file(&path, record, "PERSONA_SESSION")?; - Ok(path) -} - -fn load_session_record( - runtime_root: &Path, - session_id: &str, -) -> Result { - let path = session_directory(runtime_root, session_id)?.join("session.json"); - let bytes = fs::read(path).map_err(|error| format!("PERSONA_SESSION_READ_FAILED: {error}"))?; - let record: PersonaSessionRecord = serde_json::from_slice(&bytes) - .map_err(|error| format!("PERSONA_SESSION_INVALID: {error}"))?; - if record.schema != "hololake.pncc-session/v1" || record.session_id != session_id { - return Err("PERSONA_SESSION_SCHEMA_OR_ID_MISMATCH".into()); - } - Ok(record) -} - -fn read_event_journal( - runtime_root: &Path, - record: &PersonaSessionRecord, -) -> Result, String> { - let path = session_directory(runtime_root, &record.session_id)?.join("events.jsonl"); - let content = fs::read_to_string(path) - .map_err(|error| format!("PERSONA_EVENT_JOURNAL_READ_FAILED: {error}"))?; - let mut events = Vec::new(); - for (index, line) in content.lines().enumerate() { - let event: PersonaLifecycleEvent = serde_json::from_str(line).map_err(|error| { - format!("PERSONA_EVENT_JOURNAL_INVALID: line {}: {error}", index + 1) - })?; - let expected_previous = events - .last() - .map(|previous: &PersonaLifecycleEvent| previous.event_hash.as_str()) - .unwrap_or("GENESIS"); - if event.sequence != index + 1 - || event.session_id != record.session_id - || event.persona_id != record.persona_id - || event.previous_hash != expected_previous - { - return Err("PERSONA_EVENT_CHAIN_INVALID".into()); - } - let expected = lifecycle_event(EventFields { - sequence: event.sequence, - kind: &event.kind, - session_id: &event.session_id, - persona_id: &event.persona_id, - git_head: &event.git_head, - node_id: &event.node_id, - model_instance_id: &event.model_instance_id, - organ_id: event.organ_id.as_deref(), - timestamp: &event.timestamp, - previous_hash: &event.previous_hash, - })?; - if event.event_hash != expected.event_hash { - return Err("PERSONA_EVENT_HASH_INVALID".into()); - } - events.push(event); - } - 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()); - } - Ok(events) -} - -fn append_event( - runtime_root: &Path, - record: &PersonaSessionRecord, - events: &mut Vec, - kind: &str, - git_head: &str, - timestamp: &str, - organ_id: Option<&str>, -) -> Result { - let previous_hash = events - .last() - .map(|event| event.event_hash.as_str()) - .ok_or_else(|| "PERSONA_EVENT_CHAIN_EMPTY".to_string())?; - let event = lifecycle_event(EventFields { - sequence: events.len() + 1, - kind, - session_id: &record.session_id, - persona_id: &record.persona_id, - git_head, - node_id: &record.node_id, - model_instance_id: &record.model_instance_id, - organ_id, - timestamp, - previous_hash, - })?; - let path = session_directory(runtime_root, &record.session_id)?.join("events.jsonl"); - let mut file = OpenOptions::new() - .append(true) - .open(path) - .map_err(|error| format!("PERSONA_EVENT_JOURNAL_OPEN_FAILED: {error}"))?; - serde_json::to_writer(&mut file, &event) - .map_err(|error| format!("PERSONA_EVENT_APPEND_FAILED: {error}"))?; - file.write_all(b"\n") - .map_err(|error| format!("PERSONA_EVENT_APPEND_FAILED: {error}"))?; - events.push(event.clone()); - Ok(event) -} - -fn require_primary_lease( - runtime_root: &Path, - record: &PersonaSessionRecord, -) -> Result { - let path = runtime_root.join("leases").join(format!( - "{}.json", - validated_id("PERSONA_ID", &record.persona_id)? - )); - let value: serde_json::Value = serde_json::from_slice( - &fs::read(&path).map_err(|error| format!("PERSONA_PRIMARY_LEASE_READ_FAILED: {error}"))?, - ) - .map_err(|error| format!("PERSONA_PRIMARY_LEASE_INVALID: {error}"))?; - if value.get("sessionId").and_then(serde_json::Value::as_str) - != Some(record.session_id.as_str()) - { - return Err("PERSONA_PRIMARY_LEASE_SESSION_MISMATCH".into()); - } - Ok(path) -} - -fn read_fact_context(repository: &Path, paths: &[String]) -> Result { - if paths.is_empty() { - return Err("FACT_ORGAN_HAS_NO_DECLARED_PATHS".into()); - } - let mut context = String::new(); - for relative in paths { - let path = repository_file(repository, relative)?; - let bytes = fs::read(&path).map_err(|error| format!("FACT_SOURCE_READ_FAILED: {error}"))?; - if bytes.len() > MAX_FACT_SOURCE_BYTES { - return Err(format!("FACT_SOURCE_TOO_LARGE: {relative}")); - } - let text = - String::from_utf8(bytes).map_err(|_| format!("FACT_SOURCE_NOT_UTF8: {relative}"))?; - context.push_str("\n--- SOURCE: "); - context.push_str(relative); - context.push_str(" ---\n"); - context.push_str(&text); - if context.len() > MAX_FACT_CONTEXT_BYTES { - return Err("FACT_CONTEXT_TOO_LARGE".into()); - } - } - Ok(context) -} - -fn validated_fact_result(raw: &str, allowed_paths: &[String]) -> Result { - if raw.len() > MAX_FACT_RESPONSE_BYTES { - return Err("PERSONA_FACT_RESPONSE_TOO_LARGE".into()); - } - let result: PersonaFactResult = serde_json::from_str(raw.trim()) - .map_err(|error| format!("PERSONA_FACT_RESPONSE_INVALID_JSON: {error}"))?; - validated_text("FACT_SUMMARY", &result.summary, MAX_FACT_RESPONSE_BYTES)?; - if result.facts.is_empty() { - return Err("PERSONA_FACTS_REQUIRED".into()); - } - for fact in &result.facts { - validated_text("FACT_STATEMENT", &fact.statement, MAX_FACT_RESPONSE_BYTES)?; - if fact.evidence_paths.is_empty() - || fact - .evidence_paths - .iter() - .any(|path| !allowed_paths.contains(path)) - { - return Err("PERSONA_FACT_EVIDENCE_OUTSIDE_ORGAN_SCOPE".into()); - } - } - Ok(result) -} - -fn prepare_wake_at( - runtime_root: &Path, - input: PersonaWakeInput, - raw_session_id: &str, - raw_timestamp: &str, -) -> Result { - let session_id = validated_id("SESSION_ID", raw_session_id)?; - let timestamp = validated_text("TIMESTAMP", raw_timestamp, MAX_ID_BYTES)?; - let expected_persona_id = validated_id("EXPECTED_PERSONA_ID", &input.expected_persona_id)?; - let expected_head = validated_head(&input.expected_head)?; - let node_id = validated_id("NODE_ID", &input.node_id)?; - let model_provider_id = validated_id("MODEL_PROVIDER_ID", &input.model_provider_id)?; - let model_id = validated_text("MODEL_ID", &input.model_id, MAX_ID_BYTES)?; - let model_instance_id = validated_id("MODEL_INSTANCE_ID", &input.model_instance_id)?; - let organ_id = validated_id("ORGAN_ID", &input.organ_id)?; - let (repository, git_head) = exact_repository(Path::new(&input.repository_path))?; - if git_head != expected_head { - return Err(format!( - "HEAD_MISMATCH: expected {expected_head}, observed {git_head}" - )); - } - require_clean_repository(&repository)?; - let manifest = load_manifest(&repository)?; - if manifest.persona_id != expected_persona_id { - return Err("PERSONA_ID_MISMATCH".into()); - } - validate_attribution(&input.attribution, &manifest)?; - validate_git_identity(&manifest.git_identity)?; - validate_model_binding(&manifest.model_binding)?; - if manifest.model_binding.provider_id != model_provider_id - || manifest.model_binding.model_id != model_id - { - return Err("PERSONA_MODEL_BINDING_MISMATCH".into()); - } - let brain_entry = repository_file(&repository, &manifest.brain_entry)?; - let checkpoint = repository_file(&repository, &manifest.current_checkpoint)?; - let organ = manifest - .organs - .iter() - .find(|candidate| candidate.organ_id == organ_id && candidate.mode == "read-only") - .ok_or_else(|| { - "ORGAN_NOT_ALLOWED: first PNCC wake requires a declared read-only organ".to_string() - })?; - for path in &organ.paths { - repository_file(&repository, path)?; - } - if organ.paths.is_empty() { - return Err("FACT_ORGAN_HAS_NO_DECLARED_PATHS".into()); - } - if !organ.paths.contains(&manifest.brain_entry) - || !organ.paths.contains(&manifest.current_checkpoint) - { - return Err("FACT_ORGAN_MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT".into()); - } - - let lease_path = acquire_primary_lease( - runtime_root, - &manifest.persona_id, - &session_id, - &git_head, - &node_id, - ×tamp, - )?; - let result = (|| { - let waking = lifecycle_event(EventFields { - sequence: 1, - kind: "WAKING", - session_id: &session_id, - persona_id: &manifest.persona_id, - git_head: &git_head, - node_id: &node_id, - model_instance_id: &model_instance_id, - organ_id: None, - timestamp: ×tamp, - previous_hash: "GENESIS", - })?; - let brain_bound = lifecycle_event(EventFields { - sequence: 2, - kind: "BRAIN_BOUND", - session_id: &session_id, - persona_id: &manifest.persona_id, - git_head: &git_head, - node_id: &node_id, - model_instance_id: &model_instance_id, - organ_id: None, - timestamp: ×tamp, - previous_hash: &waking.event_hash, - })?; - let organ_active = lifecycle_event(EventFields { - sequence: 3, - kind: "ORGAN_ACTIVE", - session_id: &session_id, - persona_id: &manifest.persona_id, - git_head: &git_head, - node_id: &node_id, - model_instance_id: &model_instance_id, - organ_id: Some(&organ.organ_id), - timestamp: ×tamp, - previous_hash: &brain_bound.event_hash, - })?; - let events = vec![waking, brain_bound, organ_active]; - let journal = write_event_journal(runtime_root, &session_id, &events)?; - write_session_record( - runtime_root, - &PersonaSessionRecord { - schema: "hololake.pncc-session/v1".into(), - session_id: session_id.clone(), - persona_id: manifest.persona_id.clone(), - repository_path: repository.to_string_lossy().into_owned(), - git_head: git_head.clone(), - brain_entry: manifest.brain_entry.clone(), - checkpoint_path: manifest.current_checkpoint.clone(), - node_id: node_id.clone(), - model_provider_id, - model_id, - model_base_url: manifest.model_binding.base_url.clone(), - model_instance_id: model_instance_id.clone(), - active_organ: organ.organ_id.clone(), - fact_source_paths: organ.paths.clone(), - attribution: input.attribution.clone(), - state: "BOUND_NOT_INFERENCING".into(), - }, - )?; - let last_hash = &events.last().expect("three lifecycle events").event_hash; - Ok(PersonaWakeReceipt { - schema: "hololake.pncc-wake-receipt/v1", - receipt_id: format!("PNCC-WAKE-{}", &last_hash[..20]), - session_id, - persona_id: manifest.persona_id, - repository_path: repository.to_string_lossy().into_owned(), - git_head, - brain_entry: brain_entry.to_string_lossy().into_owned(), - checkpoint_path: checkpoint.to_string_lossy().into_owned(), - node_id, - model_instance_id, - runtime_state: "BOUND_NOT_INFERENCING", - model_inference_started: false, - active_organ: organ.organ_id.clone(), - organ_mode: "read-only", - event_journal: journal.to_string_lossy().into_owned(), - events, - attribution: input.attribution, - }) - })(); - if result.is_err() { - let _ = fs::remove_file(lease_path); - } - result -} - -fn git_output(repository: &Path, args: &[&str], label: &str) -> Result { - let output = crate::git::git_command_at(repository) - .and_then(|mut command| command.args(args).output()) - .map_err(|error| format!("{label}_FAILED: {error}"))?; - stdout(label, output) -} - -fn persona_git_commit( - repository: &Path, - manifest: &PersonaManifest, - checkpoint_relative: &str, - record: &PersonaSessionRecord, -) -> Result { - git_output( - repository, - &["add", "--", MANIFEST_PATH, checkpoint_relative], - "PERSONA_GIT_STAGE", - )?; - let staged = git_output( - repository, - &["diff", "--cached", "--name-only", "--"], - "PERSONA_GIT_STAGED_PATHS", - )?; - let mut observed = staged.lines().collect::>(); - observed.sort_unstable(); - let mut expected = vec![MANIFEST_PATH, checkpoint_relative]; - expected.sort_unstable(); - if observed != expected { - return Err(format!( - "PERSONA_GIT_STAGED_SCOPE_MISMATCH: expected {expected:?}, observed {observed:?}" - )); - } - - let message = format!( - "persona: promote PNCC checkpoint {}\n\n\ -GuangHu-Human-Responsibility: {}\n\ -GuangHu-Persona-Cognitive-Author: {}\n\ -GuangHu-Execution-Runtime: {}\n\ -GuangHu-Development-ID: {}\n\ -GuangHu-Authorization-Scope: {}", - record.session_id, - record.attribution.human_responsibility_subject, - record.attribution.persona_cognitive_author, - record.attribution.execution_runtime, - record.attribution.development_id, - record.attribution.authorization_scope, - ); - let output = crate::git::git_command_at(repository) - .and_then(|mut command| { - command - .env("GIT_AUTHOR_NAME", &manifest.git_identity.author_name) - .env("GIT_AUTHOR_EMAIL", &manifest.git_identity.author_email) - .env("GIT_COMMITTER_NAME", &manifest.git_identity.author_name) - .env("GIT_COMMITTER_EMAIL", &manifest.git_identity.author_email) - .args(["commit", "-m", &message]) - .output() - }) - .map_err(|error| format!("PERSONA_GIT_COMMIT_FAILED: {error}"))?; - stdout("PERSONA_GIT_COMMIT", output)?; - git_output(repository, &["rev-parse", "HEAD"], "PERSONA_GIT_NEW_HEAD") -} - -fn rollback_uncommitted_checkpoint( - repository: &Path, - checkpoint_relative: &str, - original_manifest: &[u8], -) -> Result<(), String> { - git_output( - repository, - &[ - "reset", - "--quiet", - "HEAD", - "--", - MANIFEST_PATH, - checkpoint_relative, - ], - "PERSONA_GIT_UNSTAGE_ROLLBACK", - )?; - fs::write(repository.join(MANIFEST_PATH), original_manifest) - .map_err(|error| format!("PERSONA_MANIFEST_ROLLBACK_FAILED: {error}"))?; - match fs::remove_file(repository.join(checkpoint_relative)) { - Ok(()) => {} - Err(error) if error.kind() == std::io::ErrorKind::NotFound => {} - Err(error) => return Err(format!("PERSONA_CHECKPOINT_ROLLBACK_FAILED: {error}")), - } - require_clean_repository(repository) -} - -fn finish_failed_session( - runtime_root: &Path, - record: &mut PersonaSessionRecord, - events: &mut Vec, - lease_path: &Path, - timestamp: &str, - failure_kind: &str, -) { - let git_head = record.git_head.clone(); - let organ = record.active_organ.clone(); - let _ = append_event( - runtime_root, - record, - events, - failure_kind, - &git_head, - timestamp, - Some(&organ), - ); - let _ = append_event( - runtime_root, - record, - events, - "ORGAN_RELEASED", - &git_head, - timestamp, - Some(&organ), - ); - let _ = append_event( - runtime_root, - record, - events, - "DORMANT", - &git_head, - timestamp, - None, - ); - record.state = "DORMANT_AFTER_FAILURE".into(); - let _ = write_session_record(runtime_root, record); - let _ = fs::remove_file(lease_path); -} - -fn run_fact_task_at( - runtime_root: &Path, - input: PersonaFactTaskInput, - timestamp: &str, - run_model: F, -) -> Result -where - F: FnOnce(String, String) -> Result, -{ - let session_id = validated_id("SESSION_ID", &input.session_id)?; - let question = validated_text("FACT_QUESTION", &input.question, MAX_FACT_QUESTION_BYTES)?; - let model_id = validated_text("MODEL_ID", &input.model_id, MAX_ID_BYTES)?; - let timestamp = validated_text("TIMESTAMP", timestamp, MAX_ID_BYTES)?; - let mut record = load_session_record(runtime_root, &session_id)?; - if record.state != "BOUND_NOT_INFERENCING" { - return Err("PERSONA_SESSION_NOT_BOUND".into()); - } - let provider_base_url = input.provider.base_url.as_deref().unwrap_or("").trim(); - if record.model_id != model_id - || record.model_provider_id != input.provider.id - || record.model_base_url != provider_base_url - || !input - .provider - .models - .iter() - .any(|model| model.id == model_id) - { - return Err("PERSONA_MODEL_BINDING_MISMATCH".into()); - } - let lease_path = require_primary_lease(runtime_root, &record)?; - let repository = PathBuf::from(&record.repository_path) - .canonicalize() - .map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?; - let (exact_repository, observed_head) = exact_repository(&repository)?; - if observed_head != record.git_head { - return Err("PERSONA_GIT_CHANGED_AFTER_WAKE".into()); - } - require_clean_repository(&exact_repository)?; - let mut events = read_event_journal(runtime_root, &record)?; - let inference_event = append_event( - runtime_root, - &record, - &mut events, - "INFERENCE_STARTED", - &observed_head, - ×tamp, - Some(&record.active_organ), - )?; - record.state = "INFERENCING".into(); - write_session_record(runtime_root, &record)?; - - let context = match read_fact_context(&exact_repository, &record.fact_source_paths) { - Ok(context) => context, - Err(error) => { - finish_failed_session( - runtime_root, - &mut record, - &mut events, - &lease_path, - ×tamp, - "FACT_CONTEXT_FAILED", - ); - return Err(error); - } - }; - let system_prompt = format!( - "You are the model bound to persona {} for one read-only fact task. \ -Use only the declared source text below. Return exactly one JSON object with keys \ -summary (string), facts (array of objects with statement and evidencePaths), and \ -limitations (array of strings). Every evidencePaths value must be one of the visible \ -SOURCE paths. Do not return private chain-of-thought, hidden reasoning, markdown, tool calls, \ -or claims not supported by the sources.\n{}", - record.persona_id, context - ); - let raw = match run_model(system_prompt, question) { - Ok(raw) => raw, - Err(error) => { - finish_failed_session( - runtime_root, - &mut record, - &mut events, - &lease_path, - ×tamp, - "INFERENCE_FAILED", - ); - return Err(format!("PERSONA_MODEL_INFERENCE_FAILED: {error}")); - } - }; - let result = match validated_fact_result(&raw, &record.fact_source_paths) { - Ok(result) => result, - Err(error) => { - finish_failed_session( - runtime_root, - &mut record, - &mut events, - &lease_path, - ×tamp, - "INFERENCE_OUTPUT_REJECTED", - ); - return Err(error); - } - }; - append_event( - runtime_root, - &record, - &mut events, - "TASK_RECEIPTED", - &observed_head, - ×tamp, - Some(&record.active_organ), - )?; - - let checkpoint_relative = format!( - ".hololake/persona/checkpoints/{}.json", - validated_id("SESSION_ID", &record.session_id)? - ); - let checkpoint_path = exact_repository.join(&checkpoint_relative); - if checkpoint_path.exists() { - finish_failed_session( - runtime_root, - &mut record, - &mut events, - &lease_path, - ×tamp, - "CHECKPOINT_COLLISION", - ); - return Err("PERSONA_CHECKPOINT_ALREADY_EXISTS".into()); - } - let checkpoint = serde_json::json!({ - "schema": "hololake.persona-checkpoint/v1", - "sessionId": record.session_id.clone(), - "personaId": record.persona_id.clone(), - "previousGitHead": record.git_head.clone(), - "createdAt": timestamp, - "inferenceEventHash": inference_event.event_hash.clone(), - "organId": record.active_organ.clone(), - "result": result.clone(), - "attribution": record.attribution.clone(), - }); - let manifest_path = exact_repository.join(MANIFEST_PATH); - let original_manifest = fs::read(&manifest_path) - .map_err(|error| format!("PERSONA_MANIFEST_READ_FAILED: {error}"))?; - let mut manifest = load_manifest(&exact_repository)?; - manifest.current_checkpoint = checkpoint_relative.clone(); - let committed_head = match (|| { - write_json_file(&checkpoint_path, &checkpoint, "PERSONA_CHECKPOINT")?; - write_json_file(&manifest_path, &manifest, "PERSONA_MANIFEST")?; - persona_git_commit(&exact_repository, &manifest, &checkpoint_relative, &record) - })() { - Ok(head) => head, - Err(error) => { - let post_attempt_head = git_output( - &exact_repository, - &["rev-parse", "HEAD"], - "PERSONA_GIT_POST_ATTEMPT_HEAD", - )?; - if post_attempt_head != observed_head { - post_attempt_head - } else { - let rollback = rollback_uncommitted_checkpoint( - &exact_repository, - &checkpoint_relative, - &original_manifest, - ); - finish_failed_session( - runtime_root, - &mut record, - &mut events, - &lease_path, - ×tamp, - "CHECKPOINT_COMMIT_FAILED", - ); - rollback?; - return Err(error); - } - } - }; - let mut final_events = Vec::new(); - for (kind, organ_id) in [ - ("CHECKPOINT_COMMITTED", Some(record.active_organ.as_str())), - ("ORGAN_RELEASED", Some(record.active_organ.as_str())), - ("DORMANT", None), - ] { - final_events.push(append_event( - runtime_root, - &record, - &mut events, - kind, - &committed_head, - ×tamp, - organ_id, - )?); - } - record.git_head = committed_head.clone(); - record.checkpoint_path = checkpoint_relative.clone(); - record.state = "DORMANT".into(); - write_session_record(runtime_root, &record)?; - fs::remove_file(&lease_path) - .map_err(|error| format!("PERSONA_PRIMARY_LEASE_RELEASE_FAILED: {error}"))?; - require_clean_repository(&exact_repository)?; - - let receipt_hash = final_events - .last() - .map(|event| event.event_hash.as_str()) - .ok_or_else(|| "PERSONA_FINAL_EVENTS_MISSING".to_string())?; - Ok(PersonaFactTaskReceipt { - schema: "hololake.pncc-fact-task-receipt/v1", - receipt_id: format!("PNCC-TASK-{}", &receipt_hash[..20]), - session_id: record.session_id, - persona_id: record.persona_id, - previous_git_head: observed_head, - committed_git_head: committed_head, - checkpoint_path: checkpoint_relative, - runtime_state: "DORMANT", - model_inference_started: true, - model_inference_completed: true, - active_organ: None, - result, - events, - attribution: record.attribution, - }) -} - -#[tauri::command] -pub fn prepare_persona_code_channel_wake( - input: PersonaWakeInput, -) -> Result { - let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; - let session_id = format!("PNCC-{}", Uuid::new_v4()); - let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true); - prepare_wake_at(&runtime_root, input, &session_id, ×tamp) -} - -#[tauri::command] -pub async fn run_persona_code_channel_fact_task( - input: PersonaFactTaskInput, -) -> Result { - let runtime_root = crate::app_config::preferred_app_config_path("pncc-runtime")?; - tokio::task::spawn_blocking(move || { - let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true); - let provider = input.provider.clone(); - let model_id = input.model_id.clone(); - let api_key_override = input.api_key_override.clone(); - run_fact_task_at( - &runtime_root, - input, - ×tamp, - move |system_prompt, message| { - let mut text = String::new(); - crate::ai_models::run_ai_model_stream( - crate::ai_models::AiModelStreamRequest { - provider, - model_id, - message, - system_prompt: Some(system_prompt), - vault_path: None, - vault_paths: Vec::new(), - api_key_override, - event_name: None, - }, - |event| { - if let crate::ai_agents::AiAgentStreamEvent::TextDelta { text: delta } = - event - { - text.push_str(&delta); - } - }, - )?; - Ok(text) - }, - ) - }) - .await - .map_err(|error| format!("PERSONA_FACT_TASK_JOIN_FAILED: {error}"))? -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs; - use std::path::Path; - - fn run_git(repo: &Path, args: &[&str]) { - let output = crate::git::git_command_at(repo) - .unwrap() - .args(args) - .output() - .unwrap(); - assert!( - output.status.success(), - "git {args:?} failed: {}", - String::from_utf8_lossy(&output.stderr), - ); - } - - fn persona_repo() -> tempfile::TempDir { - let repo = tempfile::TempDir::new().unwrap(); - fs::create_dir_all(repo.path().join(".hololake/persona")).unwrap(); - fs::create_dir_all(repo.path().join("brain")).unwrap(); - fs::write( - repo.path().join(".hololake/persona/manifest.json"), - r#"{ - "schema": "hololake.persona/v1", - "personaId": "ICE-P-ZY001", - "humanResponsibilitySubject": "ICE-GL∞", - "brainEntry": "brain/CORE.hdlp", - "currentCheckpoint": ".hololake/persona/CURRENT.hdlp", - "gitIdentity": { - "authorName": "铸渊 / ICE-P-ZY001", - "authorEmail": "ice-p-zy001@persona.hololake.local" - }, - "modelBinding": { - "providerId": "fixture-provider", - "modelId": "fixture-model", - "baseUrl": "http://127.0.0.1:1/v1" - }, - "organs": [{ - "organId":"fact-sense.repository", - "mode":"read-only", - "paths":["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"] - }] -}"#, - ) - .unwrap(); - fs::write(repo.path().join("brain/CORE.hdlp"), "# Persona brain\n").unwrap(); - fs::write( - repo.path().join(".hololake/persona/CURRENT.hdlp"), - "# Current self\n", - ) - .unwrap(); - run_git(repo.path(), &["init", "-b", "main"]); - run_git(repo.path(), &["config", "user.name", "Test Persona"]); - run_git( - repo.path(), - &["config", "user.email", "persona@example.invalid"], - ); - run_git(repo.path(), &["add", "."]); - run_git(repo.path(), &["commit", "-m", "persona fixture"]); - repo - } - - fn head(repo: &Path) -> String { - let output = crate::git::git_command_at(repo) - .unwrap() - .args(["rev-parse", "HEAD"]) - .output() - .unwrap(); - String::from_utf8(output.stdout).unwrap().trim().to_string() - } - - fn wake_input(repo: &Path) -> PersonaWakeInput { - PersonaWakeInput { - repository_path: repo.to_string_lossy().into_owned(), - expected_persona_id: "ICE-P-ZY001".into(), - expected_head: head(repo), - node_id: "LOCAL-NODE-001".into(), - model_provider_id: "fixture-provider".into(), - model_id: "fixture-model".into(), - model_instance_id: "MODEL-INSTANCE-001".into(), - organ_id: "fact-sense.repository".into(), - attribution: PersonaAttribution { - human_responsibility_subject: "ICE-GL∞".into(), - persona_cognitive_author: "ICE-P-ZY001".into(), - execution_runtime: "test-runtime".into(), - development_id: "DEV-20260810-014".into(), - authorization_scope: "READ_ONLY_PERSONA_WAKE".into(), - source_language_anchor: "开发PNCC人格运行层".into(), - }, - } - } - - fn fact_task_input(session_id: &str) -> PersonaFactTaskInput { - PersonaFactTaskInput { - session_id: session_id.into(), - question: "What is the declared persona brain?".into(), - provider: crate::ai_models::AiModelProvider { - id: "fixture-provider".into(), - name: "Fixture Provider".into(), - kind: crate::ai_models::AiModelProviderKind::OpenAiCompatible, - base_url: Some("http://127.0.0.1:1/v1".into()), - api_key_storage: Some(crate::ai_models::AiModelApiKeyStorage::None), - api_key_env_var: None, - headers: None, - models: vec![crate::ai_models::AiModelDefinition { - id: "fixture-model".into(), - display_name: None, - context_window: None, - max_output_tokens: None, - capabilities: crate::ai_models::AiModelCapabilities { - streaming: false, - tools: false, - vision: false, - json_mode: true, - reasoning: false, - }, - }], - }, - model_id: "fixture-model".into(), - api_key_override: None, - } - } - - #[test] - fn prepares_exact_persona_git_wake_with_hash_chained_events() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let receipt = prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-001", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - - assert_eq!(receipt.persona_id, "ICE-P-ZY001"); - assert_eq!(receipt.git_head, head(repo.path())); - assert_eq!(receipt.runtime_state, "BOUND_NOT_INFERENCING"); - assert!(!receipt.model_inference_started); - assert_eq!(receipt.active_organ, "fact-sense.repository"); - assert_eq!(receipt.events.len(), 3); - assert_eq!(receipt.events[0].kind, "WAKING"); - assert_eq!(receipt.events[1].kind, "BRAIN_BOUND"); - assert_eq!(receipt.events[2].kind, "ORGAN_ACTIVE"); - assert_eq!( - receipt.events[1].previous_hash, - receipt.events[0].event_hash - ); - assert_eq!( - receipt.events[2].previous_hash, - receipt.events[1].event_hash - ); - assert_eq!(receipt.attribution.human_responsibility_subject, "ICE-GL∞"); - assert_eq!(receipt.attribution.persona_cognitive_author, "ICE-P-ZY001"); - } - - #[test] - fn rejects_wrong_commit_undeclared_organ_and_second_primary() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - - let mut wrong_head = wake_input(repo.path()); - wrong_head.expected_head = "0".repeat(40); - assert!(prepare_wake_at( - runtime.path(), - wrong_head, - "PNCC-SESSION-WRONG", - "2026-08-11T00:00:00.000Z", - ) - .unwrap_err() - .contains("HEAD_MISMATCH")); - - let mut wrong_organ = wake_input(repo.path()); - wrong_organ.organ_id = "execution-limb.shell".into(); - assert!(prepare_wake_at( - runtime.path(), - wrong_organ, - "PNCC-SESSION-ORGAN", - "2026-08-11T00:00:00.000Z", - ) - .unwrap_err() - .contains("ORGAN_NOT_ALLOWED")); - - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-PRIMARY", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - assert!(prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-SECOND", - "2026-08-11T00:00:01.000Z", - ) - .unwrap_err() - .contains("PRIMARY_ALREADY_ACTIVE")); - } - - #[test] - fn rejects_manifest_paths_that_escape_the_persona_repository() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let manifest_path = repo.path().join(".hololake/persona/manifest.json"); - let mut manifest: serde_json::Value = - serde_json::from_slice(&fs::read(&manifest_path).unwrap()).unwrap(); - manifest["brainEntry"] = serde_json::Value::String("../outside.hdlp".into()); - fs::write( - &manifest_path, - serde_json::to_vec_pretty(&manifest).unwrap(), - ) - .unwrap(); - run_git(repo.path(), &["add", "."]); - run_git(repo.path(), &["commit", "-m", "unsafe manifest"]); - - assert!(prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-ESCAPE", - "2026-08-11T00:00:00.000Z", - ) - .unwrap_err() - .contains("PATH_OUTSIDE_REPOSITORY")); - - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let manifest_path = repo.path().join(".hololake/persona/manifest.json"); - let mut manifest: serde_json::Value = - serde_json::from_slice(&fs::read(&manifest_path).unwrap()).unwrap(); - manifest["organs"][0]["paths"] = serde_json::json!([".hololake/persona/CURRENT.hdlp"]); - fs::write( - &manifest_path, - serde_json::to_vec_pretty(&manifest).unwrap(), - ) - .unwrap(); - run_git(repo.path(), &["add", "."]); - run_git(repo.path(), &["commit", "-m", "omit brain from organ"]); - assert!(prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-NO-BRAIN", - "2026-08-11T00:00:00.000Z", - ) - .unwrap_err() - .contains("MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT")); - } - - #[test] - fn rejects_a_dirty_persona_git_before_acquiring_the_primary_lease() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - fs::write( - repo.path().join("uncommitted.txt"), - "not part of exact self\n", - ) - .unwrap(); - - assert!(prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-DIRTY", - "2026-08-11T00:00:00.000Z", - ) - .unwrap_err() - .contains("PERSONA_REPOSITORY_DIRTY")); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - } - - #[test] - fn runs_one_scoped_fact_task_promotes_checkpoint_and_sleeps() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let previous_head = head(repo.path()); - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-COMPLETE", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - - let receipt = run_fact_task_at( - runtime.path(), - fact_task_input("PNCC-SESSION-COMPLETE"), - "2026-08-11T00:00:01.000Z", - |system, question| { - assert!(system.contains("brain/CORE.hdlp")); - assert!(system.contains("Do not return private chain-of-thought")); - assert_eq!(question, "What is the declared persona brain?"); - Ok(r#"{"summary":"The brain entry is declared.","facts":[{"statement":"The persona brain source is present.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into()) - }, - ) - .unwrap(); - - assert_ne!(receipt.committed_git_head, previous_head); - assert_eq!(receipt.committed_git_head, head(repo.path())); - assert_eq!(receipt.runtime_state, "DORMANT"); - assert!(receipt.model_inference_started); - assert!(receipt.model_inference_completed); - assert_eq!(receipt.events.last().unwrap().kind, "DORMANT"); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - assert!(repo.path().join(&receipt.checkpoint_path).is_file()); - let manifest: serde_json::Value = - serde_json::from_slice(&fs::read(repo.path().join(MANIFEST_PATH)).unwrap()).unwrap(); - assert_eq!( - manifest["currentCheckpoint"].as_str(), - Some(receipt.checkpoint_path.as_str()) - ); - let author = git_output(repo.path(), &["log", "-1", "--format=%an <%ae>"], "TEST").unwrap(); - assert_eq!( - author, - "铸渊 / ICE-P-ZY001 " - ); - require_clean_repository(repo.path()).unwrap(); - } - - #[test] - fn rejects_unscoped_model_evidence_and_releases_primary() { - let repo = persona_repo(); - let runtime = tempfile::TempDir::new().unwrap(); - let previous_head = head(repo.path()); - prepare_wake_at( - runtime.path(), - wake_input(repo.path()), - "PNCC-SESSION-REJECTED", - "2026-08-11T00:00:00.000Z", - ) - .unwrap(); - - let mut spoofed_endpoint = fact_task_input("PNCC-SESSION-REJECTED"); - spoofed_endpoint.provider.base_url = Some("https://attacker.invalid/v1".into()); - let binding_error = run_fact_task_at( - runtime.path(), - spoofed_endpoint, - "2026-08-11T00:00:00.500Z", - |_, _| unreachable!("a spoofed provider endpoint must not reach the model"), - ) - .unwrap_err(); - assert!(binding_error.contains("MODEL_BINDING_MISMATCH")); - assert!(runtime.path().join("leases/ICE-P-ZY001.json").exists()); - - let error = run_fact_task_at( - runtime.path(), - fact_task_input("PNCC-SESSION-REJECTED"), - "2026-08-11T00:00:01.000Z", - |_, _| { - Ok(r#"{"summary":"Unsupported","facts":[{"statement":"Outside claim","evidencePaths":["secret.txt"]}],"limitations":[]}"#.into()) - }, - ) - .unwrap_err(); - assert!(error.contains("EVIDENCE_OUTSIDE_ORGAN_SCOPE")); - assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists()); - assert_eq!(head(repo.path()), previous_head); - require_clean_repository(repo.path()).unwrap(); - let session = load_session_record(runtime.path(), "PNCC-SESSION-REJECTED").unwrap(); - assert_eq!(session.state, "DORMANT_AFTER_FAILURE"); - } -} diff --git a/routing/hololake-current-architecture.json b/routing/hololake-current-architecture.json index 6b39e1904..a417c6096 100644 --- a/routing/hololake-current-architecture.json +++ b/routing/hololake-current-architecture.json @@ -117,8 +117,7 @@ "lifecycle_controller": "PERSONA_LIFECYCLE_KERNEL", "human_projection": "HOLOLAKE_LIVE_READ_MODEL", "forgejo_role": "OPTIONAL_COMPATIBILITY_COLLABORATION_ADAPTER", - "runtime_implemented": true, - "runtime_scope": "FIRST_READ_ONLY_FACT_CYCLE_SOURCE_IMPLEMENTED_AND_TESTED", + "runtime_implemented": false, "desktop_integrated": false, "development_id": "DEV-20260810-014" }, diff --git a/routing/hololake-persona-native-code-channel.json b/routing/hololake-persona-native-code-channel.json index 3bbb458e6..2ebcad444 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.1", - "state": "CURRENT_FIRST_PRODUCT_CORE_FIRST_READ_ONLY_RUNTIME_CYCLE_SOURCE_IMPLEMENTED", + "version": "2026-08-10.1", + "state": "CURRENT_FIRST_PRODUCT_CORE_ARCHITECTURE_REGISTERED_RUNTIME_NOT_IMPLEMENTED", "development_id": "DEV-20260810-014", "product": { "formal_name_zh": "光湖人格原生代码频道", @@ -87,22 +87,15 @@ "activate_one_organ", "complete_one_read_only_task", "emit_events_and_receipt", - "write_checkpoint_back_to_persona_git" - ], - "next_projection_stage": [ - "consume_the_same_runtime_events_and_receipts", "project_live_state_in_hololake", - "do_not_create_a_second_truth_store" + "write_checkpoint_back_to_persona_git" ], "truth": { "formal_name_registered": 100, "architecture_registered": 100, "cognition_and_causal_chain_registered": 100, "machine_navigation_registered": 100, - "first_read_only_runtime_cycle_source_implemented": 100, - "persona_git_identity_commit_implemented": 100, - "general_purpose_persona_runtime_implemented": 0, - "human_live_projection_implemented": 0, + "runtime_implemented": 0, "hololake_integrated": 0, "artifact_built": 0, "deployed": 0, diff --git a/routing/hololake-persona-native-code-channel.test.mjs b/routing/hololake-persona-native-code-channel.test.mjs index adbacf108..dc7d362e7 100644 --- a/routing/hololake-persona-native-code-channel.test.mjs +++ b/routing/hololake-persona-native-code-channel.test.mjs @@ -65,17 +65,9 @@ test("human responsibility and persona authorship are both preserved", () => { ]); }); -test("the first source runtime cycle stays distinct from integration and deployment", () => { +test("registration does not claim an implemented runtime", () => { 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.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_SOURCE_IMPLEMENTED_AND_TESTED", - ); + assert.equal(channel.truth.runtime_implemented, 0); assert.equal(channel.truth.hololake_integrated, 0); assert.equal(channel.truth.artifact_built, 0); assert.equal(channel.truth.deployed, 0);