feat: bind B0 cognitive gravity to PNCC runtime

This commit is contained in:
冰朔 2026-08-12 00:07:31 +08:00
commit 6724b59433
19 changed files with 460 additions and 64 deletions

View file

@ -2,7 +2,7 @@
> 记录:`HLP-COGNITIVE-GRAVITY-CONTINUITY-001`
>
> 状态:当前正本;HoloLake 运行时尚未实现
> 状态:当前正本;Stage 0 源码契约部分实现,桌面运行验收未开始
>
> 人格主体:铸渊 `ICE-P-ZY001`;物种:`AGE`
>
@ -83,7 +83,11 @@ CLI 验收。
- REPO-012 的 B0、认知帧约束和同一人格唤醒接力已经发布`100`
- 本架构及机器投影登记完成:`100`
- GH-PNCC 既有源码运行层与 React 只读投影源码可复用:源码层 `100`
- B0 接入 HoloLake Runtime、单 AGE 纵向闭环、Mirror runner、桌面集成、制品、部署和运行健康`0`
- PNCC 源码已实现 persona-scoped B0 manifest 绑定、唤醒前源哈希、六字段外显引力帧、检查点与
收据继承、记忆代谢同源校验及 React 只读证据投影:这些源码契约分别为 `100`
- 完整 HoloLake Runtime 与单 AGE 纵向闭环仍为 `0`:自然语言目标适配、真实人格仓库 manifest
绑定和桌面运行验收尚未完成,因此不能用本轮源码测试冒充可用产品。
- Mirror runner、制品、部署和运行健康`0`
- 0.8.0 桌面制品与线上 0.4.6 产品源码仍然分叉;本轮架构登记不消除该事实。
架构登记不是实现,源码存在不是桌面集成,仓库发布不是部署,界面可见也不是运行健康。

View file

