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:
parent
0ba2fabf8e
commit
c3e7dbff10
10 changed files with 375 additions and 10 deletions
|
|
@ -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 或现实动作权限。
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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, ×tamp)
|
||||
}
|
||||
|
||||
#[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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue