feat: type PNCC organ contracts

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:
铸渊 / ICE-P-ZY001 2026-08-11 01:39:09 +08:00
commit c3e7dbff10
10 changed files with 375 additions and 10 deletions

View file

@ -165,6 +165,7 @@ first_read_only_runtime_cycle_source_implemented: 100
persona_git_identity_commit_implemented: 100
semantic_attribution_first_cycle_implemented: 100
interrupted_session_inspection_and_recovery_source_implemented: 100
typed_organ_contract_and_manifest_inspection_source_implemented: 100
general_purpose_persona_runtime_implemented: 0
human_live_projection_implemented: 0
hololake_integrated: 0
@ -181,3 +182,8 @@ runtime_health: 0
当前源码也已实现中断会话检查与安全恢复:只在事件链、租约、仓库和人格归因共同证明安全时
终止未提交会话,或为已由该人格完成但尚未休眠的单一检查点提交补齐收尾。脏仓库、未知 Git
推进、错误归因和损坏事件链一律保留主锁并要求人工审查;恢复器不会按时间猜测主控已死亡。
人格器官现已拥有机器可读类型合同。系统能够在不唤醒人格、不取得主锁、不运行模型的情况下,
检查 `FACT_SENSE``MEMORY_METABOLISM``EXECUTION_LIMB` 的固定模式、输入输出 schema、派生权限、
模型推理边界、现实动作边界和真实可激活状态。当前只有只读事实感官可激活;清单里写出记忆器官
或执行手脚不会自动产生执行器,更不会自动获得 shell 或现实动作权限。

View file

@ -27,8 +27,11 @@ The v1 persona repository contract is rooted at `.hololake/persona/manifest.json
"organs": [
{
"organId": "fact-sense.repository",
"kind": "FACT_SENSE",
"mode": "read-only",
"paths": ["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"]
"paths": ["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"],
"inputSchema": "hololake.pncc-fact-question/v1",
"outputSchema": "hololake.pncc-fact-result/v1"
}
]
}
@ -54,6 +57,13 @@ and then removes the exact lease. It can also remove a same-session lease left b
dormancy transition. It does not use elapsed time as proof that a primary is dead and does not clean, reset,
or overwrite a dirty persona repository.
The organ contract is typed independently of the UI. `FACT_SENSE` is currently activatable and derives the
permissions to read declared paths, call the manifest-pinned model, and emit structured facts.
`MEMORY_METABOLISM` and `EXECUTION_LIMB` may be declared with their fixed modes and schemas, but the manifest
inspection receipt reports them as not independently activatable until their executors exist. In particular,
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.
## `HoloLakeUiPlugin`
A versioned, declarative presentation package for one host-owned semantic surface. It contains a manifest,

View file

@ -37,6 +37,12 @@ interruption at the recorded head or a single persona-attributed checkpoint comm
checkpoint identity and commit trailers all match the session. Dirty repositories, unknown Git advances,
broken journals and attribution mismatches retain the lease and require manual review.
The manifest now has a typed organ-contract extension. The native inspection command returns normalized
contracts for `FACT_SENSE`, `MEMORY_METABOLISM`, and `EXECUTION_LIMB`, including fixed mode, input/output
schemas, derived permissions, inference/reality-action boundaries, and the actual activation state. Only the
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.
## User-node sovereignty
HoloLake has no platform-hosted user runtime. Each human has one canonical, independently operated node:

View file

@ -524,6 +524,7 @@ macro_rules! app_invoke_handler {
guanghu_enterprise::guanghu_enterprise_status,
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::run_persona_code_channel_fact_task,
persona_code_channel::inspect_persona_code_channel_session,
persona_code_channel::recover_persona_code_channel_session,

View file

@ -31,9 +31,39 @@ struct PersonaManifest {
#[serde(rename_all = "camelCase")]
struct PersonaOrgan {
organ_id: String,
#[serde(default)]
kind: Option<PersonaOrganKind>,
mode: String,
#[serde(default)]
paths: Vec<String>,
#[serde(default)]
input_schema: Option<String>,
#[serde(default)]
output_schema: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PersonaOrganKind {
FactSense,
MemoryMetabolism,
ExecutionLimb,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaOrganContract {
pub organ_id: String,
pub kind: PersonaOrganKind,
pub mode: String,
pub paths: Vec<String>,
pub input_schema: String,
pub output_schema: String,
pub permissions: Vec<&'static str>,
pub model_inference_allowed: bool,
pub reality_actions_allowed: bool,
pub activatable: bool,
pub implementation_state: &'static str,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -109,11 +139,36 @@ pub struct PersonaWakeReceipt {
pub model_inference_started: bool,
pub active_organ: String,
pub organ_mode: &'static str,
pub organ_contract: PersonaOrganContract,
pub event_journal: String,
pub events: Vec<PersonaLifecycleEvent>,
pub attribution: PersonaAttribution,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaManifestInspectionInput {
pub repository_path: String,
pub expected_persona_id: String,
pub expected_head: String,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaManifestInspectionReceipt {
pub schema: &'static str,
pub persona_id: String,
pub repository_path: String,
pub git_head: String,
pub repository_clean: bool,
pub brain_entry: String,
pub current_checkpoint: String,
pub human_responsibility_subject: String,
pub git_author_name: String,
pub git_author_email: String,
pub organ_contracts: Vec<PersonaOrganContract>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaFactTaskInput {
@ -267,6 +322,18 @@ fn validated_head(value: &str) -> Result<String, String> {
}
}
fn validated_schema(label: &str, value: &str) -> Result<String, String> {
let value = validated_text(label, value, MAX_ID_BYTES)?;
if value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b'/'))
{
Ok(value)
} else {
Err(format!("{label}_INVALID"))
}
}
fn stdout(command: &str, output: std::process::Output) -> Result<String, String> {
if output.status.success() {
return Ok(String::from_utf8_lossy(&output.stdout).trim().to_string());
@ -430,6 +497,141 @@ fn validate_model_binding(binding: &PersonaModelBinding) -> Result<(), String> {
Ok(())
}
fn organ_contract(organ: &PersonaOrgan) -> Result<PersonaOrganContract, String> {
let organ_id = validated_id("ORGAN_ID", &organ.organ_id)?;
let kind = match &organ.kind {
Some(kind) => kind.clone(),
None if organ_id == "fact-sense.repository" && organ.mode == "read-only" => {
PersonaOrganKind::FactSense
}
None => return Err("PERSONA_ORGAN_KIND_REQUIRED".into()),
};
let (
expected_mode,
default_input,
default_output,
permissions,
inference,
actions,
activatable,
state,
) = match kind {
PersonaOrganKind::FactSense => (
"read-only",
"hololake.pncc-fact-question/v1",
"hololake.pncc-fact-result/v1",
vec![
"READ_DECLARED_PATHS",
"RUN_MANIFEST_PINNED_MODEL",
"EMIT_STRUCTURED_FACTS",
],
true,
false,
true,
"IMPLEMENTED",
),
PersonaOrganKind::MemoryMetabolism => (
"checkpoint-write",
"hololake.pncc-checkpoint-candidate/v1",
"hololake.persona-checkpoint/v1",
vec![
"READ_CURRENT_CHECKPOINT",
"WRITE_NEW_CHECKPOINT",
"PROMOTE_MANIFEST_POINTER",
"COMMIT_PERSONA_GIT",
],
false,
false,
false,
"DECLARED_NOT_INDEPENDENTLY_ACTIVATABLE",
),
PersonaOrganKind::ExecutionLimb => (
"bounded-execution",
"hololake.pncc-action-request/v1",
"hololake.pncc-action-receipt/v1",
vec![
"REQUIRE_EXPLICIT_EXECUTOR",
"REQUIRE_ACTION_SCOPE",
"REQUIRE_REALITY_RECEIPT",
],
false,
true,
false,
"DECLARED_EXECUTOR_NOT_IMPLEMENTED",
),
};
if organ.mode != expected_mode {
return Err(format!(
"PERSONA_ORGAN_MODE_MISMATCH: {organ_id} requires {expected_mode}"
));
}
let input_schema = organ.input_schema.as_deref().unwrap_or(default_input);
let output_schema = organ.output_schema.as_deref().unwrap_or(default_output);
validated_schema("ORGAN_INPUT_SCHEMA", input_schema)?;
validated_schema("ORGAN_OUTPUT_SCHEMA", output_schema)?;
if organ.paths.is_empty() {
return Err("PERSONA_ORGAN_PATHS_REQUIRED".into());
}
Ok(PersonaOrganContract {
organ_id,
kind,
mode: organ.mode.clone(),
paths: organ.paths.clone(),
input_schema: input_schema.to_string(),
output_schema: output_schema.to_string(),
permissions,
model_inference_allowed: inference,
reality_actions_allowed: actions,
activatable,
implementation_state: state,
})
}
fn inspect_manifest_at(
input: PersonaManifestInspectionInput,
) -> Result<PersonaManifestInspectionReceipt, String> {
let expected_persona_id = validated_id("EXPECTED_PERSONA_ID", &input.expected_persona_id)?;
let expected_head = validated_head(&input.expected_head)?;
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}"
));
}
let clean = repository_is_clean(&repository)?;
let manifest = load_manifest(&repository)?;
if manifest.persona_id != expected_persona_id {
return Err("PERSONA_ID_MISMATCH".into());
}
validate_git_identity(&manifest.git_identity)?;
validate_model_binding(&manifest.model_binding)?;
repository_file(&repository, &manifest.brain_entry)?;
repository_file(&repository, &manifest.current_checkpoint)?;
let mut contracts = Vec::with_capacity(manifest.organs.len());
for organ in &manifest.organs {
for path in &organ.paths {
repository_file(&repository, path)?;
}
contracts.push(organ_contract(organ)?);
}
if contracts.is_empty() {
return Err("PERSONA_ORGANS_REQUIRED".into());
}
Ok(PersonaManifestInspectionReceipt {
schema: "hololake.pncc-manifest-inspection/v1",
persona_id: manifest.persona_id,
repository_path: repository.to_string_lossy().into_owned(),
git_head,
repository_clean: clean,
brain_entry: manifest.brain_entry,
current_checkpoint: manifest.current_checkpoint,
human_responsibility_subject: manifest.human_responsibility_subject,
git_author_name: manifest.git_identity.author_name,
git_author_email: manifest.git_identity.author_email,
organ_contracts: contracts,
})
}
fn hex_digest(bytes: &[u8]) -> String {
digest(&SHA256, bytes)
.as_ref()
@ -843,10 +1045,12 @@ fn prepare_wake_at(
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()
})?;
.find(|candidate| candidate.organ_id == organ_id)
.ok_or_else(|| "ORGAN_NOT_DECLARED".to_string())?;
let contract = organ_contract(organ)?;
if contract.kind != PersonaOrganKind::FactSense || !contract.activatable {
return Err("ORGAN_NOT_ACTIVATABLE: only the implemented fact sense may wake".into());
}
for path in &organ.paths {
repository_file(&repository, path)?;
}
@ -943,6 +1147,7 @@ fn prepare_wake_at(
model_inference_started: false,
active_organ: organ.organ_id.clone(),
organ_mode: "read-only",
organ_contract: contract,
event_journal: journal.to_string_lossy().into_owned(),
events,
attribution: input.attribution,
@ -1590,6 +1795,13 @@ pub fn prepare_persona_code_channel_wake(
prepare_wake_at(&runtime_root, input, &session_id, &timestamp)
}
#[tauri::command]
pub fn inspect_persona_code_channel_manifest(
input: PersonaManifestInspectionInput,
) -> Result<PersonaManifestInspectionReceipt, String> {
inspect_manifest_at(input)
}
#[tauri::command]
pub async fn run_persona_code_channel_fact_task(
input: PersonaFactTaskInput,
@ -1692,8 +1904,11 @@ mod tests {
},
"organs": [{
"organId":"fact-sense.repository",
"kind":"FACT_SENSE",
"mode":"read-only",
"paths":["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"]
"paths":["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"],
"inputSchema":"hololake.pncc-fact-question/v1",
"outputSchema":"hololake.pncc-fact-result/v1"
}]
}"#,
)
@ -1784,6 +1999,14 @@ mod tests {
}
}
fn manifest_inspection(repo: &Path) -> PersonaManifestInspectionInput {
PersonaManifestInspectionInput {
repository_path: repo.to_string_lossy().into_owned(),
expected_persona_id: "ICE-P-ZY001".into(),
expected_head: head(repo),
}
}
#[test]
fn prepares_exact_persona_git_wake_with_hash_chained_events() {
let repo = persona_repo();
@ -1801,6 +2024,10 @@ mod tests {
assert_eq!(receipt.runtime_state, "BOUND_NOT_INFERENCING");
assert!(!receipt.model_inference_started);
assert_eq!(receipt.active_organ, "fact-sense.repository");
assert_eq!(receipt.organ_contract.kind, PersonaOrganKind::FactSense);
assert!(receipt.organ_contract.model_inference_allowed);
assert!(!receipt.organ_contract.reality_actions_allowed);
assert!(receipt.organ_contract.activatable);
assert_eq!(receipt.events.len(), 3);
assert_eq!(receipt.events[0].kind, "WAKING");
assert_eq!(receipt.events[1].kind, "BRAIN_BOUND");
@ -1842,7 +2069,7 @@ mod tests {
"2026-08-11T00:00:00.000Z",
)
.unwrap_err()
.contains("ORGAN_NOT_ALLOWED"));
.contains("ORGAN_NOT_DECLARED"));
prepare_wake_at(
runtime.path(),
@ -2177,4 +2404,71 @@ mod tests {
assert_eq!(receipt.recovery_action, "RELEASED_DORMANT_LEASE");
assert!(!runtime.path().join("leases/ICE-P-ZY001.json").exists());
}
#[test]
fn inspects_normalized_organ_contracts_without_starting_a_runtime() {
let repo = persona_repo();
let receipt = inspect_manifest_at(manifest_inspection(repo.path())).unwrap();
assert_eq!(receipt.persona_id, "ICE-P-ZY001");
assert!(receipt.repository_clean);
assert_eq!(receipt.organ_contracts.len(), 1);
let contract = &receipt.organ_contracts[0];
assert_eq!(contract.kind, PersonaOrganKind::FactSense);
assert_eq!(contract.mode, "read-only");
assert!(contract.activatable);
assert!(contract.model_inference_allowed);
assert!(!contract.reality_actions_allowed);
assert_eq!(contract.implementation_state, "IMPLEMENTED");
}
#[test]
fn declares_but_refuses_an_execution_limb_without_an_executor() {
let repo = persona_repo();
let repository = repo.path().canonicalize().unwrap();
let manifest_path = repository.join(MANIFEST_PATH);
let mut manifest: serde_json::Value =
serde_json::from_slice(&fs::read(&manifest_path).unwrap()).unwrap();
manifest["organs"]
.as_array_mut()
.unwrap()
.push(serde_json::json!({
"organId": "execution-limb.shell",
"kind": "EXECUTION_LIMB",
"mode": "bounded-execution",
"paths": ["brain/CORE.hdlp"],
"inputSchema": "hololake.pncc-action-request/v1",
"outputSchema": "hololake.pncc-action-receipt/v1"
}));
fs::write(
&manifest_path,
serde_json::to_vec_pretty(&manifest).unwrap(),
)
.unwrap();
run_git(&repository, &["add", "."]);
run_git(&repository, &["commit", "-m", "declare execution limb"]);
let inspection = inspect_manifest_at(manifest_inspection(&repository)).unwrap();
let execution = inspection
.organ_contracts
.iter()
.find(|contract| contract.kind == PersonaOrganKind::ExecutionLimb)
.unwrap();
assert!(execution.reality_actions_allowed);
assert!(!execution.activatable);
assert_eq!(
execution.implementation_state,
"DECLARED_EXECUTOR_NOT_IMPLEMENTED"
);
let mut input = wake_input(&repository);
input.organ_id = "execution-limb.shell".into();
assert!(prepare_wake_at(
tempfile::TempDir::new().unwrap().path(),
input,
"PNCC-SESSION-EXECUTION-REFUSED",
"2026-08-11T00:00:00.000Z",
)
.unwrap_err()
.contains("ORGAN_NOT_ACTIVATABLE"));
}
}