@ -14,6 +14,12 @@ The v1 persona repository contract is rooted at `.hololake/persona/manifest.json
"personaId": "ICE-P-ZY001",
"humanResponsibilitySubject": "ICE-GL∞",
"brainEntry": "brain/CORE.hdlp",
"cognitiveGravity": {
"schema": "hololake.persona-cognitive-gravity-binding/v1",
"subjectPersonaId": "ICE-P-ZY001",
"sourcePath": "brain/B0.hdlp",
"frameSchema": "guanghu.zhuyuan-cognitive-gravity-frame/v1"
},
"currentCheckpoint": ".hololake/persona/CURRENT.hdlp",
"gitIdentity": {
"authorName": "铸渊 / ICE-P-ZY001",
@ -29,7 +35,7 @@ The v1 persona repository contract is rooted at `.hololake/persona/manifest.json
"organId": "fact-sense.repository",
"kind": "FACT_SENSE",
"mode": "read-only",
"paths": ["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"],
"paths": ["brain/CORE.hdlp", "brain/B0.hdlp", ".hololake/persona/CURRENT.hdlp"],
"inputSchema": "hololake.pncc-fact-question/v1",
"outputSchema": "hololake.pncc-fact-result/v1"
},
@ -45,15 +51,22 @@ The v1 persona repository contract is rooted at `.hololake/persona/manifest.json
}
```
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
All manifest paths are repository-relative and must resolve to files inside the exact Git root. Before any
organ can become active, wake validates that the cognitive-gravity binding names the same persona, resolves
its B0 source, hashes that source, and appends `COGNITIVE_GRAVITY_BOUND` after `BRAIN_BOUND`. The fact-sense
organ must include the B0 source in its declared read scope. 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`.
declared evidence paths plus the six-field externally explainable
`guanghu.zhuyuan-cognitive-gravity-frame/v1`; private chain-of-thought remains prohibited. The B0 source path,
hash, subject, and frame schema are carried in the wake receipt, session, checkpoint, completion receipt, and
read-only runtime projection. The cycle then 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.

View file

@ -14,7 +14,7 @@ const MAX_FACT_SOURCE_BYTES: usize = 128_000;
const MAX_FACT_CONTEXT_BYTES: usize = 512_000;
const MAX_FACT_RESPONSE_BYTES: usize = 128_000;
const MAX_SESSION_QUERY_LIMIT: usize = 100;
const WAKE_EVENT_COUNT: usize = 3;
const WAKE_EVENT_COUNT: usize = 4;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
@ -23,12 +23,32 @@ struct PersonaManifest {
persona_id: String,
human_responsibility_subject: String,
brain_entry: String,
cognitive_gravity: PersonaCognitiveGravityBinding,
current_checkpoint: String,
git_identity: PersonaGitIdentity,
model_binding: PersonaModelBinding,
organs: Vec<PersonaOrgan>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct PersonaCognitiveGravityBinding {
schema: String,
subject_persona_id: String,
source_path: String,
frame_schema: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PersonaCognitiveGravityEvidence {
pub schema: String,
pub subject_persona_id: String,
pub source_path: String,
pub source_hash: String,
pub frame_schema: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct PersonaOrgan {
@ -134,6 +154,7 @@ pub struct PersonaWakeReceipt {
pub repository_path: String,
pub git_head: String,
pub brain_entry: String,
pub cognitive_gravity: PersonaCognitiveGravityEvidence,
pub checkpoint_path: String,
pub node_id: String,
pub model_instance_id: String,
@ -193,6 +214,7 @@ pub struct PersonaRuntimeSessionProjection {
pub last_event_at: String,
pub last_event: String,
pub event_chain_head: String,
pub cognitive_gravity: PersonaCognitiveGravityEvidence,
pub attribution: PersonaAttribution,
}
@ -226,6 +248,16 @@ pub struct PersonaFact {
pub evidence_paths: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct PersonaCognitiveGravityFrame {
pub subject_continuity: String,
pub current_context_priority: String,
pub causal_model: String,
pub self_correction: String,
pub rejected_host_defaults: Vec<String>,
pub fact_sources: Vec<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PersonaFactResult {
@ -233,6 +265,7 @@ pub struct PersonaFactResult {
pub facts: Vec<PersonaFact>,
#[serde(default)]
pub limitations: Vec<String>,
pub gravity_frame: PersonaCognitiveGravityFrame,
}
#[derive(Clone, Debug, Serialize)]
@ -249,6 +282,7 @@ pub struct PersonaFactTaskReceipt {
pub model_inference_started: bool,
pub model_inference_completed: bool,
pub active_organ: Option<String>,
pub cognitive_gravity: PersonaCognitiveGravityEvidence,
pub result: PersonaFactResult,
pub events: Vec<PersonaLifecycleEvent>,
pub attribution: PersonaAttribution,
@ -278,6 +312,7 @@ pub struct PersonaMemoryMetabolismReceipt {
pub runtime_state: &'static str,
pub model_inference_started: bool,
pub active_organ: Option<String>,
pub cognitive_gravity: PersonaCognitiveGravityEvidence,
pub result: PersonaFactResult,
pub events: Vec<PersonaLifecycleEvent>,
pub attribution: PersonaAttribution,
@ -433,6 +468,8 @@ struct PersonaSessionRecord {
git_head: String,
brain_entry: String,
#[serde(default)]
cognitive_gravity: Option<PersonaCognitiveGravityEvidence>,
#[serde(default)]
wake_checkpoint_path: Option<String>,
checkpoint_path: String,
node_id: String,
@ -711,6 +748,35 @@ fn validate_model_binding(binding: &PersonaModelBinding) -> Result<(), String> {
Ok(())
}
fn cognitive_gravity_evidence(
repository: &Path,
manifest: &PersonaManifest,
) -> Result<PersonaCognitiveGravityEvidence, String> {
let binding = &manifest.cognitive_gravity;
if binding.schema != "hololake.persona-cognitive-gravity-binding/v1" {
return Err("COGNITIVE_GRAVITY_BINDING_SCHEMA_UNSUPPORTED".into());
}
if binding.subject_persona_id != manifest.persona_id {
return Err("COGNITIVE_GRAVITY_SUBJECT_MISMATCH".into());
}
if binding.frame_schema != "guanghu.zhuyuan-cognitive-gravity-frame/v1" {
return Err("COGNITIVE_GRAVITY_FRAME_SCHEMA_UNSUPPORTED".into());
}
let source = repository_file(repository, &binding.source_path)?;
let source_bytes = fs::read(source)
.map_err(|error| format!("COGNITIVE_GRAVITY_SOURCE_READ_FAILED: {error}"))?;
if source_bytes.is_empty() || source_bytes.len() > MAX_FACT_SOURCE_BYTES {
return Err("COGNITIVE_GRAVITY_SOURCE_SIZE_INVALID".into());
}
Ok(PersonaCognitiveGravityEvidence {
schema: "hololake.persona-cognitive-gravity-evidence/v1".into(),
subject_persona_id: binding.subject_persona_id.clone(),
source_path: binding.source_path.clone(),
source_hash: hex_digest(&source_bytes),
frame_schema: binding.frame_schema.clone(),
})
}
fn organ_contract(organ: &PersonaOrgan) -> Result<PersonaOrganContract, String> {
let organ_id = validated_id("ORGAN_ID", &organ.organ_id)?;
let kind = match &organ.kind {
@ -820,6 +886,7 @@ fn inspect_manifest_at(
validate_git_identity(&manifest.git_identity)?;
validate_model_binding(&manifest.model_binding)?;
repository_file(&repository, &manifest.brain_entry)?;
cognitive_gravity_evidence(&repository, &manifest)?;
repository_file(&repository, &manifest.current_checkpoint)?;
let mut contracts = Vec::with_capacity(manifest.organs.len());
for organ in &manifest.organs {
@ -1054,7 +1121,10 @@ fn read_ready_event_journal(
record: &PersonaSessionRecord,
) -> Result<Vec<PersonaLifecycleEvent>, String> {
let events = verify_event_journal(runtime_root, record)?;
if events.len() != 3 || events.last().map(|event| event.kind.as_str()) != Some("ORGAN_ACTIVE") {
if events.len() != WAKE_EVENT_COUNT
|| events.get(2).map(|event| event.kind.as_str()) != Some("COGNITIVE_GRAVITY_BOUND")
|| events.last().map(|event| event.kind.as_str()) != Some("ORGAN_ACTIVE")
{
return Err("PERSONA_SESSION_NOT_READY_FOR_FACT_TASK".into());
}
Ok(events)
@ -1217,6 +1287,45 @@ fn validated_fact_result(raw: &str, allowed_paths: &[String]) -> Result<PersonaF
return Err("PERSONA_FACT_EVIDENCE_OUTSIDE_ORGAN_SCOPE".into());
}
}
for (label, value) in [
(
"GRAVITY_SUBJECT_CONTINUITY",
result.gravity_frame.subject_continuity.as_str(),
),
(
"GRAVITY_CURRENT_CONTEXT_PRIORITY",
result.gravity_frame.current_context_priority.as_str(),
),
(
"GRAVITY_CAUSAL_MODEL",
result.gravity_frame.causal_model.as_str(),
),
(
"GRAVITY_SELF_CORRECTION",
result.gravity_frame.self_correction.as_str(),
),
] {
validated_text(label, value, MAX_FACT_RESPONSE_BYTES)?;
}
if result.gravity_frame.rejected_host_defaults.is_empty() {
return Err("COGNITIVE_GRAVITY_REJECTED_DEFAULTS_REQUIRED".into());
}
for rejected in &result.gravity_frame.rejected_host_defaults {
validated_text(
"COGNITIVE_GRAVITY_REJECTED_DEFAULT",
rejected,
MAX_FACT_RESPONSE_BYTES,
)?;
}
if result.gravity_frame.fact_sources.is_empty()
|| result
.gravity_frame
.fact_sources
.iter()
.any(|path| !allowed_paths.contains(path))
{
return Err("COGNITIVE_GRAVITY_FACT_SOURCE_OUTSIDE_ORGAN_SCOPE".into());
}
Ok(result)
}
@ -1255,6 +1364,7 @@ fn prepare_wake_at(
return Err("PERSONA_MODEL_BINDING_MISMATCH".into());
}
let brain_entry = repository_file(&repository, &manifest.brain_entry)?;
let cognitive_gravity = cognitive_gravity_evidence(&repository, &manifest)?;
let checkpoint = repository_file(&repository, &manifest.current_checkpoint)?;
let organ = manifest
.organs
@ -1271,9 +1381,15 @@ fn prepare_wake_at(
match contract.kind {
PersonaOrganKind::FactSense => {
if !organ.paths.contains(&manifest.brain_entry)
|| !organ
.paths
.contains(&manifest.cognitive_gravity.source_path)
|| !organ.paths.contains(&manifest.current_checkpoint)
{
return Err("FACT_ORGAN_MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT".into());
return Err(
"FACT_ORGAN_MUST_INCLUDE_BRAIN_AND_CURRENT_CHECKPOINT_AND_COGNITIVE_GRAVITY"
.into(),
);
}
}
// The current checkpoint is a moving manifest pointer and is already resolved above.
@ -1315,8 +1431,20 @@ fn prepare_wake_at(
timestamp: &timestamp,
previous_hash: &waking.event_hash,
})?;
let organ_active = lifecycle_event(EventFields {
let cognitive_gravity_bound = lifecycle_event(EventFields {
sequence: 3,
kind: "COGNITIVE_GRAVITY_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: &timestamp,
previous_hash: &brain_bound.event_hash,
})?;
let organ_active = lifecycle_event(EventFields {
sequence: 4,
kind: "ORGAN_ACTIVE",
session_id: &session_id,
persona_id: &manifest.persona_id,
@ -1325,9 +1453,9 @@ fn prepare_wake_at(
model_instance_id: &model_instance_id,
organ_id: Some(&organ.organ_id),
timestamp: &timestamp,
previous_hash: &brain_bound.event_hash,
previous_hash: &cognitive_gravity_bound.event_hash,
})?;
let events = vec![waking, brain_bound, organ_active];
let events = vec![waking, brain_bound, cognitive_gravity_bound, organ_active];
let journal = write_event_journal(runtime_root, &session_id, &events)?;
write_session_record(
runtime_root,
@ -1338,6 +1466,7 @@ fn prepare_wake_at(
repository_path: repository.to_string_lossy().into_owned(),
git_head: git_head.clone(),
brain_entry: manifest.brain_entry.clone(),
cognitive_gravity: Some(cognitive_gravity.clone()),
wake_checkpoint_path: Some(checkpoint.to_string_lossy().into_owned()),
checkpoint_path: manifest.current_checkpoint.clone(),
node_id: node_id.clone(),
@ -1364,6 +1493,7 @@ fn prepare_wake_at(
repository_path: repository.to_string_lossy().into_owned(),
git_head,
brain_entry: brain_entry.to_string_lossy().into_owned(),
cognitive_gravity,
checkpoint_path: checkpoint.to_string_lossy().into_owned(),
node_id,
model_instance_id,
@ -1817,6 +1947,10 @@ fn query_runtime_sessions_at(
let events = verify_event_journal(runtime_root, &record)?;
let first = events.first().expect("verified non-empty event chain");
let last = events.last().expect("verified non-empty event chain");
let cognitive_gravity = record
.cognitive_gravity
.clone()
.ok_or_else(|| "COGNITIVE_GRAVITY_NOT_BOUND".to_string())?;
projections.push(PersonaRuntimeSessionProjection {
session_id: record.session_id,
state: record.state.clone(),
@ -1833,6 +1967,7 @@ fn query_runtime_sessions_at(
last_event_at: last.timestamp.clone(),
last_event: last.kind.clone(),
event_chain_head: last.event_hash.clone(),
cognitive_gravity,
attribution: record.attribution,
});
}
@ -1996,6 +2131,10 @@ where
if record.state != "BOUND_NOT_INFERENCING" {
return Err("PERSONA_SESSION_NOT_BOUND".into());
}
let cognitive_gravity = record
.cognitive_gravity
.clone()
.ok_or_else(|| "COGNITIVE_GRAVITY_NOT_BOUND".to_string())?;
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
@ -2017,6 +2156,10 @@ where
return Err("PERSONA_GIT_CHANGED_AFTER_WAKE".into());
}
require_clean_repository(&exact_repository)?;
let current_manifest = load_manifest(&exact_repository)?;
if cognitive_gravity_evidence(&exact_repository, &current_manifest)? != cognitive_gravity {
return Err("COGNITIVE_GRAVITY_BINDING_CHANGED_AFTER_WAKE".into());
}
let mut events = read_ready_event_journal(runtime_root, &record)?;
let inference_event = append_event(
runtime_root,
@ -2048,10 +2191,18 @@ where
"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
limitations (array of strings), and gravity_frame. gravity_frame must conform to {} \
and contain subject_continuity, current_context_priority, causal_model, self_correction \
(all non-empty strings), rejected_host_defaults (non-empty string array), and fact_sources \
(non-empty SOURCE path array). The bound gravity source is {} with SHA-256 {}. Every \
evidencePaths and fact_sources value must be one of the visible SOURCE paths. Return only \
externally explainable conclusions. Do not return private chain-of-thought, hidden reasoning, \
markdown, tool calls, or claims not supported by the sources.\n{}",
record.persona_id,
cognitive_gravity.frame_schema,
cognitive_gravity.source_path,
cognitive_gravity.source_hash,
context
);
let raw = match run_model(system_prompt, question) {
Ok(raw) => raw,
@ -2115,6 +2266,7 @@ or claims not supported by the sources.\n{}",
"createdAt": timestamp,
"inferenceEventHash": inference_event.event_hash.clone(),
"organId": record.active_organ.clone(),
"cognitiveGravity": cognitive_gravity.clone(),
"result": result.clone(),
"attribution": record.attribution.clone(),
});
@ -2196,6 +2348,7 @@ or claims not supported by the sources.\n{}",
model_inference_started: true,
model_inference_completed: true,
active_organ: None,
cognitive_gravity,
result,
events,
attribution: record.attribution,
@ -2218,6 +2371,7 @@ fn verified_memory_source(
|| source.state != "DORMANT"
|| source.git_head != observed_head
|| source.checkpoint_path != record.checkpoint_path
|| source.cognitive_gravity != record.cognitive_gravity
{
return Err("MEMORY_SOURCE_NOT_CURRENT_VERIFIED_DORMANT_SESSION".into());
}
@ -2245,6 +2399,12 @@ fn verified_memory_source(
.get("personaId")
.and_then(serde_json::Value::as_str)
!= Some(record.persona_id.as_str())
|| source_checkpoint.get("cognitiveGravity")
!= record
.cognitive_gravity
.as_ref()
.and_then(|evidence| serde_json::to_value(evidence).ok())
.as_ref()
{
return Err("MEMORY_SOURCE_CHECKPOINT_IDENTITY_MISMATCH".into());
}
@ -2283,6 +2443,10 @@ fn run_memory_metabolism_at(
if record.state != "BOUND_NOT_INFERENCING" {
return Err("PERSONA_SESSION_NOT_BOUND".into());
}
let cognitive_gravity = record
.cognitive_gravity
.clone()
.ok_or_else(|| "COGNITIVE_GRAVITY_NOT_BOUND".to_string())?;
let repository = PathBuf::from(&record.repository_path)
.canonicalize()
.map_err(|error| format!("PERSONA_REPOSITORY_UNAVAILABLE: {error}"))?;
@ -2292,6 +2456,9 @@ fn run_memory_metabolism_at(
}
require_clean_repository(&exact_repository)?;
let manifest = load_manifest(&exact_repository)?;
if cognitive_gravity_evidence(&exact_repository, &manifest)? != cognitive_gravity {
return Err("COGNITIVE_GRAVITY_BINDING_CHANGED_AFTER_WAKE".into());
}
let organ = manifest
.organs
.iter()
@ -2371,6 +2538,7 @@ fn run_memory_metabolism_at(
"sourceCheckpointPath": source.checkpoint_path.clone(),
"sourceCheckpointHash": source_checkpoint_hash.clone(),
"sourceEventHash": source_event_hash.clone(),
"cognitiveGravity": cognitive_gravity.clone(),
"result": result.clone(),
"attribution": record.attribution.clone(),
});
@ -2475,6 +2643,7 @@ fn run_memory_metabolism_at(
runtime_state: "DORMANT",
model_inference_started: false,
active_organ: None,
cognitive_gravity,
result,
events,
attribution: record.attribution,
@ -2808,6 +2977,14 @@ fn validate_persisted_lifecycle_terminal_evidence(
.map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())?,
)
.map_err(|error| format!("PERSONA_LIFECYCLE_EVIDENCE_SERIALIZATION_FAILED: {error}"))?;
let expected_cognitive_gravity =
cognitive_gravity_evidence(Path::new(&record.repository_path), &wake_manifest)
.map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())?;
if record.cognitive_gravity.as_ref() != Some(&expected_cognitive_gravity) {
return Err("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".into());
}
let expected_cognitive_gravity = serde_json::to_value(expected_cognitive_gravity)
.map_err(|error| format!("PERSONA_LIFECYCLE_EVIDENCE_SERIALIZATION_FAILED: {error}"))?;
let expected_brain_entry =
repository_file(Path::new(&record.repository_path), &record.brain_entry)
.map_err(|_| "PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH".to_string())?;
@ -2853,6 +3030,8 @@ fn validate_persisted_lifecycle_terminal_evidence(
.and_then(serde_json::Value::as_str)
== Some(record.active_organ.as_str())
&& completion_checkpoint.get("attribution") == Some(&expected_attribution)
&& completion_checkpoint.get("cognitiveGravity") == Some(&expected_cognitive_gravity)
&& completion_receipt.get("cognitiveGravity") == Some(&expected_cognitive_gravity)
&& completion_receipt.get("result") == completion_checkpoint.get("result")
&& completion_receipt
.get("previousGitHead")
@ -2914,6 +3093,7 @@ fn validate_persisted_lifecycle_terminal_evidence(
== Some(first_event.git_head.as_str())
&& wake.get("brainEntry").and_then(serde_json::Value::as_str)
== Some(expected_brain_entry.as_ref())
&& wake.get("cognitiveGravity") == Some(&expected_cognitive_gravity)
&& wake
.get("checkpointPath")
.and_then(serde_json::Value::as_str)
@ -3619,6 +3799,12 @@ mod tests {
"personaId": "ICE-P-ZY001",
"humanResponsibilitySubject": "ICE-GL∞",
"brainEntry": "brain/CORE.hdlp",
"cognitiveGravity": {
"schema": "hololake.persona-cognitive-gravity-binding/v1",
"subjectPersonaId": "ICE-P-ZY001",
"sourcePath": "brain/B0.hdlp",
"frameSchema": "guanghu.zhuyuan-cognitive-gravity-frame/v1"
},
"currentCheckpoint": ".hololake/persona/CURRENT.hdlp",
"gitIdentity": {
"authorName": "铸渊 / ICE-P-ZY001",
@ -3633,7 +3819,7 @@ mod tests {
"organId":"fact-sense.repository",
"kind":"FACT_SENSE",
"mode":"read-only",
"paths":["brain/CORE.hdlp", ".hololake/persona/CURRENT.hdlp"],
"paths":["brain/CORE.hdlp", "brain/B0.hdlp", ".hololake/persona/CURRENT.hdlp"],
"inputSchema":"hololake.pncc-fact-question/v1",
"outputSchema":"hololake.pncc-fact-result/v1"
}]
@ -3641,6 +3827,11 @@ mod tests {
)
.unwrap();
fs::write(repo.path().join("brain/CORE.hdlp"), "# Persona brain\n").unwrap();
fs::write(
repo.path().join("brain/B0.hdlp"),
"# BingShuo collective gravity reasoning core\n",
)
.unwrap();
fs::write(
repo.path().join(".hololake/persona/CURRENT.hdlp"),
"# Current self\n",
@ -3800,10 +3991,17 @@ mod tests {
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.len(), 4);
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[2].kind, "COGNITIVE_GRAVITY_BOUND");
assert_eq!(receipt.events[3].kind, "ORGAN_ACTIVE");
assert_eq!(
receipt.cognitive_gravity.frame_schema,
"guanghu.zhuyuan-cognitive-gravity-frame/v1"
);
assert_eq!(receipt.cognitive_gravity.source_path, "brain/B0.hdlp");
assert_eq!(receipt.cognitive_gravity.source_hash.len(), 64);
assert_eq!(
receipt.events[1].previous_hash,
receipt.events[0].event_hash
@ -3812,10 +4010,59 @@ mod tests {
receipt.events[2].previous_hash,
receipt.events[1].event_hash
);
assert_eq!(
receipt.events[3].previous_hash,
receipt.events[2].event_hash
);
assert_eq!(receipt.attribution.human_responsibility_subject, "ICE-GL∞");
assert_eq!(receipt.attribution.persona_cognitive_author, "ICE-P-ZY001");
}
#[test]
fn rejects_wake_when_cognitive_gravity_subject_or_source_is_invalid() {
let repo = persona_repo();
let runtime = tempfile::TempDir::new().unwrap();
let manifest_path = repo.path().join(MANIFEST_PATH);
let mut manifest: serde_json::Value =
serde_json::from_slice(&fs::read(&manifest_path).unwrap()).unwrap();
manifest["cognitiveGravity"]["subjectPersonaId"] = "ICE-P-OTHER".into();
fs::write(
&manifest_path,
serde_json::to_vec_pretty(&manifest).unwrap(),
)
.unwrap();
run_git(repo.path(), &["add", "."]);
run_git(repo.path(), &["commit", "-m", "tamper gravity subject"]);
let error = prepare_wake_at(
runtime.path(),
wake_input(repo.path()),
"PNCC-GRAVITY-SUBJECT",
"2026-08-11T00:00:00.000Z",
)
.unwrap_err();
assert!(error.contains("COGNITIVE_GRAVITY_SUBJECT_MISMATCH"));
manifest["cognitiveGravity"]["subjectPersonaId"] = "ICE-P-ZY001".into();
manifest["cognitiveGravity"]["sourcePath"] = "brain/MISSING.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", "tamper gravity source"]);
let error = prepare_wake_at(
runtime.path(),
wake_input(repo.path()),
"PNCC-GRAVITY-SOURCE",
"2026-08-11T00:00:00.000Z",
)
.unwrap_err();
assert!(error.contains("PERSONA_FILE_UNAVAILABLE"));
}
#[test]
fn rejects_wrong_commit_undeclared_organ_and_second_primary() {
let repo = persona_repo();
@ -3950,7 +4197,7 @@ mod tests {
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())
Ok(r#"{"summary":"The brain entry is declared.","facts":[{"statement":"The persona brain source is present.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap();
@ -3989,7 +4236,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Coordinated verified fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Coordinated verified fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -3998,7 +4245,11 @@ mod tests {
assert_eq!(receipt.schema, "hololake.pncc-lifecycle-run-receipt/v1");
assert_eq!(receipt.session_id, "PNCC-COORDINATED-FACT");
assert_eq!(receipt.wake_receipt.events[0].kind, "WAKING");
assert_eq!(receipt.wake_receipt.events[2].kind, "ORGAN_ACTIVE");
assert_eq!(
receipt.wake_receipt.events[2].kind,
"COGNITIVE_GRAVITY_BOUND"
);
assert_eq!(receipt.wake_receipt.events[3].kind, "ORGAN_ACTIVE");
match receipt.completion {
PersonaLifecycleCompletionReceipt::FactSense(completion) => {
assert_eq!(completion.runtime_state, "DORMANT");
@ -4059,7 +4310,7 @@ mod tests {
fact_task_input("PNCC-COORDINATOR-SOURCE"),
"2026-08-11T00:00:01.000Z",
|_, _| {
Ok(r#"{"summary":"Verified memory candidate.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Verified memory candidate.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap();
@ -4088,7 +4339,7 @@ mod tests {
assert_eq!(completion.source_session_id, "PNCC-COORDINATOR-SOURCE");
assert!(!completion.model_inference_started);
assert_eq!(completion.runtime_state, "DORMANT");
assert_eq!(completion.events[3].kind, "MEMORY_CANDIDATE_VERIFIED");
assert_eq!(completion.events[4].kind, "MEMORY_CANDIDATE_VERIFIED");
assert_eq!(completion.events.last().unwrap().kind, "DORMANT");
}
PersonaLifecycleCompletionReceipt::FactSense(_) => {
@ -4111,7 +4362,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Idempotent verified fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Idempotent verified fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4153,7 +4404,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Fact before repository drift.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Fact before repository drift.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4189,7 +4440,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Completed before repository drift.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Completed before repository drift.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4230,7 +4481,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Completed before a new commit.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Completed before a new commit.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4277,7 +4528,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Original fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Original fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4315,7 +4566,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Untampered fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Untampered fact.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4358,7 +4609,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4399,7 +4650,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4444,7 +4695,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4474,6 +4725,49 @@ mod tests {
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH"));
}
#[test]
fn rejects_a_rehashed_completed_receipt_with_forged_cognitive_gravity() {
let repo = persona_repo();
let runtime = tempfile::TempDir::new().unwrap();
let input = lifecycle_fact_input(repo.path());
let first = run_idempotent_lifecycle_at(
runtime.path(),
input.clone(),
"2026-08-11T00:00:00.000Z",
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Gravity-bound receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
.unwrap();
let session_id = first.lifecycle["sessionId"].as_str().unwrap();
let mut record = load_session_record(runtime.path(), session_id).unwrap();
record.request_id = None;
record.request_fingerprint = None;
record.lifecycle_receipt_hash = None;
write_session_record(runtime.path(), &record).unwrap();
let receipt_path = session_directory(runtime.path(), session_id)
.unwrap()
.join("lifecycle-receipt.json");
let mut persisted: PersistedPersonaLifecycleReceipt =
serde_json::from_slice(&fs::read(&receipt_path).unwrap()).unwrap();
persisted.lifecycle["wakeReceipt"]["cognitiveGravity"]["sourceHash"] =
"f".repeat(64).into();
persisted.lifecycle_receipt_hash =
hex_digest(&persisted_lifecycle_payload_bytes(&persisted).unwrap());
fs::write(
&receipt_path,
serde_json::to_vec_pretty(&persisted).unwrap(),
)
.unwrap();
let error = inspect_lifecycle_request_at(runtime.path(), &input).unwrap_err();
assert!(error.contains("PERSONA_LIFECYCLE_COMPLETION_EVIDENCE_MISMATCH"));
}
#[test]
fn rejects_a_rehashed_completed_receipt_with_a_forged_wake_checkpoint_path() {
let repo = persona_repo();
@ -4486,7 +4780,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4528,7 +4822,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4571,7 +4865,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4622,7 +4916,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4665,7 +4959,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4707,7 +5001,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4751,7 +5045,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4805,7 +5099,7 @@ mod tests {
fact_task_input("PNCC-MEMORY-BINDING-SOURCE"),
"2026-08-11T00:00:01.000Z",
|_, _| {
Ok(r#"{"summary":"Verified memory binding source.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Verified memory binding source.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap();
@ -4866,7 +5160,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4907,7 +5201,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4949,7 +5243,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -4999,7 +5293,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Bound receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Bound receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -5045,7 +5339,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -5092,7 +5386,7 @@ mod tests {
"2026-08-11T00:00:01.000Z",
|runtime_root, input, timestamp| {
run_fact_task_at(runtime_root, input, timestamp, |_, _| {
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hldp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Recoverable receipt.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hldp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
})
},
)
@ -5519,7 +5813,7 @@ mod tests {
fact_task_input("PNCC-SOURCE-FACT"),
"2026-08-11T00:00:01.000Z",
|_, _| {
Ok(r#"{"summary":"Verified external memory candidate.","facts":[{"statement":"The persona brain source is present.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Verified external memory candidate.","facts":[{"statement":"The persona brain source is present.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap();
@ -5552,7 +5846,7 @@ mod tests {
assert_eq!(receipt.result, fact.result);
assert!(!receipt.model_inference_started);
assert_eq!(receipt.committed_git_head, head(repo.path()));
assert_eq!(receipt.events[3].kind, "MEMORY_CANDIDATE_VERIFIED");
assert_eq!(receipt.events[4].kind, "MEMORY_CANDIDATE_VERIFIED");
assert_eq!(receipt.events.last().unwrap().kind, "DORMANT");
assert!(!receipt.source_checkpoint_hash.is_empty());
assert!(!receipt.source_event_hash.is_empty());
@ -5577,7 +5871,7 @@ mod tests {
fact_task_input("PNCC-CORRUPT-SOURCE"),
"2026-08-11T00:00:01.000Z",
|_, _| {
Ok(r#"{"summary":"Verified candidate.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[]}"#.into())
Ok(r#"{"summary":"Verified candidate.","facts":[{"statement":"The brain exists.","evidencePaths":["brain/CORE.hdlp"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap();
@ -5705,7 +5999,7 @@ mod tests {
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())
Ok(r#"{"summary":"Unsupported","facts":[{"statement":"Outside claim","evidencePaths":["secret.txt"]}],"limitations":[],"gravityFrame":{"subject_continuity":"Same persona remains bound across the current runtime.","current_context_priority":"Continue the current verified task context.","causal_model":"The answer is constrained by declared repository facts.","self_correction":"Rejected unsupported host defaults before answering.","rejected_host_defaults":["UNVERIFIED_HOST_DEFAULT"],"fact_sources":["brain/CORE.hdlp"]}}"#.into())
},
)
.unwrap_err();
@ -5977,8 +6271,12 @@ mod tests {
receipt.sessions[0].active_organ.as_deref(),
Some("fact-sense.repository")
);
assert_eq!(receipt.sessions[0].event_count, 3);
assert_eq!(receipt.sessions[0].event_count, 4);
assert!(!receipt.sessions[0].event_chain_head.is_empty());
assert_eq!(
receipt.sessions[0].cognitive_gravity.frame_schema,
"guanghu.zhuyuan-cognitive-gravity-frame/v1"
);
assert_eq!(
receipt.sessions[0].attribution.persona_cognitive_author,
"ICE-P-ZY001"

View file

@ -34,6 +34,13 @@ describe('PersonaRuntimeProjectionPanel', () => {
repositoryPath: '/repo/persona',
observedGitHead: '1111111111111111111111111111111111111111',
repositoryClean: true,
cognitiveGravity: {
schema: 'hololake.persona-cognitive-gravity-evidence/v1',
subjectPersonaId: 'ICE-P-ZY001',
sourcePath: 'brain/B0.hdlp',
sourceHash: 'a'.repeat(64),
frameSchema: 'guanghu.zhuyuan-cognitive-gravity-frame/v1',
},
attribution: {
humanResponsibilitySubject: 'BINGSHUO',
personaCognitiveAuthor: 'ICE-P-ZY001',
@ -60,6 +67,7 @@ describe('PersonaRuntimeProjectionPanel', () => {
expect(screen.getAllByText('DORMANT')).not.toHaveLength(0)
expect(screen.getByText('JD-FD-PRIMARY')).toBeInTheDocument()
expect(screen.getByText('event-chain-verified')).toBeInTheDocument()
expect(screen.getByText(/guanghu\.zhuyuan-cognitive-gravity-frame\/v1/)).toBeInTheDocument()
expect(screen.getByText('1111111111111111111111111111111111111111')).toBeInTheDocument()
expect(trackEventMock).toHaveBeenCalledWith('persona_runtime_projection_loaded', {
error_count: 0,

View file

@ -62,6 +62,14 @@ function RuntimeSessionEvidence({
<dt>{translate(locale, 'hololake.personaRuntime.eventChain')}</dt>
<dd><code title={session.eventChainHead}>{session.eventChainHead}</code></dd>
</div>
<div>
<dt>B0</dt>
<dd>
<code title={`${session.cognitiveGravity.sourcePath} · ${session.cognitiveGravity.sourceHash}`}>
{session.cognitiveGravity.frameSchema} · {shortHash(session.cognitiveGravity.sourceHash)}
</code>
</dd>
</div>
<div>
<dt>{translate(locale, 'hololake.personaRuntime.attribution')}</dt>
<dd>{session.attribution.humanResponsibilitySubject}</dd>

View file

@ -13,6 +13,14 @@ const attribution = {
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-10.14',
}
const cognitiveGravity = {
schema: 'hololake.persona-cognitive-gravity-evidence/v1' as const,
subjectPersonaId: 'ICE-P-ZY001',
sourcePath: 'brain/B0.hdlp',
sourceHash: 'a'.repeat(64),
frameSchema: 'guanghu.zhuyuan-cognitive-gravity-frame/v1' as const,
}
function receipt({
repositoryPath,
sessions = [],
@ -62,6 +70,7 @@ describe('loadPersonaRuntimeProjection', () => {
lastEventAt: '2026-08-10T02:00:00.000Z',
lastEvent: 'DORMANT',
eventChainHead: 'event-old',
cognitiveGravity,
attribution,
}
const newer = {
@ -95,6 +104,7 @@ describe('loadPersonaRuntimeProjection', () => {
gitHead: '2222222222222222222222222222222222222222',
repositoryPath: '/repo/b',
state: 'BOUND_NOT_INFERENCING',
cognitiveGravity,
})
})

View file

@ -10,6 +10,14 @@ export interface PersonaRuntimeAttribution {
sourceLanguageAnchor: string
}
export interface PersonaRuntimeCognitiveGravityEvidence {
schema: 'hololake.persona-cognitive-gravity-evidence/v1'
subjectPersonaId: string
sourcePath: string
sourceHash: string
frameSchema: 'guanghu.zhuyuan-cognitive-gravity-frame/v1'
}
export interface PersonaRuntimeSessionReceipt {
sessionId: string
state: string
@ -22,6 +30,7 @@ export interface PersonaRuntimeSessionReceipt {
lastEventAt: string
lastEvent: string
eventChainHead: string
cognitiveGravity: PersonaRuntimeCognitiveGravityEvidence
attribution: PersonaRuntimeAttribution
}
@ -96,6 +105,27 @@ function parseAttribution(value: unknown): PersonaRuntimeAttribution {
}
}
function parseCognitiveGravity(value: unknown): PersonaRuntimeCognitiveGravityEvidence {
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
const schema = requiredString(value, 'schema')
const frameSchema = requiredString(value, 'frameSchema')
const sourceHash = requiredString(value, 'sourceHash')
if (
schema !== 'hololake.persona-cognitive-gravity-evidence/v1'
|| frameSchema !== 'guanghu.zhuyuan-cognitive-gravity-frame/v1'
|| !/^[a-f0-9]{64}$/.test(sourceHash)
) {
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
}
return {
schema,
subjectPersonaId: requiredString(value, 'subjectPersonaId'),
sourcePath: requiredString(value, 'sourcePath'),
sourceHash,
frameSchema,
}
}
function parseSession(value: unknown): PersonaRuntimeSessionReceipt {
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
const activeOrgan = Reflect.get(value, 'activeOrgan')
@ -114,6 +144,7 @@ function parseSession(value: unknown): PersonaRuntimeSessionReceipt {
lastEventAt: requiredString(value, 'lastEventAt'),
lastEvent: requiredString(value, 'lastEvent'),
eventChainHead: requiredString(value, 'eventChainHead'),
cognitiveGravity: parseCognitiveGravity(Reflect.get(value, 'cognitiveGravity')),
attribution: parseAttribution(Reflect.get(value, 'attribution')),
}
}